summaryrefslogtreecommitdiff
path: root/src/libecbor
diff options
context:
space:
mode:
Diffstat (limited to 'src/libecbor')
-rw-r--r--src/libecbor/ecbor.c600
-rw-r--r--src/libecbor/ecbor_decoder.c780
-rw-r--r--src/libecbor/ecbor_encoder.c562
-rw-r--r--src/libecbor/ecbor_internal.h138
4 files changed, 2080 insertions, 0 deletions
diff --git a/src/libecbor/ecbor.c b/src/libecbor/ecbor.c
new file mode 100644
index 0000000..30d2ac5
--- /dev/null
+++ b/src/libecbor/ecbor.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (c) 2018 Vasile Vilvoiu <vasi.vilvoiu@gmail.com>
+ *
+ * libecbor is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include "ecbor.h"
+#include "ecbor_internal.h"
+
+uint16_t
+ecbor_uint16_from_big_endian (uint16_t value)
+{
+#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ return value;
+#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ /* TODO: ARM optimization */
+ return ((value << 8) & 0xff00)
+ | ((value >> 8) & 0x00ff);
+#else
+ #error "Endianness not supported!"
+#endif
+}
+
+uint32_t
+ecbor_uint32_from_big_endian (uint32_t value)
+{
+#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ return value;
+#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ /* TODO: ARM optimization */
+ return ((value << 8) & 0x00ff0000)
+ | ((value >> 8) & 0x0000ff00)
+ | ((value << 24) & 0xff000000)
+ | ((value >> 24) & 0x000000ff);
+#else
+ #error "Endianness not supported!"
+#endif
+}
+
+uint64_t
+ecbor_uint64_from_big_endian (uint64_t value)
+{
+#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ return value;
+#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ /* TODO: ARM optimization */
+ return ((value << 8) & 0x000000ff00000000)
+ | ((value >> 8) & 0x00000000ff000000)
+ | ((value << 24) & 0x0000ff0000000000)
+ | ((value >> 24) & 0x0000000000ff0000)
+ | ((value << 40) & 0x00ff000000000000)
+ | ((value >> 40) & 0x000000000000ff00)
+ | ((value << 56) & 0xff00000000000000)
+ | ((value >> 56) & 0x00000000000000ff);
+#else
+ #error "Endianness not supported!"
+#endif
+}
+
+float
+ecbor_fp32_from_big_endian (float value)
+{
+#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ return value;
+#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ /* while it looks like a lot of casts, we're doing this just so we follow
+ aliasing rules; the compiler will optimize */
+ uint8_t *base = (uint8_t *) &value;
+ uint32_t r = ecbor_uint32_from_big_endian(*((uint32_t *) base));
+ base = (uint8_t *) &r;
+ return *((float *) base);
+#else
+ #error "Endianness not supported!"
+#endif
+}
+
+double
+ecbor_fp64_from_big_endian (double value)
+{
+#if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+ return value;
+#elif __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ uint8_t *base = (uint8_t *) &value;
+ uint64_t r = ecbor_uint64_from_big_endian(*((uint64_t *) base));
+ base = (uint8_t *) &r;
+ return *((double *) base);
+#else
+ #error "Endianness not supported!"
+#endif
+}
+
+uint16_t
+ecbor_uint16_to_big_endian (uint16_t value)
+{
+ return ecbor_uint16_from_big_endian (value);
+}
+
+uint32_t
+ecbor_uint32_to_big_endian (uint32_t value)
+{
+ return ecbor_uint32_from_big_endian (value);
+}
+
+uint64_t
+ecbor_uint64_to_big_endian (uint64_t value)
+{
+ return ecbor_uint64_from_big_endian (value);
+}
+
+float
+ecbor_fp32_to_big_endian (float value)
+{
+ return ecbor_fp32_from_big_endian (value);
+}
+
+double
+ecbor_fp64_to_big_endian (double value)
+{
+ return ecbor_fp64_from_big_endian (value);
+}
+
+ecbor_type_t
+ecbor_get_type (ecbor_item_t *item)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+ if (item->type < ECBOR_TYPE_FIRST
+ || item->type > ECBOR_TYPE_LAST) {
+ return ECBOR_TYPE_NONE;
+ }
+ return item->type;
+}
+
+ecbor_error_t
+ecbor_get_length (ecbor_item_t *item, size_t *length)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (length);
+
+ switch (item->type) {
+ case ECBOR_TYPE_BSTR:
+ case ECBOR_TYPE_STR:
+ case ECBOR_TYPE_ARRAY:
+ *length = item->length;
+ break;
+
+ case ECBOR_TYPE_MAP:
+ *length = item->length / 2;
+ break;
+
+ default:
+ return ECBOR_ERR_INVALID_TYPE;
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_array_item (ecbor_item_t *array, size_t index,
+ ecbor_item_t *item)
+{
+ ecbor_error_t rc;
+
+ if (!array) {
+ return ECBOR_ERR_NULL_ARRAY;
+ }
+ ECBOR_INTERNAL_CHECK_TYPE (array->type, ECBOR_TYPE_ARRAY);
+ ECBOR_INTERNAL_CHECK_BOUNDS (index, array->length);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+
+ if (array->child) {
+ /* parsed in tree mode */
+ ecbor_item_t *item_ptr;
+ rc = ecbor_get_array_item_ptr (array, index, &item_ptr);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* copy contents */
+ (*item) = (*item_ptr);
+ } else {
+ /* parsed in normal mode, we must re-parse it */
+ ecbor_decode_context_t context;
+ size_t i;
+
+ rc = ecbor_initialize_decode (&context, array->value.items, array->size);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ for (i = 0; i <= index; i ++) {
+ rc = ecbor_decode (&context, item);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_BUFFER) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else {
+ return rc;
+ }
+ }
+ }
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_array_item_ptr (ecbor_item_t *array, size_t index,
+ ecbor_item_t **item)
+{
+
+ size_t i;
+
+ if (!array) {
+ return ECBOR_ERR_NULL_ARRAY;
+ }
+ ECBOR_INTERNAL_CHECK_TYPE (array->type, ECBOR_TYPE_ARRAY);
+ ECBOR_INTERNAL_CHECK_BOUNDS (index, array->length);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+ if (!array->child) {
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ (*item) = array->child;
+ for (i = 0; i < index; i ++) {
+ (*item) = (*item)->next;
+ if (!(*item)) {
+ /* internal error, this should not have happened */
+ return ECBOR_ERR_UNKNOWN;
+ }
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_map_item (ecbor_item_t *map, size_t index, ecbor_item_t *key,
+ ecbor_item_t *value)
+{
+ ecbor_error_t rc;
+
+ if (!map) {
+ return ECBOR_ERR_NULL_MAP;
+ }
+
+ ECBOR_INTERNAL_CHECK_TYPE (map->type, ECBOR_TYPE_MAP);
+ ECBOR_INTERNAL_CHECK_BOUNDS ((index * 2), map->length);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (key);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (value);
+
+ if (map->child) {
+ /* parsed in tree mode */
+ ecbor_item_t *k, *v;
+ rc = ecbor_get_map_item_ptr (map, index, &k, &v);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* copy contents */
+ (*key) = (*k);
+ (*value) = (*v);
+ } else {
+ /* parsed in normal mode */
+ ecbor_decode_context_t context;
+ size_t i;
+
+ rc = ecbor_initialize_decode (&context, map->value.items, map->size);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ for (i = 0; i <= index; i ++) {
+ rc = ecbor_decode (&context, key);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_BUFFER) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else {
+ return rc;
+ }
+ }
+
+ rc = ecbor_decode (&context, value);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_BUFFER) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else {
+ return rc;
+ }
+ }
+ }
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_map_item_ptr (ecbor_item_t *map, size_t index, ecbor_item_t **key,
+ ecbor_item_t **value)
+{
+ size_t i;
+
+ if (!map) {
+ return ECBOR_ERR_NULL_MAP;
+ }
+
+ ECBOR_INTERNAL_CHECK_TYPE (map->type, ECBOR_TYPE_MAP);
+ ECBOR_INTERNAL_CHECK_BOUNDS ((index * 2), map->length);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (key);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (value);
+ if (!map->child) {
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ (*key) = map->child;
+ for (i = 0; i < index * 2; i ++) {
+ (*key) = (*key)->next;
+ if (!(*key)) {
+ /* internal error, this should not have happened */
+ return ECBOR_ERR_UNKNOWN;
+ }
+ }
+
+ (*value) = (*key)->next;
+ if (!(*value)) {
+ /* internal error, this should not have happened */
+ return ECBOR_ERR_UNKNOWN;
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_tag_item (ecbor_item_t *tag, ecbor_item_t *item)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (tag);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (item);
+ ECBOR_INTERNAL_CHECK_TYPE (tag->type, ECBOR_TYPE_TAG);
+
+ if (tag->child) {
+ /* parsed in tree mode; copy contents */
+ (*item) = (*tag->child);
+ } else {
+ /* parsed in normal mode */
+ ecbor_decode_context_t context;
+ ecbor_error_t rc;
+
+ rc = ecbor_initialize_decode (&context, tag->value.tag.child, tag->size);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ rc = ecbor_decode (&context, item);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_BUFFER) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else {
+ return rc;
+ }
+ }
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_tag_item_ptr (ecbor_item_t *tag, ecbor_item_t **item)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (tag);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (item);
+ ECBOR_INTERNAL_CHECK_TYPE (tag->type, ECBOR_TYPE_TAG);
+ if (!tag->child) {
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ (*item) = tag->child;
+ return ECBOR_OK;
+}
+
+
+#define ECBOR_GET_INTEGER_INTERNAL(item, value, etype, btype) \
+{ \
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item); \
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (value); \
+ ECBOR_INTERNAL_CHECK_TYPE ((item)->type, (etype)); \
+ (*value) = (btype) item->value.uinteger; \
+ return (item->size - 1 <= sizeof(btype) \
+ ? ECBOR_OK \
+ : ECBOR_ERR_VALUE_OVERFLOW); \
+}
+
+ecbor_error_t
+ecbor_get_uint8 (ecbor_item_t *item, uint8_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_UINT, uint8_t)
+
+ecbor_error_t
+ecbor_get_uint16 (ecbor_item_t *item, uint16_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_UINT, uint16_t)
+
+ecbor_error_t
+ecbor_get_uint32 (ecbor_item_t *item, uint32_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_UINT, uint32_t)
+
+ecbor_error_t
+ecbor_get_uint64 (ecbor_item_t *item, uint64_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_UINT, uint64_t)
+
+ecbor_error_t
+ecbor_get_int8 (ecbor_item_t *item, int8_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_NINT, int8_t)
+
+ecbor_error_t
+ecbor_get_int16 (ecbor_item_t *item, int16_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_NINT, int16_t)
+
+ecbor_error_t
+ecbor_get_int32 (ecbor_item_t *item, int32_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_NINT, int32_t)
+
+ecbor_error_t
+ecbor_get_int64 (ecbor_item_t *item, int64_t *value)
+ECBOR_GET_INTEGER_INTERNAL (item, value, ECBOR_TYPE_NINT, int64_t)
+
+#undef ECBOR_GET_INTEGER_INTERNAL
+
+
+static __attribute__((noinline)) ecbor_error_t
+ecbor_get_string_internal (ecbor_item_t *str, uint8_t **value,
+ ecbor_type_t type)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (str);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (value);
+ ECBOR_INTERNAL_CHECK_TYPE (str->type, type);
+
+ if (str->is_indefinite) {
+ return ECBOR_ERR_WONT_RETURN_INDEFINITE;
+ }
+
+ (*value) = (uint8_t *) str->value.string.str;
+
+ return ECBOR_OK;
+}
+
+static __attribute__((noinline)) ecbor_error_t
+ecbor_get_string_chunk_count_internal (ecbor_item_t *str, size_t *count,
+ ecbor_type_t type)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (str);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (count);
+ ECBOR_INTERNAL_CHECK_TYPE (str->type, type);
+
+ if (!str->is_indefinite) {
+ return ECBOR_ERR_WONT_RETURN_DEFINITE;
+ }
+
+ (*count) = str->value.string.n_chunks;
+
+ return ECBOR_OK;
+}
+
+static __attribute__((noinline)) ecbor_error_t
+ecbor_get_string_chunk_internal (ecbor_item_t *str, size_t index,
+ ecbor_item_t *chunk, ecbor_type_t type)
+{
+ ecbor_decode_context_t context;
+ ecbor_error_t rc;
+ size_t i;
+
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (str);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (chunk);
+ ECBOR_INTERNAL_CHECK_TYPE (str->type, type);
+
+ if (!str->is_indefinite) {
+ return ECBOR_ERR_WONT_RETURN_DEFINITE;
+ }
+ ECBOR_INTERNAL_CHECK_BOUNDS (index, str->value.string.n_chunks)
+
+ /* locate chunk */
+ rc = ecbor_initialize_decode (&context, str->value.string.str, str->size);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ for (i = 0; i <= index; i ++) {
+ rc = ecbor_decode (&context, chunk);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_BUFFER) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else {
+ return rc;
+ }
+ }
+ }
+
+ /* check chunk */
+ if (chunk->type != type) {
+ return ECBOR_ERR_INVALID_CHUNK_MAJOR_TYPE;
+ }
+ if (chunk->is_indefinite) {
+ return ECBOR_ERR_NESTET_INDEFINITE_STRING;
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_str (ecbor_item_t *str, char **value)
+{
+ return ecbor_get_string_internal (str, (uint8_t **)value, ECBOR_TYPE_STR);
+}
+
+ecbor_error_t
+ecbor_get_str_chunk_count (ecbor_item_t *str, size_t *count)
+{
+ return ecbor_get_string_chunk_count_internal (str, count, ECBOR_TYPE_STR);
+}
+
+ecbor_error_t
+ecbor_get_str_chunk (ecbor_item_t *str, size_t index, ecbor_item_t *chunk)
+{
+ return ecbor_get_string_chunk_internal (str, index, chunk, ECBOR_TYPE_STR);
+}
+
+ecbor_error_t
+ecbor_get_bstr (ecbor_item_t *str, uint8_t **value)
+{
+ return ecbor_get_string_internal (str, value, ECBOR_TYPE_BSTR);
+}
+
+ecbor_error_t
+ecbor_get_bstr_chunk_count (ecbor_item_t *str, size_t *count)
+{
+ return ecbor_get_string_chunk_count_internal (str, count, ECBOR_TYPE_BSTR);
+}
+
+ecbor_error_t
+ecbor_get_bstr_chunk (ecbor_item_t *str, size_t index, ecbor_item_t *chunk)
+{
+ return ecbor_get_string_chunk_internal (str, index, chunk, ECBOR_TYPE_BSTR);
+}
+
+
+ecbor_error_t
+ecbor_get_tag_value (ecbor_item_t *tag, uint64_t *tag_value)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (tag);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (tag_value);
+ ECBOR_INTERNAL_CHECK_TYPE (tag->type, ECBOR_TYPE_TAG);
+
+ (*tag_value) = tag->value.tag.tag_value;
+ return ECBOR_OK;
+}
+
+
+ecbor_error_t
+ecbor_get_fp32 (ecbor_item_t *item, float *value)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (value);
+ ECBOR_INTERNAL_CHECK_TYPE (item->type, ECBOR_TYPE_FP32);
+
+ (*value) = item->value.fp32;
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_get_fp64 (ecbor_item_t *item, double *value)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (value);
+ ECBOR_INTERNAL_CHECK_TYPE (item->type, ECBOR_TYPE_FP64);
+
+ (*value) = item->value.fp64;
+ return ECBOR_OK;
+}
+
+
+ecbor_error_t
+ecbor_get_bool (ecbor_item_t *item, uint8_t *value)
+{
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+ ECBOR_INTERNAL_CHECK_VALUE_PTR (value);
+ ECBOR_INTERNAL_CHECK_TYPE (item->type, ECBOR_TYPE_BOOL);
+
+ (*value) = (uint8_t) item->value.uinteger;
+ return ECBOR_OK;
+}
+
+void
+ecbor_memcpy (void *dest, void *src, size_t num)
+{
+ /* TODO: Replace with memcpy when platform supports */
+ /* TODO: Optimize */
+ while (num > 4) {
+ *((uint32_t *) dest) = *((uint32_t *) src);
+ num -= 4;
+ }
+ while (num) {
+ *((uint8_t *) dest) = *((uint8_t *) src);
+ num --;
+ }
+} \ No newline at end of file
diff --git a/src/libecbor/ecbor_decoder.c b/src/libecbor/ecbor_decoder.c
new file mode 100644
index 0000000..a422dd9
--- /dev/null
+++ b/src/libecbor/ecbor_decoder.c
@@ -0,0 +1,780 @@
+/*
+ * Copyright (c) 2018 Vasile Vilvoiu <vasi.vilvoiu@gmail.com>
+ *
+ * libecbor is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include "ecbor.h"
+#include "ecbor_internal.h"
+
+static ecbor_error_t
+ecbor_initialize_decode_internal (ecbor_decode_context_t *context,
+ const uint8_t *buffer,
+ size_t buffer_size)
+{
+ ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context);
+ if (!buffer) {
+ return ECBOR_ERR_NULL_INPUT_BUFFER;
+ }
+
+ context->in_position = buffer;
+ context->bytes_left = buffer_size;
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_initialize_decode (ecbor_decode_context_t *context,
+ const uint8_t *buffer,
+ size_t buffer_size)
+{
+ ecbor_error_t rc =
+ ecbor_initialize_decode_internal (context, buffer, buffer_size);
+
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ context->mode = ECBOR_MODE_DECODE;
+ context->items = NULL;
+ context->item_capacity = 0;
+ context->n_items = 0;
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_initialize_decode_streamed (ecbor_decode_context_t *context,
+ const uint8_t *buffer,
+ size_t buffer_size)
+{
+ ecbor_error_t rc =
+ ecbor_initialize_decode_internal (context, buffer, buffer_size);
+
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ context->mode = ECBOR_MODE_DECODE_STREAMED;
+ context->items = NULL;
+ context->item_capacity = 0;
+ context->n_items = 0;
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_initialize_decode_tree (ecbor_decode_context_t *context,
+ const uint8_t *buffer,
+ size_t buffer_size,
+ ecbor_item_t *item_buffer,
+ size_t item_capacity)
+{
+ ecbor_error_t rc =
+ ecbor_initialize_decode_internal (context, buffer, buffer_size);
+
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ if (!item_buffer) {
+ return ECBOR_ERR_NULL_ITEM_BUFFER;
+ }
+
+ context->mode = ECBOR_MODE_DECODE_TREE;
+ context->items = item_buffer;
+ context->item_capacity = item_capacity;
+ context->n_items = 0;
+
+ return ECBOR_OK;
+}
+
+static inline ecbor_error_t
+ecbor_decode_uint (ecbor_decode_context_t *context,
+ uint64_t *value,
+ size_t *size,
+ uint8_t additional)
+{
+ if (additional < 24) {
+ /* value stored in additional information */
+ (*value) = additional;
+ (*size) = 1;
+ } else {
+ /* compute storage size */
+ (*size) = (0x1 << (additional - ECBOR_ADDITIONAL_1BYTE));
+ if (context->bytes_left < (*size)) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+
+ /* read actual value */
+ switch (additional) {
+ case ECBOR_ADDITIONAL_1BYTE:
+ (*value) = *((uint8_t *) context->in_position);
+ break;
+
+ case ECBOR_ADDITIONAL_2BYTE:
+ (*value) =
+ ecbor_uint16_from_big_endian (*((uint16_t *) context->in_position));
+ break;
+
+ case ECBOR_ADDITIONAL_4BYTE:
+ (*value) =
+ ecbor_uint32_from_big_endian (*((uint32_t *) context->in_position));
+ break;
+
+ case ECBOR_ADDITIONAL_8BYTE:
+ (*value) =
+ ecbor_uint64_from_big_endian (*((uint64_t *) context->in_position));
+ break;
+
+ default:
+ return ECBOR_ERR_INVALID_ADDITIONAL;
+ }
+
+ /* advance buffer */
+ context->in_position += (*size);
+ context->bytes_left -= (*size);
+
+ /* meter the first byte */
+ (*size) ++;
+ }
+
+ return ECBOR_OK;
+}
+
+static inline ecbor_error_t
+ecbor_decode_fp32 (ecbor_decode_context_t *context,
+ float *value,
+ size_t *size)
+{
+ /* compute storage size */
+ (*size) = sizeof (float);
+ if (context->bytes_left < (*size)) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+
+ /* read actual value */
+ (*value) =
+ ecbor_fp32_from_big_endian (*((float *) context->in_position));
+
+ /* advance buffer */
+ context->in_position += (*size);
+ context->bytes_left -= (*size);
+
+ /* meter the first byte */
+ (*size) ++;
+
+ return ECBOR_OK;
+}
+
+static inline ecbor_error_t
+ecbor_decode_fp64 (ecbor_decode_context_t *context,
+ double *value,
+ size_t *size)
+{
+ /* compute storage size */
+ (*size) = sizeof (double);
+ if (context->bytes_left < (*size)) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+
+ /* read actual value */
+ (*value) =
+ ecbor_fp64_from_big_endian (*((double *) context->in_position));
+
+ /* advance buffer */
+ context->in_position += (*size);
+ context->bytes_left -= (*size);
+
+ /* meter the first byte */
+ (*size) ++;
+
+ return ECBOR_OK;
+}
+
+static inline ecbor_error_t
+ecbor_decode_simple_value (ecbor_item_t *item)
+{
+ switch (item->value.uinteger) {
+ case ECBOR_SIMPLE_FALSE:
+ case ECBOR_SIMPLE_TRUE:
+ item->type = ECBOR_TYPE_BOOL;
+ item->value.uinteger =
+ (item->value.uinteger == ECBOR_SIMPLE_FALSE ? 0 : 1);
+ break;
+
+ case ECBOR_SIMPLE_NULL:
+ item->type = ECBOR_TYPE_NULL;
+ break;
+
+ case ECBOR_SIMPLE_UNDEFINED:
+ item->type = ECBOR_TYPE_UNDEFINED;
+ break;
+
+ default:
+ return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED;
+ }
+
+ return ECBOR_OK;
+}
+
+static __attribute__((noinline)) ecbor_error_t
+ecbor_decode_next_internal (ecbor_decode_context_t *context,
+ ecbor_item_t *item,
+ int8_t is_chunk,
+ ecbor_type_t chunk_mtype)
+{
+ uint8_t additional;
+
+ if (context->bytes_left == 0) {
+ return ECBOR_END_OF_BUFFER;
+ }
+ if (context->mode != ECBOR_MODE_DECODE
+ && context->mode != ECBOR_MODE_DECODE_STREAMED) {
+ /* only allow known modes of operation; junk in <mode> will generate
+ undefined behaviour */
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ /* clear item, just so we do not leave garbage on partial read */
+ (*item) = null_item;
+
+ /* extract major type (most significant three bits) and additional info */
+ item->type = (*context->in_position >> 5) & 0x07;
+ additional = (*context->in_position & 0x1f);
+ context->in_position ++; context->bytes_left --;
+
+ /* check mandatory major type (in case we are reading string chunks);
+ we do not want to continue parsing a malformed indefinite string and
+ potentially explode the stack with subsequent calls */
+ if (is_chunk && chunk_mtype != item->type) {
+ if (item->type == (ecbor_type_t) ECBOR_TYPE_SPECIAL
+ && additional == ECBOR_ADDITIONAL_INDEFINITE) {
+ /* this is a valid stop code, pass it directly; note that this branch is
+ only taken when inside an indefinite string */
+ return ECBOR_END_OF_INDEFINITE;
+ } else {
+ /* this is not a stop code, and the item has the wrong major type */
+ return ECBOR_ERR_INVALID_CHUNK_MAJOR_TYPE;
+ }
+ }
+
+ /* handle type */
+ switch (item->type) {
+
+ /*
+ * Integer types
+ */
+ case ECBOR_TYPE_UINT:
+ return ecbor_decode_uint (context, &item->value.uinteger, &item->size,
+ additional);
+
+ case ECBOR_TYPE_NINT:
+ {
+ /* read negative value as unsigned */
+ ecbor_error_t rc = ecbor_decode_uint (context, &item->value.uinteger,
+ &item->size, additional);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* parse as negative */
+ item->value.integer = (-1) - item->value.uinteger;
+ }
+ break;
+
+ /*
+ * String types
+ */
+ case ECBOR_TYPE_BSTR:
+ case ECBOR_TYPE_STR:
+ /* keep buffer pointer from current pointer */
+ item->value.string.str = context->in_position;
+ item->value.string.n_chunks = 0;
+
+ /* discriminate between definite and indefinite strings; we do not treat
+ * indefinite strings as we do indefinite maps and arrays, in the sense
+ * that we do not allow the user to manually walk each chunk
+ */
+ if (additional == ECBOR_ADDITIONAL_INDEFINITE) {
+ ecbor_item_t chunk;
+ ecbor_error_t rc;
+
+ /* mark accordingly */
+ item->is_indefinite = true;
+ item->size = 1; /* already processed first byte */
+
+ /* do not allow nested indefinite length strings */
+ if (is_chunk) {
+ return ECBOR_ERR_NESTET_INDEFINITE_STRING;
+ }
+
+ /* indefinite lenght string; read through all blocks to compute size */
+ item->value.string.str = context->in_position;
+
+ while (true) {
+ /* read next chunk */
+ rc = ecbor_decode_next_internal (context, &chunk, true,
+ item->type);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_INDEFINITE) {
+ /* stop code found, break from loop */
+ item->size += chunk.size; /* meter stop code as well */
+ break;
+ } else if (rc == ECBOR_END_OF_BUFFER) {
+ /* treat a valid end of buffer as invalid since we did not yet
+ find the stop code */
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else if (rc != ECBOR_OK) {
+ /* other error */
+ return rc;
+ }
+ }
+
+ /* add chunk size and length to item */
+ item->size += chunk.size;
+ item->length += chunk.length;
+ item->value.string.n_chunks ++;
+ }
+ } else {
+ /* read size of buffer */
+ uint64_t len;
+ ecbor_error_t rc = ecbor_decode_uint (context, &len, &item->size,
+ additional);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ /* if sizeof(size_t) < sizeof(uint64_t), and payload is >4GB, we're
+ fucked */
+ item->length = len;
+
+ /* advance */
+ if (context->bytes_left < item->length) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+ context->in_position += item->length;
+ context->bytes_left -= item->length;
+
+ /* meter length of string to size of item */
+ item->size += item->length;
+ }
+ break;
+
+ /*
+ * Arrays and maps
+ */
+ case ECBOR_TYPE_ARRAY:
+ case ECBOR_TYPE_MAP:
+ /* discriminate between definite and indefinite maps and arrays */
+ if (additional == ECBOR_ADDITIONAL_INDEFINITE) {
+ /* mark accordingly */
+ item->is_indefinite = true;
+ item->size = 1; /* already processed first byte */
+
+ /* keep buffer pointer from current pointer */
+ item->value.items = context->in_position;
+
+ if (context->mode != ECBOR_MODE_DECODE_STREAMED) {
+ /* we have an indefinite map or array and we're not in streamed mode;
+ we have to walk children to compute size and advance to next item */
+ ecbor_item_t child;
+ ecbor_error_t rc;
+
+ while (true) {
+ /* read next chunk */
+ rc = ecbor_decode_next_internal (context, &child, false,
+ ECBOR_TYPE_NONE);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_INDEFINITE) {
+ /* stop code found, break from loop */
+ item->size += child.size; /* meter stop code as well */
+ break;
+ } else if (rc == ECBOR_END_OF_BUFFER) {
+ /* treat a valid end of buffer as invalid since we did not yet
+ find the stop code */
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else if (rc != ECBOR_OK) {
+ /* other error */
+ return rc;
+ }
+ }
+
+ /* add chunk size to item size */
+ item->size += child.size;
+ item->length ++;
+ }
+
+ if ((item->type == ECBOR_TYPE_MAP) && (item->length % 2 != 0)) {
+ /* incomplete key-value pair; we expect maps to have even number of
+ items */
+ return ECBOR_ERR_INVALID_KEY_VALUE_PAIR;
+ }
+ }
+ } else {
+ uint64_t len;
+
+ /* read size of map or array */
+ ecbor_error_t rc = ecbor_decode_uint (context, &len,
+ &item->size, additional);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* improbable to have a map larger than 4M, but if we do and size_t is
+ less than 64bit, this will create problems */
+ item->length = len;
+
+ /* keep buffer pointer from current pointer */
+ item->value.items = context->in_position;
+
+ if (item->type == ECBOR_TYPE_MAP) {
+ /* we keep the total number of items in length, yet the map has the
+ number of key-value pairs encoded in the length */
+ item->length *= 2;
+ }
+
+ if (context->mode != ECBOR_MODE_DECODE_STREAMED) {
+ ecbor_item_t child;
+ ecbor_error_t rc;
+ size_t child_no;
+
+ /* not in streamed mode; compute size so we can advance */
+ for (child_no = 0; child_no < item->length; child_no ++) {
+ /* read next child */
+ rc = ecbor_decode_next_internal (context, &child, false,
+ ECBOR_TYPE_NONE);
+ if (rc != ECBOR_OK) {
+ if (rc == ECBOR_END_OF_INDEFINITE) {
+ /* stop code found, but none is expected */
+ return ECBOR_ERR_INVALID_STOP_CODE;
+ } else if (rc == ECBOR_END_OF_BUFFER) {
+ /* treat a valid end of buffer as invalid since we did not yet
+ reach the end of the map or array */
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ } else if (rc != ECBOR_OK) {
+ /* other error */
+ return rc;
+ }
+ }
+
+ /* add child size to item size */
+ item->size += child.size;
+ }
+ }
+ }
+ break;
+
+ case ECBOR_TYPE_TAG:
+ {
+ ecbor_error_t rc;
+
+ /* decode tag */
+ rc = ecbor_decode_uint (context, &item->value.tag.tag_value,
+ &item->size, additional);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* keep child pointer */
+ item->value.tag.child = context->in_position;
+ item->length = 1;
+
+ if (context->mode != ECBOR_MODE_DECODE_STREAMED) {
+ ecbor_item_t child;
+
+ /* not in streamed mode; compute size so we can advance */
+ rc = ecbor_decode_next_internal (context, &child, false,
+ ECBOR_TYPE_NONE);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* add child size to item size */
+ item->size += child.size;
+ }
+ }
+ break;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+ case ECBOR_TYPE_SPECIAL:
+#pragma GCC diagnostic pop
+ if (additional == ECBOR_ADDITIONAL_INDEFINITE) {
+ /* stop code */
+ item->size = 1;
+ item->type = ECBOR_TYPE_STOP_CODE;
+ return ECBOR_END_OF_INDEFINITE;
+ } else if (additional <= ECBOR_ADDITIONAL_1BYTE) {
+ ecbor_error_t rc;
+ /* read integer simple value */
+ rc = ecbor_decode_uint (context, &item->value.uinteger, &item->size,
+ additional);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* parse simple value */
+ return ecbor_decode_simple_value (item);
+ } else if (additional == ECBOR_ADDITIONAL_2BYTE) {
+ /* currently we do not support half float */
+ return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED;
+ } else if (additional == ECBOR_ADDITIONAL_4BYTE) {
+ /* floating point 32bit */
+ item->type = ECBOR_TYPE_FP32;
+ return ecbor_decode_fp32 (context, &item->value.fp32, &item->size);
+ } else if (additional == ECBOR_ADDITIONAL_8BYTE) {
+ /* floating point 64bit */
+ item->type = ECBOR_TYPE_FP64;
+ return ecbor_decode_fp64 (context, &item->value.fp64, &item->size);
+ } else {
+ /* currently unassigned according to RFC */
+ return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED;
+ }
+ break;
+
+ default:
+ /* while this would theoretically be a case for invalid type, any 3 bit
+ value should be a valid type, so this branch is an internal error and
+ should never happen */
+ return ECBOR_ERR_UNKNOWN;
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_decode (ecbor_decode_context_t *context, ecbor_item_t *item)
+{
+ ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+
+ if (context->mode != ECBOR_MODE_DECODE
+ && context->mode != ECBOR_MODE_DECODE_STREAMED) {
+ /* context is for wrong mode */
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ /* we just get the next item */
+ return ecbor_decode_next_internal (context, item, false, ECBOR_TYPE_NONE);
+}
+
+extern ecbor_error_t
+ecbor_decode_tree (ecbor_decode_context_t *context, ecbor_item_t **root)
+{
+ enum {
+ CONSUME_NODE = 0,
+ ANALYZE_STOP_CODE,
+ LINK_FIRST_NODE,
+ LINK_NODE,
+ CHECK_END_OF_DEFINITE,
+ CHECK_END,
+ END
+ } state = CONSUME_NODE;
+ uint8_t last_was_stop_code = 0;
+ ecbor_error_t rc = ECBOR_OK;
+ ecbor_item_t *curr_node = NULL, *new_node = NULL;
+
+ ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (root);
+
+ if (context->mode != ECBOR_MODE_DECODE_TREE) {
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ /* initialization */
+ context->n_items = 0;
+ (*root) = NULL;
+
+ /* step into streamed mode; some of the semantic checks will be done here */
+ context->mode = ECBOR_MODE_DECODE_STREAMED;
+
+ /* consume until end of buffer or error */
+ while (state != END) {
+
+ /* state change */
+ switch (state) {
+ case CONSUME_NODE:
+ /* allocate new node */
+ if (context->n_items >= context->item_capacity) {
+ rc = ECBOR_ERR_END_OF_ITEM_BUFFER;
+ goto end;
+ }
+
+ new_node = &context->items[context->n_items];
+ context->n_items ++;
+
+ /* consume next item */
+ rc = ecbor_decode_next_internal (context, new_node, false,
+ ECBOR_TYPE_NONE);
+ if (rc == ECBOR_END_OF_INDEFINITE) {
+ state = ANALYZE_STOP_CODE;
+ context->n_items --;
+ rc = ECBOR_OK;
+ } else if (rc == ECBOR_END_OF_BUFFER) {
+ state = CHECK_END;
+ rc = ECBOR_OK;
+ } else if (rc == ECBOR_OK) {
+ state = (curr_node ? LINK_NODE : LINK_FIRST_NODE);
+ } else {
+ /* some kind of error */
+ goto end;
+ }
+ break;
+
+ case ANALYZE_STOP_CODE:
+ if ((!ECBOR_IS_MAP (curr_node) && !ECBOR_IS_ARRAY (curr_node))
+ || ECBOR_IS_DEFINITE (curr_node)
+ || (ECBOR_IS_INDEFINITE (curr_node) && last_was_stop_code)) {
+ /* stop code refers to parent node */
+ curr_node = curr_node->parent;
+ if (!curr_node) {
+ rc = ECBOR_ERR_UNKNOWN;
+ goto end;
+ }
+ }
+
+ if ((ECBOR_IS_MAP (curr_node) || ECBOR_IS_ARRAY (curr_node))
+ && ECBOR_IS_INDEFINITE (curr_node)) {
+ /* correct stop code */
+ state = CHECK_END_OF_DEFINITE;
+ /* set stop code flag; this needs to be reset when curr_node changes */
+ last_was_stop_code = 1;
+ } else {
+ rc = ECBOR_ERR_INVALID_STOP_CODE;
+ goto end;
+ }
+ break;
+
+ case LINK_FIRST_NODE:
+ /* first node, skip checks */
+ curr_node = new_node;
+ new_node->index = 0;
+ state = CONSUME_NODE;
+ break;
+
+ case LINK_NODE:
+ {
+ uint8_t is_unfinished_tag =
+ ECBOR_IS_TAG (curr_node) && !curr_node->child;
+ uint8_t is_unfinished_array_or_map =
+ (ECBOR_IS_ARRAY (curr_node) || ECBOR_IS_MAP (curr_node))
+ && ((ECBOR_IS_DEFINITE (curr_node) && (curr_node->length > 0)
+ && !curr_node->child)
+ || (ECBOR_IS_INDEFINITE (curr_node) && !last_was_stop_code));
+
+ if (is_unfinished_tag || is_unfinished_array_or_map) {
+ /* link as child */
+ curr_node->child = new_node;
+ new_node->parent = curr_node;
+ new_node->index = 0;
+ } else {
+ /* link as sibling */
+ curr_node->next = new_node;
+ new_node->parent = curr_node->parent;
+ new_node->index = curr_node->index + 1;
+ }
+
+ /* new current node */
+ curr_node = new_node;
+ last_was_stop_code = 0;
+
+ /* count indefinite arrays and maps */
+ if (curr_node->parent
+ && (ECBOR_IS_ARRAY (curr_node->parent)
+ || ECBOR_IS_MAP (curr_node->parent))
+ && ECBOR_IS_INDEFINITE (curr_node->parent)) {
+ curr_node->parent->length ++;
+ }
+
+ /* check end of definite arrays and maps */
+ state = CHECK_END_OF_DEFINITE;
+ }
+ break;
+
+ case CHECK_END_OF_DEFINITE:
+ {
+ uint8_t is_unfinished_tag =
+ ECBOR_IS_TAG (curr_node) && !curr_node->child;
+ uint8_t is_unfinished_array_or_map =
+ (ECBOR_IS_ARRAY (curr_node) || ECBOR_IS_MAP (curr_node))
+ && ((ECBOR_IS_DEFINITE (curr_node) && (curr_node->length > 0)
+ && !curr_node->child)
+ || (ECBOR_IS_INDEFINITE (curr_node) && !last_was_stop_code));
+
+ if (!is_unfinished_tag && !is_unfinished_array_or_map) {
+ while (curr_node->parent
+ && (((ECBOR_IS_ARRAY (curr_node->parent) || ECBOR_IS_MAP (curr_node->parent))
+ && ECBOR_IS_DEFINITE (curr_node->parent)
+ && (curr_node->parent->length == (curr_node->index + 1)))
+ ||
+ (ECBOR_IS_TAG (curr_node->parent)))) {
+ /* up one level */
+ curr_node = curr_node->parent;
+ last_was_stop_code = 0;
+ }
+ }
+
+ /* consume next */
+ state = CONSUME_NODE;
+ }
+ break;
+
+ case CHECK_END:
+ /* check for bad end scenatios */
+ if (!curr_node) {
+ /* first node was stop code, bad end */
+ rc = ECBOR_ERR_INVALID_END_OF_BUFFER;
+ goto end;
+ }
+ if (curr_node->parent) {
+ /* we're not top level, bad end */
+ rc = ECBOR_ERR_INVALID_END_OF_BUFFER;
+ goto end;
+ }
+ if (ECBOR_IS_TAG (curr_node) && !curr_node->child) {
+ /* unfinished tag, bad end */
+ rc = ECBOR_ERR_INVALID_END_OF_BUFFER;
+ goto end;
+ }
+ if (ECBOR_IS_MAP (curr_node) || ECBOR_IS_ARRAY (curr_node)) {
+ if (ECBOR_IS_DEFINITE (curr_node) && !curr_node->child
+ && curr_node->length > 0) {
+ /* unfinished definite array or map, bad end */
+ rc = ECBOR_ERR_INVALID_END_OF_BUFFER;
+ goto end;
+ }
+ if (ECBOR_IS_INDEFINITE (curr_node) && !last_was_stop_code) {
+ /* unfinished indefinite array or map, bad end */
+ rc = ECBOR_ERR_INVALID_END_OF_BUFFER;
+ goto end;
+ }
+ }
+
+ /* done! */
+ state = END;
+ break;
+
+ default:
+ rc = ECBOR_ERR_UNKNOWN;
+ goto end;
+ }
+ }
+
+end:
+ /* return to tree mode and return error code */
+ context->mode = ECBOR_MODE_DECODE_TREE;
+
+ rc = (rc == ECBOR_END_OF_BUFFER ? ECBOR_OK : rc);
+ if (rc != ECBOR_OK) {
+ /* make sure we don't expose garbage to user */
+ context->n_items = 0;
+ }
+
+ /* return root node */
+ if (context->n_items > 0) {
+ (*root) = &context->items[0];
+ }
+
+ return rc;
+} \ No newline at end of file
diff --git a/src/libecbor/ecbor_encoder.c b/src/libecbor/ecbor_encoder.c
new file mode 100644
index 0000000..b144644
--- /dev/null
+++ b/src/libecbor/ecbor_encoder.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 2018 Vasile Vilvoiu <vasi.vilvoiu@gmail.com>
+ *
+ * libecbor is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include "ecbor.h"
+#include "ecbor_internal.h"
+
+ecbor_error_t
+ecbor_initialize_encode (ecbor_encode_context_t *context,
+ uint8_t *buffer,
+ size_t buffer_size)
+{
+ ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context);
+ if (!buffer) {
+ return ECBOR_ERR_NULL_OUTPUT_BUFFER;
+ }
+
+ context->out_position = buffer;
+ context->bytes_left = buffer_size;
+ context->mode = ECBOR_MODE_ENCODE;
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_initialize_encode_streamed (ecbor_encode_context_t *context,
+ uint8_t *buffer,
+ size_t buffer_size)
+{
+ ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context);
+ if (!buffer) {
+ return ECBOR_ERR_NULL_OUTPUT_BUFFER;
+ }
+
+ context->out_position = buffer;
+ context->bytes_left = buffer_size;
+ context->mode = ECBOR_MODE_ENCODE_STREAMED;
+
+ return ECBOR_OK;
+}
+
+static ecbor_error_t
+ecbor_encode_uint (ecbor_encode_context_t *context, uint8_t major_type,
+ uint64_t value)
+{
+ uint8_t size = 0;
+
+ /* compute storage size */
+ if (value <= ECBOR_ADDITIONAL_LAST_INTEGER) {
+ size = 1;
+ } else if (value <= 0xFF) {
+ size = 2;
+ } else if (value <= 0xFFFF) {
+ size = 3;
+ } else if (value <= 0xFFFFFFFF) {
+ size = 5;
+ } else {
+ size = 9;
+ }
+
+ /* check buffer */
+ if (context->bytes_left < size) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+
+ /* write value */
+ if (value <= ECBOR_ADDITIONAL_LAST_INTEGER) {
+ /* value coded in header */
+ (*context->out_position) = ((major_type & 0x7) << 5) | (value & 0x1f);
+ context->out_position ++;
+ context->bytes_left --;
+ } else if (value <= 0xFF) {
+ /* 1 extra byte */
+ (*context->out_position) =
+ ((major_type & 0x7) << 5) | ECBOR_ADDITIONAL_1BYTE;
+ context->out_position ++;
+ (*context->out_position) = (uint8_t) value;
+ context->out_position ++;
+ context->bytes_left -= 2;
+ } else if (value <= 0xFFFF) {
+ /* 2 extra bytes */
+ (*context->out_position) =
+ ((major_type & 0x7) << 5) | ECBOR_ADDITIONAL_2BYTE;
+ context->out_position ++;
+ (*((uint16_t *)context->out_position)) =
+ ecbor_uint16_to_big_endian ((uint16_t) value);
+ context->out_position += 2;
+ context->bytes_left -= 3;
+ } else if (value <= 0xFFFFFFFF) {
+ /* 4 extra bytes */
+ (*context->out_position) =
+ ((major_type & 0x7) << 5) | ECBOR_ADDITIONAL_4BYTE;
+ context->out_position ++;
+ (*((uint32_t *)context->out_position)) =
+ ecbor_uint32_to_big_endian ((uint32_t) value);
+ context->out_position += 4;
+ context->bytes_left -= 5;
+ } else {
+ /* 8 extra bytes */
+ (*context->out_position) =
+ ((major_type & 0x7) << 5) | ECBOR_ADDITIONAL_8BYTE;
+ context->out_position ++;
+ (*((uint64_t *)context->out_position)) =
+ ecbor_uint64_to_big_endian ((uint64_t) value);
+ context->out_position += 8;
+ context->bytes_left -= 9;
+ }
+
+ return ECBOR_OK;
+}
+
+static ecbor_error_t
+ecbor_encode_header (ecbor_encode_context_t *context, uint8_t major_type,
+ uint8_t additional)
+{
+ if (context->bytes_left < 1) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+
+ (*context->out_position) =
+ ((((uint8_t)major_type) & 0x7) << 5) | (additional & 0x1f);
+ context->out_position ++;
+ context->bytes_left --;
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_encode (ecbor_encode_context_t *context, ecbor_item_t *item)
+{
+ ecbor_error_t rc;
+
+ ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item);
+
+ if (context->bytes_left == 0) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+ if (context->mode != ECBOR_MODE_ENCODE
+ && context->mode != ECBOR_MODE_ENCODE_STREAMED) {
+ /* only allow known modes of operation; junk in <mode> will generate
+ undefined behaviour */
+ return ECBOR_ERR_WRONG_MODE;
+ }
+
+ switch (item->type) {
+ case ECBOR_TYPE_UINT:
+ rc = ecbor_encode_uint (context, ECBOR_TYPE_UINT, item->value.uinteger);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ break;
+
+ case ECBOR_TYPE_NINT:
+ {
+ uint64_t value = (-1) - item->value.integer;
+ rc = ecbor_encode_uint (context, ECBOR_TYPE_NINT, value);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ }
+ break;
+
+ case ECBOR_TYPE_BSTR:
+ case ECBOR_TYPE_STR:
+ if (item->is_indefinite) {
+ if (context->mode == ECBOR_MODE_ENCODE) {
+ /* we do not support indefinite strings in non-stream mode */
+ return ECBOR_ERR_WONT_ENCODE_INDEFINITE;
+ }
+
+ /* write just header */
+ rc = ecbor_encode_header (context, item->type,
+ ECBOR_ADDITIONAL_INDEFINITE);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ } else {
+ /* write header */
+ rc = ecbor_encode_uint (context, item->type, item->length);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ if (context->mode == ECBOR_MODE_ENCODE && item->length > 0) {
+ /* write string */
+ if (!item->value.string.str) {
+ return ECBOR_ERR_NULL_VALUE;
+ }
+ if (context->bytes_left < item->length) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+
+ ecbor_memcpy ((void *) context->out_position,
+ (void *) item->value.string.str,
+ item->length);
+ context->out_position += item->length;
+ context->bytes_left -= item->length;
+ }
+ }
+ break;
+
+ case ECBOR_TYPE_ARRAY:
+ case ECBOR_TYPE_MAP:
+ if (item->is_indefinite) {
+ if (context->mode == ECBOR_MODE_ENCODE) {
+ /* we do not support indefinite maps and arrays in non-stream mode */
+ return ECBOR_ERR_WONT_ENCODE_INDEFINITE;
+ }
+
+ /* write just header */
+ rc = ecbor_encode_header (context, item->type,
+ ECBOR_ADDITIONAL_INDEFINITE);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ } else {
+ size_t written_len;
+
+ if (item->type == ECBOR_TYPE_MAP) {
+ if (item->length % 2) {
+ return ECBOR_ERR_INVALID_KEY_VALUE_PAIR;
+ }
+ written_len = item->length / 2;
+ } else {
+ written_len = item->length;
+ }
+
+ /* write header */
+ rc = ecbor_encode_uint (context, item->type, written_len);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ if (context->mode == ECBOR_MODE_ENCODE && item->length > 0) {
+ size_t remaining = item->length;
+ ecbor_item_t *current = item->child;
+
+ while (remaining) {
+ /* write item */
+ if (!current) {
+ return ECBOR_ERR_NULL_ITEM;
+ }
+
+ rc = ecbor_encode (context, current);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ current = current->next;
+ }
+ }
+ }
+ break;
+
+ case ECBOR_TYPE_TAG:
+ /* write header */
+ rc = ecbor_encode_uint (context, item->type, item->value.tag.tag_value);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ if (context->mode == ECBOR_MODE_ENCODE) {
+ /* write tagged item */
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (item->child);
+
+ rc = ecbor_encode (context, item->child);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ }
+ break;
+
+ case ECBOR_TYPE_STOP_CODE:
+ rc = ecbor_encode_header (context, ECBOR_TYPE_SPECIAL,
+ ECBOR_ADDITIONAL_INDEFINITE);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ break;
+
+ case ECBOR_TYPE_FP32:
+ {
+ float value_bigend =
+ ecbor_fp32_to_big_endian (item->value.fp32);
+
+ /* write header */
+ rc = ecbor_encode_header (context, ECBOR_TYPE_SPECIAL,
+ ECBOR_ADDITIONAL_4BYTE);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* write value */
+ if (context->bytes_left < sizeof (float)) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+ (*((float *)context->out_position)) = value_bigend;
+ context->out_position += sizeof (float);
+ context->bytes_left -= sizeof (float);
+ }
+ break;
+
+ case ECBOR_TYPE_FP64:
+ {
+ double value_bigend =
+ ecbor_fp64_to_big_endian (item->value.fp64);
+
+ /* write header */
+ rc = ecbor_encode_header (context, ECBOR_TYPE_SPECIAL,
+ ECBOR_ADDITIONAL_4BYTE);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+
+ /* write value */
+ if (context->bytes_left < sizeof (double)) {
+ return ECBOR_ERR_INVALID_END_OF_BUFFER;
+ }
+ (*((double *)context->out_position)) = value_bigend;
+ context->out_position += sizeof (double);
+ context->bytes_left -= sizeof (double);
+ }
+ break;
+
+ case ECBOR_TYPE_BOOL:
+ rc = ecbor_encode_header (context, ECBOR_TYPE_SPECIAL,
+ (item->value.integer ? ECBOR_SIMPLE_TRUE
+ : ECBOR_SIMPLE_FALSE));
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ break;
+
+ case ECBOR_TYPE_NULL:
+ rc = ecbor_encode_header (context, ECBOR_TYPE_SPECIAL,
+ ECBOR_SIMPLE_NULL);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ break;
+
+ case ECBOR_TYPE_UNDEFINED:
+ rc = ecbor_encode_header (context, ECBOR_TYPE_SPECIAL,
+ ECBOR_SIMPLE_UNDEFINED);
+ if (rc != ECBOR_OK) {
+ return rc;
+ }
+ break;
+
+ default:
+ return ECBOR_ERR_INVALID_TYPE;
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_item_t
+ecbor_int (int64_t value)
+{
+ ecbor_item_t r = null_item;
+ if (value >= 0) {
+ r.type = ECBOR_TYPE_UINT;
+ r.value.uinteger = (uint64_t) value;
+ } else {
+ r.type = ECBOR_TYPE_NINT;
+ r.value.integer = value;
+ }
+ return r;
+}
+
+ecbor_item_t
+ecbor_uint (int64_t value)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_UINT;
+ r.value.uinteger = (uint64_t) value;
+ return r;
+}
+
+ecbor_item_t
+ecbor_bstr (uint8_t *bstr, size_t length)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_BSTR;
+ r.value.string.str = bstr;
+ r.length = length;
+ return r;
+}
+
+ecbor_item_t
+ecbor_str (char *str, size_t length)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_STR;
+ r.value.string.str = (uint8_t *)str;
+ r.length = length;
+ return r;
+}
+
+ecbor_item_t
+ecbor_tag (ecbor_item_t *child, uint64_t tag_value)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_TAG;
+ r.child = child;
+ r.value.tag.tag_value = tag_value;
+ return r;
+}
+
+ecbor_item_t
+ecbor_fp32 (float value)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_FP32;
+ r.value.fp32 = value;
+ return r;
+}
+
+ecbor_item_t
+ecbor_fp64 (double value)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_FP64;
+ r.value.fp64 = value;
+ return r;
+}
+
+ecbor_item_t
+ecbor_bool (uint8_t value)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_BOOL;
+ r.value.integer = value;
+ return r;
+}
+
+ecbor_item_t
+ecbor_null (void)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_NULL;
+ return r;
+}
+
+ecbor_item_t
+ecbor_undefined (void)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_UNDEFINED;
+ return r;
+}
+
+ecbor_item_t
+ecbor_array_token (size_t length)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_ARRAY;
+ r.length = length;
+ return r;
+}
+
+ecbor_item_t
+ecbor_indefinite_array_token (void)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_ARRAY;
+ r.is_indefinite = true;
+ return r;
+}
+
+ecbor_item_t
+ecbor_map_token (size_t length)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_MAP;
+ r.length = length;
+ return r;
+}
+
+ecbor_item_t
+ecbor_indefinite_map_token (void)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_MAP;
+ r.is_indefinite = true;
+ return r;
+}
+
+ecbor_item_t
+ecbor_stop_code (void)
+{
+ ecbor_item_t r = null_item;
+ r.type = ECBOR_TYPE_STOP_CODE;
+ return r;
+}
+
+
+/* Array and map builders */
+ecbor_error_t
+ecbor_array (ecbor_item_t *array, ecbor_item_t *items, size_t length)
+{
+ size_t i;
+
+ if (!array) {
+ return ECBOR_ERR_NULL_ARRAY;
+ }
+
+ array->type = ECBOR_TYPE_ARRAY;
+ array->length = length;
+ array->child = items;
+
+ if (length > 0) {
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (items);
+ items->parent = array;
+
+ for (i = 1; i < length; i ++) {
+ items->next = (items + 1);
+ items ++;
+ }
+ }
+
+ return ECBOR_OK;
+}
+
+ecbor_error_t
+ecbor_map (ecbor_item_t *map, ecbor_item_t *keys, ecbor_item_t *values,
+ size_t length)
+{
+ size_t i;
+
+ if (!map) {
+ return ECBOR_ERR_NULL_MAP;
+ }
+
+ map->type = ECBOR_TYPE_MAP;
+ map->length = length;
+
+ if (length > 0) {
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (keys);
+ ECBOR_INTERNAL_CHECK_ITEM_PTR (values);
+
+ map->child = keys;
+ keys->parent = map;
+ keys->next = values;
+ values->parent = map;
+ keys ++;
+ values ++;
+
+ for (i = 1; i < length; i ++) {
+ values->next = (keys + 1);
+ keys ++;
+ keys->next = (values + 1);
+ values ++;
+ }
+ }
+
+ return ECBOR_OK;
+} \ No newline at end of file
diff --git a/src/libecbor/ecbor_internal.h b/src/libecbor/ecbor_internal.h
new file mode 100644
index 0000000..9612c1a
--- /dev/null
+++ b/src/libecbor/ecbor_internal.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2018 Vasile Vilvoiu <vasi.vilvoiu@gmail.com>
+ *
+ * libecbor is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#ifndef _ECBOR_INTERNAL_H_
+#define _ECBOR_INTERNAL_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* Don't rely on <stdbool.h> for this */
+#define false 0
+#define true 1
+
+/* CBOR major type 7 must not be exposed to user, but translated to other types */
+enum {
+ ECBOR_TYPE_SPECIAL = 7
+};
+
+/* Additional value meanings */
+enum {
+ ECBOR_ADDITIONAL_LAST_INTEGER = 23,
+
+ ECBOR_ADDITIONAL_1BYTE = 24,
+ ECBOR_ADDITIONAL_2BYTE = 25,
+ ECBOR_ADDITIONAL_4BYTE = 26,
+ ECBOR_ADDITIONAL_8BYTE = 27,
+
+ ECBOR_ADDITIONAL_INDEFINITE = 31
+};
+
+/* Simple value meanings */
+enum {
+ ECBOR_SIMPLE_FALSE = 20,
+ ECBOR_SIMPLE_TRUE = 21,
+ ECBOR_SIMPLE_NULL = 22,
+ ECBOR_SIMPLE_UNDEFINED = 23
+};
+
+/* Static item, for various initializations */
+static ecbor_item_t null_item = {
+ .type = ECBOR_TYPE_NONE,
+ .value = {
+ .tag = {
+ .tag_value = 0,
+ .child = NULL
+ }
+ },
+ .size = 0,
+ .length = 0,
+ .is_indefinite = 0,
+ .parent = NULL,
+ .child = NULL,
+ .next = NULL,
+ .index = 0
+};
+
+/* Internal checks. Most of these macros rely on the function returning an
+ error code, and a 'rc' value being declared locally */
+#define ECBOR_INTERNAL_CHECK_CONTEXT_PTR(c) \
+ { \
+ if (!(c)) { \
+ return ECBOR_ERR_NULL_CONTEXT; \
+ } \
+ }
+#define ECBOR_INTERNAL_CHECK_ITEM_PTR(i) \
+ { \
+ if (!(i)) { \
+ return ECBOR_ERR_NULL_ITEM; \
+ } \
+ }
+
+#define ECBOR_INTERNAL_CHECK_VALUE_PTR(v) \
+ { \
+ if (!(v)) { \
+ return ECBOR_ERR_NULL_VALUE; \
+ } \
+ }
+
+#define ECBOR_INTERNAL_CHECK_TYPE(t1, ref) \
+ { \
+ if ((t1) != (ref)) { \
+ return ECBOR_ERR_INVALID_TYPE; \
+ } \
+ }
+
+#define ECBOR_INTERNAL_CHECK_BOUNDS(index, limit) \
+ { \
+ if ((index) >= (limit)) { \
+ return ECBOR_ERR_INDEX_OUT_OF_BOUNDS; \
+ } \
+ }
+
+/*
+ * Endianness
+ */
+extern uint16_t
+ecbor_uint16_from_big_endian (uint16_t value);
+
+extern uint32_t
+ecbor_uint32_from_big_endian (uint32_t value);
+
+extern uint64_t
+ecbor_uint64_from_big_endian (uint64_t value);
+
+extern float
+ecbor_fp32_from_big_endian (float value);
+
+extern double
+ecbor_fp64_from_big_endian (double value);
+
+
+extern uint16_t
+ecbor_uint16_to_big_endian (uint16_t value);
+
+extern uint32_t
+ecbor_uint32_to_big_endian (uint32_t value);
+
+extern uint64_t
+ecbor_uint64_to_big_endian (uint64_t value);
+
+extern float
+ecbor_fp32_to_big_endian (float value);
+
+extern double
+ecbor_fp64_to_big_endian (double value);
+
+
+/*
+ * Memory
+ */
+extern void
+ecbor_memcpy (void *dest, void *src, size_t num);
+
+#endif \ No newline at end of file