From e3f3c0d12eb83af50f984332a8ddae6f27197b12 Mon Sep 17 00:00:00 2001 From: rimio Date: Mon, 5 Mar 2018 23:32:48 +0200 Subject: Change dir structure to accomodate multiple tools --- CMakeLists.txt | 8 +- src/ecbor-describe/ecbor_describe.c | 483 ++++++++++++++++++++++ src/ecbor.c | 600 --------------------------- src/ecbor_decoder.c | 780 ------------------------------------ src/ecbor_describe.c | 483 ---------------------- src/ecbor_encoder.c | 562 -------------------------- src/ecbor_internal.h | 138 ------- src/libecbor/ecbor.c | 600 +++++++++++++++++++++++++++ src/libecbor/ecbor_decoder.c | 780 ++++++++++++++++++++++++++++++++++++ src/libecbor/ecbor_encoder.c | 562 ++++++++++++++++++++++++++ src/libecbor/ecbor_internal.h | 138 +++++++ 11 files changed, 2567 insertions(+), 2567 deletions(-) create mode 100644 src/ecbor-describe/ecbor_describe.c delete mode 100644 src/ecbor.c delete mode 100644 src/ecbor_decoder.c delete mode 100644 src/ecbor_describe.c delete mode 100644 src/ecbor_encoder.c delete mode 100644 src/ecbor_internal.h create mode 100644 src/libecbor/ecbor.c create mode 100644 src/libecbor/ecbor_decoder.c create mode 100644 src/libecbor/ecbor_encoder.c create mode 100644 src/libecbor/ecbor_internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a828b66..4980705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,13 +38,13 @@ set (LIB_INCLUDES ) set (LIB_SOURCES - "${SRC_DIR}/ecbor.c" - "${SRC_DIR}/ecbor_encoder.c" - "${SRC_DIR}/ecbor_decoder.c" + "${SRC_DIR}/libecbor/ecbor.c" + "${SRC_DIR}/libecbor/ecbor_encoder.c" + "${SRC_DIR}/libecbor/ecbor_decoder.c" ) set (DESCRIBE_TOOL_SOURCES - "${SRC_DIR}/ecbor_describe.c" + "${SRC_DIR}/ecbor-describe/ecbor_describe.c" ) # Targets diff --git a/src/ecbor-describe/ecbor_describe.c b/src/ecbor-describe/ecbor_describe.c new file mode 100644 index 0000000..52b2eed --- /dev/null +++ b/src/ecbor-describe/ecbor_describe.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2018 Vasile Vilvoiu + * + * libecbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Command line arguments + */ +static struct option long_options[] = { + { "tree", no_argument, 0, 't' }, + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +/* + * Item buffer for tree mode + */ +#define MAX_ITEMS 1024 +static ecbor_item_t items_buffer[MAX_ITEMS]; + +void +print_help (void); +void +print_ecbor_error (ecbor_error_t err); +ecbor_error_t +print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix); + +/* + * Print help + */ +void +print_help (void) +{ + printf ("Usage: ecbor-describe [options] \n"); + printf (" options:\n"); + printf (" -t, --tree Use tree decoding mode\n"); + printf (" -h, --help Display this help message\n"); +} + +void +print_ecbor_error (ecbor_error_t err) +{ + printf ("ECBOR error %d\n", err); +} + +ecbor_error_t +print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) +{ + static const unsigned int max_str_print_len = 64; + static const char *msg_too_large = ""; + ecbor_type_t mt; + ecbor_error_t rc; + unsigned int i; + + for(i = 0; i < level * 2; i ++) putchar(' '); + printf (prefix); + + mt = ecbor_get_type (item); + switch (mt) { + case ECBOR_TYPE_NINT: + { + int64_t val; + + rc = ecbor_get_int64 (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[NINT] value %lld\n", (long long int)val); + } + break; + + case ECBOR_TYPE_UINT: + { + uint64_t val; + + rc = ecbor_get_uint64 (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[UINT] value %llu\n", (unsigned long long int) val); + } + break; + + case ECBOR_TYPE_STR: + { + size_t len; + + rc = ecbor_get_length (item, &len); + if (rc != ECBOR_OK) { + return rc; + } + + if (ECBOR_IS_INDEFINITE (item)) { + size_t nchunks; + + rc = ecbor_get_str_chunk_count (item, &nchunks); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[STR] len %u (indefinite)\n", (unsigned int) len); + for (i = 0; i < nchunks; i ++) { + ecbor_item_t chunk; + + rc = ecbor_get_str_chunk (item, i, &chunk); + if (rc != ECBOR_OK) { + return rc; + } + + rc = print_ecbor_item (&chunk, level + 1, ""); + if (rc != ECBOR_OK) { + return rc; + } + } + } else { + char *val; + + rc = ecbor_get_str (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[STR] len %u value '%.*s'\n", (unsigned int) len, + (int)(len <= max_str_print_len ? len : strlen(msg_too_large)), + (len <= max_str_print_len ? val : msg_too_large)); + } + } + break; + + case ECBOR_TYPE_BSTR: + { + size_t len; + + rc = ecbor_get_length (item, &len); + if (rc != ECBOR_OK) { + return rc; + } + + if (ECBOR_IS_INDEFINITE (item)) { + size_t nchunks; + + rc = ecbor_get_bstr_chunk_count (item, &nchunks); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[BSTR] len %u (indefinite)\n", (unsigned int) len); + for (i = 0; i < nchunks; i ++) { + ecbor_item_t chunk; + + rc = ecbor_get_bstr_chunk (item, i, &chunk); + if (rc != ECBOR_OK) { + return rc; + } + + rc = print_ecbor_item (&chunk, level + 1, ""); + if (rc != ECBOR_OK) { + return rc; + } + } + } else { + uint8_t *val; + + rc = ecbor_get_bstr (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[BSTR] len %u value ", (unsigned int) len); + if (len > max_str_print_len) { + printf ("'%s'\n", msg_too_large); + } else { + printf ("'"); + for (i = 0; i < len; i ++) { + printf("%02x", val[i]); + } + printf ("'\n"); + } + } + } + break; + + case ECBOR_TYPE_ARRAY: + { + size_t len, i; + + rc = ecbor_get_length (item, &len); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[ARRAY] len %u %s\n", (unsigned int) len, + (ECBOR_IS_INDEFINITE (item) ? "(indefinite)" : "")); + for (i = 0; i < len; i ++) { + ecbor_item_t child; + + rc = ecbor_get_array_item (item, i, &child); + if (rc != ECBOR_OK) { + return rc; + } + + rc = print_ecbor_item (&child, level+1, ""); + if (rc != ECBOR_OK) { + return rc; + } + } + } + break; + + case ECBOR_TYPE_MAP: + { + size_t len, i; + char kv_msg[100] = { 0 }; + + rc = ecbor_get_length (item, &len); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[MAP] len %u %s\n", (unsigned int) len, + (ECBOR_IS_INDEFINITE (item) ? "(indefinite)" : "")); + for (i = 0; i < len; i ++) { + ecbor_item_t k, v; + + rc = ecbor_get_map_item (item, i, &k, &v); + if (rc != ECBOR_OK) { + return rc; + } + + sprintf (kv_msg, "key[%u]: ", (unsigned int) i); + rc = print_ecbor_item (&k, level+2, kv_msg); + if (rc != ECBOR_OK) { + return rc; + } + + sprintf (kv_msg, "val[%u]: ", (unsigned int) i); + rc = print_ecbor_item (&v, level+2, kv_msg); + if (rc != ECBOR_OK) { + return rc; + } + } + } + break; + + case ECBOR_TYPE_TAG: + { + uint64_t val; + ecbor_item_t child; + + rc = ecbor_get_tag_value (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[TAG] value %lld\n", (long long int)val); + + rc = ecbor_get_tag_item (item, &child); + if (rc != ECBOR_OK) { + return rc; + } + + rc = print_ecbor_item (&child, level+1, ""); + if (rc != ECBOR_OK) { + return rc; + } + } + break; + + case ECBOR_TYPE_FP32: + { + float val; + + rc = ecbor_get_fp32 (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[FP32] value %f\n", val); + } + break; + + case ECBOR_TYPE_FP64: + { + double val; + + rc = ecbor_get_fp64 (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[FP64] value %f\n", val); + } + break; + + case ECBOR_TYPE_BOOL: + { + uint8_t val; + + rc = ecbor_get_bool (item, &val); + if (rc != ECBOR_OK) { + return rc; + } + + printf ("[BOOL] value %s\n", (val ? "true" : "false")); + } + break; + + case ECBOR_TYPE_NULL: + printf ("[NULL]\n"); + break; + + case ECBOR_TYPE_UNDEFINED: + printf ("[UNDEFINED]\n"); + break; + + default: + printf ("[UNKNOWN]\n"); + break; + } + + return ECBOR_OK; +} + +/* + * Program entry + */ +int +main(int argc, char **argv) +{ + char *filename = NULL; + unsigned char *cbor = NULL; + long int cbor_length = 0; + int tree_mode = 0; + + /* parse arguments */ + while (1) { + int option_index, c; + + c = getopt_long (argc, argv, "ht", long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 't': + tree_mode = 1; + break; + + default: + print_help (); + return 0; + } + } + + if (optind != (argc-1)) { + fprintf (stderr, "Expecting exactly one file name!\n"); + print_help (); + return -1; + } else { + filename = strdup (argv[optind]); + } + + /* load CBOR data from file */ + { + FILE *fp; + + fprintf (stderr, "Reading CBOR from file '%s'\n", filename); + fp = fopen (filename, "rb"); + if (!fp) { + fprintf (stderr, "Error opening file!\n"); + return -1; + } + + if (fseek (fp, 0L, SEEK_END)) { + fprintf (stderr, "Error seeking end of file!\n"); + fclose (fp); + return -1; + } + cbor_length = ftell(fp); + if (cbor_length < 0) { + fprintf (stderr, "Error determining input size!\n"); + fclose (fp); + return -1; + } + if (fseek (fp, 0L, SEEK_SET)) { + fprintf (stderr, "Error seeking beginning of file!\n"); + fclose (fp); + return -1; + } + + cbor = (unsigned char *) malloc (cbor_length); + if (!cbor) { + fprintf (stderr, "Error allocating %d bytes!\n", (int) cbor_length); + fclose (fp); + return -1; + } + + if (fread (cbor, 1, cbor_length, fp) != (size_t) cbor_length) { + fprintf (stderr, "Error reading %d bytes!\n", (int) cbor_length); + fclose (fp); + return -1; + } + + fclose (fp); + free (filename); + } + + /* parse CBOR data */ + { + ecbor_decode_context_t context; + ecbor_error_t rc; + + if (tree_mode) { + rc = ecbor_initialize_decode_tree (&context, cbor, cbor_length, + items_buffer, MAX_ITEMS); + } else { + rc = ecbor_initialize_decode (&context, cbor, cbor_length); + } + if (rc != ECBOR_OK) { + print_ecbor_error (rc); + return -1; + } + + fprintf (stderr, "CBOR objects:\n"); + + if (tree_mode) { + /* decode all */ + ecbor_item_t *item; + + rc = ecbor_decode_tree (&context, &item); + if (rc != ECBOR_OK) { + print_ecbor_error (rc); + return -1; + } + + while (item) { + rc = print_ecbor_item (item, 0, ""); + if (rc != ECBOR_OK) { + print_ecbor_error (rc); + return -1; + } + + item = item->next; + } + } else { + /* normal mode, consume */ + ecbor_item_t item; + + while (1) { + rc = ecbor_decode (&context, &item); + if (rc != ECBOR_OK) { + if (rc == ECBOR_END_OF_BUFFER) { + break; + } + print_ecbor_error (rc); + return -1; + } + + rc = print_ecbor_item (&item, 0, ""); + if (rc != ECBOR_OK) { + print_ecbor_error (rc); + return -1; + } + } + } + } + + free (cbor); + + /* all ok */ + return 0; +} \ No newline at end of file diff --git a/src/ecbor.c b/src/ecbor.c deleted file mode 100644 index 30d2ac5..0000000 --- a/src/ecbor.c +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright (c) 2018 Vasile Vilvoiu - * - * 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/ecbor_decoder.c b/src/ecbor_decoder.c deleted file mode 100644 index a422dd9..0000000 --- a/src/ecbor_decoder.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * Copyright (c) 2018 Vasile Vilvoiu - * - * 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 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/ecbor_describe.c b/src/ecbor_describe.c deleted file mode 100644 index 52b2eed..0000000 --- a/src/ecbor_describe.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright (c) 2018 Vasile Vilvoiu - * - * libecbor is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include -#include -#include -#include -#include -#include - -/* - * Command line arguments - */ -static struct option long_options[] = { - { "tree", no_argument, 0, 't' }, - { "help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } -}; - -/* - * Item buffer for tree mode - */ -#define MAX_ITEMS 1024 -static ecbor_item_t items_buffer[MAX_ITEMS]; - -void -print_help (void); -void -print_ecbor_error (ecbor_error_t err); -ecbor_error_t -print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix); - -/* - * Print help - */ -void -print_help (void) -{ - printf ("Usage: ecbor-describe [options] \n"); - printf (" options:\n"); - printf (" -t, --tree Use tree decoding mode\n"); - printf (" -h, --help Display this help message\n"); -} - -void -print_ecbor_error (ecbor_error_t err) -{ - printf ("ECBOR error %d\n", err); -} - -ecbor_error_t -print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) -{ - static const unsigned int max_str_print_len = 64; - static const char *msg_too_large = ""; - ecbor_type_t mt; - ecbor_error_t rc; - unsigned int i; - - for(i = 0; i < level * 2; i ++) putchar(' '); - printf (prefix); - - mt = ecbor_get_type (item); - switch (mt) { - case ECBOR_TYPE_NINT: - { - int64_t val; - - rc = ecbor_get_int64 (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[NINT] value %lld\n", (long long int)val); - } - break; - - case ECBOR_TYPE_UINT: - { - uint64_t val; - - rc = ecbor_get_uint64 (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[UINT] value %llu\n", (unsigned long long int) val); - } - break; - - case ECBOR_TYPE_STR: - { - size_t len; - - rc = ecbor_get_length (item, &len); - if (rc != ECBOR_OK) { - return rc; - } - - if (ECBOR_IS_INDEFINITE (item)) { - size_t nchunks; - - rc = ecbor_get_str_chunk_count (item, &nchunks); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[STR] len %u (indefinite)\n", (unsigned int) len); - for (i = 0; i < nchunks; i ++) { - ecbor_item_t chunk; - - rc = ecbor_get_str_chunk (item, i, &chunk); - if (rc != ECBOR_OK) { - return rc; - } - - rc = print_ecbor_item (&chunk, level + 1, ""); - if (rc != ECBOR_OK) { - return rc; - } - } - } else { - char *val; - - rc = ecbor_get_str (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[STR] len %u value '%.*s'\n", (unsigned int) len, - (int)(len <= max_str_print_len ? len : strlen(msg_too_large)), - (len <= max_str_print_len ? val : msg_too_large)); - } - } - break; - - case ECBOR_TYPE_BSTR: - { - size_t len; - - rc = ecbor_get_length (item, &len); - if (rc != ECBOR_OK) { - return rc; - } - - if (ECBOR_IS_INDEFINITE (item)) { - size_t nchunks; - - rc = ecbor_get_bstr_chunk_count (item, &nchunks); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[BSTR] len %u (indefinite)\n", (unsigned int) len); - for (i = 0; i < nchunks; i ++) { - ecbor_item_t chunk; - - rc = ecbor_get_bstr_chunk (item, i, &chunk); - if (rc != ECBOR_OK) { - return rc; - } - - rc = print_ecbor_item (&chunk, level + 1, ""); - if (rc != ECBOR_OK) { - return rc; - } - } - } else { - uint8_t *val; - - rc = ecbor_get_bstr (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[BSTR] len %u value ", (unsigned int) len); - if (len > max_str_print_len) { - printf ("'%s'\n", msg_too_large); - } else { - printf ("'"); - for (i = 0; i < len; i ++) { - printf("%02x", val[i]); - } - printf ("'\n"); - } - } - } - break; - - case ECBOR_TYPE_ARRAY: - { - size_t len, i; - - rc = ecbor_get_length (item, &len); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[ARRAY] len %u %s\n", (unsigned int) len, - (ECBOR_IS_INDEFINITE (item) ? "(indefinite)" : "")); - for (i = 0; i < len; i ++) { - ecbor_item_t child; - - rc = ecbor_get_array_item (item, i, &child); - if (rc != ECBOR_OK) { - return rc; - } - - rc = print_ecbor_item (&child, level+1, ""); - if (rc != ECBOR_OK) { - return rc; - } - } - } - break; - - case ECBOR_TYPE_MAP: - { - size_t len, i; - char kv_msg[100] = { 0 }; - - rc = ecbor_get_length (item, &len); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[MAP] len %u %s\n", (unsigned int) len, - (ECBOR_IS_INDEFINITE (item) ? "(indefinite)" : "")); - for (i = 0; i < len; i ++) { - ecbor_item_t k, v; - - rc = ecbor_get_map_item (item, i, &k, &v); - if (rc != ECBOR_OK) { - return rc; - } - - sprintf (kv_msg, "key[%u]: ", (unsigned int) i); - rc = print_ecbor_item (&k, level+2, kv_msg); - if (rc != ECBOR_OK) { - return rc; - } - - sprintf (kv_msg, "val[%u]: ", (unsigned int) i); - rc = print_ecbor_item (&v, level+2, kv_msg); - if (rc != ECBOR_OK) { - return rc; - } - } - } - break; - - case ECBOR_TYPE_TAG: - { - uint64_t val; - ecbor_item_t child; - - rc = ecbor_get_tag_value (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[TAG] value %lld\n", (long long int)val); - - rc = ecbor_get_tag_item (item, &child); - if (rc != ECBOR_OK) { - return rc; - } - - rc = print_ecbor_item (&child, level+1, ""); - if (rc != ECBOR_OK) { - return rc; - } - } - break; - - case ECBOR_TYPE_FP32: - { - float val; - - rc = ecbor_get_fp32 (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[FP32] value %f\n", val); - } - break; - - case ECBOR_TYPE_FP64: - { - double val; - - rc = ecbor_get_fp64 (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[FP64] value %f\n", val); - } - break; - - case ECBOR_TYPE_BOOL: - { - uint8_t val; - - rc = ecbor_get_bool (item, &val); - if (rc != ECBOR_OK) { - return rc; - } - - printf ("[BOOL] value %s\n", (val ? "true" : "false")); - } - break; - - case ECBOR_TYPE_NULL: - printf ("[NULL]\n"); - break; - - case ECBOR_TYPE_UNDEFINED: - printf ("[UNDEFINED]\n"); - break; - - default: - printf ("[UNKNOWN]\n"); - break; - } - - return ECBOR_OK; -} - -/* - * Program entry - */ -int -main(int argc, char **argv) -{ - char *filename = NULL; - unsigned char *cbor = NULL; - long int cbor_length = 0; - int tree_mode = 0; - - /* parse arguments */ - while (1) { - int option_index, c; - - c = getopt_long (argc, argv, "ht", long_options, &option_index); - if (c == -1) { - break; - } - - switch (c) { - case 't': - tree_mode = 1; - break; - - default: - print_help (); - return 0; - } - } - - if (optind != (argc-1)) { - fprintf (stderr, "Expecting exactly one file name!\n"); - print_help (); - return -1; - } else { - filename = strdup (argv[optind]); - } - - /* load CBOR data from file */ - { - FILE *fp; - - fprintf (stderr, "Reading CBOR from file '%s'\n", filename); - fp = fopen (filename, "rb"); - if (!fp) { - fprintf (stderr, "Error opening file!\n"); - return -1; - } - - if (fseek (fp, 0L, SEEK_END)) { - fprintf (stderr, "Error seeking end of file!\n"); - fclose (fp); - return -1; - } - cbor_length = ftell(fp); - if (cbor_length < 0) { - fprintf (stderr, "Error determining input size!\n"); - fclose (fp); - return -1; - } - if (fseek (fp, 0L, SEEK_SET)) { - fprintf (stderr, "Error seeking beginning of file!\n"); - fclose (fp); - return -1; - } - - cbor = (unsigned char *) malloc (cbor_length); - if (!cbor) { - fprintf (stderr, "Error allocating %d bytes!\n", (int) cbor_length); - fclose (fp); - return -1; - } - - if (fread (cbor, 1, cbor_length, fp) != (size_t) cbor_length) { - fprintf (stderr, "Error reading %d bytes!\n", (int) cbor_length); - fclose (fp); - return -1; - } - - fclose (fp); - free (filename); - } - - /* parse CBOR data */ - { - ecbor_decode_context_t context; - ecbor_error_t rc; - - if (tree_mode) { - rc = ecbor_initialize_decode_tree (&context, cbor, cbor_length, - items_buffer, MAX_ITEMS); - } else { - rc = ecbor_initialize_decode (&context, cbor, cbor_length); - } - if (rc != ECBOR_OK) { - print_ecbor_error (rc); - return -1; - } - - fprintf (stderr, "CBOR objects:\n"); - - if (tree_mode) { - /* decode all */ - ecbor_item_t *item; - - rc = ecbor_decode_tree (&context, &item); - if (rc != ECBOR_OK) { - print_ecbor_error (rc); - return -1; - } - - while (item) { - rc = print_ecbor_item (item, 0, ""); - if (rc != ECBOR_OK) { - print_ecbor_error (rc); - return -1; - } - - item = item->next; - } - } else { - /* normal mode, consume */ - ecbor_item_t item; - - while (1) { - rc = ecbor_decode (&context, &item); - if (rc != ECBOR_OK) { - if (rc == ECBOR_END_OF_BUFFER) { - break; - } - print_ecbor_error (rc); - return -1; - } - - rc = print_ecbor_item (&item, 0, ""); - if (rc != ECBOR_OK) { - print_ecbor_error (rc); - return -1; - } - } - } - } - - free (cbor); - - /* all ok */ - return 0; -} \ No newline at end of file diff --git a/src/ecbor_encoder.c b/src/ecbor_encoder.c deleted file mode 100644 index b144644..0000000 --- a/src/ecbor_encoder.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright (c) 2018 Vasile Vilvoiu - * - * 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 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/ecbor_internal.h b/src/ecbor_internal.h deleted file mode 100644 index 9612c1a..0000000 --- a/src/ecbor_internal.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2018 Vasile Vilvoiu - * - * 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 -#include - -/* Don't rely on 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 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 + * + * 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 + * + * 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 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 + * + * 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 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 + * + * 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 +#include + +/* Don't rely on 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 -- cgit v1.2.3