diff --git a/.clang-tidy b/.clang-tidy index ca1d08ad9..af7fcd282 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,7 +1,7 @@ --- Checks: 'clang-diagnostic-*,clang-analyzer-*,readability-*,modernize-*,bugprone-*,misc-*,google-runtime-int,llvm-header-guard,fuchsia-restrict-system-includes,-clang-analyzer-valist.Uninitialized,-clang-analyzer-security.insecureAPI.rand,-clang-analyzer-alpha.*,-readability-magic-numbers,-readability-non-const-parameter,-readability-avoid-const-params-in-decls,-readability-else-after-return,-readability-isolate-declaration,-readability-uppercase-literal-suffix,-bugprone-sizeof-expression,-bugprone-easily-swappable-parameters,-readability-identifier-length,-misc-no-recursion,-readability-function-cognitive-complexity,-readability-magic-numbers' WarningsAsErrors: '*' -HeaderFilterRegex: '.*\.[h|inl]$' +HeaderFilterRegex: '^(?!.*external).*\.[h|inl]$' FormatStyle: 'file' CheckOptions: - key: readability-braces-around-statements.ShortStatementLines diff --git a/CMakeLists.txt b/CMakeLists.txt index 30826bdbf..d7cedeef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,12 @@ file(GLOB AWS_COMMON_SRC ) file (GLOB AWS_COMMON_EXTERNAL_SRC - "source/external/*.c") + "source/external/*.c" + "source/external/libcbor/*.c" + "source/external/libcbor/cbor/*.c" + "source/external/libcbor/cbor/internal/*.c" + ) + option(AWS_NUM_CPU_CORES "Number of CPU cores of the target machine. Useful when cross-compiling." 0) @@ -213,6 +218,10 @@ endif() set_target_properties(${PROJECT_NAME} PROPERTIES VERSION 1.0.0) set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION 1) +# libcbor files do includes like: #include "cbor/cbor_export.h" +# To make these paths work, add the location we're storing them as a search path. +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/source/external/libcbor) target_include_directories(${PROJECT_NAME} PUBLIC $ $) diff --git a/THIRD-PARTY-LICENSES.txt b/THIRD-PARTY-LICENSES.txt index 7ea3c219d..4a3e34675 100644 --- a/THIRD-PARTY-LICENSES.txt +++ b/THIRD-PARTY-LICENSES.txt @@ -55,3 +55,29 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------ + +** libcbor; version 0.11.0 -- https://github.com/PJK/libcbor +Copyright (c) 2014-2017 Pavel Kalvoda + +MIT License + +Copyright (c) 2014-2017 Pavel Kalvoda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/include/aws/common/cbor.h b/include/aws/common/cbor.h new file mode 100644 index 000000000..1c220ecb7 --- /dev/null +++ b/include/aws/common/cbor.h @@ -0,0 +1,449 @@ +#ifndef AWS_COMMON_CBOR_H +#define AWS_COMMON_CBOR_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +AWS_PUSH_SANE_WARNING_LEVEL +AWS_EXTERN_C_BEGIN + +/** + * The types use by APIs, not 1:1 with major type. + * It's an extension for cbor major type in RFC8949 section 3.1 + * Major type 0 - AWS_CBOR_TYPE_UINT + * Major type 1 - AWS_CBOR_TYPE_NEGINT + * Major type 2 - AWS_CBOR_TYPE_BYTES/AWS_CBOR_TYPE_INDEF_BYTES_START + * Major type 3 - AWS_CBOR_TYPE_TEXT/AWS_CBOR_TYPE_INDEF_TEXT_START + * Major type 4 - AWS_CBOR_TYPE_ARRAY_START/AWS_CBOR_TYPE_INDEF_ARRAY_START + * Major type 5 - AWS_CBOR_TYPE_MAP_START/AWS_CBOR_TYPE_INDEF_MAP_START + * Major type 6 - AWS_CBOR_TYPE_TAG + * Major type 7 + * - 20/21 - AWS_CBOR_TYPE_BOOL + * - 22 - AWS_CBOR_TYPE_NULL + * - 23 - AWS_CBOR_TYPE_UNDEFINED + * - 25/26/27 - AWS_CBOR_TYPE_FLOAT + * - 31 - AWS_CBOR_TYPE_BREAK + * - rest of value are not supported. + */ +enum aws_cbor_type { + AWS_CBOR_TYPE_UNKNOWN = 0, + + AWS_CBOR_TYPE_UINT, + AWS_CBOR_TYPE_NEGINT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_BYTES, + AWS_CBOR_TYPE_TEXT, + + AWS_CBOR_TYPE_ARRAY_START, + AWS_CBOR_TYPE_MAP_START, + + AWS_CBOR_TYPE_TAG, + + AWS_CBOR_TYPE_BOOL, + AWS_CBOR_TYPE_NULL, + AWS_CBOR_TYPE_UNDEFINED, + AWS_CBOR_TYPE_BREAK, + + AWS_CBOR_TYPE_INDEF_BYTES_START, + AWS_CBOR_TYPE_INDEF_TEXT_START, + AWS_CBOR_TYPE_INDEF_ARRAY_START, + AWS_CBOR_TYPE_INDEF_MAP_START, +}; + +/** + * The common tags, refer to RFC8949 section 3.4 + * Expected value type followed by the tag: + * AWS_CBOR_TAG_STANDARD_TIME - AWS_CBOR_TYPE_TEXT + * AWS_CBOR_TAG_EPOCH_TIME - AWS_CBOR_TYPE_UINT/AWS_CBOR_TYPE_NEGINT/AWS_CBOR_TYPE_FLOAT + * AWS_CBOR_TAG_UNSIGNED_BIGNUM - AWS_CBOR_TYPE_BYTES + * AWS_CBOR_TAG_NEGATIVE_BIGNUM - AWS_CBOR_TYPE_BYTES + * AWS_CBOR_TAG_DECIMAL_FRACTION - AWS_CBOR_TYPE_ARRAY_START/AWS_CBOR_TYPE_INDEF_ARRAY_START + **/ +#define AWS_CBOR_TAG_STANDARD_TIME 0 +#define AWS_CBOR_TAG_EPOCH_TIME 1 +#define AWS_CBOR_TAG_UNSIGNED_BIGNUM 2 +#define AWS_CBOR_TAG_NEGATIVE_BIGNUM 3 +#define AWS_CBOR_TAG_DECIMAL_FRACTION 4 + +struct aws_cbor_encoder; +struct aws_cbor_decoder; + +/******************************************************************************* + * ENCODE + ******************************************************************************/ + +/* Return c-string for aws_cbor_type */ +AWS_COMMON_API +const char *aws_cbor_type_cstr(enum aws_cbor_type type); + +/** + * @brief Create a new cbor encoder. Creating a encoder with a temporay buffer. + * Every aws_cbor_encoder_write_* will encode directly into the buffer to follow the encoded data. + * + * @param allocator + * @return aws_cbor_encoder + */ +AWS_COMMON_API +struct aws_cbor_encoder *aws_cbor_encoder_new(struct aws_allocator *allocator); + +AWS_COMMON_API +struct aws_cbor_encoder *aws_cbor_encoder_destroy(struct aws_cbor_encoder *encoder); + +/** + * @brief Get the current encoded data from encoder. The encoded data has the same lifetime as the encoder, and once + * any other function call invoked for the encoder, the encoded data is no longer valid. + * + * @param encoder + * @return struct aws_byte_cursor from the encoder buffer. + */ +AWS_COMMON_API +struct aws_byte_cursor aws_cbor_encoder_get_encoded_data(const struct aws_cbor_encoder *encoder); + +/** + * @brief Clear the current encoded buffer from encoder. + * + * @param encoder + */ +AWS_COMMON_API +void aws_cbor_encoder_reset(struct aws_cbor_encoder *encoder); + +/** + * @brief Encode a AWS_CBOR_TYPE_UINT value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * + * TODO: maybe add a width of the encoded value. + * + * @param encoder + * @param value value to encode. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_uint(struct aws_cbor_encoder *encoder, uint64_t value); + +/** + * @brief Encode a AWS_CBOR_TYPE_NEGINT value to "smallest possible" in encoder's buffer. + * It represents (-1 - value). + * Referring to RFC8949 section 4.2.1 + * + * + * @param encoder + * @param value The argument to encode to negative integer, which is (-1 - expected_val) + */ +AWS_COMMON_API +void aws_cbor_encoder_write_negint(struct aws_cbor_encoder *encoder, uint64_t value); + +/** + * @brief Encode a AWS_CBOR_TYPE_FLOAT value to "smallest possible", but will not be encoded into half-precision float, + * as it's not well supported cross languages. + * + * To be more specific, it will be encoded into integer/negative/float + * (Order with priority) when the conversation will not cause precision loss. + * + * @param encoder + * @param value value to encode. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_float(struct aws_cbor_encoder *encoder, double value); + +/** + * @brief Encode a AWS_CBOR_TYPE_BYTES value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1, the length of "from" will be encoded first and then the value of "from" will + * be followed. + * + * @param encoder + * @param from value to encode. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_bytes(struct aws_cbor_encoder *encoder, struct aws_byte_cursor from); + +/** + * @brief Encode a AWS_CBOR_TYPE_TEXT value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1, the length of "from" will be encoded first and then the value of "from" will + * be followed. + * + * @param encoder + * @param from value to encode. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_text(struct aws_cbor_encoder *encoder, struct aws_byte_cursor from); + +/** + * @brief Encode a AWS_CBOR_TYPE_ARRAY_START value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * The "number_entries" is the cbor data items should be followed as the content of the array. + * Notes: it's user's responsibility to keep the integrity of the array to be encoded. + * + * @param encoder + * @param number_entries The number of data item in array. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_array_start(struct aws_cbor_encoder *encoder, size_t number_entries); + +/** + * @brief Encode a AWS_CBOR_TYPE_MAP_START value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * The "number_entries" is the number of pair of cbor data items as key and value should be followed as the content of + * the map. + * + * Notes: it's user's responsibility to keep the integrity of the map to be encoded. + * + * @param encoder + * @param number_entries The number of data item in map. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_map_start(struct aws_cbor_encoder *encoder, size_t number_entries); + +/** + * @brief Encode a AWS_CBOR_TYPE_TAG value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * The following cbor data item will be the content of the tagged value. + * Notes: it's user's responsibility to keep the integrity of the tagged value to follow the RFC8949 section 3.4 + * + * @param encoder + * @param tag_number The tag value to encode. + */ +AWS_COMMON_API +void aws_cbor_encoder_write_tag(struct aws_cbor_encoder *encoder, uint64_t tag_number); + +/** + * @brief Encode a simple value AWS_CBOR_TYPE_NULL + * + * @param encoder + */ +AWS_COMMON_API +void aws_cbor_encoder_write_null(struct aws_cbor_encoder *encoder); + +/** + * @brief Encode a simple value AWS_CBOR_TYPE_UNDEFINED + * + * @param encoder + */ +AWS_COMMON_API +void aws_cbor_encoder_write_undefined(struct aws_cbor_encoder *encoder); + +/** + * @brief Encode a simple value AWS_CBOR_TYPE_BOOL + * + * @param encoder + */ +AWS_COMMON_API +void aws_cbor_encoder_write_bool(struct aws_cbor_encoder *encoder, bool value); + +/** + * @brief Encode a simple value AWS_CBOR_TYPE_BREAK + * + * Notes: no error checking, it's user's responsibility to track the break + * to close the corresponding indef_start + */ +AWS_COMMON_API +void aws_cbor_encoder_write_break(struct aws_cbor_encoder *encoder); + +/** + * @brief Encode a AWS_CBOR_TYPE_INDEF_BYTES_START + * + * Notes: no error checking, it's user's responsibility to add corresponding data and the break + * to close the indef_start + */ +AWS_COMMON_API +void aws_cbor_encoder_write_indef_bytes_start(struct aws_cbor_encoder *encoder); +/** + * @brief Encode a AWS_CBOR_TYPE_INDEF_TEXT_START + * + * Notes: no error checking, it's user's responsibility to add corresponding data + * and the break to close the indef_start + */ +AWS_COMMON_API +void aws_cbor_encoder_write_indef_text_start(struct aws_cbor_encoder *encoder); +/** + * @brief Encode a AWS_CBOR_TYPE_INDEF_ARRAY_START + * + * Notes: no error checking, it's user's responsibility to add corresponding data + * and the break to close the indef_start + */ +AWS_COMMON_API +void aws_cbor_encoder_write_indef_array_start(struct aws_cbor_encoder *encoder); +/** + * @brief Encode a AWS_CBOR_TYPE_INDEF_MAP_START + * + * Notes: no error checking, it's user's responsibility to add corresponding data + * and the break to close the indef_start + */ +AWS_COMMON_API +void aws_cbor_encoder_write_indef_map_start(struct aws_cbor_encoder *encoder); + +/******************************************************************************* + * DECODE + ******************************************************************************/ + +/** + * @brief Create a cbor decoder to take src to decode. + * The typical usage of decoder will be: + * - If the next element type only accept what expected, `aws_cbor_decoder_pop_next_*` + * - If the next element type accept different type, invoke `aws_cbor_decoder_peek_type` first, then based on the type + * to invoke corresponding `aws_cbor_decoder_pop_next_*` + * - If the next element type doesn't have corrsponding value, specifically: AWS_CBOR_TYPE_NULL, + * AWS_CBOR_TYPE_UNDEFINED, AWS_CBOR_TYPE_INF_*_START, AWS_CBOR_TYPE_BREAK, call + * `aws_cbor_decoder_consume_next_single_element` to consume it and continues for further decoding. + * - To ignore the next data item (the element and the content of it), `aws_cbor_decoder_consume_next_whole_data_item` + * + * Note: it's caller's responsibilty to keep the src outlive the decoder. + * + * @param allocator + * @param src The src data to decode from. + * @return decoder + */ +AWS_COMMON_API +struct aws_cbor_decoder *aws_cbor_decoder_new(struct aws_allocator *allocator, struct aws_byte_cursor src); + +AWS_COMMON_API +struct aws_cbor_decoder *aws_cbor_decoder_destroy(struct aws_cbor_decoder *decoder); + +/** + * @brief Get the length of the remaining bytes of the source. Once the source was decoded, it will be consumed, + * and result in decrease of the remaining length of bytes. + * + * @param decoder + * @return The length of bytes remaining of the decoder source. + */ +AWS_COMMON_API +size_t aws_cbor_decoder_get_remaining_length(const struct aws_cbor_decoder *decoder); + +/** + * @brief Decode the next element and store it in the decoder cache if there was no element cached. + * If there was element cached, just return the type of the cached element. + * + * @param decoder + * @param out_type + * @return AWS_OP_SUCCESS if succeed, AWS_OP_ERR for any decoding error and corresponding error code will be raised. + */ +AWS_COMMON_API +int aws_cbor_decoder_peek_type(struct aws_cbor_decoder *decoder, enum aws_cbor_type *out_type); + +/** + * @brief Consume the next data item, includes all the content within the data item. + * + * As an example for the following cbor, this function will consume all the data + * as it's only one cbor data item, an indefinite map with 2 key, value pair: + * 0xbf6346756ef563416d7421ff + * BF -- Start indefinite-length map + * 63 -- First key, UTF-8 string length 3 + * 46756e -- "Fun" + * F5 -- First value, true + * 63 -- Second key, UTF-8 string length 3 + * 416d74 -- "Amt" + * 21 -- Second value, -2 + * FF -- "break" + * + * Notes: this function will not ensure the data item is well-formed. + * + * @param src The src to parse data from + * @return AWS_OP_SUCCESS successfully consumed the next data item, otherwise AWS_OP_ERR. + */ +AWS_COMMON_API +int aws_cbor_decoder_consume_next_whole_data_item(struct aws_cbor_decoder *decoder); + +/** + * @brief Consume the next single element, without the content followed by the element. + * + * As an example for the following cbor, this function will only consume the + * 0xBF, "Start indefinite-length map", not any content of the map represented. + * The next element to decode will start from 0x63 + * 0xbf6346756ef563416d7421ff + * BF -- Start indefinite-length map + * 63 -- First key, UTF-8 string length 3 + * 46756e -- "Fun" + * F5 -- First value, true + * 63 -- Second key, UTF-8 string length 3 + * 416d74 -- "Amt" + * 21 -- Second value, -2 + * FF -- "break" + * + * @param decoder The decoder to parse data from + * @return AWS_OP_SUCCESS successfully consumed the next element, otherwise AWS_OP_ERR. + */ +AWS_COMMON_API +int aws_cbor_decoder_consume_next_single_element(struct aws_cbor_decoder *decoder); + +/** + * @brief Get the next element based on the type. If the next element doesn't match the expected type. Error will be + * raised. If the next element already been cached, it will consume the cached item when no error was returned. + * Specifically: + * AWS_CBOR_TYPE_UINT - aws_cbor_decoder_pop_next_unsigned_int_val + * AWS_CBOR_TYPE_NEGINT - aws_cbor_decoder_pop_next_negative_int_val, it represents (-1 - *out) + * AWS_CBOR_TYPE_FLOAT - aws_cbor_decoder_pop_next_double_val + * AWS_CBOR_TYPE_BYTES - aws_cbor_decoder_pop_next_bytes_val + * AWS_CBOR_TYPE_TEXT - aws_cbor_decoder_pop_next_text_val + * + * @param decoder + * @param out + * @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR. + */ +AWS_COMMON_API +int aws_cbor_decoder_pop_next_unsigned_int_val(struct aws_cbor_decoder *decoder, uint64_t *out); +AWS_COMMON_API +int aws_cbor_decoder_pop_next_negative_int_val(struct aws_cbor_decoder *decoder, uint64_t *out); +AWS_COMMON_API +int aws_cbor_decoder_pop_next_float_val(struct aws_cbor_decoder *decoder, double *out); +AWS_COMMON_API +int aws_cbor_decoder_pop_next_boolean_val(struct aws_cbor_decoder *decoder, bool *out); +AWS_COMMON_API +int aws_cbor_decoder_pop_next_bytes_val(struct aws_cbor_decoder *decoder, struct aws_byte_cursor *out); +AWS_COMMON_API +int aws_cbor_decoder_pop_next_text_val(struct aws_cbor_decoder *decoder, struct aws_byte_cursor *out); + +/** + * @brief Get the next AWS_CBOR_TYPE_ARRAY_START element. Only consume the AWS_CBOR_TYPE_ARRAY_START element and set the + * size of array to *out_size, not the content of the array. The next *out_size cbor data items will be the content of + * the array for a valid cbor data, + * + * Notes: For indefinite-length, this function will fail with "AWS_ERROR_CBOR_UNEXPECTED_TYPE". The designed way to + * handle indefinite-length is: + * - Get AWS_CBOR_TYPE_INDEF_ARRAY_START from _peek_type + * - call `aws_cbor_decoder_consume_next_single_element` to pop the indefinite-length start. + * - Decode the next data item until AWS_CBOR_TYPE_BREAK read. + * + * @param decoder + * @param out_size store the size of array if succeed. + * @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR. + */ +AWS_COMMON_API +int aws_cbor_decoder_pop_next_array_start(struct aws_cbor_decoder *decoder, uint64_t *out_size); + +/** + * @brief Get the next AWS_CBOR_TYPE_MAP_START element. Only consume the AWS_CBOR_TYPE_MAP_START element and set the + * size of array to *out_size, not the content of the map. The next *out_size pair of cbor data items as key and value + * will be the content of the array for a valid cbor data, + * + * Notes: For indefinite-length, this function will fail with "AWS_ERROR_CBOR_UNEXPECTED_TYPE". The designed way to + * handle indefinite-length is: + * - Get AWS_CBOR_TYPE_INDEF_MAP_START from _peek_type + * - call `aws_cbor_decoder_consume_next_single_element` to pop the indefinite-length start. + * - Decode the next data item until AWS_CBOR_TYPE_BREAK read. + * + * @param decoder + * @param out_size store the size of map if succeed. + * @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR. + */ +AWS_COMMON_API +int aws_cbor_decoder_pop_next_map_start(struct aws_cbor_decoder *decoder, uint64_t *out_size); + +/** + * @brief Get the next AWS_CBOR_TYPE_TAG element. Only consume the AWS_CBOR_TYPE_TAG element and set the + * tag value to *out_tag_val, not the content of the tagged. The next cbor data item will be the content of the tagged + * value for a valid cbor data. + * + * @param decoder + * @param out_size store the size of map if succeed. + * @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR. + */ +AWS_COMMON_API +int aws_cbor_decoder_pop_next_tag_val(struct aws_cbor_decoder *decoder, uint64_t *out_tag_val); + +AWS_EXTERN_C_END +AWS_POP_SANE_WARNING_LEVEL + +#endif // AWS_COMMON_CBOR_H diff --git a/include/aws/common/error.h b/include/aws/common/error.h index 8c5d45f69..a798b6677 100644 --- a/include/aws/common/error.h +++ b/include/aws/common/error.h @@ -216,6 +216,8 @@ enum aws_common_error { AWS_ERROR_FILE_OPEN_FAILURE, AWS_ERROR_FILE_READ_FAILURE, AWS_ERROR_FILE_WRITE_FAILURE, + AWS_ERROR_INVALID_CBOR, + AWS_ERROR_CBOR_UNEXPECTED_TYPE, AWS_ERROR_END_COMMON_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_COMMON_PACKAGE_ID) }; diff --git a/include/aws/common/logging.h b/include/aws/common/logging.h index f71a375b5..444465aa1 100644 --- a/include/aws/common/logging.h +++ b/include/aws/common/logging.h @@ -89,6 +89,7 @@ enum aws_common_log_subject { AWS_LS_COMMON_BUS, AWS_LS_COMMON_TEST, AWS_LS_COMMON_JSON_PARSER, + AWS_LS_COMMON_CBOR, AWS_LS_COMMON_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_C_COMMON_PACKAGE_ID) }; diff --git a/include/aws/common/macros.h b/include/aws/common/macros.h index f5c64c6aa..03e50a218 100644 --- a/include/aws/common/macros.h +++ b/include/aws/common/macros.h @@ -131,6 +131,24 @@ enum { AWS_CACHE_LINE = 64 }; # define AWS_SUPPRESS_TSAN #endif +#if defined(__has_feature) +# if __has_feature(undefined_behavior_sanitizer) +# define AWS_SUPPRESS_UBSAN __attribute__((no_sanitize("undefined"))) +# endif +#elif defined(__SANITIZE_UNDEFINED__) +# if defined(__GNUC__) +# define AWS_SUPPRESS_UBSAN __attribute__((no_sanitize_undefined)) +# else +# define AWS_SUPPRESS_UBSAN +# endif +#else +# define AWS_SUPPRESS_UBSAN +#endif + +#if !defined(AWS_SUPPRESS_UBSAN) +# define AWS_SUPPRESS_UBSAN +#endif + /* If this is C++, restrict isn't supported. If this is not at least C99 on gcc and clang, it isn't supported. * If visual C++ building in C mode, the restrict definition is __restrict. * This just figures all of that out based on who's including this header file. */ diff --git a/include/aws/common/private/byte_buf.h b/include/aws/common/private/byte_buf.h index 81c08cbba..a9eebf034 100644 --- a/include/aws/common/private/byte_buf.h +++ b/include/aws/common/private/byte_buf.h @@ -15,4 +15,23 @@ */ AWS_COMMON_API size_t aws_nospec_mask(size_t index, size_t bound); +/** + * Expand the buffer appropriately to meet the requested capacity. + * + * If the the buffer's capacity is currently larger than the request capacity, the + * function does nothing (no shrink is performed). + */ +AWS_COMMON_API +int aws_byte_buf_reserve_smart(struct aws_byte_buf *buffer, size_t requested_capacity); + +/** + * Convenience function that attempts to increase the capacity of a buffer relative to the current + * length appropriately. + * + * If the the buffer's capacity is currently larger than the request capacity, the + * function does nothing (no shrink is performed). + */ +AWS_COMMON_API +int aws_byte_buf_reserve_smart_relative(struct aws_byte_buf *buffer, size_t additional_length); + #endif /* AWS_COMMON_PRIVATE_BYTE_BUF_H */ diff --git a/include/aws/common/private/json_impl.h b/include/aws/common/private/external_module_impl.h similarity index 64% rename from include/aws/common/private/json_impl.h rename to include/aws/common/private/external_module_impl.h index df9d81a5c..d3f75be39 100644 --- a/include/aws/common/private/json_impl.h +++ b/include/aws/common/private/external_module_impl.h @@ -1,5 +1,5 @@ -#ifndef AWS_COMMON_PRIVATE_JSON_IMPL_H -#define AWS_COMMON_PRIVATE_JSON_IMPL_H +#ifndef AWS_COMMON_PRIVATE_EXTERNAL_MODULE_IMPL_H +#define AWS_COMMON_PRIVATE_EXTERNAL_MODULE_IMPL_H /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -19,4 +19,8 @@ void aws_json_module_init(struct aws_allocator *allocator); */ void aws_json_module_cleanup(void); -#endif // AWS_COMMON_PRIVATE_JSON_IMPL_H +void aws_cbor_module_init(struct aws_allocator *allocator); + +void aws_cbor_module_cleanup(void); + +#endif // AWS_COMMON_PRIVATE_EXTERNAL_MODULE_IMPL_H diff --git a/scripts/import_libcbor.py b/scripts/import_libcbor.py new file mode 100644 index 000000000..37b5df9a3 --- /dev/null +++ b/scripts/import_libcbor.py @@ -0,0 +1,129 @@ +import os +import tempfile +import shutil +import subprocess +import argparse +import re + + +def parse_version(version_string): + match = re.fullmatch(r'v(\d+)\.(\d+)\.(\d+)', version_string) + if not match: + raise ValueError("Invalid version string") + return match.group(1), match.group(2), match.group(3) + + +argument_parser = argparse.ArgumentParser( + description="Helper to import libcbor as external dependency.") + +argument_parser.add_argument("--version", + required=True, help="Version string to import") + +args = argument_parser.parse_args() +major_version, minor_version, patch_version = parse_version(args.version) + +GENERATED_NOTES = """/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/import_libcbor.py + * and any modifications should be in there. + */ +""" + +CBOR_EXPORT_H = """ +#ifndef CBOR_EXPORT_H +#define CBOR_EXPORT_H + +/* Don't export anything from libcbor */ +#define CBOR_EXPORT + +#endif /* CBOR_EXPORT_H */ +""" + +CONFIGURATION_H = f""" +#ifndef LIBCBOR_CONFIGURATION_H +#define LIBCBOR_CONFIGURATION_H + +#define CBOR_MAJOR_VERSION {major_version} +#define CBOR_MINOR_VERSION {minor_version} +#define CBOR_PATCH_VERSION {patch_version} + +#define CBOR_BUFFER_GROWTH 2 +#define CBOR_MAX_STACK_SIZE 2048 +#define CBOR_PRETTY_PRINTER 1 + +#if defined(_MSC_VER) +# define CBOR_RESTRICT_SPECIFIER +#else +# define CBOR_RESTRICT_SPECIFIER restrict +#endif + +#define CBOR_INLINE_SPECIFIER + +/* Ignore the compiler warnings for libcbor. */ +#ifdef _MSC_VER +# pragma warning(disable : 4028) +# pragma warning(disable : 4715) +# pragma warning(disable : 4232) +# pragma warning(disable : 4068) +# pragma warning(disable : 4244) +# pragma warning(disable : 4701) +# pragma warning(disable : 4703) +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wreturn-type" +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wreturn-type" +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#endif // LIBCBOR_CONFIGURATION_H +""" + + +# Create a temporary directory for cloning the repository +temp_repo_dir = tempfile.mkdtemp() + +try: + # Clone the repository into the temporary directory + repo_url = "https://github.com/PJK/libcbor.git" + clone_command = f"git clone {repo_url} {temp_repo_dir}" + subprocess.run(clone_command, shell=True, check=True) + subprocess.run(["git", "checkout", "tags/" + args.version], + cwd=temp_repo_dir, check=True) + + # Create a separate folder for the copied files + output_dir = os.path.join( + os.path.dirname(__file__), "..", "source", "external", "libcbor") + shutil.rmtree(output_dir, ignore_errors=True) + os.makedirs(output_dir, exist_ok=True) + + # Copy files ending with .c and .h from the /src directory + src_dir = os.path.join(temp_repo_dir, "src") + for root, dirs, files in os.walk(src_dir): + dir_name = os.path.basename(root) + for file in files: + if file.endswith((".c", ".h")): + # copy the source code to ../source/external/libcbor + src_file = os.path.join(root, file) + rel_path = os.path.relpath(src_file, src_dir) + dest_file = os.path.join(output_dir, rel_path) + os.makedirs(os.path.dirname(dest_file), exist_ok=True) + shutil.copy(src_file, dest_file) + + # Use our customized configurations + with open(os.path.join(output_dir, "cbor/cbor_export.h"), "w") as file: + file.write(GENERATED_NOTES) + file.write(CBOR_EXPORT_H) + with open(os.path.join(output_dir, "cbor/configuration.h"), "w") as file: + file.write(GENERATED_NOTES) + file.write(CONFIGURATION_H) + +except Exception as e: + print(f"An error occurred: {e}") + +finally: + # Remove the temporary directory + shutil.rmtree(temp_repo_dir, ignore_errors=True) diff --git a/source/byte_buf.c b/source/byte_buf.c index b815b6bfb..da3748e81 100644 --- a/source/byte_buf.c +++ b/source/byte_buf.c @@ -816,6 +816,25 @@ int aws_byte_buf_reserve_relative(struct aws_byte_buf *buffer, size_t additional return aws_byte_buf_reserve(buffer, requested_capacity); } +int aws_byte_buf_reserve_smart(struct aws_byte_buf *buffer, size_t requested_capacity) { + + if (requested_capacity <= buffer->capacity) { + AWS_POSTCONDITION(aws_byte_buf_is_valid(buffer)); + return AWS_OP_SUCCESS; + } + size_t double_current_capacity = aws_add_size_saturating(buffer->capacity, buffer->capacity); + size_t new_capacity = aws_max_size(requested_capacity, double_current_capacity); + return aws_byte_buf_reserve(buffer, new_capacity); +} + +int aws_byte_buf_reserve_smart_relative(struct aws_byte_buf *buffer, size_t additional_length) { + size_t requested_capacity = 0; + if (AWS_UNLIKELY(aws_add_size_checked(buffer->len, additional_length, &requested_capacity))) { + return AWS_OP_ERR; + } + return aws_byte_buf_reserve_smart(buffer, requested_capacity); +} + struct aws_byte_cursor aws_byte_cursor_right_trim_pred( const struct aws_byte_cursor *source, aws_byte_predicate_fn *predicate) { diff --git a/source/cbor.c b/source/cbor.c new file mode 100644 index 000000000..b6ec4f239 --- /dev/null +++ b/source/cbor.c @@ -0,0 +1,647 @@ +#include "external/libcbor/cbor.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +static bool s_aws_cbor_module_initialized = false; + +const static size_t s_cbor_element_width_64bit = 9; +const static size_t s_cbor_element_width_32bit = 5; + +enum s_cbor_simple_val { + AWS_CBOR_SIMPLE_VAL_FALSE = 20, + AWS_CBOR_SIMPLE_VAL_TRUE = 21, + AWS_CBOR_SIMPLE_VAL_NULL = 22, + AWS_CBOR_SIMPLE_VAL_UNDEFINED = 23, + AWS_CBOR_SIMPLE_VAL_BREAK = 31, +}; + +void aws_cbor_module_init(struct aws_allocator *allocator) { + (void)allocator; + if (!s_aws_cbor_module_initialized) { + /* Not allow any allocation from libcbor */ + cbor_set_allocs(NULL, NULL, NULL); + s_aws_cbor_module_initialized = true; + } +} + +void aws_cbor_module_cleanup(void) { + if (s_aws_cbor_module_initialized) { + s_aws_cbor_module_initialized = false; + } +} + +/* Return c-string for aws_cbor_type */ +const char *aws_cbor_type_cstr(enum aws_cbor_type type) { + /* clang-format off */ + switch (type) { + case (AWS_CBOR_TYPE_UINT): return "AWS_CBOR_TYPE_UINT"; + case (AWS_CBOR_TYPE_NEGINT): return "AWS_CBOR_TYPE_NEGINT"; + case (AWS_CBOR_TYPE_FLOAT): return "AWS_CBOR_TYPE_FLOAT"; + case (AWS_CBOR_TYPE_BYTES): return "AWS_CBOR_TYPE_BYTES"; + case (AWS_CBOR_TYPE_TEXT): return "AWS_CBOR_TYPE_TEXT"; + case (AWS_CBOR_TYPE_ARRAY_START): return "AWS_CBOR_TYPE_ARRAY_START"; + case (AWS_CBOR_TYPE_MAP_START): return "AWS_CBOR_TYPE_MAP_START"; + case (AWS_CBOR_TYPE_TAG): return "AWS_CBOR_TYPE_TAG"; + case (AWS_CBOR_TYPE_BOOL): return "AWS_CBOR_TYPE_BOOL"; + case (AWS_CBOR_TYPE_NULL): return "AWS_CBOR_TYPE_NULL"; + case (AWS_CBOR_TYPE_UNDEFINED): return "AWS_CBOR_TYPE_UNDEFINED"; + case (AWS_CBOR_TYPE_BREAK): return "AWS_CBOR_TYPE_BREAK"; + case (AWS_CBOR_TYPE_INDEF_BYTES_START): return "AWS_CBOR_TYPE_INDEF_BYTES_START"; + case (AWS_CBOR_TYPE_INDEF_TEXT_START): return "AWS_CBOR_TYPE_INDEF_TEXT_START"; + case (AWS_CBOR_TYPE_INDEF_ARRAY_START): return "AWS_CBOR_TYPE_INDEF_ARRAY_START"; + case (AWS_CBOR_TYPE_INDEF_MAP_START): return "AWS_CBOR_TYPE_INDEF_MAP_START"; + default: return ""; + } + /* clang-format on */ +} + +/******************************************************************************* + * ENCODE + ******************************************************************************/ + +struct aws_cbor_encoder { + struct aws_allocator *allocator; + struct aws_byte_buf encoded_buf; +}; + +struct aws_cbor_encoder *aws_cbor_encoder_new(struct aws_allocator *allocator) { + struct aws_cbor_encoder *encoder = aws_mem_calloc(allocator, 1, sizeof(struct aws_cbor_encoder)); + encoder->allocator = allocator; + aws_byte_buf_init(&encoder->encoded_buf, allocator, 256); + + return encoder; +} + +struct aws_cbor_encoder *aws_cbor_encoder_destroy(struct aws_cbor_encoder *encoder) { + aws_byte_buf_clean_up(&encoder->encoded_buf); + aws_mem_release(encoder->allocator, encoder); + return NULL; +} + +struct aws_byte_cursor aws_cbor_encoder_get_encoded_data(const struct aws_cbor_encoder *encoder) { + return aws_byte_cursor_from_buf(&encoder->encoded_buf); +} + +void aws_cbor_encoder_reset(struct aws_cbor_encoder *encoder) { + aws_byte_buf_reset(&encoder->encoded_buf, false); +} + +static uint8_t *s_get_encoder_current_position(struct aws_cbor_encoder *encoder) { + return encoder->encoded_buf.buffer + encoder->encoded_buf.len; +} + +static size_t s_get_encoder_remaining_len(struct aws_cbor_encoder *encoder) { + return encoder->encoded_buf.capacity - encoder->encoded_buf.len; +} + +/** + * @brief Marcos to ensure the encoder have enough space to encode the value into the buffer using given `fn`, and then + * encode it. + */ +#define ENCODE_THROUGH_LIBCBOR(encoder, length_to_reserve, value, fn) \ + do { \ + int error = aws_byte_buf_reserve_smart_relative(&(encoder)->encoded_buf, length_to_reserve); \ + (void)error; \ + AWS_FATAL_ASSERT(error == AWS_ERROR_SUCCESS); \ + size_t encoded_len = fn(value, s_get_encoder_current_position(encoder), s_get_encoder_remaining_len(encoder)); \ + AWS_FATAL_ASSERT((encoded_len) != 0); \ + (encoder)->encoded_buf.len += (encoded_len); \ + } while (false) + +void aws_cbor_encoder_write_uint(struct aws_cbor_encoder *encoder, uint64_t value) { + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit, value, cbor_encode_uint); +} + +void aws_cbor_encoder_write_negint(struct aws_cbor_encoder *encoder, uint64_t value) { + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit, value, cbor_encode_negint); +} + +void aws_cbor_encoder_write_single_float(struct aws_cbor_encoder *encoder, float value) { + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_32bit, value, cbor_encode_single); +} + +void aws_cbor_encoder_write_float(struct aws_cbor_encoder *encoder, double value) { + /** + * As suggested by AWS SDK SEP, write the float value as small as possible. But, do not encode to half-float. + * Convert the float value to integer if the conversion will not cause any precision loss. + */ + if (!isfinite(value)) { + /* For special value: NAN/INFINITY, type cast to float and encode into single float. */ + aws_cbor_encoder_write_single_float(encoder, (float)value); + return; + } + /* Conversation from int to floating-type is implementation defined if loss of precision */ + if (value <= (double)INT64_MAX && value >= (double)INT64_MIN) { + /** + * A prvalue of a floating point type can be converted to a prvalue of an integer type. The conversion + * truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot + * be represented in the destination type. + * Check against the INT64 range to avoid undefined behavior + * + * Comparing against INT64_MAX instead of UINT64_MAX to simplify the code, which may loss the opportunity to + * convert the UINT64 range from double to uint64_t. However, converting double to uint64_t will not benefit the + * total length encoded. + **/ + int64_t int_value = (int64_t)value; + if (value == (double)int_value) { + if (int_value < 0) { + aws_cbor_encoder_write_negint(encoder, (uint64_t)(-1 - int_value)); + } else { + aws_cbor_encoder_write_uint(encoder, (uint64_t)(int_value)); + } + return; + } + } + if (value <= FLT_MAX && value >= -FLT_MAX) { + /* Only try to convert the value within the range of float. */ + float float_value = (float)value; + double converted_value = (double)float_value; + /* Try to cast a round trip to detect any precision loss. */ + if (value == converted_value) { + aws_cbor_encoder_write_single_float(encoder, float_value); + return; + } + } + + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit, value, cbor_encode_double); +} + +void aws_cbor_encoder_write_map_start(struct aws_cbor_encoder *encoder, size_t number_entries) { + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit, number_entries, cbor_encode_map_start); +} + +void aws_cbor_encoder_write_tag(struct aws_cbor_encoder *encoder, uint64_t tag_number) { + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit, tag_number, cbor_encode_tag); +} + +void aws_cbor_encoder_write_array_start(struct aws_cbor_encoder *encoder, size_t number_entries) { + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit, number_entries, cbor_encode_array_start); +} + +void aws_cbor_encoder_write_bytes(struct aws_cbor_encoder *encoder, struct aws_byte_cursor from) { + /* Reserve the bytes for the byte string start cbor item and the actual bytes */ + /* Encode the first cbor item for byte string */ + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit + from.len, from.len, cbor_encode_bytestring_start); + /* Append the actual bytes to follow the cbor item */ + aws_byte_buf_append(&encoder->encoded_buf, &from); +} + +void aws_cbor_encoder_write_text(struct aws_cbor_encoder *encoder, struct aws_byte_cursor from) { + /* Reserve the bytes for the byte string start cbor item and the actual string */ + /* Encode the first cbor item for byte string */ + ENCODE_THROUGH_LIBCBOR(encoder, s_cbor_element_width_64bit + from.len, from.len, cbor_encode_string_start); + /* Append the actual string to follow the cbor item */ + aws_byte_buf_append(&encoder->encoded_buf, &from); +} + +void aws_cbor_encoder_write_bool(struct aws_cbor_encoder *encoder, bool value) { + /* Major type 7 (simple), value 20 (false) and 21 (true) */ + uint8_t ctrl_value = value == true ? AWS_CBOR_SIMPLE_VAL_TRUE : AWS_CBOR_SIMPLE_VAL_FALSE; + ENCODE_THROUGH_LIBCBOR(encoder, 1, ctrl_value, cbor_encode_ctrl); +} + +void aws_cbor_encoder_write_null(struct aws_cbor_encoder *encoder) { + /* Major type 7 (simple), value 22 (null) */ + ENCODE_THROUGH_LIBCBOR(encoder, 1, AWS_CBOR_SIMPLE_VAL_NULL /*null*/, cbor_encode_ctrl); +} + +void aws_cbor_encoder_write_undefined(struct aws_cbor_encoder *encoder) { + /* Major type 7 (simple), value 23 (undefined) */ + ENCODE_THROUGH_LIBCBOR(encoder, 1, AWS_CBOR_SIMPLE_VAL_UNDEFINED /*undefined*/, cbor_encode_ctrl); +} + +static void s_cbor_encoder_write_type_only(struct aws_cbor_encoder *encoder, enum aws_cbor_type type) { + /* All inf start takes 1 byte only */ + aws_byte_buf_reserve_smart_relative(&encoder->encoded_buf, 1); + size_t encoded_len = 0; + switch (type) { + case AWS_CBOR_TYPE_INDEF_BYTES_START: + encoded_len = cbor_encode_indef_bytestring_start( + s_get_encoder_current_position(encoder), s_get_encoder_remaining_len(encoder)); + break; + case AWS_CBOR_TYPE_INDEF_TEXT_START: + encoded_len = cbor_encode_indef_string_start( + s_get_encoder_current_position(encoder), s_get_encoder_remaining_len(encoder)); + break; + case AWS_CBOR_TYPE_INDEF_ARRAY_START: + encoded_len = cbor_encode_indef_array_start( + s_get_encoder_current_position(encoder), s_get_encoder_remaining_len(encoder)); + break; + case AWS_CBOR_TYPE_INDEF_MAP_START: + encoded_len = cbor_encode_indef_map_start( + s_get_encoder_current_position(encoder), s_get_encoder_remaining_len(encoder)); + break; + case AWS_CBOR_TYPE_BREAK: + encoded_len = + cbor_encode_break(s_get_encoder_current_position(encoder), s_get_encoder_remaining_len(encoder)); + break; + + default: + AWS_ASSERT(false); + break; + } + AWS_ASSERT(encoded_len == 1); + encoder->encoded_buf.len += encoded_len; +} +void aws_cbor_encoder_write_indef_bytes_start(struct aws_cbor_encoder *encoder) { + s_cbor_encoder_write_type_only(encoder, AWS_CBOR_TYPE_INDEF_BYTES_START); +} + +void aws_cbor_encoder_write_indef_text_start(struct aws_cbor_encoder *encoder) { + s_cbor_encoder_write_type_only(encoder, AWS_CBOR_TYPE_INDEF_TEXT_START); +} + +void aws_cbor_encoder_write_indef_array_start(struct aws_cbor_encoder *encoder) { + s_cbor_encoder_write_type_only(encoder, AWS_CBOR_TYPE_INDEF_ARRAY_START); +} + +void aws_cbor_encoder_write_indef_map_start(struct aws_cbor_encoder *encoder) { + s_cbor_encoder_write_type_only(encoder, AWS_CBOR_TYPE_INDEF_MAP_START); +} + +void aws_cbor_encoder_write_break(struct aws_cbor_encoder *encoder) { + s_cbor_encoder_write_type_only(encoder, AWS_CBOR_TYPE_BREAK); +} + +/******************************************************************************* + * DECODE + ******************************************************************************/ + +struct aws_cbor_decoder_context { + enum aws_cbor_type type; + + /* All the values only valid when the type is set to corresponding type. */ + union { + uint64_t unsigned_int_val; + uint64_t negative_int_val; + double float_val; + uint64_t tag_val; + bool boolean_val; + struct aws_byte_cursor bytes_val; + struct aws_byte_cursor text_val; + uint64_t map_start; + uint64_t array_start; + } u; +}; + +struct aws_cbor_decoder { + struct aws_allocator *allocator; + + struct aws_byte_cursor src; + + struct aws_cbor_decoder_context cached_context; + + /* Error code during decoding. Fail the decoding process without recovering, */ + int error_code; +}; + +struct aws_cbor_decoder *aws_cbor_decoder_new(struct aws_allocator *allocator, struct aws_byte_cursor src) { + + struct aws_cbor_decoder *decoder = aws_mem_calloc(allocator, 1, sizeof(struct aws_cbor_decoder)); + decoder->allocator = allocator; + decoder->src = src; + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + return decoder; +} + +struct aws_cbor_decoder *aws_cbor_decoder_destroy(struct aws_cbor_decoder *decoder) { + aws_mem_release(decoder->allocator, decoder); + return NULL; +} + +size_t aws_cbor_decoder_get_remaining_length(const struct aws_cbor_decoder *decoder) { + return decoder->src.len; +} + +#define LIBCBOR_VALUE_CALLBACK(field, callback_type, cbor_type) \ + static void s_##field##_callback(void *ctx, callback_type val) { \ + struct aws_cbor_decoder *decoder = ctx; \ + AWS_ASSERT((decoder)->cached_context.type == AWS_CBOR_TYPE_UNKNOWN); \ + (decoder)->cached_context.u.field = val; \ + (decoder)->cached_context.type = cbor_type; \ + } + +LIBCBOR_VALUE_CALLBACK(unsigned_int_val, uint64_t, AWS_CBOR_TYPE_UINT) +LIBCBOR_VALUE_CALLBACK(negative_int_val, uint64_t, AWS_CBOR_TYPE_NEGINT) +LIBCBOR_VALUE_CALLBACK(boolean_val, bool, AWS_CBOR_TYPE_BOOL) +LIBCBOR_VALUE_CALLBACK(float_val, double, AWS_CBOR_TYPE_FLOAT) +LIBCBOR_VALUE_CALLBACK(map_start, uint64_t, AWS_CBOR_TYPE_MAP_START) +LIBCBOR_VALUE_CALLBACK(array_start, uint64_t, AWS_CBOR_TYPE_ARRAY_START) +LIBCBOR_VALUE_CALLBACK(tag_val, uint64_t, AWS_CBOR_TYPE_TAG) + +static void s_uint8_callback(void *ctx, uint8_t data) { + s_unsigned_int_val_callback(ctx, (uint64_t)data); +} + +static void s_uint16_callback(void *ctx, uint16_t data) { + s_unsigned_int_val_callback(ctx, (uint64_t)data); +} + +static void s_uint32_callback(void *ctx, uint32_t data) { + s_unsigned_int_val_callback(ctx, (uint64_t)data); +} + +static void s_negint8_callback(void *ctx, uint8_t data) { + s_negative_int_val_callback(ctx, (uint64_t)data); +} + +static void s_negint16_callback(void *ctx, uint16_t data) { + s_negative_int_val_callback(ctx, (uint64_t)data); +} + +static void s_negint32_callback(void *ctx, uint32_t data) { + s_negative_int_val_callback(ctx, (uint64_t)data); +} + +static void s_float_callback(void *ctx, float data) { + s_float_val_callback(ctx, (double)data); +} + +static void s_bytes_callback(void *ctx, const unsigned char *cbor_data, uint64_t length) { + struct aws_cbor_decoder *decoder = ctx; + AWS_ASSERT((decoder)->cached_context.type == AWS_CBOR_TYPE_UNKNOWN); + if (length > SIZE_MAX) { + AWS_LOGF_ERROR(AWS_LS_COMMON_CBOR, "Decoded a bytes with %" PRIu64 " bytes causing overflow .", length); + decoder->error_code = AWS_ERROR_OVERFLOW_DETECTED; + return; + } + decoder->cached_context.type = AWS_CBOR_TYPE_BYTES; + decoder->cached_context.u.bytes_val.ptr = (uint8_t *)cbor_data; + decoder->cached_context.u.bytes_val.len = (size_t)length; +} + +static void s_str_callback(void *ctx, const unsigned char *cbor_data, uint64_t length) { + struct aws_cbor_decoder *decoder = ctx; + AWS_ASSERT((decoder)->cached_context.type == AWS_CBOR_TYPE_UNKNOWN); + if (length > SIZE_MAX) { + AWS_LOGF_ERROR(AWS_LS_COMMON_CBOR, "Decoded a string with %" PRIu64 " bytes causing overflow .", length); + decoder->error_code = AWS_ERROR_OVERFLOW_DETECTED; + return; + } + decoder->cached_context.type = AWS_CBOR_TYPE_TEXT; + decoder->cached_context.u.text_val.ptr = (uint8_t *)cbor_data; + decoder->cached_context.u.text_val.len = (size_t)length; +} + +#define LIBCBOR_SIMPLE_CALLBACK(field, cbor_type) \ + static void s_##field##_callback(void *ctx) { \ + struct aws_cbor_decoder *decoder = ctx; \ + AWS_ASSERT((decoder)->cached_context.type == AWS_CBOR_TYPE_UNKNOWN); \ + (decoder)->cached_context.type = cbor_type; \ + } + +LIBCBOR_SIMPLE_CALLBACK(inf_bytes, AWS_CBOR_TYPE_INDEF_BYTES_START) +LIBCBOR_SIMPLE_CALLBACK(inf_str, AWS_CBOR_TYPE_INDEF_TEXT_START) +LIBCBOR_SIMPLE_CALLBACK(inf_array, AWS_CBOR_TYPE_INDEF_ARRAY_START) +LIBCBOR_SIMPLE_CALLBACK(inf_map, AWS_CBOR_TYPE_INDEF_MAP_START) + +LIBCBOR_SIMPLE_CALLBACK(inf_break, AWS_CBOR_TYPE_BREAK) +LIBCBOR_SIMPLE_CALLBACK(undefined, AWS_CBOR_TYPE_UNDEFINED) +LIBCBOR_SIMPLE_CALLBACK(null, AWS_CBOR_TYPE_NULL) + +static struct cbor_callbacks s_callbacks = { + /** Unsigned int */ + .uint64 = s_unsigned_int_val_callback, + /** Unsigned int */ + .uint32 = s_uint32_callback, + /** Unsigned int */ + .uint16 = s_uint16_callback, + /** Unsigned int */ + .uint8 = s_uint8_callback, + + /** Negative int */ + .negint64 = s_negative_int_val_callback, + /** Negative int */ + .negint32 = s_negint32_callback, + /** Negative int */ + .negint16 = s_negint16_callback, + /** Negative int */ + .negint8 = s_negint8_callback, + + /** Indefinite byte string start */ + .byte_string_start = s_inf_bytes_callback, + /** Definite byte string */ + .byte_string = s_bytes_callback, + + /** Definite string */ + .string = s_str_callback, + /** Indefinite string start */ + .string_start = s_inf_str_callback, + + /** Definite array */ + .indef_array_start = s_inf_array_callback, + /** Indefinite array */ + .array_start = s_array_start_callback, + + /** Definite map */ + .indef_map_start = s_inf_map_callback, + /** Indefinite map */ + .map_start = s_map_start_callback, + + /** Tags */ + .tag = s_tag_val_callback, + + /** Half float */ + .float2 = s_float_callback, + /** Single float */ + .float4 = s_float_callback, + /** Double float */ + .float8 = s_float_val_callback, + /** Undef */ + .undefined = s_undefined_callback, + /** Null */ + .null = s_null_callback, + /** Bool */ + .boolean = s_boolean_val_callback, + + /** Indefinite item break */ + .indef_break = s_inf_break_callback, +}; + +/** + * decode the next element to the cached_content. + */ +static int s_cbor_decode_next_element(struct aws_cbor_decoder *decoder) { + struct cbor_decoder_result result = cbor_stream_decode(decoder->src.ptr, decoder->src.len, &s_callbacks, decoder); + switch (result.status) { + case CBOR_DECODER_NEDATA: + AWS_LOGF_ERROR( + AWS_LS_COMMON_CBOR, + "The decoder doesn't have enough data to decode the next element. At least %zu bytes more needed.", + result.required); + decoder->error_code = AWS_ERROR_INVALID_CBOR; + break; + case CBOR_DECODER_ERROR: + AWS_LOGF_ERROR(AWS_LS_COMMON_CBOR, "The cbor data is malformed to decode."); + decoder->error_code = AWS_ERROR_INVALID_CBOR; + break; + default: + break; + } + + if (decoder->error_code) { + /* Error happened during decoding */ + return aws_raise_error(decoder->error_code); + } + + aws_byte_cursor_advance(&decoder->src, result.read); + + return AWS_OP_SUCCESS; +} + +#define GET_NEXT_ITEM(field, out_type, expected_cbor_type) \ + /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ + int aws_cbor_decoder_pop_next_##field(struct aws_cbor_decoder *decoder, out_type *out) { \ + if ((decoder)->error_code) { \ + /* Error happened during decoding */ \ + return aws_raise_error((decoder)->error_code); \ + } \ + if ((decoder)->cached_context.type != AWS_CBOR_TYPE_UNKNOWN) { \ + /* There was a cached context, check if the cached one meets the expected. */ \ + goto decode_done; \ + } \ + if (s_cbor_decode_next_element(decoder)) { \ + return AWS_OP_ERR; \ + } \ + decode_done: \ + if ((decoder)->cached_context.type != (expected_cbor_type)) { \ + AWS_LOGF_ERROR( \ + AWS_LS_COMMON_CBOR, \ + "The decoder got unexpected type: %d (%s), while expecting type: %d (%s).", \ + (decoder)->cached_context.type, \ + aws_cbor_type_cstr((decoder)->cached_context.type), \ + (expected_cbor_type), \ + aws_cbor_type_cstr(expected_cbor_type)); \ + return aws_raise_error(AWS_ERROR_CBOR_UNEXPECTED_TYPE); \ + } else { \ + /* Clear the cache as we give it out. */ \ + (decoder)->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; \ + *out = (decoder)->cached_context.u.field; \ + } \ + return AWS_OP_SUCCESS; \ + } + +GET_NEXT_ITEM(unsigned_int_val, uint64_t, AWS_CBOR_TYPE_UINT) +GET_NEXT_ITEM(negative_int_val, uint64_t, AWS_CBOR_TYPE_NEGINT) +GET_NEXT_ITEM(float_val, double, AWS_CBOR_TYPE_FLOAT) +GET_NEXT_ITEM(boolean_val, bool, AWS_CBOR_TYPE_BOOL) +GET_NEXT_ITEM(text_val, struct aws_byte_cursor, AWS_CBOR_TYPE_TEXT) +GET_NEXT_ITEM(bytes_val, struct aws_byte_cursor, AWS_CBOR_TYPE_BYTES) +GET_NEXT_ITEM(map_start, uint64_t, AWS_CBOR_TYPE_MAP_START) +GET_NEXT_ITEM(array_start, uint64_t, AWS_CBOR_TYPE_ARRAY_START) +GET_NEXT_ITEM(tag_val, uint64_t, AWS_CBOR_TYPE_TAG) + +int aws_cbor_decoder_peek_type(struct aws_cbor_decoder *decoder, enum aws_cbor_type *out_type) { + if (decoder->error_code) { + /* Error happened during decoding */ + return aws_raise_error(decoder->error_code); + } + + if (decoder->cached_context.type != AWS_CBOR_TYPE_UNKNOWN) { + /* There was a cached context, return the type. */ + *out_type = decoder->cached_context.type; + return AWS_OP_SUCCESS; + } + + /* Decode */ + if (s_cbor_decode_next_element(decoder)) { + return AWS_OP_ERR; + } + *out_type = decoder->cached_context.type; + return AWS_OP_SUCCESS; +} + +int aws_cbor_decoder_consume_next_whole_data_item(struct aws_cbor_decoder *decoder) { + if (decoder->error_code) { + /* Error happened during decoding */ + return aws_raise_error(decoder->error_code); + } + + if (decoder->cached_context.type == AWS_CBOR_TYPE_UNKNOWN) { + /* There was no cache, decode the next item */ + if (s_cbor_decode_next_element(decoder)) { + return AWS_OP_ERR; + } + } + switch (decoder->cached_context.type) { + case AWS_CBOR_TYPE_TAG: + /* Read the next data item */ + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + if (aws_cbor_decoder_consume_next_whole_data_item(decoder)) { + return AWS_OP_ERR; + } + break; + case AWS_CBOR_TYPE_MAP_START: { + uint64_t num_map_item = decoder->cached_context.u.map_start; + /* Reset type */ + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + for (uint64_t i = 0; i < num_map_item; i++) { + /* Key */ + if (aws_cbor_decoder_consume_next_whole_data_item(decoder)) { + return AWS_OP_ERR; + } + /* Value */ + if (aws_cbor_decoder_consume_next_whole_data_item(decoder)) { + return AWS_OP_ERR; + } + } + break; + } + case AWS_CBOR_TYPE_ARRAY_START: { + uint64_t num_array_item = decoder->cached_context.u.array_start; + /* Reset type */ + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + for (uint64_t i = 0; i < num_array_item; i++) { + /* item */ + if (aws_cbor_decoder_consume_next_whole_data_item(decoder)) { + return AWS_OP_ERR; + } + } + break; + } + case AWS_CBOR_TYPE_INDEF_BYTES_START: + case AWS_CBOR_TYPE_INDEF_TEXT_START: + case AWS_CBOR_TYPE_INDEF_ARRAY_START: + case AWS_CBOR_TYPE_INDEF_MAP_START: { + enum aws_cbor_type next_type; + /* Reset the cache for the tag val */ + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + if (aws_cbor_decoder_peek_type(decoder, &next_type)) { + return AWS_OP_ERR; + } + while (next_type != AWS_CBOR_TYPE_BREAK) { + if (aws_cbor_decoder_consume_next_whole_data_item(decoder)) { + return AWS_OP_ERR; + } + if (aws_cbor_decoder_peek_type(decoder, &next_type)) { + return AWS_OP_ERR; + } + } + break; + } + + default: + break; + } + + /* Done, just reset the cache */ + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + return AWS_OP_SUCCESS; +} + +int aws_cbor_decoder_consume_next_single_element(struct aws_cbor_decoder *decoder) { + enum aws_cbor_type out_type = 0; + if (aws_cbor_decoder_peek_type(decoder, &out_type)) { + return AWS_OP_ERR; + } + /* Reset the type to clear the cache. */ + decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN; + return AWS_OP_SUCCESS; +} diff --git a/source/common.c b/source/common.c index 3bedda749..1822f93e2 100644 --- a/source/common.c +++ b/source/common.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -274,6 +274,12 @@ static struct aws_error_info errors[] = { AWS_DEFINE_ERROR_INFO_COMMON( AWS_ERROR_FILE_WRITE_FAILURE, "Failed writing to file."), + AWS_DEFINE_ERROR_INFO_COMMON( + AWS_ERROR_INVALID_CBOR, + "Malformed cbor data."), + AWS_DEFINE_ERROR_INFO_COMMON( + AWS_ERROR_CBOR_UNEXPECTED_TYPE, + "Unexpected cbor type encountered."), }; /* clang-format on */ @@ -297,6 +303,8 @@ static struct aws_log_subject_info s_common_log_subject_infos[] = { DEFINE_LOG_SUBJECT_INFO(AWS_LS_COMMON_IO, "common-io", "Common IO utilities"), DEFINE_LOG_SUBJECT_INFO(AWS_LS_COMMON_BUS, "bus", "Message bus"), DEFINE_LOG_SUBJECT_INFO(AWS_LS_COMMON_TEST, "test", "Unit/integration testing"), + DEFINE_LOG_SUBJECT_INFO(AWS_LS_COMMON_JSON_PARSER, "json-parser", "Subject for json parser specific logging"), + DEFINE_LOG_SUBJECT_INFO(AWS_LS_COMMON_CBOR, "cbor", "Subject for CBOR encode and decode"), }; static struct aws_log_subject_info_list s_common_log_subject_list = { @@ -315,6 +323,7 @@ void aws_common_library_init(struct aws_allocator *allocator) { aws_register_log_subject_info_list(&s_common_log_subject_list); aws_thread_initialize_thread_management(); aws_json_module_init(allocator); + aws_cbor_module_init(allocator); /* NUMA is funky and we can't rely on libnuma.so being available. We also don't want to take a hard dependency on it, * try and load it if we can. */ @@ -385,6 +394,7 @@ void aws_common_library_clean_up(void) { aws_unregister_error_info(&s_list); aws_unregister_log_subject_info_list(&s_common_log_subject_list); aws_json_module_cleanup(); + aws_cbor_module_cleanup(); #ifdef AWS_OS_LINUX if (g_libnuma_handle) { dlclose(g_libnuma_handle); diff --git a/source/external/libcbor/allocators.c b/source/external/libcbor/allocators.c new file mode 100644 index 000000000..43c5440dd --- /dev/null +++ b/source/external/libcbor/allocators.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "cbor/common.h" + +CBOR_EXPORT _cbor_malloc_t _cbor_malloc = malloc; +CBOR_EXPORT _cbor_realloc_t _cbor_realloc = realloc; +CBOR_EXPORT _cbor_free_t _cbor_free = free; + +void cbor_set_allocs(_cbor_malloc_t custom_malloc, + _cbor_realloc_t custom_realloc, _cbor_free_t custom_free) { + _cbor_malloc = custom_malloc; + _cbor_realloc = custom_realloc; + _cbor_free = custom_free; +} diff --git a/source/external/libcbor/cbor.c b/source/external/libcbor/cbor.c new file mode 100644 index 000000000..a8b4bcd7a --- /dev/null +++ b/source/external/libcbor/cbor.c @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "cbor.h" +#include "cbor/internal/builder_callbacks.h" +#include "cbor/internal/loaders.h" + +#pragma clang diagnostic push +cbor_item_t *cbor_load(cbor_data source, size_t source_size, + struct cbor_load_result *result) { + /* Context stack */ + static struct cbor_callbacks callbacks = { + .uint8 = &cbor_builder_uint8_callback, + .uint16 = &cbor_builder_uint16_callback, + .uint32 = &cbor_builder_uint32_callback, + .uint64 = &cbor_builder_uint64_callback, + + .negint8 = &cbor_builder_negint8_callback, + .negint16 = &cbor_builder_negint16_callback, + .negint32 = &cbor_builder_negint32_callback, + .negint64 = &cbor_builder_negint64_callback, + + .byte_string = &cbor_builder_byte_string_callback, + .byte_string_start = &cbor_builder_byte_string_start_callback, + + .string = &cbor_builder_string_callback, + .string_start = &cbor_builder_string_start_callback, + + .array_start = &cbor_builder_array_start_callback, + .indef_array_start = &cbor_builder_indef_array_start_callback, + + .map_start = &cbor_builder_map_start_callback, + .indef_map_start = &cbor_builder_indef_map_start_callback, + + .tag = &cbor_builder_tag_callback, + + .null = &cbor_builder_null_callback, + .undefined = &cbor_builder_undefined_callback, + .boolean = &cbor_builder_boolean_callback, + .float2 = &cbor_builder_float2_callback, + .float4 = &cbor_builder_float4_callback, + .float8 = &cbor_builder_float8_callback, + .indef_break = &cbor_builder_indef_break_callback}; + + if (source_size == 0) { + result->error.code = CBOR_ERR_NODATA; + return NULL; + } + struct _cbor_stack stack = _cbor_stack_init(); + + /* Target for callbacks */ + struct _cbor_decoder_context context = (struct _cbor_decoder_context){ + .stack = &stack, .creation_failed = false, .syntax_error = false}; + struct cbor_decoder_result decode_result; + *result = + (struct cbor_load_result){.read = 0, .error = {.code = CBOR_ERR_NONE}}; + + do { + if (source_size > result->read) { /* Check for overflows */ + decode_result = + cbor_stream_decode(source + result->read, source_size - result->read, + &callbacks, &context); + } else { + result->error = (struct cbor_error){.code = CBOR_ERR_NOTENOUGHDATA, + .position = result->read}; + goto error; + } + + switch (decode_result.status) { + case CBOR_DECODER_FINISHED: + /* Everything OK */ + { + result->read += decode_result.read; + break; + } + case CBOR_DECODER_NEDATA: + /* Data length doesn't match MTB expectation */ + { + result->error.code = CBOR_ERR_NOTENOUGHDATA; + goto error; + } + case CBOR_DECODER_ERROR: + /* Reserved/malformed item */ + { + result->error.code = CBOR_ERR_MALFORMATED; + goto error; + } + } + + if (context.creation_failed) { + /* Most likely unsuccessful allocation - our callback has failed */ + result->error.code = CBOR_ERR_MEMERROR; + goto error; + } else if (context.syntax_error) { + result->error.code = CBOR_ERR_SYNTAXERROR; + goto error; + } + } while (stack.size > 0); + + return context.root; + +error: + result->error.position = result->read; + // debug_print("Failed with decoder error %d at %d\n", result->error.code, + // result->error.position); cbor_describe(stack.top->item, stdout); + /* Free the stack */ + while (stack.size > 0) { + cbor_decref(&stack.top->item); + _cbor_stack_pop(&stack); + } + return NULL; +} + +static cbor_item_t *_cbor_copy_int(cbor_item_t *item, bool negative) { + cbor_item_t *res; + switch (cbor_int_get_width(item)) { + case CBOR_INT_8: + res = cbor_build_uint8(cbor_get_uint8(item)); + break; + case CBOR_INT_16: + res = cbor_build_uint16(cbor_get_uint16(item)); + break; + case CBOR_INT_32: + res = cbor_build_uint32(cbor_get_uint32(item)); + break; + case CBOR_INT_64: + res = cbor_build_uint64(cbor_get_uint64(item)); + break; + } + + if (negative) cbor_mark_negint(res); + + return res; +} + +static cbor_item_t *_cbor_copy_float_ctrl(cbor_item_t *item) { + // cppcheck-suppress missingReturn + switch (cbor_float_get_width(item)) { + case CBOR_FLOAT_0: + return cbor_build_ctrl(cbor_ctrl_value(item)); + case CBOR_FLOAT_16: + return cbor_build_float2(cbor_float_get_float2(item)); + case CBOR_FLOAT_32: + return cbor_build_float4(cbor_float_get_float4(item)); + case CBOR_FLOAT_64: + return cbor_build_float8(cbor_float_get_float8(item)); + } +} + +cbor_item_t *cbor_copy(cbor_item_t *item) { + // cppcheck-suppress missingReturn + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: + return _cbor_copy_int(item, false); + case CBOR_TYPE_NEGINT: + return _cbor_copy_int(item, true); + case CBOR_TYPE_BYTESTRING: + if (cbor_bytestring_is_definite(item)) { + return cbor_build_bytestring(cbor_bytestring_handle(item), + cbor_bytestring_length(item)); + } else { + cbor_item_t *res = cbor_new_indefinite_bytestring(); + if (res == NULL) { + return NULL; + } + + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) { + cbor_item_t *chunk_copy = + cbor_copy(cbor_bytestring_chunks_handle(item)[i]); + if (chunk_copy == NULL) { + cbor_decref(&res); + return NULL; + } + if (!cbor_bytestring_add_chunk(res, chunk_copy)) { + cbor_decref(&chunk_copy); + cbor_decref(&res); + return NULL; + } + cbor_decref(&chunk_copy); + } + return res; + } + case CBOR_TYPE_STRING: + if (cbor_string_is_definite(item)) { + return cbor_build_stringn((const char *)cbor_string_handle(item), + cbor_string_length(item)); + } else { + cbor_item_t *res = cbor_new_indefinite_string(); + if (res == NULL) { + return NULL; + } + + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) { + cbor_item_t *chunk_copy = + cbor_copy(cbor_string_chunks_handle(item)[i]); + if (chunk_copy == NULL) { + cbor_decref(&res); + return NULL; + } + if (!cbor_string_add_chunk(res, chunk_copy)) { + cbor_decref(&chunk_copy); + cbor_decref(&res); + return NULL; + } + cbor_decref(&chunk_copy); + } + return res; + } + case CBOR_TYPE_ARRAY: { + cbor_item_t *res; + if (cbor_array_is_definite(item)) { + res = cbor_new_definite_array(cbor_array_size(item)); + } else { + res = cbor_new_indefinite_array(); + } + if (res == NULL) { + return NULL; + } + + for (size_t i = 0; i < cbor_array_size(item); i++) { + cbor_item_t *entry_copy = cbor_copy(cbor_move(cbor_array_get(item, i))); + if (entry_copy == NULL) { + cbor_decref(&res); + return NULL; + } + if (!cbor_array_push(res, entry_copy)) { + cbor_decref(&entry_copy); + cbor_decref(&res); + return NULL; + } + cbor_decref(&entry_copy); + } + return res; + } + case CBOR_TYPE_MAP: { + cbor_item_t *res; + if (cbor_map_is_definite(item)) { + res = cbor_new_definite_map(cbor_map_size(item)); + } else { + res = cbor_new_indefinite_map(); + } + if (res == NULL) { + return NULL; + } + + struct cbor_pair *it = cbor_map_handle(item); + for (size_t i = 0; i < cbor_map_size(item); i++) { + cbor_item_t *key_copy = cbor_copy(it[i].key); + if (key_copy == NULL) { + cbor_decref(&res); + return NULL; + } + cbor_item_t *value_copy = cbor_copy(it[i].value); + if (value_copy == NULL) { + cbor_decref(&res); + cbor_decref(&key_copy); + return NULL; + } + if (!cbor_map_add(res, (struct cbor_pair){.key = key_copy, + .value = value_copy})) { + cbor_decref(&res); + cbor_decref(&key_copy); + cbor_decref(&value_copy); + return NULL; + } + cbor_decref(&key_copy); + cbor_decref(&value_copy); + } + return res; + } + case CBOR_TYPE_TAG: { + cbor_item_t *item_copy = cbor_copy(cbor_move(cbor_tag_item(item))); + if (item_copy == NULL) { + return NULL; + } + cbor_item_t *tag = cbor_build_tag(cbor_tag_value(item), item_copy); + cbor_decref(&item_copy); + return tag; + } + case CBOR_TYPE_FLOAT_CTRL: + return _cbor_copy_float_ctrl(item); + } +} + +#if CBOR_PRETTY_PRINTER + +#include +#include +#include + +#define __STDC_FORMAT_MACROS + +static int _pow(int b, int ex) { + if (ex == 0) return 1; + int res = b; + while (--ex > 0) res *= b; + return res; +} + +static void _cbor_type_marquee(FILE *out, char *label, int indent) { + fprintf(out, "%*.*s[%s] ", indent, indent, " ", label); +} + +static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) { + const int indent_offset = 4; + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: { + _cbor_type_marquee(out, "CBOR_TYPE_UINT", indent); + fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item))); + fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item)); + break; + } + case CBOR_TYPE_NEGINT: { + _cbor_type_marquee(out, "CBOR_TYPE_NEGINT", indent); + fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item))); + fprintf(out, "Value: -%" PRIu64 " - 1\n", cbor_get_int(item)); + break; + } + case CBOR_TYPE_BYTESTRING: { + _cbor_type_marquee(out, "CBOR_TYPE_BYTESTRING", indent); + if (cbor_bytestring_is_indefinite(item)) { + fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n", + cbor_bytestring_chunk_count(item)); + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) + _cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out, + indent + indent_offset); + } else { + const unsigned char *data = cbor_bytestring_handle(item); + fprintf(out, "Definite, Length: %zuB, Data:\n", + cbor_bytestring_length(item)); + fprintf(out, "%*s", indent + indent_offset, " "); + for (size_t i = 0; i < cbor_bytestring_length(item); i++) + fprintf(out, "%02x", (int)(data[i] & 0xff)); + fprintf(out, "\n"); + } + break; + } + case CBOR_TYPE_STRING: { + _cbor_type_marquee(out, "CBOR_TYPE_STRING", indent); + if (cbor_string_is_indefinite(item)) { + fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n", + cbor_string_chunk_count(item)); + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) + _cbor_nested_describe(cbor_string_chunks_handle(item)[i], out, + indent + indent_offset); + } else { + fprintf(out, "Definite, Length: %zuB, Codepoints: %zu, Data:\n", + cbor_string_length(item), cbor_string_codepoint_count(item)); + fprintf(out, "%*s", indent + indent_offset, " "); + // Note: The string is not escaped, whitespace and control character + // will be printed in verbatim and take effect. + fwrite(cbor_string_handle(item), sizeof(unsigned char), + cbor_string_length(item), out); + fprintf(out, "\n"); + } + break; + } + case CBOR_TYPE_ARRAY: { + _cbor_type_marquee(out, "CBOR_TYPE_ARRAY", indent); + if (cbor_array_is_definite(item)) { + fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_array_size(item)); + } else { + fprintf(out, "Indefinite, Size: %zu, Contents:\n", + cbor_array_size(item)); + } + + for (size_t i = 0; i < cbor_array_size(item); i++) + _cbor_nested_describe(cbor_array_handle(item)[i], out, + indent + indent_offset); + break; + } + case CBOR_TYPE_MAP: { + _cbor_type_marquee(out, "CBOR_TYPE_MAP", indent); + if (cbor_map_is_definite(item)) { + fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_map_size(item)); + } else { + fprintf(out, "Indefinite, Size: %zu, Contents:\n", cbor_map_size(item)); + } + + // TODO: Label and group keys and values + for (size_t i = 0; i < cbor_map_size(item); i++) { + fprintf(out, "%*sMap entry %zu\n", indent + indent_offset, " ", i); + _cbor_nested_describe(cbor_map_handle(item)[i].key, out, + indent + 2 * indent_offset); + _cbor_nested_describe(cbor_map_handle(item)[i].value, out, + indent + 2 * indent_offset); + } + break; + } + case CBOR_TYPE_TAG: { + _cbor_type_marquee(out, "CBOR_TYPE_TAG", indent); + fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item)); + _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, + indent + indent_offset); + break; + } + case CBOR_TYPE_FLOAT_CTRL: { + _cbor_type_marquee(out, "CBOR_TYPE_FLOAT_CTRL", indent); + if (cbor_float_ctrl_is_ctrl(item)) { + if (cbor_is_bool(item)) + fprintf(out, "Bool: %s\n", cbor_get_bool(item) ? "true" : "false"); + else if (cbor_is_undef(item)) + fprintf(out, "Undefined\n"); + else if (cbor_is_null(item)) + fprintf(out, "Null\n"); + else + fprintf(out, "Simple value: %d\n", cbor_ctrl_value(item)); + } else { + fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item))); + fprintf(out, "Value: %lf\n", cbor_float_get_float(item)); + } + break; + } + } +} + +void cbor_describe(cbor_item_t *item, FILE *out) { + _cbor_nested_describe(item, out, 0); +} + +#endif diff --git a/source/external/libcbor/cbor.h b/source/external/libcbor/cbor.h new file mode 100644 index 000000000..46ef8f267 --- /dev/null +++ b/source/external/libcbor/cbor.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_H_ +#define LIBCBOR_H_ + +#include "cbor/common.h" +#include "cbor/data.h" + +#include "cbor/arrays.h" +#include "cbor/bytestrings.h" +#include "cbor/floats_ctrls.h" +#include "cbor/ints.h" +#include "cbor/maps.h" +#include "cbor/strings.h" +#include "cbor/tags.h" + +#include "cbor/callbacks.h" +#include "cbor/cbor_export.h" +#include "cbor/encoding.h" +#include "cbor/serialization.h" +#include "cbor/streaming.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * High level decoding + * ============================================================================ + */ + +/** Loads data item from a buffer + * + * @param source The buffer + * @param source_size + * @param[out] result Result indicator. #CBOR_ERR_NONE on success + * @return Decoded CBOR item. The item's reference count is initialized to one. + * @return `NULL` on failure. In that case, \p result contains the location and + * description of the error. + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_load( + cbor_data source, size_t source_size, struct cbor_load_result* result); + +/** Take a deep copy of an item + * + * All items this item points to (array and map members, string chunks, tagged + * items) will be copied recursively using #cbor_copy. The new item doesn't + * alias or point to any items from the original \p item. All the reference + * counts in the new structure are set to one. + * + * @param item item to copy + * @return Reference to the new item. The item's reference count is initialized + * to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_copy(cbor_item_t* item); + +#if CBOR_PRETTY_PRINTER +#include + +CBOR_EXPORT void cbor_describe(cbor_item_t* item, FILE* out); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_H_ diff --git a/source/external/libcbor/cbor/arrays.c b/source/external/libcbor/cbor/arrays.c new file mode 100644 index 000000000..a23bbe3cd --- /dev/null +++ b/source/external/libcbor/cbor/arrays.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "arrays.h" +#include +#include "internal/memory_utils.h" + +size_t cbor_array_size(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_array(item)); + return item->metadata.array_metadata.end_ptr; +} + +size_t cbor_array_allocated(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_array(item)); + return item->metadata.array_metadata.allocated; +} + +cbor_item_t *cbor_array_get(const cbor_item_t *item, size_t index) { + return cbor_incref(((cbor_item_t **)item->data)[index]); +} + +bool cbor_array_set(cbor_item_t *item, size_t index, cbor_item_t *value) { + if (index == item->metadata.array_metadata.end_ptr) { + return cbor_array_push(item, value); + } else if (index < item->metadata.array_metadata.end_ptr) { + return cbor_array_replace(item, index, value); + } else { + return false; + } +} + +bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value) { + if (index >= item->metadata.array_metadata.end_ptr) return false; + /* We cannot use cbor_array_get as that would increase the refcount */ + cbor_intermediate_decref(((cbor_item_t **)item->data)[index]); + ((cbor_item_t **)item->data)[index] = cbor_incref(value); + return true; +} + +bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) { + CBOR_ASSERT(cbor_isa_array(array)); + struct _cbor_array_metadata *metadata = + (struct _cbor_array_metadata *)&array->metadata; + cbor_item_t **data = (cbor_item_t **)array->data; + if (cbor_array_is_definite(array)) { + /* Do not reallocate definite arrays */ + if (metadata->end_ptr >= metadata->allocated) { + return false; + } + data[metadata->end_ptr++] = pushee; + } else { + /* Exponential realloc */ + if (metadata->end_ptr >= metadata->allocated) { + // Check for overflows first + if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) { + return false; + } + + size_t new_allocation = metadata->allocated == 0 + ? 1 + : CBOR_BUFFER_GROWTH * metadata->allocated; + + unsigned char *new_data = _cbor_realloc_multiple( + array->data, sizeof(cbor_item_t *), new_allocation); + if (new_data == NULL) { + return false; + } + + array->data = new_data; + metadata->allocated = new_allocation; + } + ((cbor_item_t **)array->data)[metadata->end_ptr++] = pushee; + } + cbor_incref(pushee); + return true; +} + +bool cbor_array_is_definite(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_array(item)); + return item->metadata.array_metadata.type == _CBOR_METADATA_DEFINITE; +} + +bool cbor_array_is_indefinite(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_array(item)); + return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE; +} + +cbor_item_t **cbor_array_handle(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_array(item)); + return (cbor_item_t **)item->data; +} + +cbor_item_t *cbor_new_definite_array(size_t size) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + cbor_item_t **data = _cbor_alloc_multiple(sizeof(cbor_item_t *), size); + _CBOR_DEPENDENT_NOTNULL(item, data); + + for (size_t i = 0; i < size; i++) { + data[i] = NULL; + } + + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_ARRAY, + .metadata = {.array_metadata = {.type = _CBOR_METADATA_DEFINITE, + .allocated = size, + .end_ptr = 0}}, + .data = (unsigned char *)data}; + + return item; +} + +cbor_item_t *cbor_new_indefinite_array(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_ARRAY, + .metadata = {.array_metadata = {.type = _CBOR_METADATA_INDEFINITE, + .allocated = 0, + .end_ptr = 0}}, + .data = NULL /* Can be safely realloc-ed */ + }; + return item; +} diff --git a/source/external/libcbor/cbor/arrays.h b/source/external/libcbor/cbor/arrays.h new file mode 100644 index 000000000..db19e59d0 --- /dev/null +++ b/source/external/libcbor/cbor/arrays.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_ARRAYS_H +#define LIBCBOR_ARRAYS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Get the number of members + * + * @param item An array + * @return The number of members + */ +_CBOR_NODISCARD +CBOR_EXPORT size_t cbor_array_size(const cbor_item_t* item); + +/** Get the size of the allocated storage + * + * @param item An array + * @return The size of the allocated storage (number of items) + */ +_CBOR_NODISCARD +CBOR_EXPORT size_t cbor_array_allocated(const cbor_item_t* item); + +/** Get item by index + * + * @param item An array + * @param index The index (zero-based) + * @return Reference to the item, or `NULL` in case of boundary violation. + * + * Increases the reference count of the underlying item. The returned reference + * must be released using #cbor_decref. + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t* cbor_array_get(const cbor_item_t* item, size_t index); + +/** Set item by index + * + * If the index is out of bounds, the array is not modified and false is + * returned. Creating arrays with holes is not possible. + * + * @param item An array + * @param value The item to assign + * @param index The index (zero-based) + * @return `true` on success, `false` on allocation failure. + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_array_set(cbor_item_t* item, size_t index, + cbor_item_t* value); + +/** Replace item at an index + * + * The reference to the item being replaced will be released using #cbor_decref. + * + * @param item An array + * @param value The item to assign. Its reference count will be increased by + * one. + * @param index The index (zero-based) + * @return true on success, false on allocation failure. + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_array_replace(cbor_item_t* item, size_t index, + cbor_item_t* value); + +/** Is the array definite? + * + * @param item An array + * @return Is the array definite? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_array_is_definite(const cbor_item_t* item); + +/** Is the array indefinite? + * + * @param item An array + * @return Is the array indefinite? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_array_is_indefinite(const cbor_item_t* item); + +/** Get the array contents + * + * The items may be reordered and modified as long as references remain + * consistent. + * + * @param item An array item + * @return An array of #cbor_item_t pointers of size #cbor_array_size. + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t** cbor_array_handle(const cbor_item_t* item); + +/** Create new definite array + * + * @param size Number of slots to preallocate + * @return Reference to the new array item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t* cbor_new_definite_array(size_t size); + +/** Create new indefinite array + * + * @return Reference to the new array item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t* cbor_new_indefinite_array(void); + +/** Append to the end + * + * For indefinite items, storage may be reallocated. For definite items, only + * the preallocated capacity is available. + * + * @param array An array + * @param pushee The item to push. Its reference count will be increased by + * one. + * @return `true` on success, `false` on failure + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_array_push(cbor_item_t* array, cbor_item_t* pushee); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_ARRAYS_H diff --git a/source/external/libcbor/cbor/bytestrings.c b/source/external/libcbor/cbor/bytestrings.c new file mode 100644 index 000000000..528937179 --- /dev/null +++ b/source/external/libcbor/cbor/bytestrings.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "bytestrings.h" +#include +#include "internal/memory_utils.h" + +size_t cbor_bytestring_length(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + return item->metadata.bytestring_metadata.length; +} + +unsigned char *cbor_bytestring_handle(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + return item->data; +} + +bool cbor_bytestring_is_definite(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + return item->metadata.bytestring_metadata.type == _CBOR_METADATA_DEFINITE; +} + +bool cbor_bytestring_is_indefinite(const cbor_item_t *item) { + return !cbor_bytestring_is_definite(item); +} + +cbor_item_t *cbor_new_definite_bytestring(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_BYTESTRING, + .metadata = {.bytestring_metadata = {.type = _CBOR_METADATA_DEFINITE, + .length = 0}}}; + return item; +} + +cbor_item_t *cbor_new_indefinite_bytestring(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_BYTESTRING, + .metadata = {.bytestring_metadata = {.type = _CBOR_METADATA_INDEFINITE, + .length = 0}}, + .data = _cbor_malloc(sizeof(struct cbor_indefinite_string_data))}; + _CBOR_DEPENDENT_NOTNULL(item, item->data); + *((struct cbor_indefinite_string_data *)item->data) = + (struct cbor_indefinite_string_data){ + .chunk_count = 0, + .chunk_capacity = 0, + .chunks = NULL, + }; + return item; +} + +cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length) { + cbor_item_t *item = cbor_new_definite_bytestring(); + _CBOR_NOTNULL(item); + void *content = _cbor_malloc(length); + _CBOR_DEPENDENT_NOTNULL(item, content); + memcpy(content, handle, length); + cbor_bytestring_set_handle(item, content, length); + return item; +} + +void cbor_bytestring_set_handle(cbor_item_t *item, + cbor_mutable_data CBOR_RESTRICT_POINTER data, + size_t length) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + CBOR_ASSERT(cbor_bytestring_is_definite(item)); + item->data = data; + item->metadata.bytestring_metadata.length = length; +} + +cbor_item_t **cbor_bytestring_chunks_handle(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + CBOR_ASSERT(cbor_bytestring_is_indefinite(item)); + return ((struct cbor_indefinite_string_data *)item->data)->chunks; +} + +size_t cbor_bytestring_chunk_count(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + CBOR_ASSERT(cbor_bytestring_is_indefinite(item)); + return ((struct cbor_indefinite_string_data *)item->data)->chunk_count; +} + +bool cbor_bytestring_add_chunk(cbor_item_t *item, cbor_item_t *chunk) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + CBOR_ASSERT(cbor_bytestring_is_indefinite(item)); + CBOR_ASSERT(cbor_isa_bytestring(chunk)); + CBOR_ASSERT(cbor_bytestring_is_definite(chunk)); + struct cbor_indefinite_string_data *data = + (struct cbor_indefinite_string_data *)item->data; + if (data->chunk_count == data->chunk_capacity) { + if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) { + return false; + } + + size_t new_chunk_capacity = + data->chunk_capacity == 0 ? 1 + : CBOR_BUFFER_GROWTH * (data->chunk_capacity); + + cbor_item_t **new_chunks_data = _cbor_realloc_multiple( + data->chunks, sizeof(cbor_item_t *), new_chunk_capacity); + + if (new_chunks_data == NULL) { + return false; + } + data->chunk_capacity = new_chunk_capacity; + data->chunks = new_chunks_data; + } + data->chunks[data->chunk_count++] = cbor_incref(chunk); + return true; +} diff --git a/source/external/libcbor/cbor/bytestrings.h b/source/external/libcbor/cbor/bytestrings.h new file mode 100644 index 000000000..cacd1adf9 --- /dev/null +++ b/source/external/libcbor/cbor/bytestrings.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_BYTESTRINGS_H +#define LIBCBOR_BYTESTRINGS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * Byte string manipulation + * ============================================================================ + */ + +/** Returns the length of the binary data + * + * For definite byte strings only + * + * @param item a definite bytestring + * @return length of the binary data. Zero if no chunk has been attached yet + */ +_CBOR_NODISCARD +CBOR_EXPORT size_t cbor_bytestring_length(const cbor_item_t *item); + +/** Is the byte string definite? + * + * @param item a byte string + * @return Is the byte string definite? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_bytestring_is_definite(const cbor_item_t *item); + +/** Is the byte string indefinite? + * + * @param item a byte string + * @return Is the byte string indefinite? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_bytestring_is_indefinite(const cbor_item_t *item); + +/** Get the handle to the binary data + * + * Definite items only. Modifying the data is allowed. In that case, the caller + * takes responsibility for the effect on items this item might be a part of + * + * @param item A definite byte string + * @return The address of the underlying binary data + * @return `NULL` if no data have been assigned + * yet. + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_mutable_data cbor_bytestring_handle(const cbor_item_t *item); + +/** Set the handle to the binary data + * + * @param item A definite byte string + * @param data The memory block. The caller gives up the ownership of the block. + * libcbor will deallocate it when appropriate using the `free` implementation + * configured using #cbor_set_allocs + * @param length Length of the data block + */ +CBOR_EXPORT void cbor_bytestring_set_handle( + cbor_item_t *item, cbor_mutable_data CBOR_RESTRICT_POINTER data, + size_t length); + +/** Get the handle to the array of chunks + * + * Manipulations with the memory block (e.g. sorting it) are allowed, but the + * validity and the number of chunks must be retained. + * + * @param item A indefinite byte string + * @return array of #cbor_bytestring_chunk_count definite bytestrings + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t **cbor_bytestring_chunks_handle( + const cbor_item_t *item); + +/** Get the number of chunks this string consist of + * + * @param item A indefinite bytestring + * @return The chunk count. 0 for freshly created items. + */ +_CBOR_NODISCARD +CBOR_EXPORT size_t cbor_bytestring_chunk_count(const cbor_item_t *item); + +/** Appends a chunk to the bytestring + * + * Indefinite byte strings only. + * + * May realloc the chunk storage. + * + * @param item An indefinite byte string + * @param chunk A definite byte string. Its reference count will be be increased + * by one. + * @return true on success, false on realloc failure. In that case, the refcount + * of `chunk` is not increased and the `item` is left intact. + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_bytestring_add_chunk(cbor_item_t *item, + cbor_item_t *chunk); + +/** Creates a new definite byte string + * + * The handle is initialized to `NULL` and length to 0 + * + * @return Reference to the new bytestring item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t *cbor_new_definite_bytestring(void); + +/** Creates a new indefinite byte string + * + * The chunks array is initialized to `NULL` and chunk count to 0 + * + * @return Reference to the new bytestring item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t *cbor_new_indefinite_bytestring(void); + +/** Creates a new byte string and initializes it + * + * The `handle` will be copied to a newly allocated block + * + * @param handle Block of binary data + * @param length Length of `data` + * @return Reference to the new bytestring item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_BYTESTRINGS_H diff --git a/source/external/libcbor/cbor/callbacks.c b/source/external/libcbor/cbor/callbacks.c new file mode 100644 index 000000000..bdf3f79ee --- /dev/null +++ b/source/external/libcbor/cbor/callbacks.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "callbacks.h" + +void cbor_null_uint8_callback(void *_CBOR_UNUSED(_ctx), + uint8_t _CBOR_UNUSED(_val)) {} + +void cbor_null_uint16_callback(void *_CBOR_UNUSED(_ctx), + uint16_t _CBOR_UNUSED(_val)) {} + +void cbor_null_uint32_callback(void *_CBOR_UNUSED(_ctx), + uint32_t _CBOR_UNUSED(_val)) {} + +void cbor_null_uint64_callback(void *_CBOR_UNUSED(_ctx), + uint64_t _CBOR_UNUSED(_val)) {} + +void cbor_null_negint8_callback(void *_CBOR_UNUSED(_ctx), + uint8_t _CBOR_UNUSED(_val)) {} + +void cbor_null_negint16_callback(void *_CBOR_UNUSED(_ctx), + uint16_t _CBOR_UNUSED(_val)) {} + +void cbor_null_negint32_callback(void *_CBOR_UNUSED(_ctx), + uint32_t _CBOR_UNUSED(_val)) {} + +void cbor_null_negint64_callback(void *_CBOR_UNUSED(_ctx), + uint64_t _CBOR_UNUSED(_val)) {} + +void cbor_null_string_callback(void *_CBOR_UNUSED(_ctx), + cbor_data _CBOR_UNUSED(_val), + uint64_t _CBOR_UNUSED(_val2)) {} + +void cbor_null_string_start_callback(void *_CBOR_UNUSED(_ctx)) {} + +void cbor_null_byte_string_callback(void *_CBOR_UNUSED(_ctx), + cbor_data _CBOR_UNUSED(_val), + uint64_t _CBOR_UNUSED(_val2)) {} + +void cbor_null_byte_string_start_callback(void *_CBOR_UNUSED(_ctx)) {} + +void cbor_null_array_start_callback(void *_CBOR_UNUSED(_ctx), + uint64_t _CBOR_UNUSED(_val)) {} + +void cbor_null_indef_array_start_callback(void *_CBOR_UNUSED(_ctx)) {} + +void cbor_null_map_start_callback(void *_CBOR_UNUSED(_ctx), + uint64_t _CBOR_UNUSED(_val)) {} + +void cbor_null_indef_map_start_callback(void *_CBOR_UNUSED(_ctx)) {} + +void cbor_null_tag_callback(void *_CBOR_UNUSED(_ctx), + uint64_t _CBOR_UNUSED(_val)) {} + +void cbor_null_float2_callback(void *_CBOR_UNUSED(_ctx), + float _CBOR_UNUSED(_val)) {} + +void cbor_null_float4_callback(void *_CBOR_UNUSED(_ctx), + float _CBOR_UNUSED(_val)) {} + +void cbor_null_float8_callback(void *_CBOR_UNUSED(_ctx), + double _CBOR_UNUSED(_val)) {} + +void cbor_null_null_callback(void *_CBOR_UNUSED(_ctx)) {} + +void cbor_null_undefined_callback(void *_CBOR_UNUSED(_ctx)) {} + +void cbor_null_boolean_callback(void *_CBOR_UNUSED(_ctx), + bool _CBOR_UNUSED(_val)) {} + +void cbor_null_indef_break_callback(void *_CBOR_UNUSED(_ctx)) {} + +CBOR_EXPORT const struct cbor_callbacks cbor_empty_callbacks = { + /* Type 0 - Unsigned integers */ + .uint8 = cbor_null_uint8_callback, + .uint16 = cbor_null_uint16_callback, + .uint32 = cbor_null_uint32_callback, + .uint64 = cbor_null_uint64_callback, + + /* Type 1 - Negative integers */ + .negint8 = cbor_null_negint8_callback, + .negint16 = cbor_null_negint16_callback, + .negint32 = cbor_null_negint32_callback, + .negint64 = cbor_null_negint64_callback, + + /* Type 2 - Byte strings */ + .byte_string_start = cbor_null_byte_string_start_callback, + .byte_string = cbor_null_byte_string_callback, + + /* Type 3 - Strings */ + .string_start = cbor_null_string_start_callback, + .string = cbor_null_string_callback, + + /* Type 4 - Arrays */ + .indef_array_start = cbor_null_indef_array_start_callback, + .array_start = cbor_null_array_start_callback, + + /* Type 5 - Maps */ + .indef_map_start = cbor_null_indef_map_start_callback, + .map_start = cbor_null_map_start_callback, + + /* Type 6 - Tags */ + .tag = cbor_null_tag_callback, + + /* Type 7 - Floats & misc */ + /* Type names cannot be member names */ + .float2 = cbor_null_float2_callback, + /* 2B float is not supported in standard C */ + .float4 = cbor_null_float4_callback, + .float8 = cbor_null_float8_callback, + .undefined = cbor_null_undefined_callback, + .null = cbor_null_null_callback, + .boolean = cbor_null_boolean_callback, + + /* Shared indefinites */ + .indef_break = cbor_null_indef_break_callback, +}; diff --git a/source/external/libcbor/cbor/callbacks.h b/source/external/libcbor/cbor/callbacks.h new file mode 100644 index 000000000..c7ae20568 --- /dev/null +++ b/source/external/libcbor/cbor/callbacks.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_CALLBACKS_H +#define LIBCBOR_CALLBACKS_H + +#include + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Callback prototype */ +typedef void (*cbor_int8_callback)(void *, uint8_t); + +/** Callback prototype */ +typedef void (*cbor_int16_callback)(void *, uint16_t); + +/** Callback prototype */ +typedef void (*cbor_int32_callback)(void *, uint32_t); + +/** Callback prototype */ +typedef void (*cbor_int64_callback)(void *, uint64_t); + +/** Callback prototype */ +typedef void (*cbor_simple_callback)(void *); + +/** Callback prototype */ +typedef void (*cbor_string_callback)(void *, cbor_data, uint64_t); + +/** Callback prototype */ +typedef void (*cbor_collection_callback)(void *, uint64_t); + +/** Callback prototype */ +typedef void (*cbor_float_callback)(void *, float); + +/** Callback prototype */ +typedef void (*cbor_double_callback)(void *, double); + +/** Callback prototype */ +typedef void (*cbor_bool_callback)(void *, bool); + +/** Callback bundle -- passed to the decoder */ +struct cbor_callbacks { + /** Unsigned int */ + cbor_int8_callback uint8; + /** Unsigned int */ + cbor_int16_callback uint16; + /** Unsigned int */ + cbor_int32_callback uint32; + /** Unsigned int */ + cbor_int64_callback uint64; + + /** Negative int */ + cbor_int64_callback negint64; + /** Negative int */ + cbor_int32_callback negint32; + /** Negative int */ + cbor_int16_callback negint16; + /** Negative int */ + cbor_int8_callback negint8; + + /** Definite byte string */ + cbor_simple_callback byte_string_start; + /** Indefinite byte string start */ + cbor_string_callback byte_string; + + /** Definite string */ + cbor_string_callback string; + /** Indefinite string start */ + cbor_simple_callback string_start; + + /** Definite array */ + cbor_simple_callback indef_array_start; + /** Indefinite array */ + cbor_collection_callback array_start; + + /** Definite map */ + cbor_simple_callback indef_map_start; + /** Indefinite map */ + cbor_collection_callback map_start; + + /** Tags */ + cbor_int64_callback tag; + + /** Half float */ + cbor_float_callback float2; + /** Single float */ + cbor_float_callback float4; + /** Double float */ + cbor_double_callback float8; + /** Undef */ + cbor_simple_callback undefined; + /** Null */ + cbor_simple_callback null; + /** Bool */ + cbor_bool_callback boolean; + + /** Indefinite item break */ + cbor_simple_callback indef_break; +}; + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_uint8_callback(void *, uint8_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_uint16_callback(void *, uint16_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_uint32_callback(void *, uint32_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_uint64_callback(void *, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_negint8_callback(void *, uint8_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_negint16_callback(void *, uint16_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_negint32_callback(void *, uint32_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_negint64_callback(void *, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_string_callback(void *, cbor_data, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_string_start_callback(void *); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_byte_string_callback(void *, cbor_data, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_byte_string_start_callback(void *); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_array_start_callback(void *, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_indef_array_start_callback(void *); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_map_start_callback(void *, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_indef_map_start_callback(void *); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_tag_callback(void *, uint64_t); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_float2_callback(void *, float); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_float4_callback(void *, float); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_float8_callback(void *, double); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_null_callback(void *); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_undefined_callback(void *); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_boolean_callback(void *, bool); + +/** Dummy callback implementation - does nothing */ +CBOR_EXPORT void cbor_null_indef_break_callback(void *); + +/** Dummy callback bundle - does nothing */ +CBOR_EXPORT extern const struct cbor_callbacks cbor_empty_callbacks; + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_CALLBACKS_H diff --git a/source/external/libcbor/cbor/cbor_export.h b/source/external/libcbor/cbor/cbor_export.h new file mode 100644 index 000000000..b739bb32b --- /dev/null +++ b/source/external/libcbor/cbor/cbor_export.h @@ -0,0 +1,14 @@ +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/import_libcbor.py + * and any modifications should be in there. + */ + +#ifndef CBOR_EXPORT_H +#define CBOR_EXPORT_H + +/* Don't export anything from libcbor */ +#define CBOR_EXPORT + +#endif /* CBOR_EXPORT_H */ diff --git a/source/external/libcbor/cbor/common.c b/source/external/libcbor/cbor/common.c new file mode 100644 index 000000000..efbd37ed7 --- /dev/null +++ b/source/external/libcbor/cbor/common.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "cbor/common.h" +#include "arrays.h" +#include "bytestrings.h" +#include "data.h" +#include "floats_ctrls.h" +#include "ints.h" +#include "maps.h" +#include "strings.h" +#include "tags.h" + +#ifdef DEBUG +bool _cbor_enable_assert = true; +#endif + +bool cbor_isa_uint(const cbor_item_t *item) { + return item->type == CBOR_TYPE_UINT; +} + +bool cbor_isa_negint(const cbor_item_t *item) { + return item->type == CBOR_TYPE_NEGINT; +} + +bool cbor_isa_bytestring(const cbor_item_t *item) { + return item->type == CBOR_TYPE_BYTESTRING; +} + +bool cbor_isa_string(const cbor_item_t *item) { + return item->type == CBOR_TYPE_STRING; +} + +bool cbor_isa_array(const cbor_item_t *item) { + return item->type == CBOR_TYPE_ARRAY; +} + +bool cbor_isa_map(const cbor_item_t *item) { + return item->type == CBOR_TYPE_MAP; +} + +bool cbor_isa_tag(const cbor_item_t *item) { + return item->type == CBOR_TYPE_TAG; +} + +bool cbor_isa_float_ctrl(const cbor_item_t *item) { + return item->type == CBOR_TYPE_FLOAT_CTRL; +} + +cbor_type cbor_typeof(const cbor_item_t *item) { return item->type; } + +bool cbor_is_int(const cbor_item_t *item) { + return cbor_isa_uint(item) || cbor_isa_negint(item); +} + +bool cbor_is_bool(const cbor_item_t *item) { + return cbor_isa_float_ctrl(item) && + (cbor_ctrl_value(item) == CBOR_CTRL_FALSE || + cbor_ctrl_value(item) == CBOR_CTRL_TRUE); +} + +bool cbor_is_null(const cbor_item_t *item) { + return cbor_isa_float_ctrl(item) && cbor_ctrl_value(item) == CBOR_CTRL_NULL; +} + +bool cbor_is_undef(const cbor_item_t *item) { + return cbor_isa_float_ctrl(item) && cbor_ctrl_value(item) == CBOR_CTRL_UNDEF; +} + +bool cbor_is_float(const cbor_item_t *item) { + return cbor_isa_float_ctrl(item) && !cbor_float_ctrl_is_ctrl(item); +} + +cbor_item_t *cbor_incref(cbor_item_t *item) { + item->refcount++; + return item; +} + +void cbor_decref(cbor_item_t **item_ref) { + cbor_item_t *item = *item_ref; + CBOR_ASSERT(item->refcount > 0); + if (--item->refcount == 0) { + switch (item->type) { + case CBOR_TYPE_UINT: + /* Fallthrough */ + case CBOR_TYPE_NEGINT: + /* Combined allocation, freeing the item suffices */ + { break; } + case CBOR_TYPE_BYTESTRING: { + if (cbor_bytestring_is_definite(item)) { + _cbor_free(item->data); + } else { + /* We need to decref all chunks */ + cbor_item_t **handle = cbor_bytestring_chunks_handle(item); + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) + cbor_decref(&handle[i]); + _cbor_free( + ((struct cbor_indefinite_string_data *)item->data)->chunks); + _cbor_free(item->data); + } + break; + } + case CBOR_TYPE_STRING: { + if (cbor_string_is_definite(item)) { + _cbor_free(item->data); + } else { + /* We need to decref all chunks */ + cbor_item_t **handle = cbor_string_chunks_handle(item); + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) + cbor_decref(&handle[i]); + _cbor_free( + ((struct cbor_indefinite_string_data *)item->data)->chunks); + _cbor_free(item->data); + } + break; + } + case CBOR_TYPE_ARRAY: { + /* Get all items and decref them */ + cbor_item_t **handle = cbor_array_handle(item); + size_t size = cbor_array_size(item); + for (size_t i = 0; i < size; i++) + if (handle[i] != NULL) cbor_decref(&handle[i]); + _cbor_free(item->data); + break; + } + case CBOR_TYPE_MAP: { + struct cbor_pair *handle = cbor_map_handle(item); + for (size_t i = 0; i < item->metadata.map_metadata.end_ptr; + i++, handle++) { + cbor_decref(&handle->key); + if (handle->value != NULL) cbor_decref(&handle->value); + } + _cbor_free(item->data); + break; + } + case CBOR_TYPE_TAG: { + if (item->metadata.tag_metadata.tagged_item != NULL) + cbor_decref(&item->metadata.tag_metadata.tagged_item); + _cbor_free(item->data); + break; + } + case CBOR_TYPE_FLOAT_CTRL: { + /* Floats have combined allocation */ + break; + } + } + _cbor_free(item); + *item_ref = NULL; + } +} + +void cbor_intermediate_decref(cbor_item_t *item) { cbor_decref(&item); } + +size_t cbor_refcount(const cbor_item_t *item) { return item->refcount; } + +cbor_item_t *cbor_move(cbor_item_t *item) { + item->refcount--; + return item; +} diff --git a/source/external/libcbor/cbor/common.h b/source/external/libcbor/cbor/common.h new file mode 100644 index 000000000..1d0b426cf --- /dev/null +++ b/source/external/libcbor/cbor/common.h @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_COMMON_H +#define LIBCBOR_COMMON_H + +#include +#include +#include +#include +#include + +#include "cbor/cbor_export.h" +#include "cbor/configuration.h" +#include "data.h" + +#ifdef __cplusplus +extern "C" { + +/** + * C99 is not a subset of C++ -- 'restrict' qualifier is not a part of the + * language. This is a workaround to keep it in C headers -- compilers allow + * linking non-restrict signatures with restrict implementations. + * + * If you know a nicer way, please do let me know. + */ +#define CBOR_RESTRICT_POINTER + +#else + +// MSVC + C++ workaround +#define CBOR_RESTRICT_POINTER CBOR_RESTRICT_SPECIFIER + +#endif + +static const uint8_t cbor_major_version = CBOR_MAJOR_VERSION; +static const uint8_t cbor_minor_version = CBOR_MINOR_VERSION; +static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION; + +#define CBOR_VERSION \ + _CBOR_TO_STR(CBOR_MAJOR_VERSION) \ + "." _CBOR_TO_STR(CBOR_MINOR_VERSION) "." _CBOR_TO_STR(CBOR_PATCH_VERSION) +#define CBOR_HEX_VERSION \ + ((CBOR_MAJOR_VERSION << 16) | (CBOR_MINOR_VERSION << 8) | CBOR_PATCH_VERSION) + +/* http://stackoverflow.com/questions/1644868/c-define-macro-for-debug-printing + */ +#ifdef DEBUG +#include +#define _cbor_debug_print(fmt, ...) \ + do { \ + if (DEBUG) \ + fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, \ + __VA_ARGS__); \ + } while (0) +extern bool _cbor_enable_assert; +// Like `assert`, but can be dynamically disabled in tests to allow testing +// invalid behaviors. +#define CBOR_ASSERT(e) assert(!_cbor_enable_assert || (e)) +#define _CBOR_TEST_DISABLE_ASSERT(block) \ + do { \ + _cbor_enable_assert = false; \ + block _cbor_enable_assert = true; \ + } while (0) +#else +#define debug_print(fmt, ...) \ + do { \ + } while (0) +#define CBOR_ASSERT(e) +#define _CBOR_TEST_DISABLE_ASSERT(block) \ + do { \ + block \ + } while (0) +#endif + +#define _CBOR_TO_STR_(x) #x +#define _CBOR_TO_STR(x) _CBOR_TO_STR_(x) /* enables proper double expansion */ + +#ifdef __GNUC__ +#define _CBOR_UNUSED(x) __attribute__((__unused__)) x +// TODO(https://github.com/PJK/libcbor/issues/247): Prefer [[nodiscard]] if +// available +#define _CBOR_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +#define _CBOR_UNUSED(x) __pragma(warning(suppress : 4100 4101)) x +#define _CBOR_NODISCARD +#else +#define _CBOR_UNUSED(x) x +#define _CBOR_NODISCARD +#endif + +typedef void *(*_cbor_malloc_t)(size_t); +typedef void *(*_cbor_realloc_t)(void *, size_t); +typedef void (*_cbor_free_t)(void *); + +CBOR_EXPORT extern _cbor_malloc_t _cbor_malloc; +CBOR_EXPORT extern _cbor_realloc_t _cbor_realloc; +CBOR_EXPORT extern _cbor_free_t _cbor_free; + +// Macro to short-circuit builder functions when memory allocation fails +#define _CBOR_NOTNULL(cbor_item) \ + do { \ + if (cbor_item == NULL) { \ + return NULL; \ + } \ + } while (0) + +// Macro to short-circuit builders when memory allocation of nested data fails +#define _CBOR_DEPENDENT_NOTNULL(cbor_item, pointer) \ + do { \ + if (pointer == NULL) { \ + _cbor_free(cbor_item); \ + return NULL; \ + } \ + } while (0) + +/** Sets the memory management routines to use. + * + * By default, libcbor will use the standard library `malloc`, `realloc`, and + * `free`. + * + * \rst + * .. warning:: This function modifies the global state and should therefore be + * used accordingly. Changing the memory handlers while allocated items exist + * will result in a ``free``/``malloc`` mismatch. This function is not thread + * safe with respect to both itself and all the other *libcbor* functions that + * work with the heap. + * + * .. note:: `realloc` implementation must correctly support `NULL` reallocation + * (see e.g. http://en.cppreference.com/w/c/memory/realloc) + * \endrst + * + * @param custom_malloc malloc implementation + * @param custom_realloc realloc implementation + * @param custom_free free implementation + */ +CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc, + _cbor_realloc_t custom_realloc, + _cbor_free_t custom_free); + +/* + * ============================================================================ + * Type manipulation + * ============================================================================ + */ + +/** Get the type of the item + * + * @param item + * @return The type + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_type cbor_typeof( + const cbor_item_t *item); /* Will be inlined iff link-time opt is enabled */ + +/* Standard CBOR Major item types */ + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item an #CBOR_TYPE_UINT? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item a #CBOR_TYPE_NEGINT? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item a #CBOR_TYPE_BYTESTRING? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item a #CBOR_TYPE_STRING? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_string(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item an #CBOR_TYPE_ARRAY? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_array(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item a #CBOR_TYPE_MAP? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_map(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item a #CBOR_TYPE_TAG? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t *item); + +/** Does the item have the appropriate major type? + * @param item the item + * @return Is the item a #CBOR_TYPE_FLOAT_CTRL? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t *item); + +/* Practical types with respect to their semantics (but not tag values) */ + +/** Is the item an integer, either positive or negative? + * @param item the item + * @return Is the item an integer, either positive or negative? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_is_int(const cbor_item_t *item); + +/** Is the item an a floating point number? + * @param item the item + * @return Is the item a floating point number? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_is_float(const cbor_item_t *item); + +/** Is the item an a boolean? + * @param item the item + * @return Is the item a boolean? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_is_bool(const cbor_item_t *item); + +/** Does this item represent `null` + * + * \rst + * .. warning:: This is in no way related to the value of the pointer. Passing a + * null pointer will most likely result in a crash. + * \endrst + * + * @param item the item + * @return Is the item (CBOR logical) null? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_is_null(const cbor_item_t *item); + +/** Does this item represent `undefined` + * + * \rst + * .. warning:: Care must be taken to distinguish nulls and undefined values in + * C. + * \endrst + * + * @param item the item + * @return Is the item (CBOR logical) undefined? + */ +_CBOR_NODISCARD +CBOR_EXPORT bool cbor_is_undef(const cbor_item_t *item); + +/* + * ============================================================================ + * Memory management + * ============================================================================ + */ + +/** Increases the item's reference count by one + * + * Constant complexity; items referring to this one or items being referred to + * are not updated. + * + * This function can be used to extend reference counting to client code. + * + * @param item Reference to an item + * @return The input \p item + */ +CBOR_EXPORT cbor_item_t *cbor_incref(cbor_item_t *item); + +/** Decreases the item's reference count by one, deallocating the item if needed + * + * In case the item is deallocated, the reference count of all items this item + * references will also be #cbor_decref 'ed recursively. + * + * @param item Reference to an item. Will be set to `NULL` if deallocated + */ +CBOR_EXPORT void cbor_decref(cbor_item_t **item); + +/** Decreases the item's reference count by one, deallocating the item if needed + * + * Convenience wrapper for #cbor_decref when its set-to-null behavior is not + * needed + * + * @param item Reference to an item + */ +CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t *item); + +/** Get the item's reference count + * + * \rst + * .. warning:: This does *not* account for transitive references. + * \endrst + * + * @todo Add some inline examples for reference counting + * + * @param item the item + * @return the reference count + */ +_CBOR_NODISCARD +CBOR_EXPORT size_t cbor_refcount(const cbor_item_t *item); + +/** Provides CPP-like move construct + * + * Decreases the reference count by one, but does not deallocate the item even + * if its refcount reaches zero. This is useful for passing intermediate values + * to functions that increase reference count. Should only be used with + * functions that `incref` their arguments. + * + * \rst + * .. warning:: If the item is moved without correctly increasing the reference + * count afterwards, the memory will be leaked. + * \endrst + * + * @param item Reference to an item + * @return the item with reference count decreased by one + */ +_CBOR_NODISCARD +CBOR_EXPORT cbor_item_t *cbor_move(cbor_item_t *item); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_COMMON_H diff --git a/source/external/libcbor/cbor/configuration.h b/source/external/libcbor/cbor/configuration.h new file mode 100644 index 000000000..83fe90bd3 --- /dev/null +++ b/source/external/libcbor/cbor/configuration.h @@ -0,0 +1,46 @@ +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/import_libcbor.py + * and any modifications should be in there. + */ + +#ifndef LIBCBOR_CONFIGURATION_H +#define LIBCBOR_CONFIGURATION_H + +#define CBOR_MAJOR_VERSION 0 +#define CBOR_MINOR_VERSION 11 +#define CBOR_PATCH_VERSION 0 + +#define CBOR_BUFFER_GROWTH 2 +#define CBOR_MAX_STACK_SIZE 2048 +#define CBOR_PRETTY_PRINTER 1 + +#if defined(_MSC_VER) +# define CBOR_RESTRICT_SPECIFIER +#else +# define CBOR_RESTRICT_SPECIFIER restrict +#endif + +#define CBOR_INLINE_SPECIFIER + +/* Ignore the compiler warnings for libcbor. */ +#ifdef _MSC_VER +# pragma warning(disable : 4028) +# pragma warning(disable : 4715) +# pragma warning(disable : 4232) +# pragma warning(disable : 4068) +# pragma warning(disable : 4244) +# pragma warning(disable : 4701) +# pragma warning(disable : 4703) +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wreturn-type" +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wreturn-type" +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#endif // LIBCBOR_CONFIGURATION_H diff --git a/source/external/libcbor/cbor/data.h b/source/external/libcbor/cbor/data.h new file mode 100644 index 000000000..a12e92f20 --- /dev/null +++ b/source/external/libcbor/cbor/data.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_DATA_H +#define LIBCBOR_DATA_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef const unsigned char* cbor_data; +typedef unsigned char* cbor_mutable_data; + +/** Specifies the Major type of ::cbor_item_t */ +typedef enum cbor_type { + CBOR_TYPE_UINT /** 0 - positive integers */ + , + CBOR_TYPE_NEGINT /** 1 - negative integers*/ + , + CBOR_TYPE_BYTESTRING /** 2 - byte strings */ + , + CBOR_TYPE_STRING /** 3 - strings */ + , + CBOR_TYPE_ARRAY /** 4 - arrays */ + , + CBOR_TYPE_MAP /** 5 - maps */ + , + CBOR_TYPE_TAG /** 6 - tags */ + , + CBOR_TYPE_FLOAT_CTRL /** 7 - decimals and special values (true, false, nil, + ...) */ +} cbor_type; + +/** Possible decoding errors */ +typedef enum { + CBOR_ERR_NONE, + CBOR_ERR_NOTENOUGHDATA, + CBOR_ERR_NODATA, + // TODO: Should be "malformed" or at least "malformatted". Retained for + // backwards compatibility. + CBOR_ERR_MALFORMATED, + CBOR_ERR_MEMERROR /** Memory error - item allocation failed. Is it too big for + your allocator? */ + , + CBOR_ERR_SYNTAXERROR /** Stack parsing algorithm failed */ +} cbor_error_code; + +/** Possible widths of #CBOR_TYPE_UINT items */ +typedef enum { + CBOR_INT_8, + CBOR_INT_16, + CBOR_INT_32, + CBOR_INT_64 +} cbor_int_width; + +/** Possible widths of #CBOR_TYPE_FLOAT_CTRL items */ +typedef enum { + CBOR_FLOAT_0 /** Internal use - ctrl and special values */ + , + CBOR_FLOAT_16 /** Half float */ + , + CBOR_FLOAT_32 /** Single float */ + , + CBOR_FLOAT_64 /** Double */ +} cbor_float_width; + +/** Metadata for dynamically sized types */ +typedef enum { + _CBOR_METADATA_DEFINITE, + _CBOR_METADATA_INDEFINITE +} _cbor_dst_metadata; + +/** Semantic mapping for CTRL simple values */ +typedef enum { + CBOR_CTRL_NONE = 0, + CBOR_CTRL_FALSE = 20, + CBOR_CTRL_TRUE = 21, + CBOR_CTRL_NULL = 22, + CBOR_CTRL_UNDEF = 23 +} _cbor_ctrl; + +// Metadata items use size_t (instead of uint64_t) because items in memory take +// up at least 1B per entry or string byte, so if size_t is narrower than +// uint64_t, we wouldn't be able to create them in the first place and can save +// some space. + +/** Integers specific metadata */ +struct _cbor_int_metadata { + cbor_int_width width; +}; + +/** Bytestrings specific metadata */ +struct _cbor_bytestring_metadata { + size_t length; + _cbor_dst_metadata type; +}; + +/** Strings specific metadata */ +struct _cbor_string_metadata { + size_t length; + size_t codepoint_count; /* Sum of chunks' codepoint_counts for indefinite + strings */ + _cbor_dst_metadata type; +}; + +/** Arrays specific metadata */ +struct _cbor_array_metadata { + size_t allocated; + size_t end_ptr; + _cbor_dst_metadata type; +}; + +/** Maps specific metadata */ +struct _cbor_map_metadata { + size_t allocated; + size_t end_ptr; + _cbor_dst_metadata type; +}; + +/** Arrays specific metadata + * + * The pointer is included - cbor_item_metadata is + * 2 * sizeof(size_t) + sizeof(_cbor_string_type_metadata), + * lets use the space + */ +struct _cbor_tag_metadata { + struct cbor_item_t* tagged_item; + uint64_t value; +}; + +/** Floats specific metadata - includes CTRL values */ +struct _cbor_float_ctrl_metadata { + cbor_float_width width; + uint8_t ctrl; +}; + +/** Raw memory casts helper */ +union _cbor_float_helper { + float as_float; + uint32_t as_uint; +}; + +/** Raw memory casts helper */ +union _cbor_double_helper { + double as_double; + uint64_t as_uint; +}; + +/** Union of metadata across all possible types - discriminated in #cbor_item_t + */ +union cbor_item_metadata { + struct _cbor_int_metadata int_metadata; + struct _cbor_bytestring_metadata bytestring_metadata; + struct _cbor_string_metadata string_metadata; + struct _cbor_array_metadata array_metadata; + struct _cbor_map_metadata map_metadata; + struct _cbor_tag_metadata tag_metadata; + struct _cbor_float_ctrl_metadata float_ctrl_metadata; +}; + +/** The item handle */ +typedef struct cbor_item_t { + /** Discriminated by type */ + union cbor_item_metadata metadata; + /** Reference count - initialize to 0 */ + size_t refcount; + /** Major type discriminator */ + cbor_type type; + /** Raw data block - interpretation depends on metadata */ + unsigned char* data; +} cbor_item_t; + +/** Defines cbor_item_t#data structure for indefinite strings and bytestrings + * + * Used to cast the raw representation for a sane manipulation + */ +struct cbor_indefinite_string_data { + size_t chunk_count; + size_t chunk_capacity; + cbor_item_t** chunks; +}; + +/** High-level decoding error */ +struct cbor_error { + /** Approximate position */ + size_t position; + /** Description */ + cbor_error_code code; +}; + +/** Simple pair of items for use in maps */ +struct cbor_pair { + cbor_item_t *key, *value; +}; + +/** High-level decoding result */ +struct cbor_load_result { + /** Error indicator */ + struct cbor_error error; + /** Number of bytes read */ + size_t read; +}; + +/** Streaming decoder result - status */ +enum cbor_decoder_status { + /** Decoding finished successfully (a callback has been invoked) + * + * Note that this does *not* mean that the buffer has been fully decoded; + * there may still be unread bytes for which no callback has been involved. + */ + CBOR_DECODER_FINISHED, + /** Not enough data to invoke a callback */ + // TODO: The name is inconsistent with CBOR_ERR_NOTENOUGHDATA. Retained for + // backwards compatibility. + CBOR_DECODER_NEDATA, + /** Bad data (reserved MTB, malformed value, etc.) */ + CBOR_DECODER_ERROR +}; + +/** Streaming decoder result */ +struct cbor_decoder_result { + /** Input bytes read/consumed + * + * If this is less than the size of input buffer, the client will likely + * resume parsing starting at the next byte (e.g. `buffer + result.read`). + * + * Set to 0 if the #status is not #CBOR_DECODER_FINISHED. + */ + size_t read; + + /** The decoding status */ + enum cbor_decoder_status status; + + /** Number of bytes in the input buffer needed to resume parsing + * + * Set to 0 unless the result status is #CBOR_DECODER_NEDATA. If it is, then: + * - If at least one byte was passed, #required will be set to the minimum + * number of bytes needed to invoke a decoded callback on the current + * prefix. + * + * For example: Attempting to decode a 1B buffer containing `0x19` will + * set #required to 3 as `0x19` signals a 2B integer item, so we need at + * least 3B to continue (the `0x19` MTB byte and two bytes of data needed + * to invoke #cbor_callbacks.uint16). + * + * - If there was no data at all, #read will always be set to 1 + */ + size_t required; +}; + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_DATA_H diff --git a/source/external/libcbor/cbor/encoding.c b/source/external/libcbor/cbor/encoding.c new file mode 100644 index 000000000..9d931d175 --- /dev/null +++ b/source/external/libcbor/cbor/encoding.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "encoding.h" +#include "internal/encoders.h" + +size_t cbor_encode_uint8(uint8_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint8(value, buffer, buffer_size, 0x00); +} + +size_t cbor_encode_uint16(uint16_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint16(value, buffer, buffer_size, 0x00); +} + +size_t cbor_encode_uint32(uint32_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint32(value, buffer, buffer_size, 0x00); +} + +size_t cbor_encode_uint64(uint64_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint64(value, buffer, buffer_size, 0x00); +} + +size_t cbor_encode_uint(uint64_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint(value, buffer, buffer_size, 0x00); +} + +size_t cbor_encode_negint8(uint8_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint8(value, buffer, buffer_size, 0x20); +} + +size_t cbor_encode_negint16(uint16_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint16(value, buffer, buffer_size, 0x20); +} + +size_t cbor_encode_negint32(uint32_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint32(value, buffer, buffer_size, 0x20); +} + +size_t cbor_encode_negint64(uint64_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint64(value, buffer, buffer_size, 0x20); +} + +size_t cbor_encode_negint(uint64_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint(value, buffer, buffer_size, 0x20); +} + +size_t cbor_encode_bytestring_start(size_t length, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x40); +} + +size_t _cbor_encode_byte(uint8_t value, unsigned char *buffer, + size_t buffer_size) { + if (buffer_size >= 1) { + buffer[0] = value; + return 1; + } else + return 0; +} + +size_t cbor_encode_indef_bytestring_start(unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_byte(0x5F, buffer, buffer_size); +} + +size_t cbor_encode_string_start(size_t length, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x60); +} + +size_t cbor_encode_indef_string_start(unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_byte(0x7F, buffer, buffer_size); +} + +size_t cbor_encode_array_start(size_t length, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0x80); +} + +size_t cbor_encode_indef_array_start(unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_byte(0x9F, buffer, buffer_size); +} + +size_t cbor_encode_map_start(size_t length, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint((size_t)length, buffer, buffer_size, 0xA0); +} + +size_t cbor_encode_indef_map_start(unsigned char *buffer, size_t buffer_size) { + return _cbor_encode_byte(0xBF, buffer, buffer_size); +} + +size_t cbor_encode_tag(uint64_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint(value, buffer, buffer_size, 0xC0); +} + +size_t cbor_encode_bool(bool value, unsigned char *buffer, size_t buffer_size) { + return value ? _cbor_encode_byte(0xF5, buffer, buffer_size) + : _cbor_encode_byte(0xF4, buffer, buffer_size); +} + +size_t cbor_encode_null(unsigned char *buffer, size_t buffer_size) { + return _cbor_encode_byte(0xF6, buffer, buffer_size); +} + +size_t cbor_encode_undef(unsigned char *buffer, size_t buffer_size) { + return _cbor_encode_byte(0xF7, buffer, buffer_size); +} + +size_t cbor_encode_half(float value, unsigned char *buffer, + size_t buffer_size) { + /* Assuming value is normalized */ + uint32_t val = ((union _cbor_float_helper){.as_float = value}).as_uint; + uint16_t res; + uint8_t exp = (uint8_t)((val & 0x7F800000u) >> + 23u); /* 0b0111_1111_1000_0000_0000_0000_0000_0000 */ + uint32_t mant = + val & 0x7FFFFFu; /* 0b0000_0000_0111_1111_1111_1111_1111_1111 */ + if (exp == 0xFF) { /* Infinity or NaNs */ + if (value != value) { + // We discard information bits in half-float NaNs. This is + // not required for the core CBOR protocol (it is only a suggestion in + // Section 3.9). + // See https://github.com/PJK/libcbor/issues/215 + res = (uint16_t)0x007e00; + } else { + // If the mantissa is non-zero, we have a NaN, but those are handled + // above. See + // https://en.wikipedia.org/wiki/Half-precision_floating-point_format + CBOR_ASSERT(mant == 0u); + res = (uint16_t)((val & 0x80000000u) >> 16u | 0x7C00u); + } + } else if (exp == 0x00) { /* Zeroes or subnorms */ + res = (uint16_t)((val & 0x80000000u) >> 16u | mant >> 13u); + } else { /* Normal numbers */ + int8_t logical_exp = (int8_t)(exp - 127); + CBOR_ASSERT(logical_exp == exp - 127); + + // Now we know that 2^exp <= 0 logically + if (logical_exp < -24) { + /* No unambiguous representation exists, this float is not a half float + and is too small to be represented using a half, round off to zero. + Consistent with the reference implementation. */ + res = 0; + } else if (logical_exp < -14) { + /* Offset the remaining decimal places by shifting the significand, the + value is lost. This is an implementation decision that works around the + absence of standard half-float in the language. */ + res = (uint16_t)((val & 0x80000000u) >> 16u) | // Extract sign bit + ((uint16_t)(1u << (24u + logical_exp)) + + (uint16_t)(((mant >> (-logical_exp - 2)) + 1) >> + 1)); // Round half away from zero for simplicity + } else { + res = (uint16_t)((val & 0x80000000u) >> 16u | + ((((uint8_t)logical_exp) + 15u) << 10u) | + (uint16_t)(mant >> 13u)); + } + } + return _cbor_encode_uint16(res, buffer, buffer_size, 0xE0); +} + +size_t cbor_encode_single(float value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint32( + ((union _cbor_float_helper){.as_float = value}).as_uint, buffer, + buffer_size, 0xE0); +} + +size_t cbor_encode_double(double value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint64( + ((union _cbor_double_helper){.as_double = value}).as_uint, buffer, + buffer_size, 0xE0); +} + +size_t cbor_encode_break(unsigned char *buffer, size_t buffer_size) { + return _cbor_encode_byte(0xFF, buffer, buffer_size); +} + +size_t cbor_encode_ctrl(uint8_t value, unsigned char *buffer, + size_t buffer_size) { + return _cbor_encode_uint8(value, buffer, buffer_size, 0xE0); +} diff --git a/source/external/libcbor/cbor/encoding.h b/source/external/libcbor/cbor/encoding.h new file mode 100644 index 000000000..bcc04f8a9 --- /dev/null +++ b/source/external/libcbor/cbor/encoding.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_ENCODING_H +#define LIBCBOR_ENCODING_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All cbor_encode_* methods take 2 or 3 arguments: + * - a logical `value` to encode (except for trivial items such as NULLs) + * - an output `buffer` pointer + * - a `buffer_size` specification + * + * They serialize the `value` into one or more bytes and write the bytes to the + * output `buffer` and return either the number of bytes written, or 0 if the + * `buffer_size` was too small to small to fit the serialized value (in which + * case it is not modified). + */ + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint8(uint8_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint16(uint16_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint32(uint32_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint64(uint64_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint(uint64_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint8(uint8_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint16(uint16_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint32(uint32_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint64(uint64_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint(uint64_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_bytestring_start(size_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t +cbor_encode_indef_bytestring_start(unsigned char *, size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_string_start(size_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t +cbor_encode_indef_string_start(unsigned char *, size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_array_start(size_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t +cbor_encode_indef_array_start(unsigned char *, size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_map_start(size_t, + unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_indef_map_start(unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_tag(uint64_t, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_bool(bool, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_null(unsigned char *, size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_undef(unsigned char *, size_t); + +/** Encodes a half-precision float + * + * Since there is no native representation or semantics for half floats + * in the language, we use single-precision floats, as every value that + * can be expressed as a half-float can also be expressed as a float. + * + * This however means that not all floats passed to this function can be + * unambiguously encoded. The behavior is as follows: + * - Infinity, NaN are preserved + * - Zero is preserved + * - Denormalized numbers keep their sign bit and 10 most significant bit of + * the significand + * - All other numbers + * - If the logical value of the exponent is < -24, the output is zero + * - If the logical value of the exponent is between -23 and -14, the output + * is cut off to represent the 'magnitude' of the input, by which we + * mean (-1)^{signbit} x 1.0e{exponent}. The value in the significand is + * lost. + * - In all other cases, the sign bit, the exponent, and 10 most significant + * bits of the significand are kept + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_half(float, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_single(float, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_double(double, unsigned char *, + size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_break(unsigned char *, size_t); + +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_ctrl(uint8_t, unsigned char *, + size_t); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_ENCODING_H diff --git a/source/external/libcbor/cbor/floats_ctrls.c b/source/external/libcbor/cbor/floats_ctrls.c new file mode 100644 index 000000000..57bf477d4 --- /dev/null +++ b/source/external/libcbor/cbor/floats_ctrls.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "floats_ctrls.h" +#include +#include "assert.h" + +cbor_float_width cbor_float_get_width(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + return item->metadata.float_ctrl_metadata.width; +} + +uint8_t cbor_ctrl_value(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_0); + return item->metadata.float_ctrl_metadata.ctrl; +} + +bool cbor_float_ctrl_is_ctrl(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + return cbor_float_get_width(item) == CBOR_FLOAT_0; +} + +float cbor_float_get_float2(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_16); + return *(float *)item->data; +} + +float cbor_float_get_float4(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_32); + return *(float *)item->data; +} + +double cbor_float_get_float8(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_64); + return *(double *)item->data; +} + +double cbor_float_get_float(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_float(item)); + // cppcheck-suppress missingReturn + switch (cbor_float_get_width(item)) { + case CBOR_FLOAT_0: + return NAN; + case CBOR_FLOAT_16: + return cbor_float_get_float2(item); + case CBOR_FLOAT_32: + return cbor_float_get_float4(item); + case CBOR_FLOAT_64: + return cbor_float_get_float8(item); + } +} + +bool cbor_get_bool(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_bool(item)); + return item->metadata.float_ctrl_metadata.ctrl == CBOR_CTRL_TRUE; +} + +void cbor_set_float2(cbor_item_t *item, float value) { + CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_16); + *((float *)item->data) = value; +} + +void cbor_set_float4(cbor_item_t *item, float value) { + CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_32); + *((float *)item->data) = value; +} + +void cbor_set_float8(cbor_item_t *item, double value) { + CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_64); + *((double *)item->data) = value; +} + +void cbor_set_ctrl(cbor_item_t *item, uint8_t value) { + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_0); + item->metadata.float_ctrl_metadata.ctrl = value; +} + +void cbor_set_bool(cbor_item_t *item, bool value) { + CBOR_ASSERT(cbor_is_bool(item)); + item->metadata.float_ctrl_metadata.ctrl = + value ? CBOR_CTRL_TRUE : CBOR_CTRL_FALSE; +} + +cbor_item_t *cbor_new_ctrl(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .type = CBOR_TYPE_FLOAT_CTRL, + .data = NULL, + .refcount = 1, + .metadata = {.float_ctrl_metadata = {.width = CBOR_FLOAT_0, + .ctrl = CBOR_CTRL_NONE}}}; + return item; +} + +cbor_item_t *cbor_new_float2(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .type = CBOR_TYPE_FLOAT_CTRL, + .data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.float_ctrl_metadata = {.width = CBOR_FLOAT_16}}}; + return item; +} + +cbor_item_t *cbor_new_float4(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .type = CBOR_TYPE_FLOAT_CTRL, + .data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.float_ctrl_metadata = {.width = CBOR_FLOAT_32}}}; + return item; +} + +cbor_item_t *cbor_new_float8(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 8); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .type = CBOR_TYPE_FLOAT_CTRL, + .data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.float_ctrl_metadata = {.width = CBOR_FLOAT_64}}}; + return item; +} + +cbor_item_t *cbor_new_null(void) { + cbor_item_t *item = cbor_new_ctrl(); + _CBOR_NOTNULL(item); + cbor_set_ctrl(item, CBOR_CTRL_NULL); + return item; +} + +cbor_item_t *cbor_new_undef(void) { + cbor_item_t *item = cbor_new_ctrl(); + _CBOR_NOTNULL(item); + cbor_set_ctrl(item, CBOR_CTRL_UNDEF); + return item; +} + +cbor_item_t *cbor_build_bool(bool value) { + return cbor_build_ctrl(value ? CBOR_CTRL_TRUE : CBOR_CTRL_FALSE); +} + +cbor_item_t *cbor_build_float2(float value) { + cbor_item_t *item = cbor_new_float2(); + _CBOR_NOTNULL(item); + cbor_set_float2(item, value); + return item; +} + +cbor_item_t *cbor_build_float4(float value) { + cbor_item_t *item = cbor_new_float4(); + _CBOR_NOTNULL(item); + cbor_set_float4(item, value); + return item; +} + +cbor_item_t *cbor_build_float8(double value) { + cbor_item_t *item = cbor_new_float8(); + _CBOR_NOTNULL(item); + cbor_set_float8(item, value); + return item; +} + +cbor_item_t *cbor_build_ctrl(uint8_t value) { + cbor_item_t *item = cbor_new_ctrl(); + _CBOR_NOTNULL(item); + cbor_set_ctrl(item, value); + return item; +} diff --git a/source/external/libcbor/cbor/floats_ctrls.h b/source/external/libcbor/cbor/floats_ctrls.h new file mode 100644 index 000000000..335eab832 --- /dev/null +++ b/source/external/libcbor/cbor/floats_ctrls.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_FLOATS_CTRLS_H +#define LIBCBOR_FLOATS_CTRLS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * Float manipulation + * ============================================================================ + */ + +/** Is this a ctrl value? + * + * @param item A float or ctrl item + * @return Is this a ctrl value? + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_float_ctrl_is_ctrl( + const cbor_item_t *item); + +/** Get the float width + * + * @param item A float or ctrl item + * @return The width. + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_float_width +cbor_float_get_width(const cbor_item_t *item); + +/** Get a half precision float + * + * The item must have the corresponding width + * + * @param item A half precision float + * @return half precision value + */ +_CBOR_NODISCARD CBOR_EXPORT float cbor_float_get_float2( + const cbor_item_t *item); + +/** Get a single precision float + * + * The item must have the corresponding width + * + * @param item A single precision float + * @return single precision value + */ +_CBOR_NODISCARD CBOR_EXPORT float cbor_float_get_float4( + const cbor_item_t *item); + +/** Get a double precision float + * + * The item must have the corresponding width + * + * @param item A double precision float + * @return double precision value + */ +_CBOR_NODISCARD CBOR_EXPORT double cbor_float_get_float8( + const cbor_item_t *item); + +/** Get the float value represented as double + * + * Can be used regardless of the width. + * + * @param item Any float + * @return double precision value + */ +_CBOR_NODISCARD CBOR_EXPORT double cbor_float_get_float( + const cbor_item_t *item); + +/** Get value from a boolean ctrl item + * + * @param item A ctrl item + * @return boolean value + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_get_bool(const cbor_item_t *item); + +/** Constructs a new ctrl item + * + * The width cannot be changed once the item is created + * + * @return Reference to the new ctrl item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_ctrl(void); + +/** Constructs a new float item + * + * The width cannot be changed once the item is created + * + * @return Reference to the new float item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_float2(void); + +/** Constructs a new float item + * + * The width cannot be changed once the item is created + * + * @return Reference to the new float item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_float4(void); + +/** Constructs a new float item + * + * The width cannot be changed once the item is created + * + * @return Reference to the new float item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_float8(void); + +/** Constructs new null ctrl item + * + * @return Reference to the new null item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_null(void); + +/** Constructs new undef ctrl item + * + * @return Reference to the new undef item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_undef(void); + +/** Constructs new boolean ctrl item + * + * @param value The value to use + * @return Reference to the new boolean item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_bool(bool value); + +/** Assign a control value + * + * \rst + * .. warning:: It is possible to produce an invalid CBOR value by assigning a + * invalid value using this mechanism. Please consult the standard before use. + * \endrst + * + * @param item A ctrl item + * @param value The simple value to assign. Please consult the standard for + * allowed values + */ +CBOR_EXPORT void cbor_set_ctrl(cbor_item_t *item, uint8_t value); + +/** Assign a boolean value to a boolean ctrl item + * + * @param item A ctrl item + * @param value The simple value to assign. + */ +CBOR_EXPORT void cbor_set_bool(cbor_item_t *item, bool value); + +/** Assigns a float value + * + * @param item A half precision float + * @param value The value to assign + */ +CBOR_EXPORT void cbor_set_float2(cbor_item_t *item, float value); + +/** Assigns a float value + * + * @param item A single precision float + * @param value The value to assign + */ +CBOR_EXPORT void cbor_set_float4(cbor_item_t *item, float value); + +/** Assigns a float value + * + * @param item A double precision float + * @param value The value to assign + */ +CBOR_EXPORT void cbor_set_float8(cbor_item_t *item, double value); + +/** Reads the control value + * + * @param item A ctrl item + * @return the simple value + */ +_CBOR_NODISCARD CBOR_EXPORT uint8_t cbor_ctrl_value(const cbor_item_t *item); + +/** Constructs a new float + * + * @param value the value to use + * @return Reference to the new float item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_float2(float value); + +/** Constructs a new float + * + * @param value the value to use + * @return Reference to the new float item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_float4(float value); + +/** Constructs a new float + * + * @param value the value to use + * @return Reference to the new float item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_float8(double value); + +/** Constructs a ctrl item + * + * @param value the value to use + * @return Reference to the new ctrl item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_ctrl(uint8_t value); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_FLOATS_CTRLS_H diff --git a/source/external/libcbor/cbor/internal/builder_callbacks.c b/source/external/libcbor/cbor/internal/builder_callbacks.c new file mode 100644 index 000000000..257cef3ad --- /dev/null +++ b/source/external/libcbor/cbor/internal/builder_callbacks.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * 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 + +#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. + ctx->creation_failed = + !_cbor_map_add_value(ctx->stack->top->item, item); + // Adding a value never fails since the memory is allocated when the + // key is added + CBOR_ASSERT(!ctx->creation_failed); + } 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); + + 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); + + // 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: + // Should never happen since a non-nested item cannot be on top of the + // stack. + 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); +} diff --git a/source/external/libcbor/cbor/internal/builder_callbacks.h b/source/external/libcbor/cbor/internal/builder_callbacks.h new file mode 100644 index 000000000..7893960e4 --- /dev/null +++ b/source/external/libcbor/cbor/internal/builder_callbacks.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_BUILDER_CALLBACKS_H +#define LIBCBOR_BUILDER_CALLBACKS_H + +#include "../callbacks.h" +#include "cbor/common.h" +#include "stack.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** High-level decoding context */ +struct _cbor_decoder_context { + /** Callback creating the last item has failed */ + bool creation_failed; + /** Stack expectation mismatch */ + bool syntax_error; + cbor_item_t *root; + struct _cbor_stack *stack; +}; + +/** Internal helper: Append item to the top of the stack while handling errors. + */ +void _cbor_builder_append(cbor_item_t *item, struct _cbor_decoder_context *ctx); + +void cbor_builder_uint8_callback(void *, uint8_t); + +void cbor_builder_uint16_callback(void *, uint16_t); + +void cbor_builder_uint32_callback(void *, uint32_t); + +void cbor_builder_uint64_callback(void *, uint64_t); + +void cbor_builder_negint8_callback(void *, uint8_t); + +void cbor_builder_negint16_callback(void *, uint16_t); + +void cbor_builder_negint32_callback(void *, uint32_t); + +void cbor_builder_negint64_callback(void *, uint64_t); + +void cbor_builder_string_callback(void *, cbor_data, uint64_t); + +void cbor_builder_string_start_callback(void *); + +void cbor_builder_byte_string_callback(void *, cbor_data, uint64_t); + +void cbor_builder_byte_string_start_callback(void *); + +void cbor_builder_array_start_callback(void *, uint64_t); + +void cbor_builder_indef_array_start_callback(void *); + +void cbor_builder_map_start_callback(void *, uint64_t); + +void cbor_builder_indef_map_start_callback(void *); + +void cbor_builder_tag_callback(void *, uint64_t); + +void cbor_builder_float2_callback(void *, float); + +void cbor_builder_float4_callback(void *, float); + +void cbor_builder_float8_callback(void *, double); + +void cbor_builder_null_callback(void *); + +void cbor_builder_undefined_callback(void *); + +void cbor_builder_boolean_callback(void *, bool); + +void cbor_builder_indef_break_callback(void *); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_BUILDER_CALLBACKS_H diff --git a/source/external/libcbor/cbor/internal/encoders.c b/source/external/libcbor/cbor/internal/encoders.c new file mode 100644 index 000000000..49d4d7f33 --- /dev/null +++ b/source/external/libcbor/cbor/internal/encoders.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "encoders.h" +#include + +size_t _cbor_encode_uint8(uint8_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset) { + if (value <= 23) { + if (buffer_size >= 1) { + buffer[0] = value + offset; + return 1; + } + } else { + if (buffer_size >= 2) { + buffer[0] = 0x18 + offset; + buffer[1] = value; + return 2; + } + } + return 0; +} + +size_t _cbor_encode_uint16(uint16_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset) { + if (buffer_size >= 3) { + buffer[0] = 0x19 + offset; + +#ifdef IS_BIG_ENDIAN + memcpy(buffer + 1, &value, 2); +#else + buffer[1] = (unsigned char)(value >> 8); + buffer[2] = (unsigned char)value; +#endif + + return 3; + } else + return 0; +} + +size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset) { + if (buffer_size >= 5) { + buffer[0] = 0x1A + offset; + +#ifdef IS_BIG_ENDIAN + memcpy(buffer + 1, &value, 4); +#else + buffer[1] = (unsigned char)(value >> 24); + buffer[2] = (unsigned char)(value >> 16); + buffer[3] = (unsigned char)(value >> 8); + buffer[4] = (unsigned char)value; +#endif + + return 5; + } else + return 0; +} + +size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset) { + if (buffer_size >= 9) { + buffer[0] = 0x1B + offset; + +#ifdef IS_BIG_ENDIAN + memcpy(buffer + 1, &value, 8); +#else + buffer[1] = (unsigned char)(value >> 56); + buffer[2] = (unsigned char)(value >> 48); + buffer[3] = (unsigned char)(value >> 40); + buffer[4] = (unsigned char)(value >> 32); + buffer[5] = (unsigned char)(value >> 24); + buffer[6] = (unsigned char)(value >> 16); + buffer[7] = (unsigned char)(value >> 8); + buffer[8] = (unsigned char)value; +#endif + + return 9; + } else + return 0; +} + +size_t _cbor_encode_uint(uint64_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset) { + if (value <= UINT16_MAX) + if (value <= UINT8_MAX) + return _cbor_encode_uint8((uint8_t)value, buffer, buffer_size, offset); + else + return _cbor_encode_uint16((uint16_t)value, buffer, buffer_size, offset); + else if (value <= UINT32_MAX) + return _cbor_encode_uint32((uint32_t)value, buffer, buffer_size, offset); + else + return _cbor_encode_uint64((uint64_t)value, buffer, buffer_size, offset); +} diff --git a/source/external/libcbor/cbor/internal/encoders.h b/source/external/libcbor/cbor/internal/encoders.h new file mode 100644 index 000000000..7eadb7121 --- /dev/null +++ b/source/external/libcbor/cbor/internal/encoders.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_ENCODERS_H +#define LIBCBOR_ENCODERS_H + +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +_CBOR_NODISCARD +size_t _cbor_encode_uint8(uint8_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset); + +_CBOR_NODISCARD +size_t _cbor_encode_uint16(uint16_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset); + +_CBOR_NODISCARD +size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset); + +_CBOR_NODISCARD +size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset); + +_CBOR_NODISCARD +size_t _cbor_encode_uint(uint64_t value, unsigned char *buffer, + size_t buffer_size, uint8_t offset); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_ENCODERS_H diff --git a/source/external/libcbor/cbor/internal/loaders.c b/source/external/libcbor/cbor/internal/loaders.c new file mode 100644 index 000000000..cfa173de7 --- /dev/null +++ b/source/external/libcbor/cbor/internal/loaders.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "loaders.h" +#include +#include + +uint8_t _cbor_load_uint8(cbor_data source) { return (uint8_t)*source; } + +uint16_t _cbor_load_uint16(const unsigned char *source) { +#ifdef IS_BIG_ENDIAN + uint16_t result; + memcpy(&result, source, 2); + return result; +#else + return ((uint16_t) * (source + 0) << 8) + (uint8_t) * (source + 1); +#endif +} + +uint32_t _cbor_load_uint32(const unsigned char *source) { +#ifdef IS_BIG_ENDIAN + uint32_t result; + memcpy(&result, source, 4); + return result; +#else + return ((uint32_t) * (source + 0) << 0x18) + + ((uint32_t) * (source + 1) << 0x10) + + ((uint16_t) * (source + 2) << 0x08) + (uint8_t) * (source + 3); +#endif +} + +uint64_t _cbor_load_uint64(const unsigned char *source) { +#ifdef IS_BIG_ENDIAN + uint64_t result; + memcpy(&result, source, 8); + return result; +#else + return ((uint64_t) * (source + 0) << 0x38) + + ((uint64_t) * (source + 1) << 0x30) + + ((uint64_t) * (source + 2) << 0x28) + + ((uint64_t) * (source + 3) << 0x20) + + ((uint32_t) * (source + 4) << 0x18) + + ((uint32_t) * (source + 5) << 0x10) + + ((uint16_t) * (source + 6) << 0x08) + (uint8_t) * (source + 7); +#endif +} + +/* As per https://www.rfc-editor.org/rfc/rfc8949.html#name-half-precision */ +float _cbor_decode_half(unsigned char *halfp) { + int half = (halfp[0] << 8) + halfp[1]; + int exp = (half >> 10) & 0x1f; + int mant = half & 0x3ff; + double val; + if (exp == 0) + val = ldexp(mant, -24); + else if (exp != 31) + val = ldexp(mant + 1024, exp - 25); + else + val = mant == 0 ? INFINITY : NAN; + return (float)(half & 0x8000 ? -val : val); +} + +float _cbor_load_half(cbor_data source) { + /* Discard const */ + return _cbor_decode_half((unsigned char *)source); +} + +float _cbor_load_float(cbor_data source) { + union _cbor_float_helper helper = {.as_uint = _cbor_load_uint32(source)}; + return helper.as_float; +} + +double _cbor_load_double(cbor_data source) { + union _cbor_double_helper helper = {.as_uint = _cbor_load_uint64(source)}; + return helper.as_double; +} diff --git a/source/external/libcbor/cbor/internal/loaders.h b/source/external/libcbor/cbor/internal/loaders.h new file mode 100644 index 000000000..ce37563a3 --- /dev/null +++ b/source/external/libcbor/cbor/internal/loaders.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_LOADERS_H +#define LIBCBOR_LOADERS_H + +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Read the given uint from the given location, no questions asked */ +_CBOR_NODISCARD +uint8_t _cbor_load_uint8(const unsigned char *source); + +_CBOR_NODISCARD +uint16_t _cbor_load_uint16(const unsigned char *source); + +_CBOR_NODISCARD +uint32_t _cbor_load_uint32(const unsigned char *source); + +_CBOR_NODISCARD +uint64_t _cbor_load_uint64(const unsigned char *source); + +_CBOR_NODISCARD +float _cbor_load_half(cbor_data source); + +_CBOR_NODISCARD +float _cbor_load_float(cbor_data source); + +_CBOR_NODISCARD +double _cbor_load_double(cbor_data source); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_LOADERS_H diff --git a/source/external/libcbor/cbor/internal/memory_utils.c b/source/external/libcbor/cbor/internal/memory_utils.c new file mode 100644 index 000000000..bbea63cb9 --- /dev/null +++ b/source/external/libcbor/cbor/internal/memory_utils.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "memory_utils.h" +#include "cbor/common.h" + +// TODO: Consider builtins +// (https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html) + +/** Highest on bit position */ +size_t _cbor_highest_bit(size_t number) { + size_t bit = 0; + while (number != 0) { + bit++; + number >>= 1; + } + + return bit; +} + +bool _cbor_safe_to_multiply(size_t a, size_t b) { + if (a <= 1 || b <= 1) return true; + return _cbor_highest_bit(a) + _cbor_highest_bit(b) <= sizeof(size_t) * 8; +} + +bool _cbor_safe_to_add(size_t a, size_t b) { + // Unsigned integer overflow doesn't constitute UB + size_t sum = a + b; + return sum >= a && sum >= b; +} + +size_t _cbor_safe_signaling_add(size_t a, size_t b) { + if (a == 0 || b == 0) return 0; + if (_cbor_safe_to_add(a, b)) return a + b; + return 0; +} + +void* _cbor_alloc_multiple(size_t item_size, size_t item_count) { + if (_cbor_safe_to_multiply(item_size, item_count)) { + return _cbor_malloc(item_size * item_count); + } else { + return NULL; + } +} + +void* _cbor_realloc_multiple(void* pointer, size_t item_size, + size_t item_count) { + if (_cbor_safe_to_multiply(item_size, item_count)) { + return _cbor_realloc(pointer, item_size * item_count); + } else { + return NULL; + } +} diff --git a/source/external/libcbor/cbor/internal/memory_utils.h b/source/external/libcbor/cbor/internal/memory_utils.h new file mode 100644 index 000000000..696f67800 --- /dev/null +++ b/source/external/libcbor/cbor/internal/memory_utils.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_MEMORY_UTILS_H +#define LIBCBOR_MEMORY_UTILS_H + +#include +#include + +#include "cbor/common.h" + +/** Can `a` and `b` be multiplied without overflowing size_t? */ +_CBOR_NODISCARD +bool _cbor_safe_to_multiply(size_t a, size_t b); + +/** Can `a` and `b` be added without overflowing size_t? */ +_CBOR_NODISCARD +bool _cbor_safe_to_add(size_t a, size_t b); + +/** Adds `a` and `b`, propagating zeros and returning 0 on overflow. */ +_CBOR_NODISCARD +size_t _cbor_safe_signaling_add(size_t a, size_t b); + +/** Overflow-proof contiguous array allocation + * + * @param item_size + * @param item_count + * @return Region of item_size * item_count bytes, or NULL if the total size + * overflows size_t or the underlying allocator failed + */ +void* _cbor_alloc_multiple(size_t item_size, size_t item_count); + +/** Overflow-proof contiguous array reallocation + * + * This implements the OpenBSD `reallocarray` functionality. + * + * @param pointer + * @param item_size + * @param item_count + * @return Realloc'd of item_size * item_count bytes, or NULL if the total size + * overflows size_t or the underlying allocator failed + */ +void* _cbor_realloc_multiple(void* pointer, size_t item_size, + size_t item_count); + +#endif // LIBCBOR_MEMORY_UTILS_H diff --git a/source/external/libcbor/cbor/internal/stack.c b/source/external/libcbor/cbor/internal/stack.c new file mode 100644 index 000000000..2db03cbbf --- /dev/null +++ b/source/external/libcbor/cbor/internal/stack.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "stack.h" + +struct _cbor_stack _cbor_stack_init(void) { + return (struct _cbor_stack){.top = NULL, .size = 0}; +} + +void _cbor_stack_pop(struct _cbor_stack *stack) { + struct _cbor_stack_record *top = stack->top; + stack->top = stack->top->lower; + _cbor_free(top); + stack->size--; +} + +struct _cbor_stack_record *_cbor_stack_push(struct _cbor_stack *stack, + cbor_item_t *item, + size_t subitems) { + if (stack->size == CBOR_MAX_STACK_SIZE) return NULL; + struct _cbor_stack_record *new_top = + _cbor_malloc(sizeof(struct _cbor_stack_record)); + if (new_top == NULL) return NULL; + + *new_top = (struct _cbor_stack_record){stack->top, item, subitems}; + stack->top = new_top; + stack->size++; + return new_top; +} diff --git a/source/external/libcbor/cbor/internal/stack.h b/source/external/libcbor/cbor/internal/stack.h new file mode 100644 index 000000000..cf2206b40 --- /dev/null +++ b/source/external/libcbor/cbor/internal/stack.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_STACK_H +#define LIBCBOR_STACK_H + +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Simple stack record for the parser */ +struct _cbor_stack_record { + /** Pointer to the parent stack frame */ + struct _cbor_stack_record *lower; + /** Item under construction */ + cbor_item_t *item; + /** + * How many outstanding subitems are expected. + * + * For example, when we see a new definite array, `subitems` is initialized to + * the array length. With every item added, the counter is decreased. When it + * reaches zero, the stack is popped and the complete item is propagated + * upwards. + */ + size_t subitems; +}; + +/** Stack handle - contents and size */ +struct _cbor_stack { + struct _cbor_stack_record *top; + size_t size; +}; + +_CBOR_NODISCARD +struct _cbor_stack _cbor_stack_init(void); + +void _cbor_stack_pop(struct _cbor_stack *); + +_CBOR_NODISCARD +struct _cbor_stack_record *_cbor_stack_push(struct _cbor_stack *, cbor_item_t *, + size_t); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_STACK_H diff --git a/source/external/libcbor/cbor/internal/unicode.c b/source/external/libcbor/cbor/internal/unicode.c new file mode 100644 index 000000000..f87b746a3 --- /dev/null +++ b/source/external/libcbor/cbor/internal/unicode.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "unicode.h" +#include + +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 1 + +static const uint8_t utf8d[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00..1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20..3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40..5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60..7f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, /* 80..9f */ + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* a0..bf */ + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* c0..df */ + 0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, + 0x3, 0x3, 0x4, 0x3, 0x3, /* e0..ef */ + 0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, + 0x8, 0x8, 0x8, 0x8, 0x8, /* f0..ff */ + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, + 0x6, 0x1, 0x1, 0x1, 0x1, /* s0..s0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, /* s1..s2 */ + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, /* s3..s4 */ + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, /* s5..s6 */ + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s7..s8 */ +}; + +/* Copyright of this function: (c) 2008-2009 Bjoern Hoehrmann + * */ +/* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. */ +uint32_t _cbor_unicode_decode(uint32_t* state, uint32_t* codep, uint32_t byte) { + uint32_t type = utf8d[byte]; + + *codep = (*state != UTF8_ACCEPT) ? (byte & 0x3fu) | (*codep << 6) + : (0xff >> type) & (byte); + + *state = utf8d[256 + *state * 16 + type]; + return *state; +} + +size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length, + struct _cbor_unicode_status* status) { + *status = + (struct _cbor_unicode_status){.location = 0, .status = _CBOR_UNICODE_OK}; + uint32_t codepoint, state = UTF8_ACCEPT, res; + size_t pos = 0, count = 0; + + for (; pos < source_length; pos++) { + res = _cbor_unicode_decode(&state, &codepoint, source[pos]); + + if (res == UTF8_ACCEPT) { + count++; + } else if (res == UTF8_REJECT) { + goto error; + } + } + + /* Unfinished multibyte codepoint */ + if (state != UTF8_ACCEPT) goto error; + + return count; + +error: + *status = (struct _cbor_unicode_status){.location = pos, + .status = _CBOR_UNICODE_BADCP}; + return 0; +} diff --git a/source/external/libcbor/cbor/internal/unicode.h b/source/external/libcbor/cbor/internal/unicode.h new file mode 100644 index 000000000..81d03d007 --- /dev/null +++ b/source/external/libcbor/cbor/internal/unicode.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_UNICODE_H +#define LIBCBOR_UNICODE_H + +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum _cbor_unicode_status_error { _CBOR_UNICODE_OK, _CBOR_UNICODE_BADCP }; + +/** Signals unicode validation error and possibly its location */ +struct _cbor_unicode_status { + enum _cbor_unicode_status_error status; + size_t location; +}; + +_CBOR_NODISCARD +size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length, + struct _cbor_unicode_status* status); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_UNICODE_H diff --git a/source/external/libcbor/cbor/ints.c b/source/external/libcbor/cbor/ints.c new file mode 100644 index 000000000..b4d035a18 --- /dev/null +++ b/source/external/libcbor/cbor/ints.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "ints.h" + +cbor_int_width cbor_int_get_width(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + return item->metadata.int_metadata.width; +} + +uint8_t cbor_get_uint8(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_8); + return *item->data; +} + +uint16_t cbor_get_uint16(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_16); + return *(uint16_t *)item->data; +} + +uint32_t cbor_get_uint32(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_32); + return *(uint32_t *)item->data; +} + +uint64_t cbor_get_uint64(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_64); + return *(uint64_t *)item->data; +} + +uint64_t cbor_get_int(const cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + // cppcheck-suppress missingReturn + switch (cbor_int_get_width(item)) { + case CBOR_INT_8: + return cbor_get_uint8(item); + case CBOR_INT_16: + return cbor_get_uint16(item); + case CBOR_INT_32: + return cbor_get_uint32(item); + case CBOR_INT_64: + return cbor_get_uint64(item); + } +} + +void cbor_set_uint8(cbor_item_t *item, uint8_t value) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_8); + *item->data = value; +} + +void cbor_set_uint16(cbor_item_t *item, uint16_t value) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_16); + *(uint16_t *)item->data = value; +} + +void cbor_set_uint32(cbor_item_t *item, uint32_t value) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_32); + *(uint32_t *)item->data = value; +} + +void cbor_set_uint64(cbor_item_t *item, uint64_t value) { + CBOR_ASSERT(cbor_is_int(item)); + CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_64); + *(uint64_t *)item->data = value; +} + +void cbor_mark_uint(cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + item->type = CBOR_TYPE_UINT; +} + +void cbor_mark_negint(cbor_item_t *item) { + CBOR_ASSERT(cbor_is_int(item)); + item->type = CBOR_TYPE_NEGINT; +} + +cbor_item_t *cbor_new_int8(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 1); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.int_metadata = {.width = CBOR_INT_8}}, + .type = CBOR_TYPE_UINT}; + return item; +} + +cbor_item_t *cbor_new_int16(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 2); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.int_metadata = {.width = CBOR_INT_16}}, + .type = CBOR_TYPE_UINT}; + return item; +} + +cbor_item_t *cbor_new_int32(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.int_metadata = {.width = CBOR_INT_32}}, + .type = CBOR_TYPE_UINT}; + return item; +} + +cbor_item_t *cbor_new_int64(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 8); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), + .refcount = 1, + .metadata = {.int_metadata = {.width = CBOR_INT_64}}, + .type = CBOR_TYPE_UINT}; + return item; +} + +cbor_item_t *cbor_build_uint8(uint8_t value) { + cbor_item_t *item = cbor_new_int8(); + _CBOR_NOTNULL(item); + cbor_set_uint8(item, value); + cbor_mark_uint(item); + return item; +} + +cbor_item_t *cbor_build_uint16(uint16_t value) { + cbor_item_t *item = cbor_new_int16(); + _CBOR_NOTNULL(item); + cbor_set_uint16(item, value); + cbor_mark_uint(item); + return item; +} + +cbor_item_t *cbor_build_uint32(uint32_t value) { + cbor_item_t *item = cbor_new_int32(); + _CBOR_NOTNULL(item); + cbor_set_uint32(item, value); + cbor_mark_uint(item); + return item; +} + +cbor_item_t *cbor_build_uint64(uint64_t value) { + cbor_item_t *item = cbor_new_int64(); + _CBOR_NOTNULL(item); + cbor_set_uint64(item, value); + cbor_mark_uint(item); + return item; +} + +cbor_item_t *cbor_build_negint8(uint8_t value) { + cbor_item_t *item = cbor_new_int8(); + _CBOR_NOTNULL(item); + cbor_set_uint8(item, value); + cbor_mark_negint(item); + return item; +} + +cbor_item_t *cbor_build_negint16(uint16_t value) { + cbor_item_t *item = cbor_new_int16(); + _CBOR_NOTNULL(item); + cbor_set_uint16(item, value); + cbor_mark_negint(item); + return item; +} + +cbor_item_t *cbor_build_negint32(uint32_t value) { + cbor_item_t *item = cbor_new_int32(); + _CBOR_NOTNULL(item); + cbor_set_uint32(item, value); + cbor_mark_negint(item); + return item; +} + +cbor_item_t *cbor_build_negint64(uint64_t value) { + cbor_item_t *item = cbor_new_int64(); + _CBOR_NOTNULL(item); + cbor_set_uint64(item, value); + cbor_mark_negint(item); + return item; +} diff --git a/source/external/libcbor/cbor/ints.h b/source/external/libcbor/cbor/ints.h new file mode 100644 index 000000000..006aa428e --- /dev/null +++ b/source/external/libcbor/cbor/ints.h @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_INTS_H +#define LIBCBOR_INTS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * Integer (uints and negints) manipulation + * ============================================================================ + */ + +/** Extracts the integer value + * + * @param item positive or negative integer + * @return the value + */ +_CBOR_NODISCARD CBOR_EXPORT uint8_t cbor_get_uint8(const cbor_item_t *item); + +/** Extracts the integer value + * + * @param item positive or negative integer + * @return the value + */ +_CBOR_NODISCARD CBOR_EXPORT uint16_t cbor_get_uint16(const cbor_item_t *item); + +/** Extracts the integer value + * + * @param item positive or negative integer + * @return the value + */ +_CBOR_NODISCARD CBOR_EXPORT uint32_t cbor_get_uint32(const cbor_item_t *item); + +/** Extracts the integer value + * + * @param item positive or negative integer + * @return the value + */ +_CBOR_NODISCARD CBOR_EXPORT uint64_t cbor_get_uint64(const cbor_item_t *item); + +/** Extracts the integer value + * + * @param item positive or negative integer + * @return the value, extended to `uint64_t` + */ +_CBOR_NODISCARD CBOR_EXPORT uint64_t cbor_get_int(const cbor_item_t *item); + +/** Assigns the integer value + * + * @param item positive or negative integer item + * @param value the value to assign. For negative integer, the logical value is + * `-value - 1` + */ +CBOR_EXPORT void cbor_set_uint8(cbor_item_t *item, uint8_t value); + +/** Assigns the integer value + * + * @param item positive or negative integer item + * @param value the value to assign. For negative integer, the logical value is + * `-value - 1` + */ +CBOR_EXPORT void cbor_set_uint16(cbor_item_t *item, uint16_t value); + +/** Assigns the integer value + * + * @param item positive or negative integer item + * @param value the value to assign. For negative integer, the logical value is + * `-value - 1` + */ +CBOR_EXPORT void cbor_set_uint32(cbor_item_t *item, uint32_t value); + +/** Assigns the integer value + * + * @param item positive or negative integer item + * @param value the value to assign. For negative integer, the logical value is + * `-value - 1` + */ +CBOR_EXPORT void cbor_set_uint64(cbor_item_t *item, uint64_t value); + +/** Queries the integer width + * + * @param item positive or negative integer item + * @return the width + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_int_width +cbor_int_get_width(const cbor_item_t *item); + +/** Marks the integer item as a positive integer + * + * The data value is not changed + * + * @param item positive or negative integer item + */ +CBOR_EXPORT void cbor_mark_uint(cbor_item_t *item); + +/** Marks the integer item as a negative integer + * + * The data value is not changed + * + * @param item positive or negative integer item + */ +CBOR_EXPORT void cbor_mark_negint(cbor_item_t *item); + +/** Allocates new integer with 1B width + * + * The width cannot be changed once allocated + * + * @return **new** positive integer or `NULL` on memory allocation failure. The + * value is not initialized + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int8(void); + +/** Allocates new integer with 2B width + * + * The width cannot be changed once allocated + * + * @return **new** positive integer or `NULL` on memory allocation failure. The + * value is not initialized + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int16(void); + +/** Allocates new integer with 4B width + * + * The width cannot be changed once allocated + * + * @return **new** positive integer or `NULL` on memory allocation failure. The + * value is not initialized + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int32(void); + +/** Allocates new integer with 8B width + * + * The width cannot be changed once allocated + * + * @return **new** positive integer or `NULL` on memory allocation failure. The + * value is not initialized + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int64(void); + +/** Constructs a new positive integer + * + * @param value the value to use + * @return **new** positive integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint8(uint8_t value); + +/** Constructs a new positive integer + * + * @param value the value to use + * @return **new** positive integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint16(uint16_t value); + +/** Constructs a new positive integer + * + * @param value the value to use + * @return **new** positive integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint32(uint32_t value); + +/** Constructs a new positive integer + * + * @param value the value to use + * @return **new** positive integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint64(uint64_t value); + +/** Constructs a new negative integer + * + * @param value the value to use + * @return **new** negative integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint8(uint8_t value); + +/** Constructs a new negative integer + * + * @param value the value to use + * @return **new** negative integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint16(uint16_t value); + +/** Constructs a new negative integer + * + * @param value the value to use + * @return **new** negative integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint32(uint32_t value); + +/** Constructs a new negative integer + * + * @param value the value to use + * @return **new** negative integer or `NULL` on memory allocation failure + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint64(uint64_t value); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_INTS_H diff --git a/source/external/libcbor/cbor/maps.c b/source/external/libcbor/cbor/maps.c new file mode 100644 index 000000000..8a3bd6075 --- /dev/null +++ b/source/external/libcbor/cbor/maps.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "maps.h" +#include "internal/memory_utils.h" + +size_t cbor_map_size(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_map(item)); + return item->metadata.map_metadata.end_ptr; +} + +size_t cbor_map_allocated(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_map(item)); + return item->metadata.map_metadata.allocated; +} + +cbor_item_t *cbor_new_definite_map(size_t size) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_MAP, + .metadata = {.map_metadata = {.allocated = size, + .type = _CBOR_METADATA_DEFINITE, + .end_ptr = 0}}, + .data = _cbor_alloc_multiple(sizeof(struct cbor_pair), size)}; + _CBOR_DEPENDENT_NOTNULL(item, item->data); + + return item; +} + +cbor_item_t *cbor_new_indefinite_map(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_MAP, + .metadata = {.map_metadata = {.allocated = 0, + .type = _CBOR_METADATA_INDEFINITE, + .end_ptr = 0}}, + .data = NULL}; + + return item; +} + +bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key) { + CBOR_ASSERT(cbor_isa_map(item)); + struct _cbor_map_metadata *metadata = + (struct _cbor_map_metadata *)&item->metadata; + if (cbor_map_is_definite(item)) { + struct cbor_pair *data = cbor_map_handle(item); + if (metadata->end_ptr >= metadata->allocated) { + /* Don't realloc definite preallocated map */ + return false; + } + + data[metadata->end_ptr].key = key; + data[metadata->end_ptr++].value = NULL; + } else { + if (metadata->end_ptr >= metadata->allocated) { + /* Exponential realloc */ + // Check for overflows first + if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) { + return false; + } + + size_t new_allocation = metadata->allocated == 0 + ? 1 + : CBOR_BUFFER_GROWTH * metadata->allocated; + + unsigned char *new_data = _cbor_realloc_multiple( + item->data, sizeof(struct cbor_pair), new_allocation); + + if (new_data == NULL) { + return false; + } + + item->data = new_data; + metadata->allocated = new_allocation; + } + struct cbor_pair *data = cbor_map_handle(item); + data[metadata->end_ptr].key = key; + data[metadata->end_ptr++].value = NULL; + } + cbor_incref(key); + return true; +} + +bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) { + CBOR_ASSERT(cbor_isa_map(item)); + cbor_incref(value); + cbor_map_handle(item)[ + /* Move one back since we are assuming _add_key (which increased the ptr) + * was the previous operation on this object */ + item->metadata.map_metadata.end_ptr - 1] + .value = value; + return true; +} + +// TODO: Add a more convenient API like add(item, key, val) +bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair) { + CBOR_ASSERT(cbor_isa_map(item)); + if (!_cbor_map_add_key(item, pair.key)) return false; + return _cbor_map_add_value(item, pair.value); +} + +bool cbor_map_is_definite(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_map(item)); + return item->metadata.map_metadata.type == _CBOR_METADATA_DEFINITE; +} + +bool cbor_map_is_indefinite(const cbor_item_t *item) { + return !cbor_map_is_definite(item); +} + +struct cbor_pair *cbor_map_handle(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_map(item)); + return (struct cbor_pair *)item->data; +} diff --git a/source/external/libcbor/cbor/maps.h b/source/external/libcbor/cbor/maps.h new file mode 100644 index 000000000..5c05b542b --- /dev/null +++ b/source/external/libcbor/cbor/maps.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_MAPS_H +#define LIBCBOR_MAPS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * Map manipulation + * ============================================================================ + */ + +/** Get the number of pairs + * + * @param item A map + * @return The number of pairs + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_map_size(const cbor_item_t *item); + +/** Get the size of the allocated storage + * + * @param item A map + * @return Allocated storage size (as the number of #cbor_pair items) + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_map_allocated(const cbor_item_t *item); + +/** Create a new definite map + * + * @param size The number of slots to preallocate + * @return Reference to the new map item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_definite_map(size_t size); + +/** Create a new indefinite map + * + * @return Reference to the new map item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_indefinite_map(void); + +/** Add a pair to the map + * + * For definite maps, items can only be added to the preallocated space. For + * indefinite maps, the storage will be expanded as needed + * + * @param item A map + * @param pair The key-value pair to add. Reference count of the #cbor_pair.key + * and #cbor_pair.value will be increased by one. + * @return `true` on success, `false` if memory allocation failed (indefinite + * maps) or the preallocated storage is full (definite maps) + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_map_add(cbor_item_t *item, + struct cbor_pair pair); + +/** Add a key to the map + * + * Sets the value to `NULL`. Internal API. + * + * @param item A map + * @param key The key, Its reference count will be be increased by one. + * @return `true` on success, `false` if either reallocation failed or the + * preallocated storage is full + */ +_CBOR_NODISCARD CBOR_EXPORT bool _cbor_map_add_key(cbor_item_t *item, + cbor_item_t *key); + +/** Add a value to the map + * + * Assumes that #_cbor_map_add_key has been called. Internal API. + * + * @param item A map + * @param value The value. Its reference count will be be increased by one. + * @return `true` on success, `false` if either reallocation failed or the + * preallocated storage is full + */ +_CBOR_NODISCARD CBOR_EXPORT bool _cbor_map_add_value(cbor_item_t *item, + cbor_item_t *value); + +/** Is this map definite? + * + * @param item A map + * @return Is this map definite? + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_map_is_definite(const cbor_item_t *item); + +/** Is this map indefinite? + * + * @param item A map + * @return Is this map indefinite? + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_map_is_indefinite( + const cbor_item_t *item); + +/** Get the pairs storage + * + * @param item A map + * @return Array of #cbor_map_size pairs. Manipulation is possible as long as + * references remain valid. + */ +_CBOR_NODISCARD CBOR_EXPORT struct cbor_pair *cbor_map_handle( + const cbor_item_t *item); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_MAPS_H diff --git a/source/external/libcbor/cbor/serialization.c b/source/external/libcbor/cbor/serialization.c new file mode 100644 index 000000000..40f4c531d --- /dev/null +++ b/source/external/libcbor/cbor/serialization.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "serialization.h" +#include +#include "cbor/arrays.h" +#include "cbor/bytestrings.h" +#include "cbor/floats_ctrls.h" +#include "cbor/ints.h" +#include "cbor/maps.h" +#include "cbor/strings.h" +#include "cbor/tags.h" +#include "encoding.h" +#include "internal/memory_utils.h" + +size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + // cppcheck-suppress missingReturn + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: + return cbor_serialize_uint(item, buffer, buffer_size); + case CBOR_TYPE_NEGINT: + return cbor_serialize_negint(item, buffer, buffer_size); + case CBOR_TYPE_BYTESTRING: + return cbor_serialize_bytestring(item, buffer, buffer_size); + case CBOR_TYPE_STRING: + return cbor_serialize_string(item, buffer, buffer_size); + case CBOR_TYPE_ARRAY: + return cbor_serialize_array(item, buffer, buffer_size); + case CBOR_TYPE_MAP: + return cbor_serialize_map(item, buffer, buffer_size); + case CBOR_TYPE_TAG: + return cbor_serialize_tag(item, buffer, buffer_size); + case CBOR_TYPE_FLOAT_CTRL: + return cbor_serialize_float_ctrl(item, buffer, buffer_size); + } +} + +/** Largest integer that can be encoded as embedded in the item leading byte. */ +const uint64_t kMaxEmbeddedInt = 23; + +/** How many bytes will a tag for a nested item of a given `size` take when + * encoded.*/ +size_t _cbor_encoded_header_size(uint64_t size) { + if (size <= kMaxEmbeddedInt) + return 1; + else if (size <= UINT8_MAX) + return 2; + else if (size <= UINT16_MAX) + return 3; + else if (size <= UINT32_MAX) + return 5; + else + return 9; +} + +size_t cbor_serialized_size(const cbor_item_t *item) { + // cppcheck-suppress missingReturn + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: + case CBOR_TYPE_NEGINT: + switch (cbor_int_get_width(item)) { + case CBOR_INT_8: + if (cbor_get_uint8(item) <= kMaxEmbeddedInt) return 1; + return 2; + case CBOR_INT_16: + return 3; + case CBOR_INT_32: + return 5; + case CBOR_INT_64: + return 9; + } + // Note: We do not _cbor_safe_signaling_add zero-length definite strings, + // they would cause zeroes to propagate. All other items are at least one + // byte. + case CBOR_TYPE_BYTESTRING: { + if (cbor_bytestring_is_definite(item)) { + size_t header_size = + _cbor_encoded_header_size(cbor_bytestring_length(item)); + if (cbor_bytestring_length(item) == 0) return header_size; + return _cbor_safe_signaling_add(header_size, + cbor_bytestring_length(item)); + } + size_t indef_bytestring_size = 2; // Leading byte + break + cbor_item_t **chunks = cbor_bytestring_chunks_handle(item); + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) { + indef_bytestring_size = _cbor_safe_signaling_add( + indef_bytestring_size, cbor_serialized_size(chunks[i])); + } + return indef_bytestring_size; + } + case CBOR_TYPE_STRING: { + if (cbor_string_is_definite(item)) { + size_t header_size = + _cbor_encoded_header_size(cbor_string_length(item)); + if (cbor_string_length(item) == 0) return header_size; + return _cbor_safe_signaling_add(header_size, cbor_string_length(item)); + } + size_t indef_string_size = 2; // Leading byte + break + cbor_item_t **chunks = cbor_string_chunks_handle(item); + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) { + indef_string_size = _cbor_safe_signaling_add( + indef_string_size, cbor_serialized_size(chunks[i])); + } + return indef_string_size; + } + case CBOR_TYPE_ARRAY: { + size_t array_size = cbor_array_is_definite(item) + ? _cbor_encoded_header_size(cbor_array_size(item)) + : 2; // Leading byte + break + cbor_item_t **items = cbor_array_handle(item); + for (size_t i = 0; i < cbor_array_size(item); i++) { + array_size = _cbor_safe_signaling_add(array_size, + cbor_serialized_size(items[i])); + } + return array_size; + } + case CBOR_TYPE_MAP: { + size_t map_size = cbor_map_is_definite(item) + ? _cbor_encoded_header_size(cbor_map_size(item)) + : 2; // Leading byte + break + struct cbor_pair *items = cbor_map_handle(item); + for (size_t i = 0; i < cbor_map_size(item); i++) { + map_size = _cbor_safe_signaling_add( + map_size, + _cbor_safe_signaling_add(cbor_serialized_size(items[i].key), + cbor_serialized_size(items[i].value))); + } + return map_size; + } + case CBOR_TYPE_TAG: { + return _cbor_safe_signaling_add( + _cbor_encoded_header_size(cbor_tag_value(item)), + cbor_serialized_size(cbor_move(cbor_tag_item(item)))); + } + case CBOR_TYPE_FLOAT_CTRL: + switch (cbor_float_get_width(item)) { + case CBOR_FLOAT_0: + return _cbor_encoded_header_size(cbor_ctrl_value(item)); + case CBOR_FLOAT_16: + return 3; + case CBOR_FLOAT_32: + return 5; + case CBOR_FLOAT_64: + return 9; + } + } +} + +size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer, + size_t *buffer_size) { + *buffer = NULL; + size_t serialized_size = cbor_serialized_size(item); + if (serialized_size == 0) { + if (buffer_size != NULL) *buffer_size = 0; + return 0; + } + *buffer = _cbor_malloc(serialized_size); + if (*buffer == NULL) { + if (buffer_size != NULL) *buffer_size = 0; + return 0; + } + + size_t written = cbor_serialize(item, *buffer, serialized_size); + CBOR_ASSERT(written == serialized_size); + if (buffer_size != NULL) *buffer_size = serialized_size; + return written; +} + +size_t cbor_serialize_uint(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_uint(item)); + // cppcheck-suppress missingReturn + switch (cbor_int_get_width(item)) { + case CBOR_INT_8: + return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size); + case CBOR_INT_16: + return cbor_encode_uint16(cbor_get_uint16(item), buffer, buffer_size); + case CBOR_INT_32: + return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size); + case CBOR_INT_64: + return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size); + } +} + +size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_negint(item)); + // cppcheck-suppress missingReturn + switch (cbor_int_get_width(item)) { + case CBOR_INT_8: + return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size); + case CBOR_INT_16: + return cbor_encode_negint16(cbor_get_uint16(item), buffer, buffer_size); + case CBOR_INT_32: + return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size); + case CBOR_INT_64: + return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size); + } +} + +size_t cbor_serialize_bytestring(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_bytestring(item)); + if (cbor_bytestring_is_definite(item)) { + size_t length = cbor_bytestring_length(item); + size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size); + if (written > 0 && (buffer_size - written >= length)) { + memcpy(buffer + written, cbor_bytestring_handle(item), length); + return written + length; + } + return 0; + } else { + CBOR_ASSERT(cbor_bytestring_is_indefinite(item)); + size_t chunk_count = cbor_bytestring_chunk_count(item); + size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size); + if (written == 0) return 0; + + cbor_item_t **chunks = cbor_bytestring_chunks_handle(item); + for (size_t i = 0; i < chunk_count; i++) { + size_t chunk_written = cbor_serialize_bytestring( + chunks[i], buffer + written, buffer_size - written); + if (chunk_written == 0) return 0; + written += chunk_written; + } + + size_t break_written = + cbor_encode_break(buffer + written, buffer_size - written); + if (break_written == 0) return 0; + return written + break_written; + } +} + +size_t cbor_serialize_string(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_string(item)); + if (cbor_string_is_definite(item)) { + size_t length = cbor_string_length(item); + size_t written = cbor_encode_string_start(length, buffer, buffer_size); + if (written && (buffer_size - written >= length)) { + memcpy(buffer + written, cbor_string_handle(item), length); + return written + length; + } + return 0; + } else { + CBOR_ASSERT(cbor_string_is_indefinite(item)); + size_t chunk_count = cbor_string_chunk_count(item); + size_t written = cbor_encode_indef_string_start(buffer, buffer_size); + if (written == 0) return 0; + + cbor_item_t **chunks = cbor_string_chunks_handle(item); + for (size_t i = 0; i < chunk_count; i++) { + size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written, + buffer_size - written); + if (chunk_written == 0) return 0; + written += chunk_written; + } + + size_t break_written = + cbor_encode_break(buffer + written, buffer_size - written); + if (break_written == 0) return 0; + return written + break_written; + } +} + +size_t cbor_serialize_array(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_array(item)); + size_t size = cbor_array_size(item), written = 0; + cbor_item_t **handle = cbor_array_handle(item); + if (cbor_array_is_definite(item)) { + written = cbor_encode_array_start(size, buffer, buffer_size); + } else { + CBOR_ASSERT(cbor_array_is_indefinite(item)); + written = cbor_encode_indef_array_start(buffer, buffer_size); + } + if (written == 0) return 0; + + for (size_t i = 0; i < size; i++) { + size_t item_written = + cbor_serialize(*(handle++), buffer + written, buffer_size - written); + if (item_written == 0) return 0; + written += item_written; + } + + if (cbor_array_is_definite(item)) { + return written; + } else { + CBOR_ASSERT(cbor_array_is_indefinite(item)); + size_t break_written = + cbor_encode_break(buffer + written, buffer_size - written); + if (break_written == 0) return 0; + return written + break_written; + } +} + +size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_map(item)); + size_t size = cbor_map_size(item), written = 0; + struct cbor_pair *handle = cbor_map_handle(item); + + if (cbor_map_is_definite(item)) { + written = cbor_encode_map_start(size, buffer, buffer_size); + } else { + CBOR_ASSERT(cbor_map_is_indefinite(item)); + written = cbor_encode_indef_map_start(buffer, buffer_size); + } + if (written == 0) return 0; + + for (size_t i = 0; i < size; i++) { + size_t item_written = + cbor_serialize(handle->key, buffer + written, buffer_size - written); + if (item_written == 0) { + return 0; + } + written += item_written; + item_written = cbor_serialize((handle++)->value, buffer + written, + buffer_size - written); + if (item_written == 0) return 0; + written += item_written; + } + + if (cbor_map_is_definite(item)) { + return written; + } else { + CBOR_ASSERT(cbor_map_is_indefinite(item)); + size_t break_written = + cbor_encode_break(buffer + written, buffer_size - written); + if (break_written == 0) return 0; + return written + break_written; + } +} + +size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_tag(item)); + size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size); + if (written == 0) return 0; + + size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)), + buffer + written, buffer_size - written); + if (item_written == 0) return 0; + return written + item_written; +} + +size_t cbor_serialize_float_ctrl(const cbor_item_t *item, unsigned char *buffer, + size_t buffer_size) { + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + // cppcheck-suppress missingReturn + switch (cbor_float_get_width(item)) { + case CBOR_FLOAT_0: + /* CTRL - special treatment */ + return cbor_encode_ctrl(cbor_ctrl_value(item), buffer, buffer_size); + case CBOR_FLOAT_16: + return cbor_encode_half(cbor_float_get_float2(item), buffer, buffer_size); + case CBOR_FLOAT_32: + return cbor_encode_single(cbor_float_get_float4(item), buffer, + buffer_size); + case CBOR_FLOAT_64: + return cbor_encode_double(cbor_float_get_float8(item), buffer, + buffer_size); + } +} diff --git a/source/external/libcbor/cbor/serialization.h b/source/external/libcbor/cbor/serialization.h new file mode 100644 index 000000000..228ae75d6 --- /dev/null +++ b/source/external/libcbor/cbor/serialization.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_SERIALIZATION_H +#define LIBCBOR_SERIALIZATION_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * High level encoding + * ============================================================================ + */ + +/** Serialize the given item + * + * @param item A data item + * @param buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result. 0 on failure. + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize(const cbor_item_t *item, + cbor_mutable_data buffer, + size_t buffer_size); + +/** Compute the length (in bytes) of the item when serialized using + * `cbor_serialize`. + * + * Time complexity is proportional to the number of nested items. + * + * @param item A data item + * @return Length (>= 1) of the item when serialized. 0 if the length overflows + * `size_t`. + */ +_CBOR_NODISCARD CBOR_EXPORT size_t +cbor_serialized_size(const cbor_item_t *item); + +/** Serialize the given item, allocating buffers as needed + * + * Since libcbor v0.10, the return value is always the same as `buffer_size` (if + * provided, see https://github.com/PJK/libcbor/pull/251/). New clients should + * ignore the return value. + * + * \rst + * .. warning:: It is the caller's responsibility to free the buffer using an + * appropriate ``free`` implementation. + * \endrst + * + * @param item A data item + * @param[out] buffer Buffer containing the result + * @param[out] buffer_size Size of the \p buffer, or 0 on memory allocation + * failure. + * @return Length of the result in bytes + * @return 0 on memory allocation failure, in which case \p buffer is `NULL`. + */ +CBOR_EXPORT size_t cbor_serialize_alloc(const cbor_item_t *item, + unsigned char **buffer, + size_t *buffer_size); + +/** Serialize an uint + * + * @param item A uint + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_uint(const cbor_item_t *item, + cbor_mutable_data buffer, + size_t buffer_size); + +/** Serialize a negint + * + * @param item A negint + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_negint( + const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size); + +/** Serialize a bytestring + * + * @param item A bytestring + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may + * still be modified + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_bytestring( + const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size); + +/** Serialize a string + * + * @param item A string + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may + * still be modified + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_string( + const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size); +/** Serialize an array + * + * @param item An array + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may + * still be modified + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_array( + const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size); + +/** Serialize a map + * + * @param item A map + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may + * still be modified + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_map(const cbor_item_t *item, + cbor_mutable_data buffer, + size_t buffer_size); + +/** Serialize a tag + * + * @param item A tag + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may + * still be modified + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_tag(const cbor_item_t *item, + cbor_mutable_data buffer, + size_t buffer_size); + +/** Serialize a + * + * @param item A float or ctrl + * @param[out] buffer Buffer to serialize to + * @param buffer_size Size of the \p buffer + * @return Length of the result + * @return 0 if the \p buffer_size doesn't fit the result + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_float_ctrl( + const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_SERIALIZATION_H diff --git a/source/external/libcbor/cbor/streaming.c b/source/external/libcbor/cbor/streaming.c new file mode 100644 index 000000000..4b3770114 --- /dev/null +++ b/source/external/libcbor/cbor/streaming.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "streaming.h" +#include "internal/loaders.h" + +static bool claim_bytes(size_t required, size_t provided, + struct cbor_decoder_result *result) { + if (required > (provided - result->read)) { + result->required = required + result->read; + result->read = 0; + result->status = CBOR_DECODER_NEDATA; + return false; + } else { + result->read += required; + result->required = 0; + return true; + } +} + +// Use implicit capture as an exception to avoid the super long parameter list +#define CLAIM_BYTES_AND_INVOKE(callback_name, length, source_extra_offset) \ + do { \ + if (claim_bytes(length, source_size, &result)) { \ + callbacks->callback_name(context, source + 1 + source_extra_offset, \ + length); \ + } \ + } while (0) + +#define READ_CLAIM_INVOKE(callback_name, length_reader, length_bytes) \ + do { \ + if (claim_bytes(length_bytes, source_size, &result)) { \ + uint64_t length = length_reader(source + 1); \ + CLAIM_BYTES_AND_INVOKE(callback_name, length, length_bytes); \ + } \ + return result; \ + } while (0) + +struct cbor_decoder_result cbor_stream_decode( + cbor_data source, size_t source_size, + const struct cbor_callbacks *callbacks, void *context) { + // Attempt to claim the initial MTB byte + struct cbor_decoder_result result = {.status = CBOR_DECODER_FINISHED}; + if (!claim_bytes(1, source_size, &result)) { + return result; + } + + switch (*source) { + case 0x00: /* Fallthrough */ + case 0x01: /* Fallthrough */ + case 0x02: /* Fallthrough */ + case 0x03: /* Fallthrough */ + case 0x04: /* Fallthrough */ + case 0x05: /* Fallthrough */ + case 0x06: /* Fallthrough */ + case 0x07: /* Fallthrough */ + case 0x08: /* Fallthrough */ + case 0x09: /* Fallthrough */ + case 0x0A: /* Fallthrough */ + case 0x0B: /* Fallthrough */ + case 0x0C: /* Fallthrough */ + case 0x0D: /* Fallthrough */ + case 0x0E: /* Fallthrough */ + case 0x0F: /* Fallthrough */ + case 0x10: /* Fallthrough */ + case 0x11: /* Fallthrough */ + case 0x12: /* Fallthrough */ + case 0x13: /* Fallthrough */ + case 0x14: /* Fallthrough */ + case 0x15: /* Fallthrough */ + case 0x16: /* Fallthrough */ + case 0x17: + /* Embedded one byte unsigned integer */ + { + callbacks->uint8(context, _cbor_load_uint8(source)); + return result; + } + case 0x18: + /* One byte unsigned integer */ + { + if (claim_bytes(1, source_size, &result)) { + callbacks->uint8(context, _cbor_load_uint8(source + 1)); + } + return result; + } + case 0x19: + /* Two bytes unsigned integer */ + { + if (claim_bytes(2, source_size, &result)) { + callbacks->uint16(context, _cbor_load_uint16(source + 1)); + } + return result; + } + case 0x1A: + /* Four bytes unsigned integer */ + { + if (claim_bytes(4, source_size, &result)) { + callbacks->uint32(context, _cbor_load_uint32(source + 1)); + } + return result; + } + case 0x1B: + /* Eight bytes unsigned integer */ + { + if (claim_bytes(8, source_size, &result)) { + callbacks->uint64(context, _cbor_load_uint64(source + 1)); + } + return result; + } + case 0x1C: /* Fallthrough */ + case 0x1D: /* Fallthrough */ + case 0x1E: /* Fallthrough */ + case 0x1F: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0x20: /* Fallthrough */ + case 0x21: /* Fallthrough */ + case 0x22: /* Fallthrough */ + case 0x23: /* Fallthrough */ + case 0x24: /* Fallthrough */ + case 0x25: /* Fallthrough */ + case 0x26: /* Fallthrough */ + case 0x27: /* Fallthrough */ + case 0x28: /* Fallthrough */ + case 0x29: /* Fallthrough */ + case 0x2A: /* Fallthrough */ + case 0x2B: /* Fallthrough */ + case 0x2C: /* Fallthrough */ + case 0x2D: /* Fallthrough */ + case 0x2E: /* Fallthrough */ + case 0x2F: /* Fallthrough */ + case 0x30: /* Fallthrough */ + case 0x31: /* Fallthrough */ + case 0x32: /* Fallthrough */ + case 0x33: /* Fallthrough */ + case 0x34: /* Fallthrough */ + case 0x35: /* Fallthrough */ + case 0x36: /* Fallthrough */ + case 0x37: + /* Embedded one byte negative integer */ + { + callbacks->negint8(context, + _cbor_load_uint8(source) - 0x20); /* 0x20 offset */ + return result; + } + case 0x38: + /* One byte negative integer */ + { + if (claim_bytes(1, source_size, &result)) { + callbacks->negint8(context, _cbor_load_uint8(source + 1)); + } + return result; + } + case 0x39: + /* Two bytes negative integer */ + { + if (claim_bytes(2, source_size, &result)) { + callbacks->negint16(context, _cbor_load_uint16(source + 1)); + } + return result; + } + case 0x3A: + /* Four bytes negative integer */ + { + if (claim_bytes(4, source_size, &result)) { + callbacks->negint32(context, _cbor_load_uint32(source + 1)); + } + return result; + } + case 0x3B: + /* Eight bytes negative integer */ + { + if (claim_bytes(8, source_size, &result)) { + callbacks->negint64(context, _cbor_load_uint64(source + 1)); + } + return result; + } + case 0x3C: /* Fallthrough */ + case 0x3D: /* Fallthrough */ + case 0x3E: /* Fallthrough */ + case 0x3F: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0x40: /* Fallthrough */ + case 0x41: /* Fallthrough */ + case 0x42: /* Fallthrough */ + case 0x43: /* Fallthrough */ + case 0x44: /* Fallthrough */ + case 0x45: /* Fallthrough */ + case 0x46: /* Fallthrough */ + case 0x47: /* Fallthrough */ + case 0x48: /* Fallthrough */ + case 0x49: /* Fallthrough */ + case 0x4A: /* Fallthrough */ + case 0x4B: /* Fallthrough */ + case 0x4C: /* Fallthrough */ + case 0x4D: /* Fallthrough */ + case 0x4E: /* Fallthrough */ + case 0x4F: /* Fallthrough */ + case 0x50: /* Fallthrough */ + case 0x51: /* Fallthrough */ + case 0x52: /* Fallthrough */ + case 0x53: /* Fallthrough */ + case 0x54: /* Fallthrough */ + case 0x55: /* Fallthrough */ + case 0x56: /* Fallthrough */ + case 0x57: + /* Embedded length byte string */ + { + uint64_t length = _cbor_load_uint8(source) - 0x40; /* 0x40 offset */ + CLAIM_BYTES_AND_INVOKE(byte_string, length, 0); + return result; + } + case 0x58: + /* One byte length byte string */ + READ_CLAIM_INVOKE(byte_string, _cbor_load_uint8, 1); + case 0x59: + /* Two bytes length byte string */ + READ_CLAIM_INVOKE(byte_string, _cbor_load_uint16, 2); + case 0x5A: + /* Four bytes length byte string */ + READ_CLAIM_INVOKE(byte_string, _cbor_load_uint32, 4); + case 0x5B: + /* Eight bytes length byte string */ + READ_CLAIM_INVOKE(byte_string, _cbor_load_uint64, 8); + case 0x5C: /* Fallthrough */ + case 0x5D: /* Fallthrough */ + case 0x5E: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0x5F: + /* Indefinite byte string */ + { + callbacks->byte_string_start(context); + return result; + } + case 0x60: /* Fallthrough */ + case 0x61: /* Fallthrough */ + case 0x62: /* Fallthrough */ + case 0x63: /* Fallthrough */ + case 0x64: /* Fallthrough */ + case 0x65: /* Fallthrough */ + case 0x66: /* Fallthrough */ + case 0x67: /* Fallthrough */ + case 0x68: /* Fallthrough */ + case 0x69: /* Fallthrough */ + case 0x6A: /* Fallthrough */ + case 0x6B: /* Fallthrough */ + case 0x6C: /* Fallthrough */ + case 0x6D: /* Fallthrough */ + case 0x6E: /* Fallthrough */ + case 0x6F: /* Fallthrough */ + case 0x70: /* Fallthrough */ + case 0x71: /* Fallthrough */ + case 0x72: /* Fallthrough */ + case 0x73: /* Fallthrough */ + case 0x74: /* Fallthrough */ + case 0x75: /* Fallthrough */ + case 0x76: /* Fallthrough */ + case 0x77: + /* Embedded one byte length string */ + { + uint64_t length = _cbor_load_uint8(source) - 0x60; /* 0x60 offset */ + CLAIM_BYTES_AND_INVOKE(string, length, 0); + return result; + } + case 0x78: + /* One byte length string */ + READ_CLAIM_INVOKE(string, _cbor_load_uint8, 1); + case 0x79: + /* Two bytes length string */ + READ_CLAIM_INVOKE(string, _cbor_load_uint16, 2); + case 0x7A: + /* Four bytes length string */ + READ_CLAIM_INVOKE(string, _cbor_load_uint32, 4); + case 0x7B: + /* Eight bytes length string */ + READ_CLAIM_INVOKE(string, _cbor_load_uint64, 8); + case 0x7C: /* Fallthrough */ + case 0x7D: /* Fallthrough */ + case 0x7E: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0x7F: + /* Indefinite length string */ + { + callbacks->string_start(context); + return result; + } + case 0x80: /* Fallthrough */ + case 0x81: /* Fallthrough */ + case 0x82: /* Fallthrough */ + case 0x83: /* Fallthrough */ + case 0x84: /* Fallthrough */ + case 0x85: /* Fallthrough */ + case 0x86: /* Fallthrough */ + case 0x87: /* Fallthrough */ + case 0x88: /* Fallthrough */ + case 0x89: /* Fallthrough */ + case 0x8A: /* Fallthrough */ + case 0x8B: /* Fallthrough */ + case 0x8C: /* Fallthrough */ + case 0x8D: /* Fallthrough */ + case 0x8E: /* Fallthrough */ + case 0x8F: /* Fallthrough */ + case 0x90: /* Fallthrough */ + case 0x91: /* Fallthrough */ + case 0x92: /* Fallthrough */ + case 0x93: /* Fallthrough */ + case 0x94: /* Fallthrough */ + case 0x95: /* Fallthrough */ + case 0x96: /* Fallthrough */ + case 0x97: + /* Embedded one byte length array */ + { + callbacks->array_start( + context, _cbor_load_uint8(source) - 0x80); /* 0x40 offset */ + return result; + } + case 0x98: + /* One byte length array */ + { + if (claim_bytes(1, source_size, &result)) { + callbacks->array_start(context, _cbor_load_uint8(source + 1)); + } + return result; + } + case 0x99: + /* Two bytes length array */ + { + if (claim_bytes(2, source_size, &result)) { + callbacks->array_start(context, _cbor_load_uint16(source + 1)); + } + return result; + } + case 0x9A: + /* Four bytes length array */ + { + if (claim_bytes(4, source_size, &result)) { + callbacks->array_start(context, _cbor_load_uint32(source + 1)); + } + return result; + } + case 0x9B: + /* Eight bytes length array */ + { + if (claim_bytes(8, source_size, &result)) { + callbacks->array_start(context, _cbor_load_uint64(source + 1)); + } + return result; + } + case 0x9C: /* Fallthrough */ + case 0x9D: /* Fallthrough */ + case 0x9E: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0x9F: + /* Indefinite length array */ + { + callbacks->indef_array_start(context); + return result; + } + case 0xA0: /* Fallthrough */ + case 0xA1: /* Fallthrough */ + case 0xA2: /* Fallthrough */ + case 0xA3: /* Fallthrough */ + case 0xA4: /* Fallthrough */ + case 0xA5: /* Fallthrough */ + case 0xA6: /* Fallthrough */ + case 0xA7: /* Fallthrough */ + case 0xA8: /* Fallthrough */ + case 0xA9: /* Fallthrough */ + case 0xAA: /* Fallthrough */ + case 0xAB: /* Fallthrough */ + case 0xAC: /* Fallthrough */ + case 0xAD: /* Fallthrough */ + case 0xAE: /* Fallthrough */ + case 0xAF: /* Fallthrough */ + case 0xB0: /* Fallthrough */ + case 0xB1: /* Fallthrough */ + case 0xB2: /* Fallthrough */ + case 0xB3: /* Fallthrough */ + case 0xB4: /* Fallthrough */ + case 0xB5: /* Fallthrough */ + case 0xB6: /* Fallthrough */ + case 0xB7: + /* Embedded one byte length map */ + { + callbacks->map_start(context, + _cbor_load_uint8(source) - 0xA0); /* 0xA0 offset */ + return result; + } + case 0xB8: + /* One byte length map */ + { + if (claim_bytes(1, source_size, &result)) { + callbacks->map_start(context, _cbor_load_uint8(source + 1)); + } + return result; + } + case 0xB9: + /* Two bytes length map */ + { + if (claim_bytes(2, source_size, &result)) { + callbacks->map_start(context, _cbor_load_uint16(source + 1)); + } + return result; + } + case 0xBA: + /* Four bytes length map */ + { + if (claim_bytes(4, source_size, &result)) { + callbacks->map_start(context, _cbor_load_uint32(source + 1)); + } + return result; + } + case 0xBB: + /* Eight bytes length map */ + { + if (claim_bytes(8, source_size, &result)) { + callbacks->map_start(context, _cbor_load_uint64(source + 1)); + } + return result; + } + case 0xBC: /* Fallthrough */ + case 0xBD: /* Fallthrough */ + case 0xBE: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0xBF: + /* Indefinite length map */ + { + callbacks->indef_map_start(context); + return result; + } + /* See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml for tag + * assignment. All well-formed tags are processed regardless of validity + * since maintaining the known mapping would be impractical. + * + * Moreover, even tags in the reserved "standard" range are not assigned + * but may get assigned in the future (see e.g. + * https://github.com/PJK/libcbor/issues/307), so processing all tags + * improves forward compatibility. + */ + case 0xC0: /* Fallthrough */ + case 0xC1: /* Fallthrough */ + case 0xC2: /* Fallthrough */ + case 0xC3: /* Fallthrough */ + case 0xC4: /* Fallthrough */ + case 0xC5: /* Fallthrough */ + case 0xC6: /* Fallthrough */ + case 0xC7: /* Fallthrough */ + case 0xC8: /* Fallthrough */ + case 0xC9: /* Fallthrough */ + case 0xCA: /* Fallthrough */ + case 0xCB: /* Fallthrough */ + case 0xCC: /* Fallthrough */ + case 0xCD: /* Fallthrough */ + case 0xCE: /* Fallthrough */ + case 0xCF: /* Fallthrough */ + case 0xD0: /* Fallthrough */ + case 0xD1: /* Fallthrough */ + case 0xD2: /* Fallthrough */ + case 0xD3: /* Fallthrough */ + case 0xD4: /* Fallthrough */ + case 0xD5: /* Fallthrough */ + case 0xD6: /* Fallthrough */ + case 0xD7: /* Fallthrough */ + { + callbacks->tag(context, (uint64_t)(_cbor_load_uint8(source) - + 0xC0)); /* 0xC0 offset */ + return result; + } + case 0xD8: /* 1B tag */ + { + if (claim_bytes(1, source_size, &result)) { + callbacks->tag(context, _cbor_load_uint8(source + 1)); + } + return result; + } + case 0xD9: /* 2B tag */ + { + if (claim_bytes(2, source_size, &result)) { + callbacks->tag(context, _cbor_load_uint16(source + 1)); + } + return result; + } + case 0xDA: /* 4B tag */ + { + if (claim_bytes(4, source_size, &result)) { + callbacks->tag(context, _cbor_load_uint32(source + 1)); + } + return result; + } + case 0xDB: /* 8B tag */ + { + if (claim_bytes(8, source_size, &result)) { + callbacks->tag(context, _cbor_load_uint64(source + 1)); + } + return result; + } + case 0xDC: /* Fallthrough */ + case 0xDD: /* Fallthrough */ + case 0xDE: /* Fallthrough */ + case 0xDF: /* Reserved */ + { + return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; + } + case 0xE0: /* Fallthrough */ + case 0xE1: /* Fallthrough */ + case 0xE2: /* Fallthrough */ + case 0xE3: /* Fallthrough */ + case 0xE4: /* Fallthrough */ + case 0xE5: /* Fallthrough */ + case 0xE6: /* Fallthrough */ + case 0xE7: /* Fallthrough */ + case 0xE8: /* Fallthrough */ + case 0xE9: /* Fallthrough */ + case 0xEA: /* Fallthrough */ + case 0xEB: /* Fallthrough */ + case 0xEC: /* Fallthrough */ + case 0xED: /* Fallthrough */ + case 0xEE: /* Fallthrough */ + case 0xEF: /* Fallthrough */ + case 0xF0: /* Fallthrough */ + case 0xF1: /* Fallthrough */ + case 0xF2: /* Fallthrough */ + case 0xF3: /* Simple value - unassigned */ + { + return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; + } + case 0xF4: + /* False */ + { + callbacks->boolean(context, false); + return result; + } + case 0xF5: + /* True */ + { + callbacks->boolean(context, true); + return result; + } + case 0xF6: + /* Null */ + { + callbacks->null(context); + return result; + } + case 0xF7: + /* Undefined */ + { + callbacks->undefined(context); + return result; + } + case 0xF8: + /* 1B simple value, unassigned */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0xF9: + /* 2B float */ + { + if (claim_bytes(2, source_size, &result)) { + callbacks->float2(context, _cbor_load_half(source + 1)); + } + return result; + } + case 0xFA: + /* 4B float */ + { + if (claim_bytes(4, source_size, &result)) { + callbacks->float4(context, _cbor_load_float(source + 1)); + } + return result; + } + case 0xFB: + /* 8B float */ + { + if (claim_bytes(8, source_size, &result)) { + callbacks->float8(context, _cbor_load_double(source + 1)); + } + return result; + } + case 0xFC: /* Fallthrough */ + case 0xFD: /* Fallthrough */ + case 0xFE: + /* Reserved */ + { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; } + case 0xFF: + /* Break */ + callbacks->indef_break(context); + // Never happens, the switch statement is exhaustive on the 1B range; make + // compiler happy + default: + return result; + } +} diff --git a/source/external/libcbor/cbor/streaming.h b/source/external/libcbor/cbor/streaming.h new file mode 100644 index 000000000..cb908e17e --- /dev/null +++ b/source/external/libcbor/cbor/streaming.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_STREAMING_H +#define LIBCBOR_STREAMING_H + +#include "callbacks.h" +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Stateless decoder + * + * Will try parsing the \p source and will invoke the appropriate callback on + * success. Decodes one item at a time. No memory allocations occur. + * + * @param source Input buffer + * @param source_size Length of the buffer + * @param callbacks The callback bundle + * @param context An arbitrary pointer to allow for maintaining context. + */ +_CBOR_NODISCARD CBOR_EXPORT struct cbor_decoder_result cbor_stream_decode( + cbor_data source, size_t source_size, + const struct cbor_callbacks* callbacks, void* context); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_STREAMING_H diff --git a/source/external/libcbor/cbor/strings.c b/source/external/libcbor/cbor/strings.c new file mode 100644 index 000000000..6ae96545c --- /dev/null +++ b/source/external/libcbor/cbor/strings.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "strings.h" +#include +#include "internal/memory_utils.h" +#include "internal/unicode.h" + +cbor_item_t *cbor_new_definite_string(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_STRING, + .metadata = {.string_metadata = {_CBOR_METADATA_DEFINITE, 0}}}; + return item; +} + +cbor_item_t *cbor_new_indefinite_string(void) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_STRING, + .metadata = {.string_metadata = {.type = _CBOR_METADATA_INDEFINITE, + .length = 0}}, + .data = _cbor_malloc(sizeof(struct cbor_indefinite_string_data))}; + _CBOR_DEPENDENT_NOTNULL(item, item->data); + *((struct cbor_indefinite_string_data *)item->data) = + (struct cbor_indefinite_string_data){ + .chunk_count = 0, + .chunk_capacity = 0, + .chunks = NULL, + }; + return item; +} + +cbor_item_t *cbor_build_string(const char *val) { + cbor_item_t *item = cbor_new_definite_string(); + _CBOR_NOTNULL(item); + size_t len = strlen(val); + unsigned char *handle = _cbor_malloc(len); + _CBOR_DEPENDENT_NOTNULL(item, handle); + memcpy(handle, val, len); + cbor_string_set_handle(item, handle, len); + return item; +} + +cbor_item_t *cbor_build_stringn(const char *val, size_t length) { + cbor_item_t *item = cbor_new_definite_string(); + _CBOR_NOTNULL(item); + unsigned char *handle = _cbor_malloc(length); + _CBOR_DEPENDENT_NOTNULL(item, handle); + memcpy(handle, val, length); + cbor_string_set_handle(item, handle, length); + return item; +} + +void cbor_string_set_handle(cbor_item_t *item, + cbor_mutable_data CBOR_RESTRICT_POINTER data, + size_t length) { + CBOR_ASSERT(cbor_isa_string(item)); + CBOR_ASSERT(cbor_string_is_definite(item)); + item->data = data; + item->metadata.string_metadata.length = length; + struct _cbor_unicode_status unicode_status; + size_t codepoint_count = + _cbor_unicode_codepoint_count(data, length, &unicode_status); + CBOR_ASSERT(codepoint_count <= length); + if (unicode_status.status == _CBOR_UNICODE_OK) { + item->metadata.string_metadata.codepoint_count = codepoint_count; + } else { + item->metadata.string_metadata.codepoint_count = 0; + } +} + +cbor_item_t **cbor_string_chunks_handle(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_string(item)); + CBOR_ASSERT(cbor_string_is_indefinite(item)); + return ((struct cbor_indefinite_string_data *)item->data)->chunks; +} + +size_t cbor_string_chunk_count(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_string(item)); + CBOR_ASSERT(cbor_string_is_indefinite(item)); + return ((struct cbor_indefinite_string_data *)item->data)->chunk_count; +} + +bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk) { + CBOR_ASSERT(cbor_isa_string(item)); + CBOR_ASSERT(cbor_string_is_indefinite(item)); + struct cbor_indefinite_string_data *data = + (struct cbor_indefinite_string_data *)item->data; + if (data->chunk_count == data->chunk_capacity) { + if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) { + return false; + } + + size_t new_chunk_capacity = + data->chunk_capacity == 0 ? 1 + : CBOR_BUFFER_GROWTH * (data->chunk_capacity); + cbor_item_t **new_chunks_data = _cbor_realloc_multiple( + data->chunks, sizeof(cbor_item_t *), new_chunk_capacity); + + if (new_chunks_data == NULL) { + return false; + } + + data->chunk_capacity = new_chunk_capacity; + data->chunks = new_chunks_data; + } + data->chunks[data->chunk_count++] = cbor_incref(chunk); + return true; +} + +size_t cbor_string_length(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_string(item)); + return item->metadata.string_metadata.length; +} + +unsigned char *cbor_string_handle(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_string(item)); + return item->data; +} + +size_t cbor_string_codepoint_count(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_string(item)); + return item->metadata.string_metadata.codepoint_count; +} + +bool cbor_string_is_definite(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_string(item)); + return item->metadata.string_metadata.type == _CBOR_METADATA_DEFINITE; +} + +bool cbor_string_is_indefinite(const cbor_item_t *item) { + return !cbor_string_is_definite(item); +} diff --git a/source/external/libcbor/cbor/strings.h b/source/external/libcbor/cbor/strings.h new file mode 100644 index 000000000..3e03f8138 --- /dev/null +++ b/source/external/libcbor/cbor/strings.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_STRINGS_H +#define LIBCBOR_STRINGS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * String manipulation + * ============================================================================ + */ + +/** Returns the length of the underlying string in bytes + * + * There can be fewer unicode character than bytes (see + * `cbor_string_codepoint_count`). For definite strings only. + * + * @param item a definite string + * @return length of the string. Zero if no chunk has been attached yet + */ +_CBOR_NODISCARD CBOR_EXPORT size_t cbor_string_length(const cbor_item_t *item); + +/** The number of codepoints in this string + * + * Might differ from `cbor_string_length` if there are multibyte codepoints. + * If the string data is not valid UTF-8, returns 0. + * + * @param item A string + * @return The number of codepoints in this string + */ +_CBOR_NODISCARD CBOR_EXPORT size_t +cbor_string_codepoint_count(const cbor_item_t *item); + +/** Is the string definite? + * + * @param item a string + * @return Is the string definite? + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_string_is_definite( + const cbor_item_t *item); + +/** Is the string indefinite? + * + * @param item a string + * @return Is the string indefinite? + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_string_is_indefinite( + const cbor_item_t *item); + +/** Get the handle to the underlying string + * + * Definite items only. Modifying the data is allowed. In that case, the caller + * takes responsibility for the effect on items this item might be a part of + * + * @param item A definite string + * @return The address of the underlying string. + * @return `NULL` if no data have been assigned yet. + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_mutable_data +cbor_string_handle(const cbor_item_t *item); + +/** Set the handle to the underlying string + * + * The data is assumed to be a valid UTF-8 string. If the string is non-empty + * and invalid, `cbor_string_codepoint_count` will return 0. + * + * \rst + * .. warning:: Using a pointer to a stack allocated constant is a common + * mistake. Lifetime of the string will expire when it goes out of scope and + * the CBOR item will be left inconsistent. + * \endrst + * + * @param item A definite string + * @param data The memory block. The caller gives up the ownership of the block. + * libcbor will deallocate it when appropriate using its free function + * @param length Length of the data block + */ +CBOR_EXPORT void cbor_string_set_handle( + cbor_item_t *item, cbor_mutable_data CBOR_RESTRICT_POINTER data, + size_t length); + +/** Get the handle to the array of chunks + * + * Manipulations with the memory block (e.g. sorting it) are allowed, but the + * validity and the number of chunks must be retained. + * + * @param item A indefinite string + * @return array of #cbor_string_chunk_count definite strings + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t **cbor_string_chunks_handle( + const cbor_item_t *item); + +/** Get the number of chunks this string consist of + * + * @param item A indefinite string + * @return The chunk count. 0 for freshly created items. + */ +_CBOR_NODISCARD CBOR_EXPORT size_t +cbor_string_chunk_count(const cbor_item_t *item); + +/** Appends a chunk to the string + * + * Indefinite strings only. + * + * May realloc the chunk storage. + * + * @param item An indefinite string + * @param chunk A definite string item. Its reference count will be increased + * by one. + * @return `true` on success. `false` on memory allocation failure. In that + * case, the refcount of @p `chunk` is not increased and the @p `item` is left + * intact. + */ +_CBOR_NODISCARD CBOR_EXPORT bool cbor_string_add_chunk(cbor_item_t *item, + cbor_item_t *chunk); + +/** Creates a new definite string + * + * The handle is initialized to `NULL` and length to 0 + * + * @return Reference to the new string item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_definite_string(void); + +/** Creates a new indefinite string + * + * The chunks array is initialized to `NULL` and chunkcount to 0 + * + * @return Reference to the new string item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_indefinite_string(void); + +/** Creates a new string and initializes it + * + * The data from `val` will be copied to a newly allocated memory block. + * + * Note that valid UTF-8 strings do not contain null bytes, so this routine is + * correct for all valid inputs. If the input is not guaranteed to be valid, + * use `cbor_build_stringn` instead. + * + * @param val A null-terminated UTF-8 string + * @return Reference to the new string item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_string(const char *val); + +/** Creates a new string and initializes it + * + * The data from `handle` will be copied to a newly allocated memory block. + * + * All @p `length` bytes will be stored in the string, even if there are null + * bytes or invalid UTF-8 sequences. + * + * @param val A UTF-8 string, at least @p `length` bytes long + * @param length Length (in bytes) of the string passed in @p `val`. + * @return Reference to the new string item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_stringn(const char *val, + size_t length); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_STRINGS_H diff --git a/source/external/libcbor/cbor/tags.c b/source/external/libcbor/cbor/tags.c new file mode 100644 index 000000000..3f3edb0b0 --- /dev/null +++ b/source/external/libcbor/cbor/tags.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include "tags.h" + +cbor_item_t *cbor_new_tag(uint64_t value) { + cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); + _CBOR_NOTNULL(item); + + *item = (cbor_item_t){ + .refcount = 1, + .type = CBOR_TYPE_TAG, + .metadata = {.tag_metadata = {.value = value, .tagged_item = NULL}}, + .data = NULL /* Never used */ + }; + return item; +} + +cbor_item_t *cbor_tag_item(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_tag(item)); + return cbor_incref(item->metadata.tag_metadata.tagged_item); +} + +uint64_t cbor_tag_value(const cbor_item_t *item) { + CBOR_ASSERT(cbor_isa_tag(item)); + return item->metadata.tag_metadata.value; +} + +void cbor_tag_set_item(cbor_item_t *item, cbor_item_t *tagged_item) { + CBOR_ASSERT(cbor_isa_tag(item)); + cbor_incref(tagged_item); + item->metadata.tag_metadata.tagged_item = tagged_item; +} + +cbor_item_t *cbor_build_tag(uint64_t value, cbor_item_t *item) { + cbor_item_t *res = cbor_new_tag(value); + if (res == NULL) { + return NULL; + } + cbor_tag_set_item(res, item); + return res; +} diff --git a/source/external/libcbor/cbor/tags.h b/source/external/libcbor/cbor/tags.h new file mode 100644 index 000000000..a7365df10 --- /dev/null +++ b/source/external/libcbor/cbor/tags.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef LIBCBOR_TAGS_H +#define LIBCBOR_TAGS_H + +#include "cbor/cbor_export.h" +#include "cbor/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ============================================================================ + * Tag manipulation + * ============================================================================ + */ + +/** Create a new tag + * + * @param value The tag value. Please consult the tag repository + * @return Reference to the new tag item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_tag(uint64_t value); + +/** Get the tagged item + * + * @param item A tag + * @return Reference to the tagged item + * + * Increases the reference count of the underlying item. The returned reference + * must be released using #cbor_decref. + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_tag_item(const cbor_item_t *item); + +/** Get tag value + * + * @param item A tag + * @return The tag value. Please consult the tag repository + */ +_CBOR_NODISCARD CBOR_EXPORT uint64_t cbor_tag_value(const cbor_item_t *item); + +/** Set the tagged item + * + * @param item A tag + * @param tagged_item The item to tag. Its reference count will be increased + * by one. + */ +CBOR_EXPORT void cbor_tag_set_item(cbor_item_t *item, cbor_item_t *tagged_item); + +/** Build a new tag + * + * @param item The item to tag. Its reference count will be increased by + * one. + * @param value Tag value + * @return Reference to the new tag item. The item's reference count is + * initialized to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_tag(uint64_t value, + cbor_item_t *item); + +#ifdef __cplusplus +} +#endif + +#endif // LIBCBOR_TAGS_H diff --git a/source/json.c b/source/json.c index 326354d62..28524a88c 100644 --- a/source/json.c +++ b/source/json.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include "external/cJSON.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ee281ac28..d3556c5f7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -282,6 +282,8 @@ add_test_case(test_byte_buf_append_lookup_failure) add_test_case(test_byte_buf_reserve) add_test_case(test_byte_buf_reserve_initial_capacity_zero) add_test_case(test_byte_buf_reserve_relative) +add_test_case(test_byte_buf_reserve_smart) +add_test_case(test_byte_buf_reserve_smart_relative) add_test_case(test_byte_buf_reset) add_test_case(test_byte_cursor_compare_lexical) add_test_case(test_byte_cursor_compare_lookup) @@ -525,6 +527,15 @@ add_test_case(test_cross_process_lock_invalid_nonce_fails) add_test_case(host_util_is_ipv4) add_test_case(host_util_is_ipv6) +add_test_case(cbor_encode_decode_int_test) +add_test_case(cbor_encode_decode_double_test) +add_test_case(cbor_encode_decode_bool_test) +add_test_case(cbor_encode_decode_bytesstr_str_test) +add_test_case(cbor_encode_decode_array_map_test) +add_test_case(cbor_encode_decode_simple_value_test) +add_test_case(cbor_encode_decode_indef_test) +add_test_case(cbor_decode_error_handling_test) + generate_test_driver(${PROJECT_NAME}-tests) if(NOT MSVC AND NOT LEGACY_COMPILER_SUPPORT) diff --git a/tests/byte_buf_test.c b/tests/byte_buf_test.c index 545ec4a0e..035653853 100644 --- a/tests/byte_buf_test.c +++ b/tests/byte_buf_test.c @@ -4,7 +4,7 @@ */ #include - +#include #include #include @@ -798,6 +798,63 @@ static int s_test_byte_buf_reserve_relative(struct aws_allocator *allocator, voi } AWS_TEST_CASE(test_byte_buf_reserve_relative, s_test_byte_buf_reserve_relative) +static int s_test_byte_buf_reserve_smart(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + (void)allocator; + + struct aws_byte_buf buffer; + size_t base = 10; + aws_byte_buf_init(&buffer, allocator, base); + ASSERT_TRUE(aws_byte_buf_reserve_smart(&buffer, 2 * base) == AWS_OP_SUCCESS); + ASSERT_UINT_EQUALS(2 * base, buffer.capacity); + ASSERT_TRUE(aws_byte_buf_reserve_smart(&buffer, 3 * base) == AWS_OP_SUCCESS); + /* double the previous capacity instead of just expand the capacity to meet the requirement to reduce the number of + * allocations */ + ASSERT_UINT_EQUALS(4 * base, buffer.capacity); + ASSERT_TRUE(aws_byte_buf_reserve_smart(&buffer, 5 * base) == AWS_OP_SUCCESS); + /* double the previous capacity instead of just expand the capacity to meet the requirement to reduce the number of + * allocations */ + ASSERT_UINT_EQUALS(8 * base, buffer.capacity); + ASSERT_TRUE(aws_byte_buf_reserve_smart(&buffer, 5 * base) == AWS_OP_SUCCESS); + ASSERT_UINT_EQUALS(8 * base, buffer.capacity); + + aws_byte_buf_clean_up(&buffer); + + return 0; +} +AWS_TEST_CASE(test_byte_buf_reserve_smart, s_test_byte_buf_reserve_smart) + +static int s_test_byte_buf_reserve_smart_relative(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + (void)allocator; + + struct aws_byte_buf buffer; + aws_byte_buf_init(&buffer, allocator, 1); + + struct aws_byte_cursor prefix_cursor = aws_byte_cursor_from_string(s_reserve_test_prefix); + size_t length = prefix_cursor.len; + + ASSERT_TRUE(aws_byte_buf_reserve_smart_relative(&buffer, length) == AWS_OP_SUCCESS); + ASSERT_TRUE(aws_byte_buf_append(&buffer, &prefix_cursor) == AWS_OP_SUCCESS); + ASSERT_UINT_EQUALS(length, buffer.capacity); + ASSERT_TRUE(aws_byte_buf_reserve_smart_relative(&buffer, length) == AWS_OP_SUCCESS); + ASSERT_TRUE(aws_byte_buf_append(&buffer, &prefix_cursor) == AWS_OP_SUCCESS); + ASSERT_UINT_EQUALS(2 * length, buffer.capacity); + ASSERT_TRUE(aws_byte_buf_reserve_smart_relative(&buffer, length) == AWS_OP_SUCCESS); + ASSERT_TRUE(aws_byte_buf_append(&buffer, &prefix_cursor) == AWS_OP_SUCCESS); + /* 4 times base as it's expanded to twice the original capacity to prevent too many allocation */ + ASSERT_UINT_EQUALS(4 * length, buffer.capacity); + ASSERT_TRUE(aws_byte_buf_reserve_smart_relative(&buffer, length) == AWS_OP_SUCCESS); + ASSERT_TRUE(aws_byte_buf_append(&buffer, &prefix_cursor) == AWS_OP_SUCCESS); + /* still 4 times base as it doesn't need to expand again */ + ASSERT_UINT_EQUALS(4 * length, buffer.capacity); + + aws_byte_buf_clean_up(&buffer); + + return 0; +} +AWS_TEST_CASE(test_byte_buf_reserve_smart_relative, s_test_byte_buf_reserve_smart_relative) + static int s_test_byte_cursor_starts_with(struct aws_allocator *allocator, void *ctx) { (void)ctx; (void)allocator; diff --git a/tests/cbor_test.c b/tests/cbor_test.c new file mode 100644 index 000000000..585ffcca1 --- /dev/null +++ b/tests/cbor_test.c @@ -0,0 +1,489 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include + +#include + +#define CBOR_TEST_CASE(NAME) \ + AWS_TEST_CASE(NAME, s_test_##NAME); \ + static int s_test_##NAME(struct aws_allocator *allocator, void *ctx) + +CBOR_TEST_CASE(cbor_encode_decode_int_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + enum { VALUE_NUM = 6 }; + + /** + * Less than 24 only take 1 byte, + * 24 to uint8_t max takes 2 bytes + * uint8_t max to uint16_t max takes 3 bytes + * uint16_t max to uint32_t maxx takes 5 bytes + * uint32_t max to uint64_t max takes 9 bytes + */ + uint64_t values[VALUE_NUM] = {23, 24, UINT8_MAX + 1, UINT16_MAX + 1U, UINT32_MAX + 1LLU, UINT64_MAX}; + uint64_t expected_encoded_len[VALUE_NUM] = {1, 2, 3, 5, 9, 9}; + + size_t encoded_len = 0; + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + /* Unsigned int */ + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_uint(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + /* Negative int */ + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_negint(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + /* Unsigned int */ + for (size_t i = 0; i < VALUE_NUM; i++) { + uint64_t result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &result)); + ASSERT_UINT_EQUALS(values[i], result); + } + /* Negative int */ + for (size_t i = 0; i < VALUE_NUM; i++) { + uint64_t result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_negative_int_val(decoder, &result)); + ASSERT_UINT_EQUALS(values[i], result); + } + + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +CBOR_TEST_CASE(cbor_encode_decode_double_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + enum { VALUE_NUM = 10 }; + + /** + * 1 as unsigned int, takes 1 byte + * -1 as negative int, takes 1 byte + * 1.1 double, takes 9 bytes + * 1.1f is float, takes 5 bytes + * -1.1f is float, takes 5 bytes + * INFINITY will be float, takes 5 bytes + * FLT_MAX still a float, take 5 bytes + * DBL_MAX will be a double takes 9 bytes + * DBL_MIN will be a double takes 9 bytes + * HUGE_VAL + */ + double values[VALUE_NUM] = {1.0, -1.0, 1.1, 1.1f, -1.1f, INFINITY, FLT_MAX, DBL_MAX, DBL_MIN, HUGE_VAL}; + uint64_t expected_encoded_len[VALUE_NUM] = {1, 1, 9, 5, 5, 5, 5, 9, 9, 5}; + + int expected_encoded_type[VALUE_NUM] = { + AWS_CBOR_TYPE_UINT, + AWS_CBOR_TYPE_NEGINT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + AWS_CBOR_TYPE_FLOAT, + }; + + size_t encoded_len = 0; + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_float(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + /* Unsigned int, 1 */ + size_t index = 0; + uint64_t result = 0; + enum aws_cbor_type out_type = AWS_CBOR_TYPE_UNKNOWN; + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &result)); + ASSERT_TRUE(values[index++] == result); + /* negative int, -1 */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_negative_int_val(decoder, &result)); + /* Convert the decode val to expected val. */ + ASSERT_TRUE((-1 - values[index++]) == result); + /* 1.1 double */ + double double_result = 0; + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* 1.1 float */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* -1.1 float */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* INFINITY */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* FLT_MAX */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* DBL_MAX */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* DBL_MIN */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + /* HUGE_VAL */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, expected_encoded_type[index]); + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_float_val(decoder, &double_result)); + ASSERT_TRUE(values[index++] == double_result); + + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +CBOR_TEST_CASE(cbor_encode_decode_bool_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + enum { VALUE_NUM = 2 }; + bool values[VALUE_NUM] = {true, false}; + uint64_t expected_encoded_len[VALUE_NUM] = {1, 1}; + + size_t encoded_len = 0; + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_bool(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + for (size_t i = 0; i < VALUE_NUM; i++) { + bool result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_boolean_val(decoder, &result)); + ASSERT_UINT_EQUALS(values[i], result); + } + + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +CBOR_TEST_CASE(cbor_encode_decode_bytesstr_str_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + struct aws_byte_cursor val_1 = aws_byte_cursor_from_c_str("my test"); + struct aws_byte_cursor val_2 = aws_byte_cursor_from_c_str("write more tests"); + + enum { VALUE_NUM = 2 }; + struct aws_byte_cursor values[VALUE_NUM] = {val_1, val_2}; + uint64_t expected_encoded_len[VALUE_NUM] = {1 + val_1.len, 1 + val_2.len}; + + size_t encoded_len = 0; + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_text(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_bytes(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + for (size_t i = 0; i < VALUE_NUM; i++) { + struct aws_byte_cursor result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_text_val(decoder, &result)); + ASSERT_TRUE(aws_byte_cursor_eq(&result, &values[i])); + } + for (size_t i = 0; i < VALUE_NUM; i++) { + struct aws_byte_cursor result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_bytes_val(decoder, &result)); + ASSERT_TRUE(aws_byte_cursor_eq(&result, &values[i])); + } + + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +CBOR_TEST_CASE(cbor_encode_decode_array_map_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + struct aws_byte_cursor val_1 = aws_byte_cursor_from_c_str("my test"); + struct aws_byte_cursor val_2 = aws_byte_cursor_from_c_str("write more tests"); + + enum { VALUE_NUM = 2 }; + struct aws_byte_cursor values[VALUE_NUM] = {val_1, val_2}; + uint64_t expected_encoded_len[VALUE_NUM] = {1 + val_1.len, 1 + val_2.len}; + + size_t encoded_len = 0; + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + + /* Array with 2 elements */ + aws_cbor_encoder_write_array_start(encoder, 2); + struct aws_byte_cursor encoded_cursor = aws_cbor_encoder_get_encoded_data(encoder); + /* Array start with 2 element only takes 1 byte */ + ASSERT_UINT_EQUALS(encoded_len + 1, encoded_cursor.len); + encoded_len = encoded_cursor.len; + + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_text(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + + /* Map with 1 element */ + aws_cbor_encoder_write_map_start(encoder, 1); + encoded_cursor = aws_cbor_encoder_get_encoded_data(encoder); + /* Map start with 1 (key, value pair) only takes 1 byte */ + ASSERT_UINT_EQUALS(encoded_len + 1, encoded_cursor.len); + encoded_len = encoded_cursor.len; + for (size_t i = 0; i < VALUE_NUM; i++) { + aws_cbor_encoder_write_bytes(encoder, values[i]); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + ASSERT_UINT_EQUALS(encoded_len + expected_encoded_len[i], cursor.len); + encoded_len = cursor.len; + } + + /* Map with a lot element, not closure. */ + aws_cbor_encoder_write_array_start(encoder, UINT16_MAX + 1); + encoded_cursor = aws_cbor_encoder_get_encoded_data(encoder); + /* The size takes 4 bytes and one more for the cbor start item */ + ASSERT_UINT_EQUALS(encoded_len + 5, encoded_cursor.len); + encoded_len = encoded_cursor.len; + + aws_cbor_encoder_write_map_start(encoder, UINT16_MAX + 1); + encoded_cursor = aws_cbor_encoder_get_encoded_data(encoder); + /* The size takes 4 bytes and one more for the cbor start item */ + ASSERT_UINT_EQUALS(encoded_len + 5, encoded_cursor.len); + encoded_len = encoded_cursor.len; + + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + + uint64_t element_size = 0; + aws_cbor_decoder_pop_next_array_start(decoder, &element_size); + ASSERT_UINT_EQUALS(element_size, 2); + for (size_t i = 0; i < VALUE_NUM; i++) { + struct aws_byte_cursor result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_text_val(decoder, &result)); + ASSERT_TRUE(aws_byte_cursor_eq(&result, &values[i])); + } + aws_cbor_decoder_pop_next_map_start(decoder, &element_size); + ASSERT_UINT_EQUALS(element_size, 1); + for (size_t i = 0; i < VALUE_NUM; i++) { + struct aws_byte_cursor result; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_bytes_val(decoder, &result)); + ASSERT_TRUE(aws_byte_cursor_eq(&result, &values[i])); + } + aws_cbor_decoder_pop_next_array_start(decoder, &element_size); + ASSERT_UINT_EQUALS(element_size, UINT16_MAX + 1); + aws_cbor_decoder_pop_next_map_start(decoder, &element_size); + ASSERT_UINT_EQUALS(element_size, UINT16_MAX + 1); + + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +CBOR_TEST_CASE(cbor_encode_decode_simple_value_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + aws_cbor_encoder_write_null(encoder); + aws_cbor_encoder_write_undefined(encoder); + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + /* in total 2 bytes for two simple value */ + ASSERT_UINT_EQUALS(2, final_cursor.len); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + + enum aws_cbor_type out_type = AWS_CBOR_TYPE_UNKNOWN; + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, AWS_CBOR_TYPE_NULL); + ASSERT_SUCCESS(aws_cbor_decoder_consume_next_single_element(decoder)); + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, AWS_CBOR_TYPE_UNDEFINED); + ASSERT_SUCCESS(aws_cbor_decoder_consume_next_single_element(decoder)); + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +/* Test a complicate multiple stacks encode and decode */ +CBOR_TEST_CASE(cbor_encode_decode_indef_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + struct aws_byte_cursor val_1 = aws_byte_cursor_from_c_str("my test"); + struct aws_byte_cursor val_2 = aws_byte_cursor_from_c_str("write more tests"); + + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + + /* Create a non-sense stack of inf collections. */ + aws_cbor_encoder_write_indef_map_start(encoder); + /* Key */ + aws_cbor_encoder_write_text(encoder, val_1); + /* Value */ + aws_cbor_encoder_write_indef_array_start(encoder); + /* element 1 in array */ + aws_cbor_encoder_write_indef_text_start(encoder); + aws_cbor_encoder_write_text(encoder, val_1); + aws_cbor_encoder_write_text(encoder, val_2); + aws_cbor_encoder_write_break(encoder); + /* element 2 in array */ + aws_cbor_encoder_write_indef_bytes_start(encoder); + aws_cbor_encoder_write_bytes(encoder, val_1); + aws_cbor_encoder_write_bytes(encoder, val_2); + aws_cbor_encoder_write_break(encoder); + /* element 3 as a tag in array */ + aws_cbor_encoder_write_tag(encoder, AWS_CBOR_TAG_DECIMAL_FRACTION); + aws_cbor_encoder_write_indef_array_start(encoder); + aws_cbor_encoder_write_indef_bytes_start(encoder); + aws_cbor_encoder_write_bytes(encoder, val_1); + aws_cbor_encoder_write_bytes(encoder, val_2); + aws_cbor_encoder_write_break(encoder); + aws_cbor_encoder_write_break(encoder); + /* Closure for the array */ + aws_cbor_encoder_write_break(encoder); + /* Closure for the map */ + aws_cbor_encoder_write_break(encoder); + + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + + enum aws_cbor_type out_type = AWS_CBOR_TYPE_UNKNOWN; + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(out_type, AWS_CBOR_TYPE_INDEF_MAP_START); + + /* Get rid of the whole inf map with all the data content */ + ASSERT_SUCCESS(aws_cbor_decoder_consume_next_whole_data_item(decoder)); + + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} + +CBOR_TEST_CASE(cbor_decode_error_handling_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + /* Major type 7 with argument 30, 11111110, malformed CBOR */ + uint8_t invalid_data[] = {0xFE}; + struct aws_byte_cursor invalid_cbor = aws_byte_cursor_from_array(invalid_data, sizeof(invalid_data)); + + enum aws_cbor_type out_type = AWS_CBOR_TYPE_UNKNOWN; + + /* 1. Malformed cbor data */ + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, invalid_cbor); + ASSERT_FAILS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_CBOR, aws_last_error()); + aws_cbor_decoder_destroy(decoder); + + /* 2. Empty cursor */ + struct aws_byte_cursor empty = {0}; + decoder = aws_cbor_decoder_new(allocator, empty); + ASSERT_FAILS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_CBOR, aws_last_error()); + aws_cbor_decoder_destroy(decoder); + + /* 3. Try get wrong type */ + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + uint64_t val = 1; + aws_cbor_encoder_write_uint(encoder, val); + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + decoder = aws_cbor_decoder_new(allocator, final_cursor); + uint64_t out = 0; + ASSERT_FAILS(aws_cbor_decoder_pop_next_array_start(decoder, &out)); + ASSERT_UINT_EQUALS(AWS_ERROR_CBOR_UNEXPECTED_TYPE, aws_last_error()); + /* But, we can still keep decoding for the right type */ + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &out)); + ASSERT_UINT_EQUALS(val, out); + /* All the data has been consumed, now it's invalid */ + ASSERT_FAILS(aws_cbor_decoder_consume_next_whole_data_item(decoder)); + ASSERT_FAILS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_CBOR, aws_last_error()); + aws_cbor_decoder_destroy(decoder); + + /* 4. Consume data items with size */ + struct aws_byte_cursor val_1 = aws_byte_cursor_from_c_str("my test"); + aws_cbor_encoder_reset(encoder); + aws_cbor_encoder_write_map_start(encoder, 1); + /* Key */ + aws_cbor_encoder_write_text(encoder, val_1); + /* Value */ + aws_cbor_encoder_write_array_start(encoder, 1); + aws_cbor_encoder_write_tag(encoder, AWS_CBOR_TAG_NEGATIVE_BIGNUM); + aws_cbor_encoder_write_bytes(encoder, val_1); + final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + decoder = aws_cbor_decoder_new(allocator, final_cursor); + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(AWS_CBOR_TYPE_MAP_START, out_type); + ASSERT_SUCCESS(aws_cbor_decoder_consume_next_whole_data_item(decoder)); + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + aws_cbor_decoder_destroy(decoder); + + aws_cbor_encoder_destroy(encoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} diff --git a/tests/fuzz/cbor_decoding_transitive.c b/tests/fuzz/cbor_decoding_transitive.c new file mode 100644 index 000000000..3815db6cf --- /dev/null +++ b/tests/fuzz/cbor_decoding_transitive.c @@ -0,0 +1,87 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +/* NOLINTNEXTLINE(readability-identifier-naming) */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_BYTES, 0); + struct aws_byte_cursor input = aws_byte_cursor_from_array(data, size); + aws_common_library_init(allocator); + + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, input); + union { + uint64_t unsigned_val; + uint64_t neg_val; + double double_val; + uint64_t tag_val; + bool boolean_val; + struct aws_byte_cursor str_val; + struct aws_byte_cursor bytes_val; + uint64_t map_start; + uint64_t array_start; + } cbor_data; + + enum aws_cbor_type out_type = AWS_CBOR_TYPE_UNKNOWN; + while (aws_cbor_decoder_peek_type(decoder, &out_type) == AWS_OP_SUCCESS) { + switch (out_type) { + case AWS_CBOR_TYPE_UINT: + AWS_FATAL_ASSERT( + aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &cbor_data.unsigned_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_NEGINT: + AWS_FATAL_ASSERT( + aws_cbor_decoder_pop_next_negative_int_val(decoder, &cbor_data.neg_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_FLOAT: + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_float_val(decoder, &cbor_data.double_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_BYTES: + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_bytes_val(decoder, &cbor_data.bytes_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_TEXT: + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_text_val(decoder, &cbor_data.str_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_ARRAY_START: + AWS_FATAL_ASSERT( + aws_cbor_decoder_pop_next_array_start(decoder, &cbor_data.array_start) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_MAP_START: + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_map_start(decoder, &cbor_data.map_start) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_TAG: + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_tag_val(decoder, &cbor_data.tag_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_BOOL: + AWS_FATAL_ASSERT( + aws_cbor_decoder_pop_next_boolean_val(decoder, &cbor_data.boolean_val) == AWS_OP_SUCCESS); + break; + case AWS_CBOR_TYPE_NULL: + case AWS_CBOR_TYPE_UNDEFINED: + case AWS_CBOR_TYPE_BREAK: + case AWS_CBOR_TYPE_INDEF_BYTES_START: + case AWS_CBOR_TYPE_INDEF_TEXT_START: + case AWS_CBOR_TYPE_INDEF_ARRAY_START: + case AWS_CBOR_TYPE_INDEF_MAP_START: { + enum aws_cbor_type type = AWS_CBOR_TYPE_UNKNOWN; + AWS_FATAL_ASSERT(aws_cbor_decoder_peek_type(decoder, &type) == AWS_OP_SUCCESS); + AWS_FATAL_ASSERT(type == out_type); + AWS_FATAL_ASSERT(aws_cbor_decoder_consume_next_single_element(decoder) == AWS_OP_SUCCESS); + } break; + + default: + break; + } + } + aws_cbor_decoder_destroy(decoder); + + atexit(aws_common_library_clean_up); + + /* Check for leaks */ + AWS_FATAL_ASSERT(aws_mem_tracer_count(allocator) == 0); + allocator = aws_mem_tracer_destroy(allocator); + return 0; +} diff --git a/tests/fuzz/cbor_double_encode_decode.c b/tests/fuzz/cbor_double_encode_decode.c new file mode 100644 index 000000000..744d1cff8 --- /dev/null +++ b/tests/fuzz/cbor_double_encode_decode.c @@ -0,0 +1,66 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +/* NOLINTNEXTLINE(readability-identifier-naming) */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + struct aws_allocator *allocator = aws_mem_tracer_new(aws_default_allocator(), NULL, AWS_MEMTRACE_BYTES, 0); + struct aws_byte_cursor input = aws_byte_cursor_from_array(data, size); + double val = 0; + if (!aws_byte_cursor_read_float_be64(&input, &val)) { + allocator = aws_mem_tracer_destroy(allocator); + /* Ignore the invalid double */ + return 0; + } + aws_common_library_init(allocator); + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + aws_cbor_encoder_write_float(encoder, val); + + struct aws_byte_cursor final_cursor = aws_cbor_encoder_get_encoded_data(encoder); + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, final_cursor); + enum aws_cbor_type out_type = AWS_CBOR_TYPE_UNKNOWN; + AWS_FATAL_ASSERT(aws_cbor_decoder_peek_type(decoder, &out_type) == 0); + switch (out_type) { + case AWS_CBOR_TYPE_UINT: { + uint64_t result = 0; + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &result) == 0); + AWS_FATAL_ASSERT((double)result == val); + break; + } + case AWS_CBOR_TYPE_NEGINT: { + uint64_t result = 0; + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_negative_int_val(decoder, &result) == 0); + int64_t expected_val = -1 - result; + AWS_FATAL_ASSERT(expected_val == (int64_t)val); + break; + } + case AWS_CBOR_TYPE_FLOAT: { + double result = 0; + AWS_FATAL_ASSERT(aws_cbor_decoder_pop_next_float_val(decoder, &result) == 0); + if (isnan(val)) { + AWS_FATAL_ASSERT(isnan(result)); + } else { + AWS_FATAL_ASSERT(result == val); + } + break; + } + + default: + AWS_FATAL_ASSERT(false); + break; + } + AWS_FATAL_ASSERT(aws_cbor_decoder_get_remaining_length(decoder) == 0); + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + + atexit(aws_common_library_clean_up); + + /* Check for leaks */ + AWS_FATAL_ASSERT(aws_mem_tracer_count(allocator) == 0); + allocator = aws_mem_tracer_destroy(allocator); + return 0; +}