summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrimio <vasi.vilvoiu@gmail.com>2018-03-02 01:42:43 +0200
committerrimio <vasi.vilvoiu@gmail.com>2018-03-03 01:39:53 +0200
commitd31766d827c085dea004ebf7209ab45eddaad02b (patch)
treec52e09cbf927f4d59eadff04510e84c6a630c86f
parentf2397db2cab21c9db06e8f7b69981f7eb2ca8cd5 (diff)
Various fixes and refactoring.
-rw-r--r--include/ecbor.h207
-rw-r--r--src/ecbor.c498
-rw-r--r--src/ecbor_decoder.c337
-rw-r--r--src/ecbor_describe.c101
-rw-r--r--src/ecbor_internal.h34
-rw-r--r--test/files/appendix_a/0011.answer2
-rw-r--r--test/files/appendix_a/0013.answer2
-rw-r--r--test/files/appendix_a/0047.answer2
-rw-r--r--test/files/appendix_a/0050.answer2
-rw-r--r--test/files/appendix_a/0051.answer2
-rw-r--r--test/files/appendix_a/0052.answer2
-rw-r--r--test/files/appendix_a/0053.answer2
-rw-r--r--test/files/appendix_a/0054.answer2
-rw-r--r--test/files/appendix_a/0055.answer2
-rw-r--r--test/files/appendix_a/0056.answer2
-rw-r--r--test/files/appendix_a/0057.answer2
-rw-r--r--test/files/appendix_a/0058.answer2
-rw-r--r--test/files/appendix_a/0059.answer2
-rw-r--r--test/files/appendix_a/0060.answer2
-rw-r--r--test/files/appendix_a/0061.answer2
-rw-r--r--test/files/appendix_a/0068.answer4
-rw-r--r--test/files/appendix_a/0069.answer6
-rw-r--r--test/files/appendix_a/0070.answer20
-rwxr-xr-xtest/runtests.sh35
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}"