size_t lwm2m_float64ToPlainText(double data, uint8_t ** bufferP) { uint8_t string[_PRV_STR_LENGTH * 2]; size_t length; length = utils_floatToText(data, string, _PRV_STR_LENGTH * 2); if (length == 0) return 0; *bufferP = (uint8_t *)lwm2m_malloc(length); if (NULL == *bufferP) return 0; memcpy(*bufferP, string, length); return length; }
static void test_utils_floatToText(void) { size_t i; char res[330]; int len; int compareLen; for (i = 0 ; i < sizeof(floats)/sizeof(floats[0]); i++) { len = utils_floatToText(floats[i], (uint8_t*)res, sizeof(res)); CU_ASSERT(len); if (len) { compareLen = (int)strlen(floats_text[i]); if (compareLen > DBL_DIG && floats_text[i][compareLen-2] == '.' && len > compareLen - 2 && res[compareLen-2] == '.') { compareLen = DBL_DIG; } CU_ASSERT_NSTRING_EQUAL(res, floats_text[i], compareLen); if (strncmp(res, floats_text[i], compareLen)) printf("%zu \"%g\" -> fail (%*s)\n", i, floats[i], len, res); } else { printf("%zu \"%g\" -> fail\n", i, floats[i]); } } /* Test when no significant digits fit */ double val = 1e-9; len = utils_floatToText(val, (uint8_t*)res, 6); CU_ASSERT(len); if(len) { CU_ASSERT_NSTRING_EQUAL(res, "0.0000", len); if (strncmp(res, "0.0000", len)) printf("%zu \"%g\" -> fail (%*s)\n", i, val, len, res); } else { printf("%zu \"%g\" -> fail\n", i, val); } i++; /* Test when only some significant digits fit */ val = 0.11111111111111111; len = utils_floatToText(val, (uint8_t*)res, 6); CU_ASSERT(len); if(len) { CU_ASSERT_NSTRING_EQUAL(res, "0.1111", len); if (strncmp(res, "0.1111", len)) printf("%zu \"%g\" -> fail (%*s)\n", i, val, len, res); } else { printf("%zu \"%g\" -> fail\n", i, val); } }
int lwm2m_dm_write_attributes(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_attributes_t * attrP, lwm2m_result_callback_t callback, void * userData) { #define _PRV_BUFFER_SIZE 32 lwm2m_client_t * clientP; lwm2m_transaction_t * transaction; coap_packet_t * coap_pkt; uint8_t buffer[_PRV_BUFFER_SIZE]; size_t length; LOG_ARG("clientID: %d", clientID); LOG_URI(uriP); if (attrP == NULL) return COAP_400_BAD_REQUEST; if (0 != (attrP->toSet & attrP->toClear)) return COAP_400_BAD_REQUEST; if (0 != (attrP->toSet & ATTR_FLAG_NUMERIC) && !LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST; if (ATTR_FLAG_NUMERIC == (attrP->toSet & ATTR_FLAG_NUMERIC) && (attrP->lessThan + 2 * attrP->step >= attrP->greaterThan)) return COAP_400_BAD_REQUEST; clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID); if (clientP == NULL) return COAP_404_NOT_FOUND; transaction = transaction_new(clientP->sessionH, COAP_PUT, clientP->altPath, uriP, contextP->nextMID++, 4, NULL); if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; if (callback != NULL) { dm_data_t * dataP; dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t)); if (dataP == NULL) { transaction_free(transaction); return COAP_500_INTERNAL_SERVER_ERROR; } memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t)); dataP->clientID = clientP->internalID; dataP->callback = callback; dataP->userData = userData; transaction->callback = prv_resultCallback; transaction->userData = (void *)dataP; } coap_pkt = (coap_packet_t *)transaction->message; free_multi_option(coap_pkt->uri_query); if (attrP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) { memcpy(buffer, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN); length = utils_intToText(attrP->minPeriod, buffer + ATTR_MIN_PERIOD_LEN, _PRV_BUFFER_SIZE - ATTR_MIN_PERIOD_LEN); if (length == 0) { transaction_free(transaction); return COAP_500_INTERNAL_SERVER_ERROR; } coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_MIN_PERIOD_LEN + length, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) { memcpy(buffer, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN); length = utils_intToText(attrP->maxPeriod, buffer + ATTR_MAX_PERIOD_LEN, _PRV_BUFFER_SIZE - ATTR_MAX_PERIOD_LEN); if (length == 0) { transaction_free(transaction); return COAP_500_INTERNAL_SERVER_ERROR; } coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_MAX_PERIOD_LEN + length, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) { memcpy(buffer, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN); length = utils_floatToText(attrP->greaterThan, buffer + ATTR_GREATER_THAN_LEN, _PRV_BUFFER_SIZE - ATTR_GREATER_THAN_LEN); if (length == 0) { transaction_free(transaction); return COAP_500_INTERNAL_SERVER_ERROR; } coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_GREATER_THAN_LEN + length, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN) { memcpy(buffer, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN); length = utils_floatToText(attrP->lessThan, buffer + ATTR_LESS_THAN_LEN, _PRV_BUFFER_SIZE - ATTR_LESS_THAN_LEN); if (length == 0) { transaction_free(transaction); return COAP_500_INTERNAL_SERVER_ERROR; } coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_LESS_THAN_LEN + length, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toSet & LWM2M_ATTR_FLAG_STEP) { memcpy(buffer, ATTR_STEP_STR, ATTR_STEP_LEN); length = utils_floatToText(attrP->step, buffer + ATTR_STEP_LEN, _PRV_BUFFER_SIZE - ATTR_STEP_LEN); if (length == 0) { transaction_free(transaction); return COAP_500_INTERNAL_SERVER_ERROR; } coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_STEP_LEN + length, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toClear & LWM2M_ATTR_FLAG_MIN_PERIOD) { coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t*)ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN -1, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toClear & LWM2M_ATTR_FLAG_MAX_PERIOD) { coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t*)ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN - 1, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toClear & LWM2M_ATTR_FLAG_GREATER_THAN) { coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t*)ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN - 1, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toClear & LWM2M_ATTR_FLAG_LESS_THAN) { coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t*)ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN - 1, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } if (attrP->toClear & LWM2M_ATTR_FLAG_STEP) { coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t*)ATTR_STEP_STR, ATTR_STEP_LEN - 1, 0); SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY); } contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); return transaction_send(contextP, transaction); }
static int prv_serializeAttributes(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, uint8_t * buffer, size_t uriLen, size_t bufferLen) { lwm2m_observed_t * observedP; lwm2m_watcher_t * watcherP; int head; int res; if (contextP == NULL) return 0; observedP = observe_findByUri(contextP, uriP); if (observedP == NULL || observedP->watcherList == NULL) return 0; head = 0; for (watcherP = observedP->watcherList; watcherP != NULL; watcherP = watcherP->next) { lwm2m_attributes_t * paramP; paramP = watcherP->parameters; if (paramP == NULL || paramP->toSet == 0) continue; if (observedP->watcherList->next != NULL) { // multiple servers memcpy(buffer + head, buffer, uriLen); PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_SERVER_ID_STR, ATTR_SERVER_ID_LEN); res = utils_intToText(watcherP->server->shortID, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; } else { head = uriLen; } if (paramP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) { PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN); res = utils_intToText(paramP->minPeriod, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; } if (paramP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) { PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN); res = utils_intToText(paramP->maxPeriod, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; } if (paramP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) { PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN); res = utils_floatToText(paramP->greaterThan, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; } if (paramP->toSet & LWM2M_ATTR_FLAG_LESS_THAN) { PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN); res = utils_floatToText(paramP->lessThan, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; } if (paramP->toSet & LWM2M_ATTR_FLAG_STEP) { PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE); PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_STEP_STR, ATTR_STEP_LEN); res = utils_floatToText(paramP->step, buffer + head, bufferLen - head); if (res <= 0) return -1; head += res; } PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_ATTR_END, LINK_ITEM_ATTR_END_SIZE); } if (head > 0) head -= uriLen; return head; }
// dataP array length is assumed to be 1. static int prv_textSerialize(lwm2m_data_t * dataP, uint8_t ** bufferP) { size_t res; switch (dataP->type) { case LWM2M_TYPE_STRING: *bufferP = (uint8_t *)lwm2m_malloc(dataP->value.asBuffer.length); if (*bufferP == NULL) return 0; memcpy(*bufferP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length); return (int)dataP->value.asBuffer.length; case LWM2M_TYPE_INTEGER: { uint8_t intString[_PRV_STR_LENGTH]; res = utils_intToText(dataP->value.asInteger, intString, _PRV_STR_LENGTH); if (res == 0) return -1; *bufferP = (uint8_t *)lwm2m_malloc(res); if (NULL == *bufferP) return -1; memcpy(*bufferP, intString, res); return (int)res; } case LWM2M_TYPE_FLOAT: { uint8_t floatString[_PRV_STR_LENGTH * 2]; res = utils_floatToText(dataP->value.asFloat, floatString, _PRV_STR_LENGTH * 2); if (res == 0) return -1; *bufferP = (uint8_t *)lwm2m_malloc(res); if (NULL == *bufferP) return -1; memcpy(*bufferP, floatString, res); return (int)res; } case LWM2M_TYPE_BOOLEAN: *bufferP = (uint8_t *)lwm2m_malloc(1); if (NULL == *bufferP) return -1; *bufferP[0] = dataP->value.asBoolean ? '1' : '0'; return 1; case LWM2M_TYPE_OBJECT_LINK: { char stringBuffer[11]; size_t length; length = utils_intToText(dataP->value.asObjLink.objectId, (uint8_t*)stringBuffer, 5); if (length == 0) return -1; stringBuffer[5] = ':'; res = length + 1; length = utils_intToText(dataP->value.asObjLink.objectInstanceId, (uint8_t*)stringBuffer + res, 5); if (length == 0) return -1; res += length; *bufferP = (uint8_t *)lwm2m_malloc(res); if (*bufferP == NULL) return -1; memcpy(*bufferP, stringBuffer, res); return res; } case LWM2M_TYPE_OPAQUE: { size_t length; length = utils_base64GetSize(dataP->value.asBuffer.length); *bufferP = (uint8_t *)lwm2m_malloc(length); if (*bufferP == NULL) return 0; length = utils_base64Encode(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, *bufferP, length); if (length == 0) { lwm2m_free(*bufferP); *bufferP = NULL; return 0; } return (int)length; } case LWM2M_TYPE_UNDEFINED: default: 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 (tlvP->value.asBuffer.length != 0 && 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; }
static int prv_serializeValue(const lwm2m_data_t * tlvP, uint8_t * buffer, size_t bufferLen) { size_t res; size_t head; switch (tlvP->type) { case LWM2M_TYPE_STRING: case LWM2M_TYPE_CORE_LINK: 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 = json_escapeString(buffer + head, bufferLen - head, tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length); if (!res) return -1; head += res; if (bufferLen - head < 1) return -1; buffer[head++] = JSON_ITEM_STRING_END; 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) return -1; head += res; } break; case LWM2M_TYPE_UNSIGNED_INTEGER: { uint64_t value; if (0 == lwm2m_data_decode_uint(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_uintToText(value, buffer + head, bufferLen - head); if (!res) return -1; head += res; } 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) return -1; head += res; } break; case LWM2M_TYPE_BOOLEAN: { bool value; if (0 == lwm2m_data_decode_bool(tlvP, &value)) return -1; if (value) { if (bufferLen < JSON_ITEM_BOOL_SIZE + JSON_TRUE_STRING_SIZE) return -1; memcpy(buffer, JSON_ITEM_BOOL JSON_TRUE_STRING, JSON_ITEM_BOOL_SIZE + JSON_TRUE_STRING_SIZE); head = JSON_ITEM_BOOL_SIZE + JSON_TRUE_STRING_SIZE; } else { if (bufferLen < JSON_ITEM_BOOL_SIZE + JSON_FALSE_STRING_SIZE) return -1; memcpy(buffer, JSON_ITEM_BOOL JSON_FALSE_STRING, JSON_ITEM_BOOL_SIZE + JSON_FALSE_STRING_SIZE); head = JSON_ITEM_BOOL_SIZE + JSON_FALSE_STRING_SIZE; } } break; case LWM2M_TYPE_OPAQUE: if (bufferLen < JSON_ITEM_OPAQUE_BEGIN_SIZE) return -1; memcpy(buffer, JSON_ITEM_OPAQUE_BEGIN, JSON_ITEM_OPAQUE_BEGIN_SIZE); head = JSON_ITEM_OPAQUE_BEGIN_SIZE; if (tlvP->value.asBuffer.length > 0) { res = utils_base64Encode(tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length, buffer+head, bufferLen - head); if (!res) return -1; head += res; } if (bufferLen - head < 1) return -1; buffer[head++] = JSON_ITEM_OPAQUE_END; break; case LWM2M_TYPE_OBJECT_LINK: if (bufferLen < JSON_ITEM_OBJECT_LINK_BEGIN_SIZE) return -1; memcpy(buffer, JSON_ITEM_OBJECT_LINK_BEGIN, JSON_ITEM_OBJECT_LINK_BEGIN_SIZE); head = JSON_ITEM_OBJECT_LINK_BEGIN_SIZE; res = utils_objLinkToText(tlvP->value.asObjLink.objectId, tlvP->value.asObjLink.objectInstanceId, buffer + head, bufferLen - head); if (!res) return -1; head += res; if (bufferLen - head < 1) return -1; buffer[head++] = JSON_ITEM_OBJECT_LINK_END; break; default: return -1; } return (int)head; }