diff options
| author | rimio <vasi.vilvoiu@gmail.com> | 2018-03-02 01:42:43 +0200 |
|---|---|---|
| committer | rimio <vasi.vilvoiu@gmail.com> | 2018-03-03 01:39:53 +0200 |
| commit | d31766d827c085dea004ebf7209ab45eddaad02b (patch) | |
| tree | c52e09cbf927f4d59eadff04510e84c6a630c86f | |
| parent | f2397db2cab21c9db06e8f7b69981f7eb2ca8cd5 (diff) | |
Various fixes and refactoring.
| -rw-r--r-- | include/ecbor.h | 207 | ||||
| -rw-r--r-- | src/ecbor.c | 498 | ||||
| -rw-r--r-- | src/ecbor_decoder.c | 337 | ||||
| -rw-r--r-- | src/ecbor_describe.c | 101 | ||||
| -rw-r--r-- | src/ecbor_internal.h | 34 | ||||
| -rw-r--r-- | test/files/appendix_a/0011.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0013.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0047.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0050.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0051.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0052.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0053.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0054.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0055.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0056.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0057.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0058.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0059.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0060.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0061.answer | 2 | ||||
| -rw-r--r-- | test/files/appendix_a/0068.answer | 4 | ||||
| -rw-r--r-- | test/files/appendix_a/0069.answer | 6 | ||||
| -rw-r--r-- | test/files/appendix_a/0070.answer | 20 | ||||
| -rwxr-xr-x | test/runtests.sh | 35 |
24 files changed, 816 insertions, 456 deletions
diff --git a/include/ecbor.h b/include/ecbor.h index a0c166c..6408d3d 100644 --- a/include/ecbor.h +++ b/include/ecbor.h @@ -9,6 +9,7 @@ #define _ECBOR_H_ #include <stdint.h> +#include <stddef.h> /* * Error codes @@ -22,23 +23,23 @@ typedef enum { ECBOR_ERR_NULL_CONTEXT = 10, ECBOR_ERR_NULL_INPUT_BUFFER = 11, ECBOR_ERR_NULL_OUTPUT_BUFFER = 12, - ECBOR_ERR_NULL_NODE_BUFFER = 13, + ECBOR_ERR_NULL_ITEM_BUFFER = 13, ECBOR_ERR_NULL_VALUE = 14, ECBOR_ERR_NULL_ARRAY = 15, ECBOR_ERR_NULL_MAP = 16, ECBOR_ERR_NULL_ITEM = 20, - ECBOR_ERR_NULL_NODE = 21, ECBOR_ERR_WRONG_MODE = 30, /* bounds errors */ ECBOR_ERR_INVALID_END_OF_BUFFER = 50, - ECBOR_ERR_END_OF_NODE_BUFFER = 51, - ECBOR_ERR_EMPTY_NODE_BUFFER = 52, + ECBOR_ERR_END_OF_ITEM_BUFFER = 51, + ECBOR_ERR_EMPTY_ITEM_BUFFER = 52, ECBOR_ERR_INDEX_OUT_OF_BOUNDS = 53, ECBOR_ERR_WONT_RETURN_INDEFINITE = 54, ECBOR_ERR_WONT_RETURN_DEFINITE = 55, + ECBOR_ERR_VALUE_OVERFLOW = 56, /* semantic errors */ ECBOR_ERR_CURRENTLY_NOT_SUPPORTED = 100, @@ -95,7 +96,8 @@ typedef enum { /* * CBOR Item */ -typedef struct { +typedef struct ecbor_item ecbor_item_t; +struct ecbor_item { /* item type */ ecbor_type_t type; @@ -111,35 +113,29 @@ typedef struct { } tag; struct { const uint8_t *str; - uint64_t n_chunks; + size_t n_chunks; } string; const uint8_t *items; } value; /* storage size (serialized) of item, in bytes */ - uint64_t size; + size_t size; /* length of value; can mean different things: * - payload (value) size, in bytes, for ints and strings * - number of items in arrays and maps */ - uint64_t length; + size_t length; /* non-zero if size is indefinite (for strings, maps and arrays) */ uint8_t is_indefinite; -} ecbor_item_t; - -/* - * CBOR tree node - */ -typedef struct ecbor_node ecbor_node_t; -struct ecbor_node { - ecbor_item_t item; - ecbor_node_t *parent; - ecbor_node_t *child; /* first child */ - ecbor_node_t *next; /* next in array or map */ - ecbor_node_t *prev; /* ditto */ - uint64_t index; /* index in array or map */ + + /* tree links and metainformation (only populated in tree decoder mode) */ + ecbor_item_t *parent; + ecbor_item_t *child; /* first child */ + ecbor_item_t *next; /* next in array or map */ + ecbor_item_t *prev; /* ditto */ + size_t index; /* index in array or map */ }; /* @@ -163,7 +159,7 @@ typedef struct { uint8_t *out_position; /* remaining bytes */ - unsigned int bytes_left; + size_t bytes_left; } ecbor_encode_context_t; typedef struct { @@ -174,16 +170,16 @@ typedef struct { const uint8_t *in_position; /* remaining bytes */ - uint64_t bytes_left; + size_t bytes_left; - /* node buffer */ - ecbor_node_t *nodes; + /* item buffer */ + ecbor_item_t *items; /* capacity of item buffer (in items) */ - unsigned int node_capacity; + size_t item_capacity; /* number of used items so far */ - unsigned int n_nodes; + size_t n_items; } ecbor_decode_context_t; @@ -193,19 +189,19 @@ typedef struct { extern ecbor_error_t ecbor_initialize_decode (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size); + size_t buffer_size); extern ecbor_error_t ecbor_initialize_decode_streamed (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size); + size_t buffer_size); extern ecbor_error_t ecbor_initialize_decode_tree (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size, - ecbor_node_t *node_buffer, - uint64_t node_capacity); + size_t buffer_size, + ecbor_item_t *item_buffer, + size_t item_capacity); /* @@ -220,103 +216,176 @@ extern ecbor_error_t ecbor_decode (ecbor_decode_context_t *context, ecbor_item_t *item); extern ecbor_error_t -ecbor_decode_tree (ecbor_decode_context_t *context); +ecbor_decode_tree (ecbor_decode_context_t *context, ecbor_item_t **root); /* * Tree API */ -extern ecbor_error_t -ecbor_get_root (ecbor_decode_context_t *context, ecbor_node_t **root); /* * Strict API */ + +/* Metadata */ extern ecbor_type_t ecbor_get_type (ecbor_item_t *item); extern ecbor_error_t -ecbor_get_value (ecbor_item_t *item, void *value); +ecbor_get_length (ecbor_item_t *item, size_t *length); -extern ecbor_error_t -ecbor_get_length (ecbor_item_t *item, uint64_t *length); +/* Child items */ extern ecbor_error_t -ecbor_get_array_item (ecbor_item_t *array, uint64_t index, +ecbor_get_array_item (ecbor_item_t *array, size_t index, ecbor_item_t *arr_item); extern ecbor_error_t -ecbor_get_map_item (ecbor_item_t *map, uint64_t index, ecbor_item_t *key, +ecbor_get_array_item_ptr (ecbor_item_t *array, size_t index, + ecbor_item_t **item); + +extern ecbor_error_t +ecbor_get_map_item (ecbor_item_t *map, size_t index, ecbor_item_t *key, ecbor_item_t *value); extern ecbor_error_t -ecbor_get_tag_item (ecbor_item_t *tag, ecbor_item_t *arr_item); +ecbor_get_map_item_ptr (ecbor_item_t *map, size_t index, ecbor_item_t **key, + ecbor_item_t **value); + +extern ecbor_error_t +ecbor_get_tag_item (ecbor_item_t *tag, ecbor_item_t *item); + +extern ecbor_error_t +ecbor_get_tag_item_ptr (ecbor_item_t *tag, ecbor_item_t **item); + + +/* Ints */ +extern ecbor_error_t +ecbor_get_uint8 (ecbor_item_t *item, uint8_t *value); + +extern ecbor_error_t +ecbor_get_uint16 (ecbor_item_t *item, uint16_t *value); + +extern ecbor_error_t +ecbor_get_uint32 (ecbor_item_t *item, uint32_t *value); + +extern ecbor_error_t +ecbor_get_uint64 (ecbor_item_t *item, uint64_t *value); + + +extern ecbor_error_t +ecbor_get_int8 (ecbor_item_t *item, int8_t *value); + +extern ecbor_error_t +ecbor_get_int16 (ecbor_item_t *item, int16_t *value); +extern ecbor_error_t +ecbor_get_int32 (ecbor_item_t *item, int32_t *value); extern ecbor_error_t +ecbor_get_int64 (ecbor_item_t *item, int64_t *value); + + +/* Strings */ +extern ecbor_error_t ecbor_get_str (ecbor_item_t *str, char **value); extern ecbor_error_t -ecbor_get_str_chunk_count (ecbor_item_t *str, uint64_t *count); +ecbor_get_str_chunk_count (ecbor_item_t *str, size_t *count); extern ecbor_error_t -ecbor_get_str_chunk (ecbor_item_t *str, uint64_t index, ecbor_item_t *chunk); +ecbor_get_str_chunk (ecbor_item_t *str, size_t index, ecbor_item_t *chunk); extern ecbor_error_t ecbor_get_bstr (ecbor_item_t *str, uint8_t **value); extern ecbor_error_t -ecbor_get_bstr_chunk_count (ecbor_item_t *str, uint64_t *count); +ecbor_get_bstr_chunk_count (ecbor_item_t *str, size_t *count); + +extern ecbor_error_t +ecbor_get_bstr_chunk (ecbor_item_t *str, size_t index, ecbor_item_t *chunk); + +/* Tag */ extern ecbor_error_t -ecbor_get_bstr_chunk (ecbor_item_t *str, uint64_t index, ecbor_item_t *chunk); +ecbor_get_tag_value (ecbor_item_t *tag, uint64_t *tag_value); + + +/* Floating point */ +extern ecbor_error_t +ecbor_get_fp32 (ecbor_item_t *item, float *value); + +extern ecbor_error_t +ecbor_get_fp64 (ecbor_item_t *item, double *value); + + +/* Boolean */ +extern ecbor_error_t +ecbor_get_bool (ecbor_item_t *item, uint8_t *value); /* * Inline API */ #define ECBOR_GET_TYPE(i) \ - ((i).type) + ((i)->type) +#define ECBOR_GET_LENGTH(i) \ + ((i)->type == ECBOR_TYPE_MAP ? (i)->length / 2 : (i)->length) + +#define ECBOR_GET_INT(i) \ + ((i)->value.integer) +#define ECBOR_GET_UINT(i) \ + ((i)->value.uinteger) + +#define ECBOR_GET_STRING(i) \ + ((i)->value.string) + +#define ECBOR_GET_FP32(i) \ + ((i)->value.fp32) +#define ECBOR_GET_FP64(i) \ + ((i)->value.fp64) + +#define ECBOR_GET_BOOL(i) \ + ((i)->value.uinteger) + +#define ECBOR_GET_TAG_VALUE(i) \ + ((i)->value.tag.tag_value) +#define ECBOR_GET_TAG_ITEM(i) \ + ((i)->child) #define ECBOR_IS_INDEFINITE(i) \ - ((i).is_indefinite) + ((i)->is_indefinite) #define ECBOR_IS_DEFINITE(i) \ - (!(i).is_indefinite) - + (!(i)->is_indefinite) #define ECBOR_IS_NINT(i) \ - ((i).type == ECBOR_TYPE_NINT) + ((i)->type == ECBOR_TYPE_NINT) #define ECBOR_IS_UINT(i) \ - ((i).type == ECBOR_TYPE_UINT) + ((i)->type == ECBOR_TYPE_UINT) #define ECBOR_IS_INTEGER(i) \ (ECBOR_IS_NINT(i) || ECBOR_IS_UINT(i)) #define ECBOR_IS_BSTR(i) \ - ((i).type == ECBOR_TYPE_BSTR) + ((i)->type == ECBOR_TYPE_BSTR) #define ECBOR_IS_STR(i) \ - ((i).type == ECBOR_TYPE_STR) + ((i)->type == ECBOR_TYPE_STR) #define ECBOR_IS_ARRAY(i) \ - ((i).type == ECBOR_TYPE_ARRAY) + ((i)->type == ECBOR_TYPE_ARRAY) #define ECBOR_IS_MAP(i) \ - ((i).type == ECBOR_TYPE_MAP) + ((i)->type == ECBOR_TYPE_MAP) #define ECBOR_IS_TAG(i) \ - ((i).type == ECBOR_TYPE_TAG) + ((i)->type == ECBOR_TYPE_TAG) #define ECBOR_IS_FP32(i) \ - ((i).type == ECBOR_TYPE_FP32) + ((i)->type == ECBOR_TYPE_FP32) #define ECBOR_IS_FLOAT(i) \ ECBOR_IS_FP32(i) #define ECBOR_IS_FP64(i) \ - ((i).type == ECBOR_TYPE_FP64) + ((i)->type == ECBOR_TYPE_FP64) #define ECBOR_IS_DOUBLE(i) \ ECBOR_IS_FP64(i) - -#define ECBOR_GET_INT(i) \ - ((i).value.integer) -#define ECBOR_GET_UINT(i) \ - ((i).value.uinteger) -#define ECBOR_GET_BSTR(i) \ - ((i).value.string) -#define ECBOR_GET_STR(i) \ - ((i).value.string) -#define ECBOR_GET_TAG(i) \ - ((i).value.tag.tag_value) +#define ECBOR_IS_BOOL(i) \ + ((i)->type == ECBOR_TYPE_BOOL) +#define ECBOR_IS_NULL(i) \ + ((i)->type == ECBOR_TYPE_NULL) +#define ECBOR_IS_UNDEFINED(i) \ + ((i)->type == ECBOR_TYPE_UNDEFINED) #endif
\ No newline at end of file diff --git a/src/ecbor.c b/src/ecbor.c index 1e95830..59583ba 100644 --- a/src/ecbor.c +++ b/src/ecbor.c @@ -58,38 +58,42 @@ ecbor_uint64_from_big_endian (uint64_t value) #endif } -extern float +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__) - uint32_t r = ecbor_uint32_from_big_endian(*((uint32_t *) &value)); - return *((float *) &r); + /* 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 } -extern double +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__) - uint64_t r = ecbor_uint64_from_big_endian(*((uint64_t *) &value)); - return *((double *) &r); + 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 } -extern ecbor_type_t +ecbor_type_t ecbor_get_type (ecbor_item_t *item) { - if (!item) { - return ECBOR_TYPE_NONE; - } + ECBOR_INTERNAL_CHECK_ITEM_PTR (item); if (item->type < ECBOR_TYPE_FIRST || item->type > ECBOR_TYPE_LAST) { return ECBOR_TYPE_NONE; @@ -97,50 +101,21 @@ ecbor_get_type (ecbor_item_t *item) return item->type; } -extern ecbor_error_t -ecbor_get_value (ecbor_item_t *item, void *value) +ecbor_error_t +ecbor_get_length (ecbor_item_t *item, size_t *length) { - if (!item) { - return ECBOR_ERR_NULL_ITEM; - } - if (!value) { - return ECBOR_ERR_NULL_VALUE; - } + ECBOR_INTERNAL_CHECK_ITEM_PTR (item); + ECBOR_INTERNAL_CHECK_VALUE_PTR (length); switch (item->type) { - case ECBOR_TYPE_NINT: - *((int64_t *)value) = item->value.integer; - break; - - case ECBOR_TYPE_UINT: - *((uint64_t *)value) = item->value.uinteger; - break; - case ECBOR_TYPE_BSTR: case ECBOR_TYPE_STR: - if (item->is_indefinite) { - return ECBOR_ERR_WONT_RETURN_INDEFINITE; - } - *((uint8_t **)value) = (uint8_t *) item->value.string.str; - break; - - case ECBOR_TYPE_TAG: - *((uint64_t *)value) = item->value.tag.tag_value; - break; - - case ECBOR_TYPE_FP16: - return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED; - - case ECBOR_TYPE_FP32: - *((float *)value) = item->value.fp32; + case ECBOR_TYPE_ARRAY: + *length = item->length; break; - case ECBOR_TYPE_FP64: - *((double *)value) = item->value.fp64; - break; - - case ECBOR_TYPE_BOOL: - *((uint64_t *)value) = item->value.uinteger; + case ECBOR_TYPE_MAP: + *length = item->length / 2; break; default: @@ -150,67 +125,136 @@ ecbor_get_value (ecbor_item_t *item, void *value) return ECBOR_OK; } -extern ecbor_error_t -ecbor_get_length (ecbor_item_t *item, uint64_t *length) +ecbor_error_t +ecbor_get_array_item (ecbor_item_t *array, size_t index, + ecbor_item_t *item) { - if (!item) { - return ECBOR_ERR_NULL_ITEM; - } - if (!length) { - return ECBOR_ERR_NULL_VALUE; - } - - switch (item->type) { - case ECBOR_TYPE_BSTR: - case ECBOR_TYPE_STR: - case ECBOR_TYPE_ARRAY: - *length = item->length; - break; + ecbor_error_t rc; - case ECBOR_TYPE_MAP: - *length = item->length / 2; - break; + 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); - default: - return ECBOR_ERR_INVALID_TYPE; + 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; } -extern ecbor_error_t -ecbor_get_array_item (ecbor_item_t *array, uint64_t index, - ecbor_item_t *item) +ecbor_error_t +ecbor_get_array_item_ptr (ecbor_item_t *array, size_t index, + ecbor_item_t **item) { - ecbor_decode_context_t context; - ecbor_error_t rc; - uint64_t i; + + size_t i; if (!array) { return ECBOR_ERR_NULL_ARRAY; } - if (array->type != ECBOR_TYPE_ARRAY) { - return ECBOR_ERR_INVALID_TYPE; - } - if (array->length <= index) { - return ECBOR_ERR_INDEX_OUT_OF_BOUNDS; + 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; } - if (!item) { - return ECBOR_ERR_NULL_ITEM; + + (*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; + } } - rc = ecbor_initialize_decode (&context, array->value.items, array->size); - if (rc != ECBOR_OK) { - return rc; + 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; } - - for (i = 0; i <= index; i ++) { - rc = ecbor_decode (&context, item); + + 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) { - if (rc == ECBOR_END_OF_BUFFER) { - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } else { - return rc; + 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; + } } } } @@ -218,43 +262,63 @@ ecbor_get_array_item (ecbor_item_t *array, uint64_t index, return ECBOR_OK; } -extern ecbor_error_t -ecbor_get_map_item (ecbor_item_t *map, uint64_t index, ecbor_item_t *key, - ecbor_item_t *value) +ecbor_error_t +ecbor_get_map_item_ptr (ecbor_item_t *map, size_t index, ecbor_item_t **key, + ecbor_item_t **value) { - ecbor_decode_context_t context; - ecbor_error_t rc; - uint64_t i; + size_t i; if (!map) { return ECBOR_ERR_NULL_MAP; } - if (map->type != ECBOR_TYPE_MAP) { - return ECBOR_ERR_INVALID_TYPE; - } - if (map->length <= (index * 2)) { - return ECBOR_ERR_INDEX_OUT_OF_BOUNDS; + + 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; } - if (!key || !value) { - return ECBOR_ERR_NULL_ITEM; + + (*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; + } } - rc = ecbor_initialize_decode (&context, map->value.items, map->size); - if (rc != ECBOR_OK) { - return rc; + (*value) = (*key)->next; + if (!(*value)) { + /* internal error, this should not have happened */ + return ECBOR_ERR_UNKNOWN; } - - for (i = 0; i <= index; i ++) { - rc = ecbor_decode (&context, key); + + 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) { - if (rc == ECBOR_END_OF_BUFFER) { - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } else { - return rc; - } + return rc; } - - rc = ecbor_decode (&context, value); + + rc = ecbor_decode (&context, item); if (rc != ECBOR_OK) { if (rc == ECBOR_END_OF_BUFFER) { return ECBOR_ERR_INVALID_END_OF_BUFFER; @@ -267,51 +331,75 @@ ecbor_get_map_item (ecbor_item_t *map, uint64_t index, ecbor_item_t *key, return ECBOR_OK; } -extern ecbor_error_t -ecbor_get_tag_item (ecbor_item_t *tag, ecbor_item_t *item) +ecbor_error_t +ecbor_get_tag_item_ptr (ecbor_item_t *tag, ecbor_item_t **item) { - ecbor_decode_context_t context; - ecbor_error_t rc; - - if (!tag) { - return ECBOR_ERR_NULL_ITEM; - } - if (tag->type != ECBOR_TYPE_TAG) { - return ECBOR_ERR_INVALID_TYPE; - } - if (!item) { - return ECBOR_ERR_NULL_VALUE; - } - - 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; - } + 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; } -static inline ecbor_error_t -ecbor_get_string_internal (ecbor_item_t *str, uint8_t **value, ecbor_type_t type) + +#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) { - if (!str) { - return ECBOR_ERR_NULL_ITEM; - } - if (!value) { - return ECBOR_ERR_NULL_VALUE; - } - if (str->type != type) { - return ECBOR_ERR_INVALID_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; } @@ -321,19 +409,14 @@ ecbor_get_string_internal (ecbor_item_t *str, uint8_t **value, ecbor_type_t type return ECBOR_OK; } -static inline ecbor_error_t -ecbor_get_string_chunk_count_internal (ecbor_item_t *str, uint64_t *count, +static __attribute__((noinline)) ecbor_error_t +ecbor_get_string_chunk_count_internal (ecbor_item_t *str, size_t *count, ecbor_type_t type) { - if (!str) { - return ECBOR_ERR_NULL_ITEM; - } - if (!count) { - return ECBOR_ERR_NULL_VALUE; - } - if (str->type != type) { - return ECBOR_ERR_INVALID_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; } @@ -343,30 +426,23 @@ ecbor_get_string_chunk_count_internal (ecbor_item_t *str, uint64_t *count, return ECBOR_OK; } -static inline ecbor_error_t -ecbor_get_string_chunk_internal (ecbor_item_t *str, uint64_t index, +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; - uint64_t i; + size_t i; + + ECBOR_INTERNAL_CHECK_ITEM_PTR (str); + ECBOR_INTERNAL_CHECK_VALUE_PTR (chunk); + ECBOR_INTERNAL_CHECK_TYPE (str->type, type); - if (!str) { - return ECBOR_ERR_NULL_ITEM; - } - if (!chunk) { - return ECBOR_ERR_NULL_VALUE; - } - if (str->type != type) { - return ECBOR_ERR_INVALID_TYPE; - } if (!str->is_indefinite) { return ECBOR_ERR_WONT_RETURN_DEFINITE; } - if (index >= str->value.string.n_chunks) { - return ECBOR_ERR_INDEX_OUT_OF_BOUNDS; - } - + 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) { @@ -402,13 +478,13 @@ ecbor_get_str (ecbor_item_t *str, char **value) } ecbor_error_t -ecbor_get_str_chunk_count (ecbor_item_t *str, uint64_t *count) +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, uint64_t index, ecbor_item_t *chunk) +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); } @@ -420,30 +496,60 @@ ecbor_get_bstr (ecbor_item_t *str, uint8_t **value) } ecbor_error_t -ecbor_get_bstr_chunk_count (ecbor_item_t *str, uint64_t *count) +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, uint64_t index, ecbor_item_t *chunk) +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_root (ecbor_decode_context_t *context, ecbor_node_t **root) +ecbor_get_tag_value (ecbor_item_t *tag, uint64_t *tag_value) { - if (!context) { - return ECBOR_ERR_NULL_CONTEXT; - } - if (!root) { - return ECBOR_ERR_NULL_NODE; - } - if (context->n_nodes <= 0) { - return ECBOR_ERR_EMPTY_NODE_BUFFER; - } + 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); - *root = &context->nodes[0]; + (*value) = (uint8_t) item->value.uinteger; return ECBOR_OK; }
\ No newline at end of file diff --git a/src/ecbor_decoder.c b/src/ecbor_decoder.c index 249d04f..4a80514 100644 --- a/src/ecbor_decoder.c +++ b/src/ecbor_decoder.c @@ -8,10 +8,28 @@ #include "ecbor.h" #include "ecbor_internal.h" +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, + .prev = NULL, + .index = 0 +}; + static ecbor_error_t ecbor_initialize_decode_internal (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size) + size_t buffer_size) { if (!context) { return ECBOR_ERR_NULL_CONTEXT; @@ -29,7 +47,7 @@ ecbor_initialize_decode_internal (ecbor_decode_context_t *context, ecbor_error_t ecbor_initialize_decode (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size) + size_t buffer_size) { ecbor_error_t rc = ecbor_initialize_decode_internal (context, buffer, buffer_size); @@ -39,9 +57,9 @@ ecbor_initialize_decode (ecbor_decode_context_t *context, } context->mode = ECBOR_MODE_DECODE; - context->nodes = NULL; - context->node_capacity = 0; - context->n_nodes = 0; + context->items = NULL; + context->item_capacity = 0; + context->n_items = 0; return ECBOR_OK; } @@ -49,7 +67,7 @@ ecbor_initialize_decode (ecbor_decode_context_t *context, ecbor_error_t ecbor_initialize_decode_streamed (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size) + size_t buffer_size) { ecbor_error_t rc = ecbor_initialize_decode_internal (context, buffer, buffer_size); @@ -59,9 +77,9 @@ ecbor_initialize_decode_streamed (ecbor_decode_context_t *context, } context->mode = ECBOR_MODE_DECODE_STREAMED; - context->nodes = NULL; - context->node_capacity = 0; - context->n_nodes = 0; + context->items = NULL; + context->item_capacity = 0; + context->n_items = 0; return ECBOR_OK; } @@ -69,9 +87,9 @@ ecbor_initialize_decode_streamed (ecbor_decode_context_t *context, ecbor_error_t ecbor_initialize_decode_tree (ecbor_decode_context_t *context, const uint8_t *buffer, - uint64_t buffer_size, - ecbor_node_t *node_buffer, - uint64_t node_capacity) + 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); @@ -80,14 +98,14 @@ ecbor_initialize_decode_tree (ecbor_decode_context_t *context, return rc; } - if (!node_buffer) { - return ECBOR_ERR_NULL_NODE_BUFFER; + if (!item_buffer) { + return ECBOR_ERR_NULL_ITEM_BUFFER; } context->mode = ECBOR_MODE_DECODE_TREE; - context->nodes = node_buffer; - context->node_capacity = node_capacity; - context->n_nodes = 0; + context->items = item_buffer; + context->item_capacity = item_capacity; + context->n_items = 0; return ECBOR_OK; } @@ -95,7 +113,7 @@ ecbor_initialize_decode_tree (ecbor_decode_context_t *context, static inline ecbor_error_t ecbor_decode_uint (ecbor_decode_context_t *context, uint64_t *value, - uint64_t *size, + size_t *size, uint8_t additional) { if (additional < 24) { @@ -148,7 +166,7 @@ ecbor_decode_uint (ecbor_decode_context_t *context, static inline ecbor_error_t ecbor_decode_fp32 (ecbor_decode_context_t *context, float *value, - uint64_t *size) + size_t *size) { /* compute storage size */ (*size) = sizeof (float); @@ -173,7 +191,7 @@ ecbor_decode_fp32 (ecbor_decode_context_t *context, static inline ecbor_error_t ecbor_decode_fp64 (ecbor_decode_context_t *context, double *value, - uint64_t *size) + size_t *size) { /* compute storage size */ (*size) = sizeof (double); @@ -221,13 +239,13 @@ ecbor_decode_simple_value (ecbor_item_t *item) return ECBOR_OK; } -static ecbor_error_t +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) { - unsigned int additional; + uint8_t additional; if (context->bytes_left == 0) { return ECBOR_END_OF_BUFFER; @@ -240,10 +258,7 @@ ecbor_decode_next_internal (ecbor_decode_context_t *context, } /* clear item, just so we do not leave garbage on partial read */ - item->type = ECBOR_TYPE_NONE; - item->size = 0; - item->length = 0; - item->is_indefinite = false; + (*item) = null_item; /* extract major type (most significant three bits) and additional info */ item->type = (*context->in_position >> 5) & 0x07; @@ -344,11 +359,15 @@ ecbor_decode_next_internal (ecbor_decode_context_t *context, } } else { /* read size of buffer */ - ecbor_error_t rc = ecbor_decode_uint (context, &item->length, - &item->size, additional); + 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) { @@ -413,13 +432,19 @@ ecbor_decode_next_internal (ecbor_decode_context_t *context, } } } else { + uint64_t len; + /* read size of map or array */ - ecbor_error_t rc = ecbor_decode_uint (context, &item->length, + 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; @@ -432,7 +457,7 @@ ecbor_decode_next_internal (ecbor_decode_context_t *context, if (context->mode != ECBOR_MODE_DECODE_STREAMED) { ecbor_item_t child; ecbor_error_t rc; - uint64_t child_no; + size_t child_no; /* not in streamed mode; compute size so we can advance */ for (child_no = 0; child_no < item->length; child_no ++) { @@ -558,126 +583,210 @@ ecbor_decode (ecbor_decode_context_t *context, ecbor_item_t *item) } extern ecbor_error_t -ecbor_decode_tree (ecbor_decode_context_t *context) +ecbor_decode_tree (ecbor_decode_context_t *context, ecbor_item_t **root) { ecbor_error_t rc = ECBOR_OK; - ecbor_node_t *curr_node = NULL, *prev_node = NULL, *par_node = NULL; + ecbor_item_t *curr_node = NULL, *new_node = NULL; + uint8_t link_as_sibling = 0; if (!context) { return ECBOR_ERR_NULL_CONTEXT; } + if (!root) { + return ECBOR_ERR_NULL_ITEM; + } + if (context->mode != ECBOR_MODE_DECODE_TREE) { + return ECBOR_ERR_WRONG_MODE; + } /* initialization */ - context->n_nodes = 0; + 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 (rc != ECBOR_OK) { + /* TODO: below code is a mess; must be refactored for brevity */ + while (rc == ECBOR_OK) { /* allocate new node */ - if (context->n_nodes >= context->node_capacity) { - rc = ECBOR_ERR_END_OF_NODE_BUFFER; + if (context->n_items >= context->item_capacity) { + rc = ECBOR_ERR_END_OF_ITEM_BUFFER; goto end; } - curr_node = &context->nodes[context->n_nodes]; - curr_node->next = NULL; - curr_node->prev = NULL; - curr_node->parent = NULL; - curr_node->child = NULL; - context->n_nodes ++; + new_node = &context->items[context->n_items]; + context->n_items ++; /* consume next item */ - rc = ecbor_decode_next_internal (context, &curr_node->item, false, + rc = ecbor_decode_next_internal (context, new_node, false, ECBOR_TYPE_NONE); + if (rc != ECBOR_OK && rc != ECBOR_END_OF_INDEFINITE) { + /* some kind of error */ + goto end; + } - /* handle end of indefinite */ - if (rc == ECBOR_END_OF_INDEFINITE) { - if ((!par_node) - || (par_node->item.type != ECBOR_TYPE_MAP - && par_node->item.type != ECBOR_TYPE_ARRAY) - || (!par_node->item.is_indefinite)) { - /* we are not in an indefinite map or array */ - rc = ECBOR_ERR_INVALID_STOP_CODE; - goto end; + if (curr_node) { + if (curr_node->type == ECBOR_TYPE_MAP + || curr_node->type == ECBOR_TYPE_ARRAY) { + /* + * New node may be either sibling or child + */ + + /* handle end of indefinite arrays */ + if (rc == ECBOR_END_OF_INDEFINITE) { + if (curr_node->length == 0 && curr_node->parent + && (curr_node->parent->type == ECBOR_TYPE_ARRAY + || curr_node->parent->type == ECBOR_TYPE_MAP)) { + /* if we're in a zero-length array or map, this stop code may refer + to the parent */ + curr_node = curr_node->parent; + } + + if (!curr_node->is_indefinite) { + /* this array is not indefinite */ + rc = ECBOR_ERR_INVALID_STOP_CODE; + goto end; + } + + /* end of indefinite empty array */ + link_as_sibling = 1; + context->n_items --; + rc = ECBOR_OK; + continue; + } + + /* finish any definite arrays or maps */ + while (curr_node->parent + && (curr_node->parent->type == ECBOR_TYPE_ARRAY + || curr_node->parent->type == ECBOR_TYPE_MAP) + && !curr_node->parent->is_indefinite + && (curr_node->index == curr_node->parent->length - 1)) { + /* make sure we're not finishing an array too early */ + if ((curr_node->type == ECBOR_TYPE_ARRAY + || curr_node->type == ECBOR_TYPE_MAP) + && !curr_node->child) { + break; + } + + /* one level up */ + curr_node = curr_node->parent; + link_as_sibling = 1; + } + + if (link_as_sibling) { + /* link new node as sibling */ + new_node->index = curr_node->index + 1; + new_node->parent = curr_node->parent; + curr_node->next = new_node; + curr_node = new_node; + } else { + /* link as child */ + new_node->index = 0; + new_node->parent = curr_node; + curr_node->child = new_node; + curr_node = new_node; + } + + /* count indefinite arrays and maps */ + if (curr_node->parent + && (curr_node->parent->type == ECBOR_TYPE_ARRAY + || curr_node->parent->type == ECBOR_TYPE_MAP) + && curr_node->parent->is_indefinite) { + curr_node->parent->length ++; + } + + } else { + /* + * New node is sibling of current node + */ + + /* finish any definite arrays or maps */ + while (curr_node->parent + && (curr_node->parent->type == ECBOR_TYPE_ARRAY + || curr_node->parent->type == ECBOR_TYPE_MAP) + && !curr_node->parent->is_indefinite + && (curr_node->index == curr_node->parent->length - 1)) { + /* one level up */ + curr_node = curr_node->parent; + } + + /* handle end of indefinite arrays */ + if (rc == ECBOR_END_OF_INDEFINITE) { + if (!curr_node->parent + || (curr_node->parent->type != ECBOR_TYPE_ARRAY + && curr_node->parent->type != ECBOR_TYPE_MAP) + || !curr_node->parent->is_indefinite) { + /* parent isn't indefinite; fail */ + rc = ECBOR_ERR_INVALID_STOP_CODE; + goto end; + } + + /* we've ended an indefinite array or map; go one level up */ + curr_node = curr_node->parent; + link_as_sibling = 1; + + /* reuse new_node */ + context->n_items --; + rc = ECBOR_OK; + continue; + } + + /* link new node */ + if (curr_node->type == ECBOR_TYPE_TAG && !curr_node->child) { + /* link as child; keep current node */ + new_node->index = 0; + new_node->parent = curr_node; + curr_node->child = new_node; + } else { + /* link new node as sibling */ + new_node->index = curr_node->index + 1; + new_node->parent = curr_node->parent; + curr_node->next = new_node; + curr_node = new_node; + /* count indefinite arrays and maps */ + if (curr_node->parent + && (curr_node->parent->type == ECBOR_TYPE_ARRAY + || curr_node->parent->type == ECBOR_TYPE_MAP) + && curr_node->parent->is_indefinite) { + curr_node->parent->length ++; + } + } } - - if (par_node && par_node->item.type == ECBOR_TYPE_MAP - && prev_node && (prev_node->index % 2 == 0)) { - /* map must have even number of children */ - rc = ECBOR_ERR_INVALID_KEY_VALUE_PAIR; + } else { + /* + * First node + */ + if (rc == ECBOR_END_OF_INDEFINITE) { + /* can't have first item as stop code */ + rc = ECBOR_ERR_INVALID_STOP_CODE; goto end; } - - /* jump up one level */ - curr_node = par_node; - par_node = curr_node->parent; - prev_node = curr_node->prev; - - /* continue parsing */ - rc = ECBOR_OK; - /* ignore this item, reuse it later */ - context->n_nodes --; - continue; - } - - /* check return code */ - if (rc == ECBOR_END_OF_BUFFER) { - if (par_node) { - /* we are not allowed to reach the end unless the item is top-level */ - rc = ECBOR_ERR_INVALID_END_OF_BUFFER; - goto end; - } - } else if (rc != ECBOR_OK) { - goto end; - } - - /* link in tree */ - if (prev_node) { - prev_node->next = curr_node; - curr_node->prev = prev_node; - } - if (par_node) { - curr_node->parent = par_node; - if (!prev_node) { - par_node->child = curr_node; - } + /* nothing to link, just keep it as current node */ + new_node->index = 0; + curr_node = new_node; } - /* handle new children */ - if (curr_node->item.type == ECBOR_TYPE_MAP - || curr_node->item.type == ECBOR_TYPE_ARRAY - || curr_node->item.type == ECBOR_TYPE_TAG) { - /* jump a level down */ - par_node = curr_node; - prev_node = NULL; - continue; - } - - /* handle end of definite maps and arrays, and tags */ - while (par_node - && (par_node->item.type == ECBOR_TYPE_MAP - || par_node->item.type == ECBOR_TYPE_ARRAY - || par_node->item.type == ECBOR_TYPE_TAG) - && !par_node->item.is_indefinite - && par_node->item.length == (curr_node->index + 1)) { - /* parent has filled all items, jump a level up */ - curr_node = par_node; - par_node = curr_node->parent; - prev_node = curr_node->prev; - } + /* expire linking as sibling */ + link_as_sibling = 0; } 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_nodes = 0; + 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 index fc72331..52b2eed 100644 --- a/src/ecbor_describe.c +++ b/src/ecbor_describe.c @@ -16,10 +16,17 @@ * 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 @@ -35,6 +42,7 @@ print_help (void) { printf ("Usage: ecbor-describe [options] <filename>\n"); printf (" options:\n"); + printf (" -t, --tree Use tree decoding mode\n"); printf (" -h, --help Display this help message\n"); } @@ -62,7 +70,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) { int64_t val; - rc = ecbor_get_value (item, (void *) &val); + rc = ecbor_get_int64 (item, &val); if (rc != ECBOR_OK) { return rc; } @@ -75,7 +83,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) { uint64_t val; - rc = ecbor_get_value (item, (void *) &val); + rc = ecbor_get_uint64 (item, &val); if (rc != ECBOR_OK) { return rc; } @@ -86,15 +94,15 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) case ECBOR_TYPE_STR: { - uint64_t len; + size_t len; rc = ecbor_get_length (item, &len); if (rc != ECBOR_OK) { return rc; } - if (ECBOR_IS_INDEFINITE (*item)) { - uint64_t nchunks; + if (ECBOR_IS_INDEFINITE (item)) { + size_t nchunks; rc = ecbor_get_str_chunk_count (item, &nchunks); if (rc != ECBOR_OK) { @@ -132,15 +140,15 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) case ECBOR_TYPE_BSTR: { - uint64_t len; + size_t len; rc = ecbor_get_length (item, &len); if (rc != ECBOR_OK) { return rc; } - if (ECBOR_IS_INDEFINITE (*item)) { - uint64_t nchunks; + if (ECBOR_IS_INDEFINITE (item)) { + size_t nchunks; rc = ecbor_get_bstr_chunk_count (item, &nchunks); if (rc != ECBOR_OK) { @@ -185,7 +193,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) case ECBOR_TYPE_ARRAY: { - uint64_t len, i; + size_t len, i; rc = ecbor_get_length (item, &len); if (rc != ECBOR_OK) { @@ -193,7 +201,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) } printf ("[ARRAY] len %u %s\n", (unsigned int) len, - (ECBOR_IS_INDEFINITE (*item) ? "(indefinite)" : "")); + (ECBOR_IS_INDEFINITE (item) ? "(indefinite)" : "")); for (i = 0; i < len; i ++) { ecbor_item_t child; @@ -212,7 +220,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) case ECBOR_TYPE_MAP: { - uint64_t len, i; + size_t len, i; char kv_msg[100] = { 0 }; rc = ecbor_get_length (item, &len); @@ -221,7 +229,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) } printf ("[MAP] len %u %s\n", (unsigned int) len, - (ECBOR_IS_INDEFINITE (*item) ? "(indefinite)" : "")); + (ECBOR_IS_INDEFINITE (item) ? "(indefinite)" : "")); for (i = 0; i < len; i ++) { ecbor_item_t k, v; @@ -247,10 +255,10 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) case ECBOR_TYPE_TAG: { - int64_t val; + uint64_t val; ecbor_item_t child; - rc = ecbor_get_value (item, (void *) &val); + rc = ecbor_get_tag_value (item, &val); if (rc != ECBOR_OK) { return rc; } @@ -273,7 +281,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) { float val; - rc = ecbor_get_value (item, (void *) &val); + rc = ecbor_get_fp32 (item, &val); if (rc != ECBOR_OK) { return rc; } @@ -286,7 +294,7 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) { double val; - rc = ecbor_get_value (item, (void *) &val); + rc = ecbor_get_fp64 (item, &val); if (rc != ECBOR_OK) { return rc; } @@ -297,9 +305,9 @@ print_ecbor_item (ecbor_item_t *item, unsigned int level, char *prefix) case ECBOR_TYPE_BOOL: { - uint64_t val; + uint8_t val; - rc = ecbor_get_value (item, (void *) &val); + rc = ecbor_get_bool (item, &val); if (rc != ECBOR_OK) { return rc; } @@ -333,17 +341,22 @@ 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, "h", long_options, &option_index); + 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; @@ -406,10 +419,14 @@ main(int argc, char **argv) /* parse CBOR data */ { ecbor_decode_context_t context; - ecbor_item_t item; ecbor_error_t rc; - rc = ecbor_initialize_decode (&context, cbor, cbor_length); + 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; @@ -417,20 +434,44 @@ main(int argc, char **argv) fprintf (stderr, "CBOR objects:\n"); - while (1) { - rc = ecbor_decode (&context, &item); + if (tree_mode) { + /* decode all */ + ecbor_item_t *item; + + rc = ecbor_decode_tree (&context, &item); if (rc != ECBOR_OK) { - if (rc == ECBOR_END_OF_BUFFER) { - break; - } 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; - rc = print_ecbor_item (&item, 0, ""); - if (rc != ECBOR_OK) { - print_ecbor_error (rc); - return -1; + 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; + } } } } diff --git a/src/ecbor_internal.h b/src/ecbor_internal.h index abeaf97..8013fcb 100644 --- a/src/ecbor_internal.h +++ b/src/ecbor_internal.h @@ -9,9 +9,9 @@ #define _ECBOR_INTERNAL_H_ #include <stdint.h> +#include <stddef.h> -/* Don't rely on <stddef.h> for this */ -#define NULL 0 +/* Don't rely on <stdbool.h> for this */ #define false 0 #define true 1 @@ -38,6 +38,36 @@ enum { ECBOR_SIMPLE_UNDEFINED = 23 }; +/* 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_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 */ diff --git a/test/files/appendix_a/0011.answer b/test/files/appendix_a/0011.answer index ad9e42d..14e9d30 100644 --- a/test/files/appendix_a/0011.answer +++ b/test/files/appendix_a/0011.answer @@ -1,2 +1,2 @@ [TAG] value 2 - [BSTR] len 9 value '010000000000000000' + [BSTR] len 9 value '010000000000000000' diff --git a/test/files/appendix_a/0013.answer b/test/files/appendix_a/0013.answer index aeaf0e1..d587edd 100644 --- a/test/files/appendix_a/0013.answer +++ b/test/files/appendix_a/0013.answer @@ -1,2 +1,2 @@ [TAG] value 3 - [BSTR] len 9 value '010000000000000000' + [BSTR] len 9 value '010000000000000000' diff --git a/test/files/appendix_a/0047.answer b/test/files/appendix_a/0047.answer index cece9bc..a86ac74 100644 --- a/test/files/appendix_a/0047.answer +++ b/test/files/appendix_a/0047.answer @@ -1,2 +1,2 @@ [TAG] value 0 - [STR] len 20 value '2013-03-21T20:04:00Z' + [STR] len 20 value '2013-03-21T20:04:00Z' diff --git a/test/files/appendix_a/0050.answer b/test/files/appendix_a/0050.answer index 6ac580d..2d468b6 100644 --- a/test/files/appendix_a/0050.answer +++ b/test/files/appendix_a/0050.answer @@ -1,2 +1,2 @@ [TAG] value 23 - [BSTR] len 4 value '01020304' + [BSTR] len 4 value '01020304' diff --git a/test/files/appendix_a/0051.answer b/test/files/appendix_a/0051.answer index 5ca5cc2..d33d6de 100644 --- a/test/files/appendix_a/0051.answer +++ b/test/files/appendix_a/0051.answer @@ -1,2 +1,2 @@ [TAG] value 24 - [BSTR] len 5 value '6449455446' + [BSTR] len 5 value '6449455446' diff --git a/test/files/appendix_a/0052.answer b/test/files/appendix_a/0052.answer index 1d701cd..f6fe9e1 100644 --- a/test/files/appendix_a/0052.answer +++ b/test/files/appendix_a/0052.answer @@ -1,2 +1,2 @@ [TAG] value 32 - [STR] len 22 value 'http://www.example.com' + [STR] len 22 value 'http://www.example.com' diff --git a/test/files/appendix_a/0053.answer b/test/files/appendix_a/0053.answer index e7dd1ce..8108de4 100644 --- a/test/files/appendix_a/0053.answer +++ b/test/files/appendix_a/0053.answer @@ -1 +1 @@ -[BSTR] len 0 value '' +[BSTR] len 0 value '' diff --git a/test/files/appendix_a/0054.answer b/test/files/appendix_a/0054.answer index 2e61645..03b483d 100644 --- a/test/files/appendix_a/0054.answer +++ b/test/files/appendix_a/0054.answer @@ -1 +1 @@ -[BSTR] len 4 value '01020304' +[BSTR] len 4 value '01020304' diff --git a/test/files/appendix_a/0055.answer b/test/files/appendix_a/0055.answer index f0c90c5..706df0b 100644 --- a/test/files/appendix_a/0055.answer +++ b/test/files/appendix_a/0055.answer @@ -1 +1 @@ -[STR] len 0 value '' +[STR] len 0 value '' diff --git a/test/files/appendix_a/0056.answer b/test/files/appendix_a/0056.answer index 369fb4e..ef45a4f 100644 --- a/test/files/appendix_a/0056.answer +++ b/test/files/appendix_a/0056.answer @@ -1 +1 @@ -[STR] len 1 value 'a' +[STR] len 1 value 'a' diff --git a/test/files/appendix_a/0057.answer b/test/files/appendix_a/0057.answer index edaedad..4211b81 100644 --- a/test/files/appendix_a/0057.answer +++ b/test/files/appendix_a/0057.answer @@ -1 +1 @@ -[STR] len 4 value 'IETF' +[STR] len 4 value 'IETF' diff --git a/test/files/appendix_a/0058.answer b/test/files/appendix_a/0058.answer index 6de9af7..81aeb73 100644 --- a/test/files/appendix_a/0058.answer +++ b/test/files/appendix_a/0058.answer @@ -1 +1 @@ -[STR] len 2 value '"\' +[STR] len 2 value '"\' diff --git a/test/files/appendix_a/0059.answer b/test/files/appendix_a/0059.answer index 9cdc70a..1c01793 100644 --- a/test/files/appendix_a/0059.answer +++ b/test/files/appendix_a/0059.answer @@ -1 +1 @@ -[STR] len 2 value 'ü' +[STR] len 2 value 'ü' diff --git a/test/files/appendix_a/0060.answer b/test/files/appendix_a/0060.answer index 0f1c3f1..d6774b3 100644 --- a/test/files/appendix_a/0060.answer +++ b/test/files/appendix_a/0060.answer @@ -1 +1 @@ -[STR] len 3 value '水' +[STR] len 3 value '水' diff --git a/test/files/appendix_a/0061.answer b/test/files/appendix_a/0061.answer index dedc0a7..b866c52 100644 --- a/test/files/appendix_a/0061.answer +++ b/test/files/appendix_a/0061.answer @@ -1 +1 @@ -[STR] len 4 value '𐅑' +[STR] len 4 value '𐅑' diff --git a/test/files/appendix_a/0068.answer b/test/files/appendix_a/0068.answer index e92f866..e21e37a 100644 --- a/test/files/appendix_a/0068.answer +++ b/test/files/appendix_a/0068.answer @@ -1,7 +1,7 @@ [MAP] len 2 - key[0]: [STR] len 1 value 'a' + key[0]: [STR] len 1 value 'a' val[0]: [UINT] value 1 - key[1]: [STR] len 1 value 'b' + key[1]: [STR] len 1 value 'b' val[1]: [ARRAY] len 2 [UINT] value 2 [UINT] value 3 diff --git a/test/files/appendix_a/0069.answer b/test/files/appendix_a/0069.answer index fe20a7b..bfa61f8 100644 --- a/test/files/appendix_a/0069.answer +++ b/test/files/appendix_a/0069.answer @@ -1,5 +1,5 @@ [ARRAY] len 2 - [STR] len 1 value 'a' + [STR] len 1 value 'a' [MAP] len 1 - key[0]: [STR] len 1 value 'b' - val[0]: [STR] len 1 value 'c' + key[0]: [STR] len 1 value 'b' + val[0]: [STR] len 1 value 'c' diff --git a/test/files/appendix_a/0070.answer b/test/files/appendix_a/0070.answer index 52b3a8e..5d87bb8 100644 --- a/test/files/appendix_a/0070.answer +++ b/test/files/appendix_a/0070.answer @@ -1,11 +1,11 @@ [MAP] len 5 - key[0]: [STR] len 1 value 'a' - val[0]: [STR] len 1 value 'A' - key[1]: [STR] len 1 value 'b' - val[1]: [STR] len 1 value 'B' - key[2]: [STR] len 1 value 'c' - val[2]: [STR] len 1 value 'C' - key[3]: [STR] len 1 value 'd' - val[3]: [STR] len 1 value 'D' - key[4]: [STR] len 1 value 'e' - val[4]: [STR] len 1 value 'E' + key[0]: [STR] len 1 value 'a' + val[0]: [STR] len 1 value 'A' + key[1]: [STR] len 1 value 'b' + val[1]: [STR] len 1 value 'B' + key[2]: [STR] len 1 value 'c' + val[2]: [STR] len 1 value 'C' + key[3]: [STR] len 1 value 'd' + val[3]: [STR] len 1 value 'D' + key[4]: [STR] len 1 value 'e' + val[4]: [STR] len 1 value 'E' diff --git a/test/runtests.sh b/test/runtests.sh index 96de614..29569d7 100755 --- a/test/runtests.sh +++ b/test/runtests.sh @@ -20,21 +20,26 @@ echo "============================== APPENDIX A ==============================" for f in files/appendix_a/*.bin; do answer_file=${f%.bin}.answer result_file=${f%.bin}.result - - ../bin/ecbor-describe $f > $result_file 2>/dev/null - rc=$? - - if [ ! -f $result_file ] || [ ! -f $answer_file ] || [ "$(diff $answer_file $result_file 2>/dev/null)" != "" ]; then - fail=$(($fail + 1)) - status=$FAIL_MSG - else - pass=$(($pass + 1)) - status=$PASS_MSG - fi - - machine_indented=$(printf '%-67s' "$f") - machine_indented=${machine_indented// /.} - printf "%s %s\n" "$machine_indented" "$status" + + declare -a opts=("" "--tree") + + for opt in "${opts[@]}"; do + ../bin/ecbor-describe $opt $f > $result_file 2>/dev/null + rc=$? + + if [ ! -f $result_file ] || [ ! -f $answer_file ] || [ "$(diff $answer_file $result_file 2>/dev/null)" != "" ]; then + fail=$(($fail + 1)) + status=$FAIL_MSG + else + pass=$(($pass + 1)) + status=$PASS_MSG + fi + + test_name="$f($opt)" + machine_indented=$(printf '%-67s' "$test_name") + machine_indented=${machine_indented// /.} + printf "%s %s\n" "$machine_indented" "$status" + done done echo "========================================================================" echo "Passed / Failed: ${pass}/${fail}" |
