diff options
| author | rimio <vasi.vilvoiu@gmail.com> | 2018-03-05 23:32:48 +0200 |
|---|---|---|
| committer | rimio <vasi.vilvoiu@gmail.com> | 2018-03-05 23:32:48 +0200 |
| commit | e3f3c0d12eb83af50f984332a8ddae6f27197b12 (patch) | |
| tree | a352968cbdd340f19ade64ebd0d78ca49e88aae7 /src/ecbor_decoder.c | |
| parent | f8b56be188fa5ea9706f91ec7ca513740850af02 (diff) | |
Change dir structure to accomodate multiple tools
Diffstat (limited to 'src/ecbor_decoder.c')
| -rw-r--r-- | src/ecbor_decoder.c | 780 |
1 files changed, 0 insertions, 780 deletions
diff --git a/src/ecbor_decoder.c b/src/ecbor_decoder.c deleted file mode 100644 index a422dd9..0000000 --- a/src/ecbor_decoder.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * Copyright (c) 2018 Vasile Vilvoiu <vasi.vilvoiu@gmail.com> - * - * libecbor is free software; you can redistribute it and/or modify - * it under the terms of the MIT license. See LICENSE for details. - */ - -#include "ecbor.h" -#include "ecbor_internal.h" - -static ecbor_error_t -ecbor_initialize_decode_internal (ecbor_decode_context_t *context, - const uint8_t *buffer, - size_t buffer_size) -{ - ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context); - if (!buffer) { - return ECBOR_ERR_NULL_INPUT_BUFFER; - } - - context->in_position = buffer; - context->bytes_left = buffer_size; - - return ECBOR_OK; -} - -ecbor_error_t -ecbor_initialize_decode (ecbor_decode_context_t *context, - const uint8_t *buffer, - size_t buffer_size) -{ - ecbor_error_t rc = - ecbor_initialize_decode_internal (context, buffer, buffer_size); - - if (rc != ECBOR_OK) { - return rc; - } - - context->mode = ECBOR_MODE_DECODE; - context->items = NULL; - context->item_capacity = 0; - context->n_items = 0; - - return ECBOR_OK; -} - -ecbor_error_t -ecbor_initialize_decode_streamed (ecbor_decode_context_t *context, - const uint8_t *buffer, - size_t buffer_size) -{ - ecbor_error_t rc = - ecbor_initialize_decode_internal (context, buffer, buffer_size); - - if (rc != ECBOR_OK) { - return rc; - } - - context->mode = ECBOR_MODE_DECODE_STREAMED; - context->items = NULL; - context->item_capacity = 0; - context->n_items = 0; - - return ECBOR_OK; -} - -ecbor_error_t -ecbor_initialize_decode_tree (ecbor_decode_context_t *context, - const uint8_t *buffer, - size_t buffer_size, - ecbor_item_t *item_buffer, - size_t item_capacity) -{ - ecbor_error_t rc = - ecbor_initialize_decode_internal (context, buffer, buffer_size); - - if (rc != ECBOR_OK) { - return rc; - } - - if (!item_buffer) { - return ECBOR_ERR_NULL_ITEM_BUFFER; - } - - context->mode = ECBOR_MODE_DECODE_TREE; - context->items = item_buffer; - context->item_capacity = item_capacity; - context->n_items = 0; - - return ECBOR_OK; -} - -static inline ecbor_error_t -ecbor_decode_uint (ecbor_decode_context_t *context, - uint64_t *value, - size_t *size, - uint8_t additional) -{ - if (additional < 24) { - /* value stored in additional information */ - (*value) = additional; - (*size) = 1; - } else { - /* compute storage size */ - (*size) = (0x1 << (additional - ECBOR_ADDITIONAL_1BYTE)); - if (context->bytes_left < (*size)) { - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } - - /* read actual value */ - switch (additional) { - case ECBOR_ADDITIONAL_1BYTE: - (*value) = *((uint8_t *) context->in_position); - break; - - case ECBOR_ADDITIONAL_2BYTE: - (*value) = - ecbor_uint16_from_big_endian (*((uint16_t *) context->in_position)); - break; - - case ECBOR_ADDITIONAL_4BYTE: - (*value) = - ecbor_uint32_from_big_endian (*((uint32_t *) context->in_position)); - break; - - case ECBOR_ADDITIONAL_8BYTE: - (*value) = - ecbor_uint64_from_big_endian (*((uint64_t *) context->in_position)); - break; - - default: - return ECBOR_ERR_INVALID_ADDITIONAL; - } - - /* advance buffer */ - context->in_position += (*size); - context->bytes_left -= (*size); - - /* meter the first byte */ - (*size) ++; - } - - return ECBOR_OK; -} - -static inline ecbor_error_t -ecbor_decode_fp32 (ecbor_decode_context_t *context, - float *value, - size_t *size) -{ - /* compute storage size */ - (*size) = sizeof (float); - if (context->bytes_left < (*size)) { - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } - - /* read actual value */ - (*value) = - ecbor_fp32_from_big_endian (*((float *) context->in_position)); - - /* advance buffer */ - context->in_position += (*size); - context->bytes_left -= (*size); - - /* meter the first byte */ - (*size) ++; - - return ECBOR_OK; -} - -static inline ecbor_error_t -ecbor_decode_fp64 (ecbor_decode_context_t *context, - double *value, - size_t *size) -{ - /* compute storage size */ - (*size) = sizeof (double); - if (context->bytes_left < (*size)) { - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } - - /* read actual value */ - (*value) = - ecbor_fp64_from_big_endian (*((double *) context->in_position)); - - /* advance buffer */ - context->in_position += (*size); - context->bytes_left -= (*size); - - /* meter the first byte */ - (*size) ++; - - return ECBOR_OK; -} - -static inline ecbor_error_t -ecbor_decode_simple_value (ecbor_item_t *item) -{ - switch (item->value.uinteger) { - case ECBOR_SIMPLE_FALSE: - case ECBOR_SIMPLE_TRUE: - item->type = ECBOR_TYPE_BOOL; - item->value.uinteger = - (item->value.uinteger == ECBOR_SIMPLE_FALSE ? 0 : 1); - break; - - case ECBOR_SIMPLE_NULL: - item->type = ECBOR_TYPE_NULL; - break; - - case ECBOR_SIMPLE_UNDEFINED: - item->type = ECBOR_TYPE_UNDEFINED; - break; - - default: - return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED; - } - - return ECBOR_OK; -} - -static __attribute__((noinline)) ecbor_error_t -ecbor_decode_next_internal (ecbor_decode_context_t *context, - ecbor_item_t *item, - int8_t is_chunk, - ecbor_type_t chunk_mtype) -{ - uint8_t additional; - - if (context->bytes_left == 0) { - return ECBOR_END_OF_BUFFER; - } - if (context->mode != ECBOR_MODE_DECODE - && context->mode != ECBOR_MODE_DECODE_STREAMED) { - /* only allow known modes of operation; junk in <mode> will generate - undefined behaviour */ - return ECBOR_ERR_WRONG_MODE; - } - - /* clear item, just so we do not leave garbage on partial read */ - (*item) = null_item; - - /* extract major type (most significant three bits) and additional info */ - item->type = (*context->in_position >> 5) & 0x07; - additional = (*context->in_position & 0x1f); - context->in_position ++; context->bytes_left --; - - /* check mandatory major type (in case we are reading string chunks); - we do not want to continue parsing a malformed indefinite string and - potentially explode the stack with subsequent calls */ - if (is_chunk && chunk_mtype != item->type) { - if (item->type == (ecbor_type_t) ECBOR_TYPE_SPECIAL - && additional == ECBOR_ADDITIONAL_INDEFINITE) { - /* this is a valid stop code, pass it directly; note that this branch is - only taken when inside an indefinite string */ - return ECBOR_END_OF_INDEFINITE; - } else { - /* this is not a stop code, and the item has the wrong major type */ - return ECBOR_ERR_INVALID_CHUNK_MAJOR_TYPE; - } - } - - /* handle type */ - switch (item->type) { - - /* - * Integer types - */ - case ECBOR_TYPE_UINT: - return ecbor_decode_uint (context, &item->value.uinteger, &item->size, - additional); - - case ECBOR_TYPE_NINT: - { - /* read negative value as unsigned */ - ecbor_error_t rc = ecbor_decode_uint (context, &item->value.uinteger, - &item->size, additional); - if (rc != ECBOR_OK) { - return rc; - } - - /* parse as negative */ - item->value.integer = (-1) - item->value.uinteger; - } - break; - - /* - * String types - */ - case ECBOR_TYPE_BSTR: - case ECBOR_TYPE_STR: - /* keep buffer pointer from current pointer */ - item->value.string.str = context->in_position; - item->value.string.n_chunks = 0; - - /* discriminate between definite and indefinite strings; we do not treat - * indefinite strings as we do indefinite maps and arrays, in the sense - * that we do not allow the user to manually walk each chunk - */ - if (additional == ECBOR_ADDITIONAL_INDEFINITE) { - ecbor_item_t chunk; - ecbor_error_t rc; - - /* mark accordingly */ - item->is_indefinite = true; - item->size = 1; /* already processed first byte */ - - /* do not allow nested indefinite length strings */ - if (is_chunk) { - return ECBOR_ERR_NESTET_INDEFINITE_STRING; - } - - /* indefinite lenght string; read through all blocks to compute size */ - item->value.string.str = context->in_position; - - while (true) { - /* read next chunk */ - rc = ecbor_decode_next_internal (context, &chunk, true, - item->type); - if (rc != ECBOR_OK) { - if (rc == ECBOR_END_OF_INDEFINITE) { - /* stop code found, break from loop */ - item->size += chunk.size; /* meter stop code as well */ - break; - } else if (rc == ECBOR_END_OF_BUFFER) { - /* treat a valid end of buffer as invalid since we did not yet - find the stop code */ - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } else if (rc != ECBOR_OK) { - /* other error */ - return rc; - } - } - - /* add chunk size and length to item */ - item->size += chunk.size; - item->length += chunk.length; - item->value.string.n_chunks ++; - } - } else { - /* read size of buffer */ - uint64_t len; - ecbor_error_t rc = ecbor_decode_uint (context, &len, &item->size, - additional); - if (rc != ECBOR_OK) { - return rc; - } - /* if sizeof(size_t) < sizeof(uint64_t), and payload is >4GB, we're - fucked */ - item->length = len; - - /* advance */ - if (context->bytes_left < item->length) { - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } - context->in_position += item->length; - context->bytes_left -= item->length; - - /* meter length of string to size of item */ - item->size += item->length; - } - break; - - /* - * Arrays and maps - */ - case ECBOR_TYPE_ARRAY: - case ECBOR_TYPE_MAP: - /* discriminate between definite and indefinite maps and arrays */ - if (additional == ECBOR_ADDITIONAL_INDEFINITE) { - /* mark accordingly */ - item->is_indefinite = true; - item->size = 1; /* already processed first byte */ - - /* keep buffer pointer from current pointer */ - item->value.items = context->in_position; - - if (context->mode != ECBOR_MODE_DECODE_STREAMED) { - /* we have an indefinite map or array and we're not in streamed mode; - we have to walk children to compute size and advance to next item */ - ecbor_item_t child; - ecbor_error_t rc; - - while (true) { - /* read next chunk */ - rc = ecbor_decode_next_internal (context, &child, false, - ECBOR_TYPE_NONE); - if (rc != ECBOR_OK) { - if (rc == ECBOR_END_OF_INDEFINITE) { - /* stop code found, break from loop */ - item->size += child.size; /* meter stop code as well */ - break; - } else if (rc == ECBOR_END_OF_BUFFER) { - /* treat a valid end of buffer as invalid since we did not yet - find the stop code */ - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } else if (rc != ECBOR_OK) { - /* other error */ - return rc; - } - } - - /* add chunk size to item size */ - item->size += child.size; - item->length ++; - } - - if ((item->type == ECBOR_TYPE_MAP) && (item->length % 2 != 0)) { - /* incomplete key-value pair; we expect maps to have even number of - items */ - return ECBOR_ERR_INVALID_KEY_VALUE_PAIR; - } - } - } else { - uint64_t len; - - /* read size of map or array */ - ecbor_error_t rc = ecbor_decode_uint (context, &len, - &item->size, additional); - if (rc != ECBOR_OK) { - return rc; - } - - /* improbable to have a map larger than 4M, but if we do and size_t is - less than 64bit, this will create problems */ - item->length = len; - - /* keep buffer pointer from current pointer */ - item->value.items = context->in_position; - - if (item->type == ECBOR_TYPE_MAP) { - /* we keep the total number of items in length, yet the map has the - number of key-value pairs encoded in the length */ - item->length *= 2; - } - - if (context->mode != ECBOR_MODE_DECODE_STREAMED) { - ecbor_item_t child; - ecbor_error_t rc; - size_t child_no; - - /* not in streamed mode; compute size so we can advance */ - for (child_no = 0; child_no < item->length; child_no ++) { - /* read next child */ - rc = ecbor_decode_next_internal (context, &child, false, - ECBOR_TYPE_NONE); - if (rc != ECBOR_OK) { - if (rc == ECBOR_END_OF_INDEFINITE) { - /* stop code found, but none is expected */ - return ECBOR_ERR_INVALID_STOP_CODE; - } else if (rc == ECBOR_END_OF_BUFFER) { - /* treat a valid end of buffer as invalid since we did not yet - reach the end of the map or array */ - return ECBOR_ERR_INVALID_END_OF_BUFFER; - } else if (rc != ECBOR_OK) { - /* other error */ - return rc; - } - } - - /* add child size to item size */ - item->size += child.size; - } - } - } - break; - - case ECBOR_TYPE_TAG: - { - ecbor_error_t rc; - - /* decode tag */ - rc = ecbor_decode_uint (context, &item->value.tag.tag_value, - &item->size, additional); - if (rc != ECBOR_OK) { - return rc; - } - - /* keep child pointer */ - item->value.tag.child = context->in_position; - item->length = 1; - - if (context->mode != ECBOR_MODE_DECODE_STREAMED) { - ecbor_item_t child; - - /* not in streamed mode; compute size so we can advance */ - rc = ecbor_decode_next_internal (context, &child, false, - ECBOR_TYPE_NONE); - if (rc != ECBOR_OK) { - return rc; - } - - /* add child size to item size */ - item->size += child.size; - } - } - break; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch" - case ECBOR_TYPE_SPECIAL: -#pragma GCC diagnostic pop - if (additional == ECBOR_ADDITIONAL_INDEFINITE) { - /* stop code */ - item->size = 1; - item->type = ECBOR_TYPE_STOP_CODE; - return ECBOR_END_OF_INDEFINITE; - } else if (additional <= ECBOR_ADDITIONAL_1BYTE) { - ecbor_error_t rc; - /* read integer simple value */ - rc = ecbor_decode_uint (context, &item->value.uinteger, &item->size, - additional); - if (rc != ECBOR_OK) { - return rc; - } - - /* parse simple value */ - return ecbor_decode_simple_value (item); - } else if (additional == ECBOR_ADDITIONAL_2BYTE) { - /* currently we do not support half float */ - return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED; - } else if (additional == ECBOR_ADDITIONAL_4BYTE) { - /* floating point 32bit */ - item->type = ECBOR_TYPE_FP32; - return ecbor_decode_fp32 (context, &item->value.fp32, &item->size); - } else if (additional == ECBOR_ADDITIONAL_8BYTE) { - /* floating point 64bit */ - item->type = ECBOR_TYPE_FP64; - return ecbor_decode_fp64 (context, &item->value.fp64, &item->size); - } else { - /* currently unassigned according to RFC */ - return ECBOR_ERR_CURRENTLY_NOT_SUPPORTED; - } - break; - - default: - /* while this would theoretically be a case for invalid type, any 3 bit - value should be a valid type, so this branch is an internal error and - should never happen */ - return ECBOR_ERR_UNKNOWN; - } - - return ECBOR_OK; -} - -ecbor_error_t -ecbor_decode (ecbor_decode_context_t *context, ecbor_item_t *item) -{ - ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context); - ECBOR_INTERNAL_CHECK_ITEM_PTR (item); - - if (context->mode != ECBOR_MODE_DECODE - && context->mode != ECBOR_MODE_DECODE_STREAMED) { - /* context is for wrong mode */ - return ECBOR_ERR_WRONG_MODE; - } - - /* we just get the next item */ - return ecbor_decode_next_internal (context, item, false, ECBOR_TYPE_NONE); -} - -extern ecbor_error_t -ecbor_decode_tree (ecbor_decode_context_t *context, ecbor_item_t **root) -{ - enum { - CONSUME_NODE = 0, - ANALYZE_STOP_CODE, - LINK_FIRST_NODE, - LINK_NODE, - CHECK_END_OF_DEFINITE, - CHECK_END, - END - } state = CONSUME_NODE; - uint8_t last_was_stop_code = 0; - ecbor_error_t rc = ECBOR_OK; - ecbor_item_t *curr_node = NULL, *new_node = NULL; - - ECBOR_INTERNAL_CHECK_CONTEXT_PTR (context); - ECBOR_INTERNAL_CHECK_ITEM_PTR (root); - - if (context->mode != ECBOR_MODE_DECODE_TREE) { - return ECBOR_ERR_WRONG_MODE; - } - - /* initialization */ - context->n_items = 0; - (*root) = NULL; - - /* step into streamed mode; some of the semantic checks will be done here */ - context->mode = ECBOR_MODE_DECODE_STREAMED; - - /* consume until end of buffer or error */ - while (state != END) { - - /* state change */ - switch (state) { - case CONSUME_NODE: - /* allocate new node */ - if (context->n_items >= context->item_capacity) { - rc = ECBOR_ERR_END_OF_ITEM_BUFFER; - goto end; - } - - new_node = &context->items[context->n_items]; - context->n_items ++; - - /* consume next item */ - rc = ecbor_decode_next_internal (context, new_node, false, - ECBOR_TYPE_NONE); - if (rc == ECBOR_END_OF_INDEFINITE) { - state = ANALYZE_STOP_CODE; - context->n_items --; - rc = ECBOR_OK; - } else if (rc == ECBOR_END_OF_BUFFER) { - state = CHECK_END; - rc = ECBOR_OK; - } else if (rc == ECBOR_OK) { - state = (curr_node ? LINK_NODE : LINK_FIRST_NODE); - } else { - /* some kind of error */ - goto end; - } - break; - - case ANALYZE_STOP_CODE: - if ((!ECBOR_IS_MAP (curr_node) && !ECBOR_IS_ARRAY (curr_node)) - || ECBOR_IS_DEFINITE (curr_node) - || (ECBOR_IS_INDEFINITE (curr_node) && last_was_stop_code)) { - /* stop code refers to parent node */ - curr_node = curr_node->parent; - if (!curr_node) { - rc = ECBOR_ERR_UNKNOWN; - goto end; - } - } - - if ((ECBOR_IS_MAP (curr_node) || ECBOR_IS_ARRAY (curr_node)) - && ECBOR_IS_INDEFINITE (curr_node)) { - /* correct stop code */ - state = CHECK_END_OF_DEFINITE; - /* set stop code flag; this needs to be reset when curr_node changes */ - last_was_stop_code = 1; - } else { - rc = ECBOR_ERR_INVALID_STOP_CODE; - goto end; - } - break; - - case LINK_FIRST_NODE: - /* first node, skip checks */ - curr_node = new_node; - new_node->index = 0; - state = CONSUME_NODE; - break; - - case LINK_NODE: - { - uint8_t is_unfinished_tag = - ECBOR_IS_TAG (curr_node) && !curr_node->child; - uint8_t is_unfinished_array_or_map = - (ECBOR_IS_ARRAY (curr_node) || ECBOR_IS_MAP (curr_node)) - && ((ECBOR_IS_DEFINITE (curr_node) && (curr_node->length > 0) - && !curr_node->child) - || (ECBOR_IS_INDEFINITE (curr_node) && !last_was_stop_code)); - - if (is_unfinished_tag || is_unfinished_array_or_map) { - /* link as child */ - curr_node->child = new_node; - new_node->parent = curr_node; - new_node->index = 0; - } else { - /* link as sibling */ - curr_node->next = new_node; - new_node->parent = curr_node->parent; - new_node->index = curr_node->index + 1; - } - - /* new current node */ - curr_node = new_node; - last_was_stop_code = 0; - - /* count indefinite arrays and maps */ - if (curr_node->parent - && (ECBOR_IS_ARRAY (curr_node->parent) - || ECBOR_IS_MAP (curr_node->parent)) - && ECBOR_IS_INDEFINITE (curr_node->parent)) { - curr_node->parent->length ++; - } - - /* check end of definite arrays and maps */ - state = CHECK_END_OF_DEFINITE; - } - break; - - case CHECK_END_OF_DEFINITE: - { - uint8_t is_unfinished_tag = - ECBOR_IS_TAG (curr_node) && !curr_node->child; - uint8_t is_unfinished_array_or_map = - (ECBOR_IS_ARRAY (curr_node) || ECBOR_IS_MAP (curr_node)) - && ((ECBOR_IS_DEFINITE (curr_node) && (curr_node->length > 0) - && !curr_node->child) - || (ECBOR_IS_INDEFINITE (curr_node) && !last_was_stop_code)); - - if (!is_unfinished_tag && !is_unfinished_array_or_map) { - while (curr_node->parent - && (((ECBOR_IS_ARRAY (curr_node->parent) || ECBOR_IS_MAP (curr_node->parent)) - && ECBOR_IS_DEFINITE (curr_node->parent) - && (curr_node->parent->length == (curr_node->index + 1))) - || - (ECBOR_IS_TAG (curr_node->parent)))) { - /* up one level */ - curr_node = curr_node->parent; - last_was_stop_code = 0; - } - } - - /* consume next */ - state = CONSUME_NODE; - } - break; - - case CHECK_END: - /* check for bad end scenatios */ - if (!curr_node) { - /* first node was stop code, bad end */ - rc = ECBOR_ERR_INVALID_END_OF_BUFFER; - goto end; - } - if (curr_node->parent) { - /* we're not top level, bad end */ - rc = ECBOR_ERR_INVALID_END_OF_BUFFER; - goto end; - } - if (ECBOR_IS_TAG (curr_node) && !curr_node->child) { - /* unfinished tag, bad end */ - rc = ECBOR_ERR_INVALID_END_OF_BUFFER; - goto end; - } - if (ECBOR_IS_MAP (curr_node) || ECBOR_IS_ARRAY (curr_node)) { - if (ECBOR_IS_DEFINITE (curr_node) && !curr_node->child - && curr_node->length > 0) { - /* unfinished definite array or map, bad end */ - rc = ECBOR_ERR_INVALID_END_OF_BUFFER; - goto end; - } - if (ECBOR_IS_INDEFINITE (curr_node) && !last_was_stop_code) { - /* unfinished indefinite array or map, bad end */ - rc = ECBOR_ERR_INVALID_END_OF_BUFFER; - goto end; - } - } - - /* done! */ - state = END; - break; - - default: - rc = ECBOR_ERR_UNKNOWN; - goto end; - } - } - -end: - /* return to tree mode and return error code */ - context->mode = ECBOR_MODE_DECODE_TREE; - - rc = (rc == ECBOR_END_OF_BUFFER ? ECBOR_OK : rc); - if (rc != ECBOR_OK) { - /* make sure we don't expose garbage to user */ - context->n_items = 0; - } - - /* return root node */ - if (context->n_items > 0) { - (*root) = &context->items[0]; - } - - return rc; -}
\ No newline at end of file |
