diff options
Diffstat (limited to 'contrib/libcbor/src/cbor/internal/builder_callbacks.c')
-rw-r--r-- | contrib/libcbor/src/cbor/internal/builder_callbacks.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/contrib/libcbor/src/cbor/internal/builder_callbacks.c b/contrib/libcbor/src/cbor/internal/builder_callbacks.c new file mode 100644 index 000000000000..78277f050c2b --- /dev/null +++ b/contrib/libcbor/src/cbor/internal/builder_callbacks.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com> + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "builder_callbacks.h" + +#include <string.h> + +#include "../arrays.h" +#include "../bytestrings.h" +#include "../common.h" +#include "../floats_ctrls.h" +#include "../ints.h" +#include "../maps.h" +#include "../strings.h" +#include "../tags.h" +#include "unicode.h" + +// `_cbor_builder_append` takes ownership of `item`. If adding the item to +// parent container fails, `item` will be deallocated to prevent memory. +void _cbor_builder_append(cbor_item_t *item, + struct _cbor_decoder_context *ctx) { + if (ctx->stack->size == 0) { + /* Top level item */ + ctx->root = item; + return; + } + /* Part of a bigger structure */ + switch (ctx->stack->top->item->type) { + // Handle Arrays and Maps since they can contain subitems of any type. + // Byte/string construction from chunks is handled in the respective chunk + // handlers. + case CBOR_TYPE_ARRAY: { + if (cbor_array_is_definite(ctx->stack->top->item)) { + // We don't need an explicit check for whether the item still belongs + // into this array because if there are extra items, they will cause a + // syntax error when decoded. + CBOR_ASSERT(ctx->stack->top->subitems > 0); + // This should never happen since the definite array should be + // preallocated for the expected number of items. + if (!cbor_array_push(ctx->stack->top->item, item)) { + ctx->creation_failed = true; + cbor_decref(&item); + break; + } + cbor_decref(&item); + ctx->stack->top->subitems--; + if (ctx->stack->top->subitems == 0) { + cbor_item_t *stack_item = ctx->stack->top->item; + _cbor_stack_pop(ctx->stack); + _cbor_builder_append(stack_item, ctx); + } + } else { + /* Indefinite array, don't bother with subitems */ + if (!cbor_array_push(ctx->stack->top->item, item)) { + ctx->creation_failed = true; + } + cbor_decref(&item); + } + break; + } + case CBOR_TYPE_MAP: { + // Handle both definite and indefinite maps the same initially. + // Note: We use 0 and 1 subitems to distinguish between keys and values in + // indefinite items + if (ctx->stack->top->subitems % 2) { + /* Odd record, this is a value */ + if (!_cbor_map_add_value(ctx->stack->top->item, item)) { + ctx->creation_failed = true; + cbor_decref(&item); + break; + } + } else { + /* Even record, this is a key */ + if (!_cbor_map_add_key(ctx->stack->top->item, item)) { + ctx->creation_failed = true; + cbor_decref(&item); + break; + } + } + cbor_decref(&item); + if (cbor_map_is_definite(ctx->stack->top->item)) { + CBOR_ASSERT(ctx->stack->top->subitems > 0); + ctx->stack->top->subitems--; + if (ctx->stack->top->subitems == 0) { + cbor_item_t *map_entry = ctx->stack->top->item; + _cbor_stack_pop(ctx->stack); + _cbor_builder_append(map_entry, ctx); + } + } else { + ctx->stack->top->subitems ^= + 1; /* Flip the indicator for indefinite items */ + } + break; + } + case CBOR_TYPE_TAG: { + CBOR_ASSERT(ctx->stack->top->subitems == 1); + cbor_tag_set_item(ctx->stack->top->item, item); + cbor_decref(&item); /* Give up on our reference */ + cbor_item_t *tagged_item = ctx->stack->top->item; + _cbor_stack_pop(ctx->stack); + _cbor_builder_append(tagged_item, ctx); + break; + } + // We have an item to append but nothing to append it to. + default: { + cbor_decref(&item); + ctx->syntax_error = true; + } + } +} + +#define CHECK_RES(ctx, res) \ + do { \ + if (res == NULL) { \ + ctx->creation_failed = true; \ + return; \ + } \ + } while (0) + +// Check that the length fits into size_t. If not, we cannot possibly allocate +// the required memory and should fail fast. +#define CHECK_LENGTH(ctx, length) \ + do { \ + if (length > SIZE_MAX) { \ + ctx->creation_failed = true; \ + return; \ + } \ + } while (0) + +#define PUSH_CTX_STACK(ctx, res, subitems) \ + do { \ + if (_cbor_stack_push(ctx->stack, res, subitems) == NULL) { \ + cbor_decref(&res); \ + ctx->creation_failed = true; \ + } \ + } while (0) + +void cbor_builder_uint8_callback(void *context, uint8_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int8(); + CHECK_RES(ctx, res); + cbor_mark_uint(res); + cbor_set_uint8(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_uint16_callback(void *context, uint16_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int16(); + CHECK_RES(ctx, res); + cbor_mark_uint(res); + cbor_set_uint16(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_uint32_callback(void *context, uint32_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int32(); + CHECK_RES(ctx, res); + cbor_mark_uint(res); + cbor_set_uint32(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_uint64_callback(void *context, uint64_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int64(); + CHECK_RES(ctx, res); + cbor_mark_uint(res); + cbor_set_uint64(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_negint8_callback(void *context, uint8_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int8(); + CHECK_RES(ctx, res); + cbor_mark_negint(res); + cbor_set_uint8(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_negint16_callback(void *context, uint16_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int16(); + CHECK_RES(ctx, res); + cbor_mark_negint(res); + cbor_set_uint16(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_negint32_callback(void *context, uint32_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int32(); + CHECK_RES(ctx, res); + cbor_mark_negint(res); + cbor_set_uint32(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_negint64_callback(void *context, uint64_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_int64(); + CHECK_RES(ctx, res); + cbor_mark_negint(res); + cbor_set_uint64(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_byte_string_callback(void *context, cbor_data data, + uint64_t length) { + struct _cbor_decoder_context *ctx = context; + CHECK_LENGTH(ctx, length); + unsigned char *new_handle = _cbor_malloc(length); + if (new_handle == NULL) { + ctx->creation_failed = true; + return; + } + + memcpy(new_handle, data, length); + cbor_item_t *new_chunk = cbor_new_definite_bytestring(); + + if (new_chunk == NULL) { + _cbor_free(new_handle); + ctx->creation_failed = true; + return; + } + + cbor_bytestring_set_handle(new_chunk, new_handle, length); + + // If an indef bytestring is on the stack, extend it (if it were closed, it + // would have been popped). Handle any syntax errors upstream. + if (ctx->stack->size > 0 && cbor_isa_bytestring(ctx->stack->top->item) && + cbor_bytestring_is_indefinite(ctx->stack->top->item)) { + if (!cbor_bytestring_add_chunk(ctx->stack->top->item, new_chunk)) { + ctx->creation_failed = true; + } + cbor_decref(&new_chunk); + } else { + _cbor_builder_append(new_chunk, ctx); + } +} + +void cbor_builder_byte_string_start_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_indefinite_bytestring(); + CHECK_RES(ctx, res); + PUSH_CTX_STACK(ctx, res, 0); +} + +void cbor_builder_string_callback(void *context, cbor_data data, + uint64_t length) { + struct _cbor_decoder_context *ctx = context; + CHECK_LENGTH(ctx, length); + struct _cbor_unicode_status unicode_status; + uint64_t codepoint_count = + _cbor_unicode_codepoint_count(data, length, &unicode_status); + + if (unicode_status.status != _CBOR_UNICODE_OK) { + ctx->syntax_error = true; + return; + } + CBOR_ASSERT(codepoint_count <= length); + + unsigned char *new_handle = _cbor_malloc(length); + + if (new_handle == NULL) { + ctx->creation_failed = true; + return; + } + + memcpy(new_handle, data, length); + cbor_item_t *new_chunk = cbor_new_definite_string(); + if (new_chunk == NULL) { + _cbor_free(new_handle); + ctx->creation_failed = true; + return; + } + cbor_string_set_handle(new_chunk, new_handle, length); + new_chunk->metadata.string_metadata.codepoint_count = codepoint_count; + + // If an indef string is on the stack, extend it (if it were closed, it would + // have been popped). Handle any syntax errors upstream. + if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item) && + cbor_string_is_indefinite(ctx->stack->top->item)) { + if (!cbor_string_add_chunk(ctx->stack->top->item, new_chunk)) { + ctx->creation_failed = true; + } + cbor_decref(&new_chunk); + } else { + _cbor_builder_append(new_chunk, ctx); + } +} + +void cbor_builder_string_start_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_indefinite_string(); + CHECK_RES(ctx, res); + PUSH_CTX_STACK(ctx, res, 0); +} + +void cbor_builder_array_start_callback(void *context, uint64_t size) { + struct _cbor_decoder_context *ctx = context; + CHECK_LENGTH(ctx, size); + cbor_item_t *res = cbor_new_definite_array(size); + CHECK_RES(ctx, res); + if (size > 0) { + PUSH_CTX_STACK(ctx, res, size); + } else { + _cbor_builder_append(res, ctx); + } +} + +void cbor_builder_indef_array_start_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_indefinite_array(); + CHECK_RES(ctx, res); + PUSH_CTX_STACK(ctx, res, 0); +} + +void cbor_builder_indef_map_start_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_indefinite_map(); + CHECK_RES(ctx, res); + PUSH_CTX_STACK(ctx, res, 0); +} + +void cbor_builder_map_start_callback(void *context, uint64_t size) { + struct _cbor_decoder_context *ctx = context; + CHECK_LENGTH(ctx, size); + cbor_item_t *res = cbor_new_definite_map(size); + CHECK_RES(ctx, res); + if (size > 0) { + PUSH_CTX_STACK(ctx, res, size * 2); + } else { + _cbor_builder_append(res, ctx); + } +} + +/** + * Is the (partially constructed) item indefinite? + */ +bool _cbor_is_indefinite(cbor_item_t *item) { + switch (item->type) { + case CBOR_TYPE_BYTESTRING: + return cbor_bytestring_is_indefinite(item); + case CBOR_TYPE_STRING: + return cbor_string_is_indefinite(item); + case CBOR_TYPE_ARRAY: + return cbor_array_is_indefinite(item); + case CBOR_TYPE_MAP: + return cbor_map_is_indefinite(item); + default: + return false; + } +} + +void cbor_builder_indef_break_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + /* There must be an item to break out of*/ + if (ctx->stack->size > 0) { + cbor_item_t *item = ctx->stack->top->item; + if (_cbor_is_indefinite( + item) && /* Only indefinite items can be terminated by 0xFF */ + /* Special case: we cannot append up if an indefinite map is incomplete + (we are expecting a value). */ + (item->type != CBOR_TYPE_MAP || ctx->stack->top->subitems % 2 == 0)) { + _cbor_stack_pop(ctx->stack); + _cbor_builder_append(item, ctx); + return; + } + } + + ctx->syntax_error = true; +} + +void cbor_builder_float2_callback(void *context, float value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_float2(); + CHECK_RES(ctx, res); + cbor_set_float2(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_float4_callback(void *context, float value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_float4(); + CHECK_RES(ctx, res); + cbor_set_float4(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_float8_callback(void *context, double value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_float8(); + CHECK_RES(ctx, res); + cbor_set_float8(res, value); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_null_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_null(); + CHECK_RES(ctx, res); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_undefined_callback(void *context) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_undef(); + CHECK_RES(ctx, res); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_boolean_callback(void *context, bool value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_build_bool(value); + CHECK_RES(ctx, res); + _cbor_builder_append(res, ctx); +} + +void cbor_builder_tag_callback(void *context, uint64_t value) { + struct _cbor_decoder_context *ctx = context; + cbor_item_t *res = cbor_new_tag(value); + CHECK_RES(ctx, res); + PUSH_CTX_STACK(ctx, res, 1); +} |