pick up wakaama files from https://github.com/eclipse/wakaama
Diff: core/json.c
- Revision:
- 0:c2dff8cbb91a
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/json.c Wed Apr 19 11:27:34 2017 +0000 @@ -0,0 +1,1436 @@ +/******************************************************************************* + * + * 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 +