pick up wakaama files from https://github.com/eclipse/wakaama
core/json.c
- Committer:
- terencez
- Date:
- 2017-04-19
- Revision:
- 0:1fa43ab66921
File content as of revision 0:1fa43ab66921:
/******************************************************************************* * * Copyright (c) 2015 Intel Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * David Navarro, Intel Corporation - initial API and implementation * *******************************************************************************/ #include "internals.h" #include <stdlib.h> #include <string.h> #include <stdio.h> #include <inttypes.h> #ifdef LWM2M_SUPPORT_JSON #define PRV_JSON_BUFFER_SIZE 1024 #define JSON_MIN_ARRAY_LEN 21 // e":[{"n":"N","v":X}]} #define JSON_MIN_BASE_LEN 7 // n":"N", #define JSON_ITEM_MAX_SIZE 36 // with ten characters for value #define JSON_MIN_BX_LEN 5 // bt":1 #define JSON_FALSE_STRING "false" #define JSON_TRUE_STRING "true" #define JSON_RES_ITEM_URI "{\"n\":\"" #define JSON_RES_ITEM_URI_SIZE 6 #define JSON_ITEM_BOOL_TRUE "\",\"bv\":true}," #define JSON_ITEM_BOOL_TRUE_SIZE 13 #define JSON_ITEM_BOOL_FALSE "\",\"bv\":false}," #define JSON_ITEM_BOOL_FALSE_SIZE 14 #define JSON_ITEM_NUM "\",\"v\":" #define JSON_ITEM_NUM_SIZE 6 #define JSON_ITEM_NUM_END "}," #define JSON_ITEM_NUM_END_SIZE 2 #define JSON_ITEM_STRING_BEGIN "\",\"sv\":\"" #define JSON_ITEM_STRING_BEGIN_SIZE 8 #define JSON_ITEM_STRING_END "\"}," #define JSON_ITEM_STRING_END_SIZE 3 #define JSON_BN_HEADER_1 "{\"bn\":\"" #define JSON_BN_HEADER_1_SIZE 7 #define JSON_BN_HEADER_2 "\",\"e\":[" #define JSON_BN_HEADER_2_SIZE 7 #define JSON_HEADER "{\"e\":[" #define JSON_HEADER_SIZE 6 #define JSON_FOOTER "]}" #define JSON_FOOTER_SIZE 2 #define _GO_TO_NEXT_CHAR(I,B,L) \ { \ I++; \ I += prv_skipSpace(B+I, L-I); \ if (I == L) goto error; \ } typedef enum { _STEP_START, _STEP_TOKEN, _STEP_ANY_SEPARATOR, _STEP_SEPARATOR, _STEP_QUOTED_VALUE, _STEP_VALUE, _STEP_DONE } _itemState; typedef enum { _TYPE_UNSET, _TYPE_FALSE, _TYPE_TRUE, _TYPE_FLOAT, _TYPE_STRING } _type; typedef struct { uint16_t ids[4]; _type type; uint8_t * value; size_t valueLen; } _record_t; static int prv_isReserved(char sign) { if (sign == '[' || sign == '{' || sign == ']' || sign == '}' || sign == ':' || sign == ',' || sign == '"') { return 1; } return 0; } static int prv_isWhiteSpace(uint8_t sign) { if (sign == 0x20 || sign == 0x09 || sign == 0x0A || sign == 0x0D) { return 1; } return 0; } static size_t prv_skipSpace(uint8_t * buffer, size_t bufferLen) { size_t i; i = 0; while ((i < bufferLen) && prv_isWhiteSpace(buffer[i])) { i++; } return i; } static int prv_split(uint8_t * buffer, size_t bufferLen, int * tokenStartP, int * tokenLenP, int * valueStartP, int * valueLenP) { size_t index; _itemState step; index = 0; step = _STEP_START; index = prv_skipSpace(buffer + index, bufferLen - index); if (index == bufferLen) return -1; while ((index < bufferLen) && (buffer[index] != ',')) { switch (step) { case _STEP_START: if (buffer[index] != '"') return -1; *tokenStartP = index+1; step = _STEP_TOKEN; break; case _STEP_TOKEN: if (buffer[index] == '"') { *tokenLenP = index - *tokenStartP; step = _STEP_ANY_SEPARATOR; } break; case _STEP_ANY_SEPARATOR: if (buffer[index] != ':') return -1; step = _STEP_SEPARATOR; break; case _STEP_SEPARATOR: if (buffer[index] == '"') { *valueStartP = index; step = _STEP_QUOTED_VALUE; } else if (!prv_isReserved(buffer[index])) { *valueStartP = index; step = _STEP_VALUE; } else { return -1; } break; case _STEP_QUOTED_VALUE: if (buffer[index] == '"' && buffer[index-1] != '\\' ) { *valueLenP = index - *valueStartP + 1; step = _STEP_DONE; } break; case _STEP_VALUE: if (prv_isWhiteSpace(buffer[index])) { *valueLenP = index - *valueStartP; step = _STEP_DONE; } break; case _STEP_DONE: default: return -1; } index++; if (step == _STEP_START || step == _STEP_ANY_SEPARATOR || step == _STEP_SEPARATOR || step == _STEP_DONE) { index += prv_skipSpace(buffer + index, bufferLen - index); } } if (step == _STEP_VALUE) { *valueLenP = index - *valueStartP; step = _STEP_DONE; } if (step != _STEP_DONE) return -1; return (int)index; } static int prv_countItems(uint8_t * buffer, size_t bufferLen) { int count; size_t index; int in; count = 0; index = 0; in = 0; while (index < bufferLen) { if (in == 0) { if (buffer[index] != '{') return -1; in = 1; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); } else { if (buffer[index] == '{') return -1; if (buffer[index] == '}') { in = 0; count++; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); if (buffer[index] == ']') { break; } if (buffer[index] != ',') return -1; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); } else { _GO_TO_NEXT_CHAR(index, buffer, bufferLen); } } } if (in == 1) return -1; return count; error: return -1; } static int prv_parseItem(uint8_t * buffer, size_t bufferLen, _record_t * recordP) { size_t index; memset(recordP->ids, 0xFF, 4*sizeof(uint16_t)); recordP->type = _TYPE_UNSET; recordP->value = NULL; recordP->valueLen = 0; index = 0; do { int tokenStart; int tokenLen; int valueStart; int valueLen; int next; next = prv_split(buffer+index, bufferLen-index, &tokenStart, &tokenLen, &valueStart, &valueLen); if (next < 0) return -1; switch (tokenLen) { case 1: { switch (buffer[index+tokenStart]) { case 'n': { int i; int j; if (recordP->ids[0] != LWM2M_MAX_ID) return -1; // Check for " around URI if (valueLen < 3 || buffer[index+valueStart] != '"' || buffer[index+valueStart+valueLen-1] != '"') { return -1; } // Ignore starting / if (buffer[index + valueStart + 1] == '/') { if (valueLen < 4) { return -1; } valueStart += 1; valueLen -= 1; } i = 0; j = 0; do { uint32_t readId; readId = 0; i++; while (i < valueLen-1 && buffer[index+valueStart+i] != '/') { if (buffer[index+valueStart+i] < '0' || buffer[index+valueStart+i] > '9') { return -1; } readId *= 10; readId += buffer[index+valueStart+i] - '0'; if (readId > LWM2M_MAX_ID) return -1; i++; } recordP->ids[j] = readId; j++; } while (i < valueLen-1 && j < 4 && buffer[index+valueStart+i] == '/'); if (i < valueLen-1 ) return -1; } break; case 'v': if (recordP->type != _TYPE_UNSET) return -1; recordP->type = _TYPE_FLOAT; recordP->value = buffer + index + valueStart; recordP->valueLen = valueLen; break; case 't': // TODO: support time break; default: return -1; } } break; case 2: { // "bv", "ov", or "sv" if (buffer[index+tokenStart+1] != 'v') return -1; switch (buffer[index+tokenStart]) { case 'b': if (recordP->type != _TYPE_UNSET) return -1; if (0 == lwm2m_strncmp(JSON_TRUE_STRING, (char *)buffer + index + valueStart, valueLen)) { recordP->type = _TYPE_TRUE; } else if (0 == lwm2m_strncmp(JSON_FALSE_STRING, (char *)buffer + index + valueStart, valueLen)) { recordP->type = _TYPE_FALSE; } else { return -1; } break; case 'o': if (recordP->type != _TYPE_UNSET) return -1; // TODO: support object link break; case 's': if (recordP->type != _TYPE_UNSET) return -1; // Check for " around value if (valueLen < 2 || buffer[index+valueStart] != '"' || buffer[index+valueStart+valueLen-1] != '"') { return -1; } recordP->type = _TYPE_STRING; recordP->value = buffer + index + valueStart + 1; recordP->valueLen = valueLen - 2; break; default: return -1; } } break; default: return -1; } index += next + 1; } while (index < bufferLen); return 0; } static bool prv_convertValue(_record_t * recordP, lwm2m_data_t * targetP) { switch (recordP->type) { case _TYPE_FALSE: lwm2m_data_encode_bool(false, targetP); break; case _TYPE_TRUE: lwm2m_data_encode_bool(true, targetP); break; case _TYPE_FLOAT: { size_t i; i = 0; while (i < recordP->valueLen && recordP->value[i] != '.') { i++; } if (i == recordP->valueLen) { int64_t value; if ( 1 != utils_plainTextToInt64(recordP->value, recordP->valueLen, &value)) { return false; } lwm2m_data_encode_int(value, targetP); } else { double value; if ( 1 != utils_plainTextToFloat64(recordP->value, recordP->valueLen, &value)) { return false; } lwm2m_data_encode_float(value, targetP); } } break; case _TYPE_STRING: lwm2m_data_encode_opaque(recordP->value, recordP->valueLen, targetP); targetP->type = LWM2M_TYPE_STRING; break; case _TYPE_UNSET: default: return false; } return true; } static lwm2m_data_t * prv_findDataItem(lwm2m_data_t * listP, int count, uint16_t id) { int i; i = 0; while (i < count) { if (listP[i].type != LWM2M_TYPE_UNDEFINED && listP[i].id == id) { return listP + i; } i++; } return NULL; } static uri_depth_t prv_decreaseLevel(uri_depth_t level) { switch(level) { case URI_DEPTH_OBJECT: return URI_DEPTH_OBJECT_INSTANCE; case URI_DEPTH_OBJECT_INSTANCE: return URI_DEPTH_RESOURCE; case URI_DEPTH_RESOURCE: return URI_DEPTH_RESOURCE_INSTANCE; case URI_DEPTH_RESOURCE_INSTANCE: return URI_DEPTH_RESOURCE_INSTANCE; default: return URI_DEPTH_RESOURCE; } } static lwm2m_data_t * prv_extendData(lwm2m_data_t * parentP) { lwm2m_data_t * newP; newP = lwm2m_data_new(parentP->value.asChildren.count + 1); if (newP == NULL) return NULL; if (parentP->value.asChildren.array != NULL) { memcpy(newP, parentP->value.asChildren.array, parentP->value.asChildren.count * sizeof(lwm2m_data_t)); lwm2m_free(parentP->value.asChildren.array); // do not use lwm2m_data_free() to keep pointed values } parentP->value.asChildren.array = newP; parentP->value.asChildren.count += 1; return newP + parentP->value.asChildren.count - 1; } static int prv_convertRecord(lwm2m_uri_t * uriP, _record_t * recordArray, int count, lwm2m_data_t ** dataP) { int index; int freeIndex; lwm2m_data_t * rootP; int size; uri_depth_t rootLevel; if (uriP == NULL) { size = count; *dataP = lwm2m_data_new(count); if (NULL == *dataP) return -1; rootLevel = URI_DEPTH_OBJECT; rootP = *dataP; } else { lwm2m_data_t * parentP; size = 1; *dataP = lwm2m_data_new(1); if (NULL == *dataP) return -1; (*dataP)->type = LWM2M_TYPE_OBJECT; (*dataP)->id = uriP->objectId; rootLevel = URI_DEPTH_OBJECT_INSTANCE; parentP = *dataP; if (LWM2M_URI_IS_SET_INSTANCE(uriP)) { parentP->value.asChildren.count = 1; parentP->value.asChildren.array = lwm2m_data_new(1); if (NULL == parentP->value.asChildren.array) goto error; parentP = parentP->value.asChildren.array; parentP->type = LWM2M_TYPE_OBJECT_INSTANCE; parentP->id = uriP->instanceId; rootLevel = URI_DEPTH_RESOURCE; if (LWM2M_URI_IS_SET_RESOURCE(uriP)) { parentP->value.asChildren.count = 1; parentP->value.asChildren.array = lwm2m_data_new(1); if (NULL == parentP->value.asChildren.array) goto error; parentP = parentP->value.asChildren.array; parentP->type = LWM2M_TYPE_UNDEFINED; parentP->id = uriP->resourceId; rootLevel = URI_DEPTH_RESOURCE_INSTANCE; } } parentP->value.asChildren.count = count; parentP->value.asChildren.array = lwm2m_data_new(count); if (NULL == parentP->value.asChildren.array) goto error; rootP = parentP->value.asChildren.array; } freeIndex = 0; for (index = 0 ; index < count ; index++) { lwm2m_data_t * targetP; int resSegmentIndex; int i; // check URI depth // resSegmentIndex is set to the resource segment position switch(rootLevel) { case URI_DEPTH_OBJECT: resSegmentIndex = 2; break; case URI_DEPTH_OBJECT_INSTANCE: resSegmentIndex = 1; break; case URI_DEPTH_RESOURCE: resSegmentIndex = 0; break; case URI_DEPTH_RESOURCE_INSTANCE: resSegmentIndex = -1; break; default: goto error; } for (i = 0 ; i <= resSegmentIndex ; i++) { if (recordArray[index].ids[i] == LWM2M_MAX_ID) goto error; } if (resSegmentIndex < 2) { if (recordArray[index].ids[resSegmentIndex + 2] != LWM2M_MAX_ID) goto error; } targetP = prv_findDataItem(rootP, count, recordArray[index].ids[0]); if (targetP == NULL) { targetP = rootP + freeIndex; freeIndex++; targetP->id = recordArray[index].ids[0]; targetP->type = utils_depthToDatatype(rootLevel); } if (recordArray[index].ids[1] != LWM2M_MAX_ID) { lwm2m_data_t * parentP; uri_depth_t level; parentP = targetP; level = prv_decreaseLevel(rootLevel); for (i = 1 ; i <= resSegmentIndex ; i++) { targetP = prv_findDataItem(parentP->value.asChildren.array, parentP->value.asChildren.count, recordArray[index].ids[i]); if (targetP == NULL) { targetP = prv_extendData(parentP); if (targetP == NULL) goto error; targetP->id = recordArray[index].ids[i]; targetP->type = utils_depthToDatatype(level); } level = prv_decreaseLevel(level); parentP = targetP; } if (recordArray[index].ids[resSegmentIndex + 1] != LWM2M_MAX_ID) { targetP->type = LWM2M_TYPE_MULTIPLE_RESOURCE; targetP = prv_extendData(targetP); if (targetP == NULL) goto error; targetP->id = recordArray[index].ids[resSegmentIndex + 1]; targetP->type = LWM2M_TYPE_UNDEFINED; } } if (true != prv_convertValue(recordArray + index, targetP)) goto error; } return size; error: lwm2m_data_free(size, *dataP); *dataP = NULL; return -1; } static int prv_dataStrip(int size, lwm2m_data_t * dataP, lwm2m_data_t ** resultP) { int i; int j; int realSize; realSize = 0; for (i = 0 ; i < size ; i++) { if (dataP[i].type != LWM2M_TYPE_UNDEFINED) { realSize++; } } *resultP = lwm2m_data_new(realSize); if (*resultP == NULL) return -1; j = 0; for (i = 0 ; i < size ; i++) { if (dataP[i].type != LWM2M_TYPE_UNDEFINED) { memcpy((*resultP) + j, dataP + i, sizeof(lwm2m_data_t)); if (dataP[i].type == LWM2M_TYPE_OBJECT || dataP[i].type == LWM2M_TYPE_OBJECT_INSTANCE || dataP[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) { int childLen; childLen = prv_dataStrip(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array, &((*resultP)[j].value.asChildren.array)); if (childLen <= 0) { // skip this one j--; } else { (*resultP)[j].value.asChildren.count = childLen; } } else { dataP[i].value.asBuffer.buffer = NULL; } j++; } } return realSize; } int json_parse(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, lwm2m_data_t ** dataP) { size_t index; int count = 0; bool eFound = false; bool bnFound = false; bool btFound = false; int bnStart; int bnLen; _record_t * recordArray; lwm2m_data_t * parsedP; LOG_ARG("bufferLen: %d, buffer: \"%s\"", bufferLen, (char *)buffer); LOG_URI(uriP); *dataP = NULL; recordArray = NULL; parsedP = NULL; index = prv_skipSpace(buffer, bufferLen); if (index == bufferLen) return -1; if (buffer[index] != '{') return -1; do { _GO_TO_NEXT_CHAR(index, buffer, bufferLen); if (buffer[index] != '"') goto error; if (index++ >= bufferLen) goto error; switch (buffer[index]) { case 'e': { int recordIndex; if (bufferLen-index < JSON_MIN_ARRAY_LEN) goto error; index++; if (buffer[index] != '"') goto error; if (eFound == true) goto error; eFound = true; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); if (buffer[index] != ':') goto error; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); if (buffer[index] != '[') goto error; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); count = prv_countItems(buffer + index, bufferLen - index); if (count <= 0) goto error; recordArray = (_record_t*)lwm2m_malloc(count * sizeof(_record_t)); if (recordArray == NULL) goto error; // at this point we are sure buffer[index] is '{' and all { and } are matching recordIndex = 0; while (recordIndex < count) { int itemLen; if (buffer[index] != '{') goto error; itemLen = 0; while (buffer[index + itemLen] != '}') itemLen++; if (0 != prv_parseItem(buffer + index + 1, itemLen - 1, recordArray + recordIndex)) { goto error; } recordIndex++; index += itemLen; _GO_TO_NEXT_CHAR(index, buffer, bufferLen); switch (buffer[index]) { case ',': _GO_TO_NEXT_CHAR(index, buffer, bufferLen); break; case ']': if (recordIndex == count) break; // else this is an error default: goto error; } } if (buffer[index] != ']') goto error; } break; case 'b': if (bufferLen-index < JSON_MIN_BX_LEN) goto error; index++; switch (buffer[index]) { case 't': index++; if (buffer[index] != '"') goto error; if (btFound == true) goto error; btFound = true; // TODO: handle timed values // temp: skip this token while(index < bufferLen && buffer[index] != ',' && buffer[index] != '}') index++; if (index == bufferLen) goto error; index--; // end temp break; case 'n': { int next; int tokenStart; int tokenLen; int itemLen; index++; if (buffer[index] != '"') goto error; if (bnFound == true) goto error; bnFound = true; index -= 3; itemLen = 0; while (buffer[index + itemLen] != '}' && buffer[index + itemLen] != ',' && index + itemLen < bufferLen) { itemLen++; } if (index + itemLen == bufferLen) goto error; next = prv_split(buffer+index, itemLen, &tokenStart, &tokenLen, &bnStart, &bnLen); if (next < 0) goto error; bnStart += index; index += next - 1; } break; default: goto error; } break; default: goto error; } _GO_TO_NEXT_CHAR(index, buffer, bufferLen); } while (buffer[index] == ','); if (buffer[index] != '}') goto error; if (eFound == true) { lwm2m_uri_t baseURI; lwm2m_uri_t * baseUriP; lwm2m_data_t * resultP; int size; memset(&baseURI, 0, sizeof(lwm2m_uri_t)); if (bnFound == false) { baseUriP = uriP; } else { int res; // we ignore the request URI and use the bn one. // Check for " around URI if (bnLen < 3 || buffer[bnStart] != '"' || buffer[bnStart+bnLen-1] != '"') { goto error; } bnStart += 1; bnLen -= 2; if (bnLen == 1) { if (buffer[bnStart] != '/') goto error; baseUriP = NULL; } else { res = lwm2m_stringToUri((char *)buffer + bnStart, bnLen, &baseURI); if (res < 0 || res != bnLen) goto error; baseUriP = &baseURI; } } count = prv_convertRecord(baseUriP, recordArray, count, &parsedP); lwm2m_free(recordArray); recordArray = NULL; if (count > 0 && uriP != NULL) { if (parsedP->type != LWM2M_TYPE_OBJECT || parsedP->id != uriP->objectId) goto error; if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) { size = parsedP->value.asChildren.count; resultP = parsedP->value.asChildren.array; } else { int i; resultP = NULL; // be permissive and allow full object JSON when requesting for a single instance for (i = 0 ; i < (int)parsedP->value.asChildren.count && resultP == NULL; i++) { lwm2m_data_t * targetP; targetP = parsedP->value.asChildren.array + i; if (targetP->id == uriP->instanceId) { resultP = targetP->value.asChildren.array; size = targetP->value.asChildren.count; } } if (resultP == NULL) goto error; if (LWM2M_URI_IS_SET_RESOURCE(uriP)) { lwm2m_data_t * resP; resP = NULL; for (i = 0 ; i < size && resP == NULL; i++) { lwm2m_data_t * targetP; targetP = resultP + i; if (targetP->id == uriP->resourceId) { if (targetP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) { resP = targetP->value.asChildren.array; size = targetP->value.asChildren.count; } else { size = prv_dataStrip(1, targetP, &resP); if (size <= 0) goto error; lwm2m_data_free(count, parsedP); parsedP = NULL; } } } if (resP == NULL) goto error; resultP = resP; } } } else { resultP = parsedP; size = count; } if (parsedP != NULL) { lwm2m_data_t * tempP; size = prv_dataStrip(size, resultP, &tempP); if (size <= 0) goto error; lwm2m_data_free(count, parsedP); resultP = tempP; } count = size; *dataP = resultP; } LOG_ARG("Parsing successful. count: %d", count); return count; error: LOG("Parsing failed"); if (parsedP != NULL) { lwm2m_data_free(count, parsedP); parsedP = NULL; } if (recordArray != NULL) { lwm2m_free(recordArray); } return -1; } static int prv_serializeValue(lwm2m_data_t * tlvP, uint8_t * buffer, size_t bufferLen) { int res; int head; switch (tlvP->type) { case LWM2M_TYPE_STRING: if (bufferLen < JSON_ITEM_STRING_BEGIN_SIZE) return -1; memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE); head = JSON_ITEM_STRING_BEGIN_SIZE; if (bufferLen - head < tlvP->value.asBuffer.length) return -1; memcpy(buffer + head, tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length); head += tlvP->value.asBuffer.length; if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1; memcpy(buffer + head, JSON_ITEM_STRING_END, JSON_ITEM_STRING_END_SIZE); head += JSON_ITEM_STRING_END_SIZE; break; case LWM2M_TYPE_INTEGER: { int64_t value; if (0 == lwm2m_data_decode_int(tlvP, &value)) return -1; if (bufferLen < JSON_ITEM_NUM_SIZE) return -1; memcpy(buffer, JSON_ITEM_NUM, JSON_ITEM_NUM_SIZE); head = JSON_ITEM_NUM_SIZE; res = utils_intToText(value, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; if (bufferLen - head < JSON_ITEM_NUM_END_SIZE) return -1; memcpy(buffer + head, JSON_ITEM_NUM_END, JSON_ITEM_NUM_END_SIZE); head += JSON_ITEM_NUM_END_SIZE; } break; case LWM2M_TYPE_FLOAT: { double value; if (0 == lwm2m_data_decode_float(tlvP, &value)) return -1; if (bufferLen < JSON_ITEM_NUM_SIZE) return -1; memcpy(buffer, JSON_ITEM_NUM, JSON_ITEM_NUM_SIZE); head = JSON_ITEM_NUM_SIZE; res = utils_floatToText(value, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; if (bufferLen - head < JSON_ITEM_NUM_END_SIZE) return -1; memcpy(buffer + head, JSON_ITEM_NUM_END, JSON_ITEM_NUM_END_SIZE); head += JSON_ITEM_NUM_END_SIZE; } break; case LWM2M_TYPE_BOOLEAN: { bool value; if (0 == lwm2m_data_decode_bool(tlvP, &value)) return -1; if (value == true) { if (bufferLen < JSON_ITEM_BOOL_TRUE_SIZE) return -1; memcpy(buffer, JSON_ITEM_BOOL_TRUE, JSON_ITEM_BOOL_TRUE_SIZE); head = JSON_ITEM_BOOL_TRUE_SIZE; } else { if (bufferLen < JSON_ITEM_BOOL_FALSE_SIZE) return -1; memcpy(buffer, JSON_ITEM_BOOL_FALSE, JSON_ITEM_BOOL_FALSE_SIZE); head = JSON_ITEM_BOOL_FALSE_SIZE; } } break; case LWM2M_TYPE_OPAQUE: if (bufferLen < JSON_ITEM_STRING_BEGIN_SIZE) return -1; memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE); head = JSON_ITEM_STRING_BEGIN_SIZE; res = utils_base64Encode(tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length, buffer+head, bufferLen - head); if (res == 0) return -1; head += res; if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1; memcpy(buffer + head, JSON_ITEM_STRING_END, JSON_ITEM_STRING_END_SIZE); head += JSON_ITEM_STRING_END_SIZE; break; case LWM2M_TYPE_OBJECT_LINK: // TODO: implement return -1; default: return -1; } return head; } int prv_serializeData(lwm2m_data_t * tlvP, uint8_t * parentUriStr, size_t parentUriLen, uint8_t * buffer, size_t bufferLen) { int head; int res; head = 0; switch (tlvP->type) { case LWM2M_TYPE_OBJECT: case LWM2M_TYPE_OBJECT_INSTANCE: case LWM2M_TYPE_MULTIPLE_RESOURCE: { uint8_t uriStr[URI_MAX_STRING_LEN]; size_t uriLen; size_t index; if (parentUriLen > 0) { if (URI_MAX_STRING_LEN < parentUriLen) return -1; memcpy(uriStr, parentUriStr, parentUriLen); uriLen = parentUriLen; } else { uriLen = 0; } res = utils_intToText(tlvP->id, uriStr + uriLen, URI_MAX_STRING_LEN - uriLen); if (res <= 0) return -1; uriLen += res; uriStr[uriLen] = '/'; uriLen++; head = 0; for (index = 0 ; index < tlvP->value.asChildren.count; index++) { res = prv_serializeData(tlvP->value.asChildren.array + index, uriStr, uriLen, buffer + head, bufferLen - head); if (res < 0) return -1; head += res; } } break; default: if (bufferLen < JSON_RES_ITEM_URI_SIZE) return -1; memcpy(buffer, JSON_RES_ITEM_URI, JSON_RES_ITEM_URI_SIZE); head = JSON_RES_ITEM_URI_SIZE; if (parentUriLen > 0) { if (bufferLen - head < parentUriLen) return -1; memcpy(buffer + head, parentUriStr, parentUriLen); head += parentUriLen; } res = utils_intToText(tlvP->id, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; res = prv_serializeValue(tlvP, buffer + head, bufferLen - head); if (res < 0) return -1; head += res; break; } return head; } static int prv_findAndCheckData(lwm2m_uri_t * uriP, uri_depth_t level, size_t size, lwm2m_data_t * tlvP, lwm2m_data_t ** targetP) { size_t index; int result; if (size == 0) return 0; if (size > 1) { if (tlvP[0].type == LWM2M_TYPE_OBJECT || tlvP[0].type == LWM2M_TYPE_OBJECT_INSTANCE) { for (index = 0; index < size; index++) { if (tlvP[index].type != tlvP[0].type) { *targetP = NULL; return -1; } } } else { for (index = 0; index < size; index++) { if (tlvP[index].type == LWM2M_TYPE_OBJECT || tlvP[index].type == LWM2M_TYPE_OBJECT_INSTANCE) { *targetP = NULL; return -1; } } } } *targetP = NULL; result = -1; switch (level) { case URI_DEPTH_OBJECT: if (tlvP[0].type == LWM2M_TYPE_OBJECT) { *targetP = tlvP; result = (int)size; } break; case URI_DEPTH_OBJECT_INSTANCE: switch (tlvP[0].type) { case LWM2M_TYPE_OBJECT: for (index = 0; index < size; index++) { if (tlvP[index].id == uriP->objectId) { return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); } } break; case LWM2M_TYPE_OBJECT_INSTANCE: *targetP = tlvP; result = (int)size; break; default: break; } break; case URI_DEPTH_RESOURCE: switch (tlvP[0].type) { case LWM2M_TYPE_OBJECT: for (index = 0; index < size; index++) { if (tlvP[index].id == uriP->objectId) { return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); } } break; case LWM2M_TYPE_OBJECT_INSTANCE: for (index = 0; index < size; index++) { if (tlvP[index].id == uriP->instanceId) { return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); } } break; default: *targetP = tlvP; result = (int)size; break; } break; case URI_DEPTH_RESOURCE_INSTANCE: switch (tlvP[0].type) { case LWM2M_TYPE_OBJECT: for (index = 0; index < size; index++) { if (tlvP[index].id == uriP->objectId) { return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); } } break; case LWM2M_TYPE_OBJECT_INSTANCE: for (index = 0; index < size; index++) { if (tlvP[index].id == uriP->instanceId) { return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); } } break; case LWM2M_TYPE_MULTIPLE_RESOURCE: for (index = 0; index < size; index++) { if (tlvP[index].id == uriP->resourceId) { return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP); } } break; default: *targetP = tlvP; result = (int)size; break; } break; default: break; } return result; } int json_serialize(lwm2m_uri_t * uriP, int size, lwm2m_data_t * tlvP, uint8_t ** bufferP) { int index; size_t head; uint8_t bufferJSON[PRV_JSON_BUFFER_SIZE]; uint8_t baseUriStr[URI_MAX_STRING_LEN]; int baseUriLen; uri_depth_t rootLevel; int num; lwm2m_data_t * targetP; LOG_ARG("size: %d", size); LOG_URI(uriP); if (size != 0 && tlvP == NULL) return -1; baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, &rootLevel); if (baseUriLen < 0) return -1; num = prv_findAndCheckData(uriP, rootLevel, size, tlvP, &targetP); if (num < 0) return -1; while (num == 1 && (targetP->type == LWM2M_TYPE_OBJECT || targetP->type == LWM2M_TYPE_OBJECT_INSTANCE || targetP->type == LWM2M_TYPE_MULTIPLE_RESOURCE)) { int res; res = utils_intToText(targetP->id, baseUriStr + baseUriLen, URI_MAX_STRING_LEN - baseUriLen); if (res <= 0) return 0; baseUriLen += res; if (baseUriLen >= URI_MAX_STRING_LEN -1) return 0; num = targetP->value.asChildren.count; targetP = targetP->value.asChildren.array; baseUriStr[baseUriLen] = '/'; baseUriLen++; } if (baseUriLen > 0) { memcpy(bufferJSON, JSON_BN_HEADER_1, JSON_BN_HEADER_1_SIZE); head = JSON_BN_HEADER_1_SIZE; memcpy(bufferJSON + head, baseUriStr, baseUriLen); head += baseUriLen; memcpy(bufferJSON + head, JSON_BN_HEADER_2, JSON_BN_HEADER_2_SIZE); head += JSON_BN_HEADER_2_SIZE; } else { memcpy(bufferJSON, JSON_HEADER, JSON_HEADER_SIZE); head = JSON_HEADER_SIZE; } for (index = 0 ; index < num && head < PRV_JSON_BUFFER_SIZE ; index++) { int res; res = prv_serializeData(targetP + index, NULL, 0, bufferJSON + head, PRV_JSON_BUFFER_SIZE - head); if (res < 0) return 0; head += res; } if (head + JSON_FOOTER_SIZE - 1 > PRV_JSON_BUFFER_SIZE) return 0; if (num > 0) head = head - 1; memcpy(bufferJSON + head, JSON_FOOTER, JSON_FOOTER_SIZE); head = head + JSON_FOOTER_SIZE; *bufferP = (uint8_t *)lwm2m_malloc(head); if (*bufferP == NULL) return 0; memcpy(*bufferP, bufferJSON, head); return head; } #endif