diff options
Diffstat (limited to 'src/libecbor')
| -rw-r--r-- | src/libecbor/ecbor.c | 600 | ||||
| -rw-r--r-- | src/libecbor/ecbor_decoder.c | 780 | ||||
| -rw-r--r-- | src/libecbor/ecbor_encoder.c | 562 | ||||
| -rw-r--r-- | src/libecbor/ecbor_internal.h | 138 |
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 |
