diff --git a/ext/Kconfig b/ext/Kconfig index 4242d5e0c3f3..7c06eccf3b4d 100644 --- a/ext/Kconfig +++ b/ext/Kconfig @@ -12,6 +12,8 @@ source "ext/hal/Kconfig" source "ext/lib/crypto/Kconfig" +source "ext/lib/encoding/Kconfig" + source "ext/debug/Kconfig" endmenu diff --git a/ext/lib/CMakeLists.txt b/ext/lib/CMakeLists.txt index 88a6a9e72883..4ea53ef5b0c6 100644 --- a/ext/lib/CMakeLists.txt +++ b/ext/lib/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(crypto) +add_subdirectory(encoding) diff --git a/ext/lib/encoding/CMakeLists.txt b/ext/lib/encoding/CMakeLists.txt new file mode 100644 index 000000000000..4e948835e60e --- /dev/null +++ b/ext/lib/encoding/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory_if_kconfig(tinycbor) diff --git a/ext/lib/encoding/Kconfig b/ext/lib/encoding/Kconfig new file mode 100644 index 000000000000..d048ab48994e --- /dev/null +++ b/ext/lib/encoding/Kconfig @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +menu "Encoding" + +source "ext/lib/encoding/tinycbor/Kconfig" + +endmenu diff --git a/ext/lib/encoding/tinycbor/CMakeLists.txt b/ext/lib/encoding/tinycbor/CMakeLists.txt new file mode 100644 index 000000000000..6ce5c0b1179f --- /dev/null +++ b/ext/lib/encoding/tinycbor/CMakeLists.txt @@ -0,0 +1,16 @@ +zephyr_interface_library_named(TINYCBOR) + +target_include_directories(TINYCBOR INTERFACE src) + +zephyr_library() +zephyr_library_sources( + src/cbor_buf_reader.c + src/cbor_buf_writer.c + src/cborencoder.c + src/cborerrorstrings.c + src/cborparser.c +) +zephyr_library_sources_ifdef(CONFIG_NEWLIB_LIBC src/cborparser_dup_string.c) + +zephyr_library_link_libraries(TINYCBOR) +target_link_libraries(TINYCBOR INTERFACE zephyr_interface) diff --git a/ext/lib/encoding/tinycbor/Kconfig b/ext/lib/encoding/tinycbor/Kconfig new file mode 100644 index 000000000000..fa438532e979 --- /dev/null +++ b/ext/lib/encoding/tinycbor/Kconfig @@ -0,0 +1,87 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +config TINYCBOR + bool + prompt "tinyCBOR Support" + default n + help + This option enables the tinyCBOR library. + +if TINYCBOR + +config CBOR_NO_DFLT_WRITER + bool + prompt "No default writer support" + default n + help + This option specifies whether a default writer exists. + +config CBOR_NO_DFLT_READER + bool + prompt "No default reader support" + default n + help + This option specifies whether a default reader exists. + +config CBOR_ENCODER_NO_CHECK_USER + bool + prompt "No encoder checks for user args for validity" + default n + help + This option specifies whether a check user exists for a cbor encoder. + +config CBOR_PARSER_MAX_RECURSIONS + int + prompt "Parser max recursions" + default 1024 + help + This option specifies max recursions for the parser. + +config CBOR_PARSER_NO_STRICT_CHECKS + bool + prompt "No strict parser checks" + default n + help + This option enables the strict parser checks. + +config CBOR_NO_FLOATING_POINT + bool + select NEWLIB_LIBC + prompt "No Floating point support" + default y + help + This option enables floating point support. + +config CBOR_NO_HALF_FLOAT_TYPE + bool + select NEWLIB_LIBC + prompt "No Half float type support" + default y + help + This option enables half float type support. + +config CBOR_WITHOUT_OPEN_MEMSTREAM + bool + prompt "Without open memstream" + default y + help + This option enables open memstream support. + +endif #TINYCBOR diff --git a/ext/lib/encoding/tinycbor/LICENSE b/ext/lib/encoding/tinycbor/LICENSE new file mode 100644 index 000000000000..4aad977ceb5e --- /dev/null +++ b/ext/lib/encoding/tinycbor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Intel Corporation + +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/ext/lib/encoding/tinycbor/README b/ext/lib/encoding/tinycbor/README new file mode 100644 index 000000000000..167efa06e2d3 --- /dev/null +++ b/ext/lib/encoding/tinycbor/README @@ -0,0 +1,13 @@ +Concise Binary Object Representation (CBOR) Library +--------------------------------------------------- + +To build TinyCBOR: + + make + +If you want to change the compiler or pass extra compiler flags: + + make CC=clang CFLAGS="-m32 -Oz" LDFLAGS="-m32" + +Documentation: https://intel.github.io/tinycbor/current/ + diff --git a/ext/lib/encoding/tinycbor/VERSION b/ext/lib/encoding/tinycbor/VERSION new file mode 100644 index 000000000000..2eb3c4fe4eeb --- /dev/null +++ b/ext/lib/encoding/tinycbor/VERSION @@ -0,0 +1 @@ +0.5 diff --git a/ext/lib/encoding/tinycbor/src/cbor.dox b/ext/lib/encoding/tinycbor/src/cbor.dox new file mode 100644 index 000000000000..cc7a79dde59b --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor.dox @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +/** + * \mainpage + * The TinyCBOR $(VERSION) library is a small CBOR encoder and decoder library, + * optimized for very fast operation with very small footprint. The main encoder + * and decoder functions do not allocate memory. + * + * TinyCBOR is divided into the following groups of functions and structures: + * - \ref CborGlobals + * - \ref CborEncoding + * - \ref CborParsing + * - \ref CborPretty + * - \ref CborToJson + */ + +/** + * \file + * The is the main header in TinyCBOR and defines the constants used by most functions + * as well as the structures for encoding (CborEncoder) and decoding (CborValue). + * + * \sa + */ + +/** + * \file + * The file contains the routines that are used to convert a CBOR + * data stream into JSON. + * + * \sa + */ + +/** + * \defgroup CborGlobals Global constants + * \brief Constants used by all TinyCBOR function groups. + */ + +/** + * \addtogroup CborGlobals + * @{ + */ + +/** + * \var size_t CborIndefiniteLength + * + * This variable is a constant used to indicate that the length of the map or + * array is not yet determined. It is used in functions + * cbor_encoder_create_map() and cbor_encoder_create_array() + */ + +/** + * \enum CborType + * The CborType enum contains the types known to TinyCBOR. + * + * \value CborIntegerType Type is an integer value, positive, negative or zero + * \value CborByteStringType Type is a string of arbitrary raw bytes + * \value CborTextStringType Type is a text string encoded in UTF-8 + * \value CborArrayType Type is a CBOR array + * \value CborMapType Type is a CBOR map (an associative container with key and value pairs) + * \value CborTagType Type is a CBOR tag (a 64-bit integer describing the item that follows, see CborKnownTags) + * \value CborSimpleType Type is one of CBOR Simple Types + * \value CborBooleanType Type is a boolean (true or false) + * \value CborNullType Type encodes a null + * \value CborUndefinedType Type encodes an undefined value + * \value CborHalfFloatType Type is an IEEE 754 half precision (16-bit) floating point type + * \value CborFloatType Type is an IEEE 754 single precision (32-bit) floating point type + * \value CborDoubleType Type is an IEEE 754 double precision (64-bit) floating point type + * \value CborInvalidType Type is not valid (this value is used to indicate error conditions) + */ + +/** + * \enum CborKnownTags + * The CborKnownTags enum contains known tags specified in RFC 7049, for use by the application. + * TinyCBOR does not usually interpret the meaning of these tags and does not add them to the + * output stream, unless specifically instructed to do so in functions for that effect. + * + * \value CborDateTimeStringTag Text string contains a date-time encoded in RFC 3339 format, "YYYY-MM-DD hh:mm:ss+zzzz" + * \value CborUnixTime_tTag Number is a Unix time_t quantity, the number of seconds since 1970-01-01 midnight UTC + * \value CborPositiveBignumTag Item is a CBOR byte string encoding a positive integer of arbitrary precision + * \value CborNegativeBignumTag Item is a CBOR byte string encoding a negative integer of arbitrary precision + * \value CborDecimalTag Item is a CBOR array of two integers encoding a fixed-point decimal + * \value CborBigfloatTag Item is a bigfloat + * \value CborExpectedBase64urlTag Item is a CBOR byte string that is expected to be encoded as Base64Url + * \value CborExpectedBase64Tag Item is a CBOR byte string that is expected to be encoded as Base64 + * \value CborExpectedBase16Tag Item is a CBOR byte string that is expected to be encoded as Base16 (also known as "hexdump") + * \value CborUriTag Item is a CBOR text string containing an URI (RFC 3986) or IRI (RFC 3987) + * \value CborBase64urlTag Item is a CBOR text string that was encoded as Base64Url + * \value CborBase64Tag Item is a CBOR text string that was encoded as Base64 + * \value CborRegularExpressionTag Item is a CBOR text string containing a regular expression + * \value CborMimeMessageTag Item is a CBOR text string containing a MIME message (RFC 2045, 2046, 2047, 2822) + * \value CborSignatureTag Item contains CBOR-encoded data. + * This tag is also used as "file magic," marking a file as containing CBOR + */ + +/** + * \typedef CborTag + * This typedef is an unsigned 64-bit integer. Known CBOR tags can be used from the CborKnownTags enum + * but the user application may use other tag values than the ones specified in RFC 7049. + */ + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cbor.h b/ext/lib/encoding/tinycbor/src/cbor.h new file mode 100644 index 000000000000..97fd6baf3096 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor.h @@ -0,0 +1,614 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_H +#define CBOR_H + +#ifndef assert +#include +#endif +#include +#include +#include +#include +#include + +#include "cbor_buf_writer.h" +#include "cbor_buf_reader.h" +#include "tinycbor-version.h" +#include "config.h" + +#define TINYCBOR_VERSION ((TINYCBOR_VERSION_MAJOR << 16) | (TINYCBOR_VERSION_MINOR << 8) | TINYCBOR_VERSION_PATCH) + +#ifdef __cplusplus +extern "C" { +#else +#include +#endif + +#ifndef SIZE_MAX +/* Some systems fail to define SIZE_MAX in , even though C99 requires it... + * Conversion from signed to unsigned is defined in 6.3.1.3 (Signed and unsigned integers) p2, + * which says: "the value is converted by repeatedly adding or subtracting one more than the + * maximum value that can be represented in the new type until the value is in the range of the + * new type." + * So -1 gets converted to size_t by adding SIZE_MAX + 1, which results in SIZE_MAX. + */ +# define SIZE_MAX ((size_t)-1) +#endif + +#ifndef CBOR_API +# define CBOR_API +#endif +#ifndef CBOR_PRIVATE_API +# define CBOR_PRIVATE_API +#endif +#ifndef CBOR_INLINE_API +# if defined(__cplusplus) +# define CBOR_INLINE inline +# define CBOR_INLINE_API inline +# else +# define CBOR_INLINE_API static CBOR_INLINE +# if defined(_MSC_VER) +# define CBOR_INLINE __inline +# elif defined(__GNUC__) +# define CBOR_INLINE __inline__ +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define CBOR_INLINE inline +# else +# define CBOR_INLINE +# endif +# endif +#endif + +typedef enum CborType { + CborIntegerType = 0x00, + CborByteStringType = 0x40, + CborTextStringType = 0x60, + CborArrayType = 0x80, + CborMapType = 0xa0, + CborTagType = 0xc0, + CborSimpleType = 0xe0, + CborBooleanType = 0xf5, + CborNullType = 0xf6, + CborUndefinedType = 0xf7, + CborHalfFloatType = 0xf9, + CborFloatType = 0xfa, + CborDoubleType = 0xfb, + + CborInvalidType = 0xff /* equivalent to the break byte, so it will never be used */ +} CborType; + +typedef uint64_t CborTag; +typedef enum CborKnownTags { + CborDateTimeStringTag = 0, + CborUnixTime_tTag = 1, + CborPositiveBignumTag = 2, + CborNegativeBignumTag = 3, + CborDecimalTag = 4, + CborBigfloatTag = 5, + CborCOSE_Encrypt0Tag = 16, + CborCOSE_Mac0Tag = 17, + CborCOSE_Sign1Tag = 18, + CborExpectedBase64urlTag = 21, + CborExpectedBase64Tag = 22, + CborExpectedBase16Tag = 23, + CborEncodedCborTag = 24, + CborUrlTag = 32, + CborBase64urlTag = 33, + CborBase64Tag = 34, + CborRegularExpressionTag = 35, + CborMimeMessageTag = 36, + CborCOSE_EncryptTag = 96, + CborCOSE_MacTag = 97, + CborCOSE_SignTag = 98, + CborSignatureTag = 55799 +} CborKnownTags; + +/* #define the constants so we can check with #ifdef */ +#define CborDateTimeStringTag CborDateTimeStringTag +#define CborUnixTime_tTag CborUnixTime_tTag +#define CborPositiveBignumTag CborPositiveBignumTag +#define CborNegativeBignumTag CborNegativeBignumTag +#define CborDecimalTag CborDecimalTag +#define CborBigfloatTag CborBigfloatTag +#define CborCOSE_Encrypt0Tag CborCOSE_Encrypt0Tag +#define CborCOSE_Mac0Tag CborCOSE_Mac0Tag +#define CborCOSE_Sign1Tag CborCOSE_Sign1Tag +#define CborExpectedBase64urlTag CborExpectedBase64urlTag +#define CborExpectedBase64Tag CborExpectedBase64Tag +#define CborExpectedBase16Tag CborExpectedBase16Tag +#define CborEncodedCborTag CborEncodedCborTag +#define CborUrlTag CborUrlTag +#define CborBase64urlTag CborBase64urlTag +#define CborBase64Tag CborBase64Tag +#define CborRegularExpressionTag CborRegularExpressionTag +#define CborMimeMessageTag CborMimeMessageTag +#define CborCOSE_EncryptTag CborCOSE_EncryptTag +#define CborCOSE_MacTag CborCOSE_MacTag +#define CborCOSE_SignTag CborCOSE_SignTag +#define CborSignatureTag CborSignatureTag + +/* Error API */ + +typedef enum CborError { + CborNoError = 0, + + /* errors in all modes */ + CborUnknownError, + CborErrorUnknownLength, /* request for length in array, map, or string with indeterminate length */ + CborErrorAdvancePastEOF, + CborErrorIO, + + /* parser errors streaming errors */ + CborErrorGarbageAtEnd = 256, + CborErrorUnexpectedEOF, + CborErrorUnexpectedBreak, + CborErrorUnknownType, /* can only heppen in major type 7 */ + CborErrorIllegalType, /* type not allowed here */ + CborErrorIllegalNumber, + CborErrorIllegalSimpleType, /* types of value less than 32 encoded in two bytes */ + + /* parser errors in strict mode parsing only */ + CborErrorUnknownSimpleType = 512, + CborErrorUnknownTag, + CborErrorInappropriateTagForType, + CborErrorDuplicateObjectKeys, + CborErrorInvalidUtf8TextString, + CborErrorExcludedType, + CborErrorExcludedValue, + CborErrorImproperValue, + CborErrorOverlongEncoding, + CborErrorMapKeyNotString, + CborErrorMapNotSorted, + CborErrorMapKeysNotUnique, + + /* encoder errors */ + CborErrorTooManyItems = 768, + CborErrorTooFewItems, + + /* internal implementation errors */ + CborErrorDataTooLarge = 1024, + CborErrorNestingTooDeep, + CborErrorUnsupportedType, + + /* errors in converting to JSON */ + CborErrorJsonObjectKeyIsAggregate = 1280, + CborErrorJsonObjectKeyNotString, + CborErrorJsonNotImplemented, + + CborErrorOutOfMemory = (int) (~0U / 2 + 1), + CborErrorInternalError = (int) (~0U / 2) /* INT_MAX on two's complement machines */ +} CborError; + +CBOR_API const char *cbor_error_string(CborError error); + +/* Encoder API */ +struct CborEncoder +{ + cbor_encoder_writer *writer; + void *writer_arg; +#ifndef CBOR_NO_DFLT_WRITER + struct cbor_buf_writer wr; +#endif + size_t remaining; + int flags; +}; + +typedef struct CborEncoder CborEncoder; + +static const size_t CborIndefiniteLength = SIZE_MAX; + +#ifndef CBOR_NO_DFLT_WRITER +CBOR_API void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags); +#endif +CBOR_API void cbor_encoder_cust_writer_init(CborEncoder *encoder, struct cbor_encoder_writer *w, int flags); +CBOR_API CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value); +CBOR_API CborError cbor_encode_int(CborEncoder *encoder, int64_t value); +CBOR_API CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value); +CBOR_API CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value); +CBOR_API CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag); +CBOR_API CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length); +CBOR_INLINE_API CborError cbor_encode_text_stringz(CborEncoder *encoder, const char *string) +{ return cbor_encode_text_string(encoder, string, strlen(string)); } +CBOR_API CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length); +CBOR_API CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value); +CBOR_INLINE_API int cbor_encode_bytes_written(CborEncoder *encoder) +{ return encoder->writer->bytes_written; } +CBOR_INLINE_API CborError cbor_encode_boolean(CborEncoder *encoder, bool value) +{ return cbor_encode_simple_value(encoder, (int)value - 1 + (CborBooleanType & 0x1f)); } +CBOR_INLINE_API CborError cbor_encode_null(CborEncoder *encoder) +{ return cbor_encode_simple_value(encoder, CborNullType & 0x1f); } +CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder) +{ return cbor_encode_simple_value(encoder, CborUndefinedType & 0x1f); } + +CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) +{ return cbor_encode_floating_point(encoder, CborHalfFloatType, value); } +CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value) +{ return cbor_encode_floating_point(encoder, CborFloatType, &value); } +CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value) +{ return cbor_encode_floating_point(encoder, CborDoubleType, &value); } + +CBOR_API CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length); +CBOR_API CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length); +CBOR_API CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder); +CBOR_API CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder); + +/* Parser API */ + +enum CborParserIteratorFlags +{ + CborIteratorFlag_IntegerValueTooLarge = 0x01, + CborIteratorFlag_NegativeInteger = 0x02, + CborIteratorFlag_IteratingStringChunks = 0x02, + CborIteratorFlag_UnknownLength = 0x04, + CborIteratorFlag_ContainerIsMap = 0x20 +}; + +struct CborParser +{ +#ifndef CBOR_NO_DFLT_READER + struct cbor_buf_reader br; +#endif + struct cbor_decoder_reader *d; + int end; + int flags; +}; +typedef struct CborParser CborParser; + +struct CborValue +{ + const CborParser *parser; + int offset; + uint32_t remaining; + uint32_t remainingclen; + uint16_t extra; + uint8_t type; + uint8_t flags; +}; +typedef struct CborValue CborValue; + +#ifndef CBOR_NO_DFLT_READER +CBOR_API CborError cbor_parser_init(const uint8_t *buffer, size_t size, int flags, CborParser *parser, CborValue *it); +#endif +CBOR_API CborError cbor_parser_cust_reader_init(struct cbor_decoder_reader *r, int flags, CborParser *parser, CborValue *it); + +CBOR_API CborError cbor_value_validate_basic(const CborValue *it); + +CBOR_INLINE_API bool cbor_value_at_end(const CborValue *it) +{ return it->remaining == 0; } +CBOR_API CborError cbor_value_advance_fixed(CborValue *it); +CBOR_API CborError cbor_value_advance(CborValue *it); +CBOR_INLINE_API bool cbor_value_is_container(const CborValue *it) +{ return it->type == CborArrayType || it->type == CborMapType; } +CBOR_API CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed); +CBOR_API CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed); + +CBOR_PRIVATE_API uint64_t _cbor_value_decode_int64_internal(const CborValue *value); +CBOR_INLINE_API uint64_t _cbor_value_extract_int64_helper(const CborValue *value) +{ + return value->flags & CborIteratorFlag_IntegerValueTooLarge ? + _cbor_value_decode_int64_internal(value) : value->extra; +} + +CBOR_INLINE_API bool cbor_value_is_valid(const CborValue *value) +{ return value && value->type != CborInvalidType; } +CBOR_INLINE_API CborType cbor_value_get_type(const CborValue *value) +{ return (CborType)value->type; } + +/* Null & undefined type */ +CBOR_INLINE_API bool cbor_value_is_null(const CborValue *value) +{ return value->type == CborNullType; } +CBOR_INLINE_API bool cbor_value_is_undefined(const CborValue *value) +{ return value->type == CborUndefinedType; } + +/* Booleans */ +CBOR_INLINE_API bool cbor_value_is_boolean(const CborValue *value) +{ return value->type == CborBooleanType; } +CBOR_INLINE_API CborError cbor_value_get_boolean(const CborValue *value, bool *result) +{ + assert(cbor_value_is_boolean(value)); + *result = !!value->extra; + return CborNoError; +} + +/* Simple types */ +CBOR_INLINE_API bool cbor_value_is_simple_type(const CborValue *value) +{ return value->type == CborSimpleType; } +CBOR_INLINE_API CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) +{ + assert(cbor_value_is_simple_type(value)); + *result = (uint8_t)value->extra; + return CborNoError; +} + +/* Integers */ +CBOR_INLINE_API bool cbor_value_is_integer(const CborValue *value) +{ return value->type == CborIntegerType; } +CBOR_INLINE_API bool cbor_value_is_unsigned_integer(const CborValue *value) +{ return cbor_value_is_integer(value) && (value->flags & CborIteratorFlag_NegativeInteger) == 0; } +CBOR_INLINE_API bool cbor_value_is_negative_integer(const CborValue *value) +{ return cbor_value_is_integer(value) && (value->flags & CborIteratorFlag_NegativeInteger); } + +CBOR_INLINE_API CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) +{ + assert(cbor_value_is_integer(value)); + *result = _cbor_value_extract_int64_helper(value); + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) +{ + assert(cbor_value_is_unsigned_integer(value)); + *result = _cbor_value_extract_int64_helper(value); + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t *result) +{ + assert(cbor_value_is_integer(value)); + *result = (int64_t) _cbor_value_extract_int64_helper(value); + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_int(const CborValue *value, int *result) +{ + assert(cbor_value_is_integer(value)); + *result = (int) _cbor_value_extract_int64_helper(value); + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +CBOR_API CborError cbor_value_get_int64_checked(const CborValue *value, int64_t *result); +CBOR_API CborError cbor_value_get_int_checked(const CborValue *value, int *result); + +CBOR_INLINE_API bool cbor_value_is_length_known(const CborValue *value) +{ return (value->flags & CborIteratorFlag_UnknownLength) == 0; } + +/* Tags */ +CBOR_INLINE_API bool cbor_value_is_tag(const CborValue *value) +{ return value->type == CborTagType; } +CBOR_INLINE_API CborError cbor_value_get_tag(const CborValue *value, CborTag *result) +{ + assert(cbor_value_is_tag(value)); + *result = _cbor_value_extract_int64_helper(value); + return CborNoError; +} +CBOR_API CborError cbor_value_skip_tag(CborValue *it); + +/* Strings */ +CBOR_INLINE_API bool cbor_value_is_byte_string(const CborValue *value) +{ return value->type == CborByteStringType; } +CBOR_INLINE_API bool cbor_value_is_text_string(const CborValue *value) +{ return value->type == CborTextStringType; } + +CBOR_INLINE_API CborError cbor_value_get_string_length(const CborValue *value, size_t *length) +{ + assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); + if (!cbor_value_is_length_known(value)) + return CborErrorUnknownLength; + uint64_t v = _cbor_value_extract_int64_helper(value); + *length = (size_t)v; + if (*length != v) + return CborErrorDataTooLarge; + return CborNoError; +} + +CBOR_PRIVATE_API CborError _cbor_value_copy_string(const CborValue *value, void *buffer, + size_t *buflen, CborValue *next); +CBOR_PRIVATE_API CborError _cbor_value_dup_string(const CborValue *value, void **buffer, + size_t *buflen, CborValue *next); + +CBOR_API CborError cbor_value_calculate_string_length(const CborValue *value, size_t *length); + +CBOR_INLINE_API CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_text_string(value)); + return _cbor_value_copy_string(value, buffer, buflen, next); +} +CBOR_INLINE_API CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_byte_string(value)); + return _cbor_value_copy_string(value, buffer, buflen, next); +} + +CBOR_INLINE_API CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_text_string(value)); + return _cbor_value_dup_string(value, (void **)buffer, buflen, next); +} +CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_byte_string(value)); + return _cbor_value_dup_string(value, (void **)buffer, buflen, next); +} + +CBOR_PRIVATE_API CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, + size_t *len, CborValue *next); +CBOR_INLINE_API CborError cbor_value_get_text_string_chunk(const CborValue *value, const char **bufferptr, + size_t *len, CborValue *next) +{ + assert(cbor_value_is_text_string(value)); + return _cbor_value_get_string_chunk(value, (const void **)bufferptr, len, next); +} +CBOR_INLINE_API CborError cbor_value_get_byte_string_chunk(const CborValue *value, const uint8_t **bufferptr, + size_t *len, CborValue *next) +{ + assert(cbor_value_is_byte_string(value)); + return _cbor_value_get_string_chunk(value, (const void **)bufferptr, len, next); +} + +CBOR_API CborError cbor_value_text_string_equals(const CborValue *value, const char *string, bool *result); + +/* Maps and arrays */ +CBOR_INLINE_API bool cbor_value_is_array(const CborValue *value) +{ return value->type == CborArrayType; } +CBOR_INLINE_API bool cbor_value_is_map(const CborValue *value) +{ return value->type == CborMapType; } + +CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, size_t *length) +{ + assert(cbor_value_is_array(value)); + if (!cbor_value_is_length_known(value)) + return CborErrorUnknownLength; + uint64_t v = _cbor_value_extract_int64_helper(value); + *length = (size_t)v; + if (*length != v) + return CborErrorDataTooLarge; + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_map_length(const CborValue *value, size_t *length) +{ + assert(cbor_value_is_map(value)); + if (!cbor_value_is_length_known(value)) + return CborErrorUnknownLength; + uint64_t v = _cbor_value_extract_int64_helper(value); + *length = (size_t)v; + if (*length != v) + return CborErrorDataTooLarge; + return CborNoError; +} + +CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element); + +/* Floating point */ +CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) +{ return value->type == CborHalfFloatType; } +CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result); + +CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) +{ return value->type == CborFloatType; } +CBOR_INLINE_API CborError cbor_value_get_float(const CborValue *value, float *result) +{ + assert(cbor_value_is_float(value)); + assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); + uint32_t data = (uint32_t)_cbor_value_decode_int64_internal(value); + memcpy(result, &data, sizeof(*result)); + return CborNoError; +} + +CBOR_INLINE_API bool cbor_value_is_double(const CborValue *value) +{ return value->type == CborDoubleType; } +CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double *result) +{ + assert(cbor_value_is_double(value)); + assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); + uint64_t data = _cbor_value_decode_int64_internal(value); + memcpy(result, &data, sizeof(*result)); + return CborNoError; +} + +/* Validation API */ + +enum CborValidationFlags { + /* Bit mapping: + * bits 0-7 (8 bits): canonical format + * bits 8-11 (4 bits): canonical format & strict mode + * bits 12-20 (8 bits): strict mode + * bits 21-31 (10 bits): other + */ + + CborValidateShortestIntegrals = 0x0001, + CborValidateShortestFloatingPoint = 0x0002, + CborValidateShortestNumbers = CborValidateShortestIntegrals | CborValidateShortestFloatingPoint, + CborValidateNoIndeterminateLength = 0x0100, + CborValidateMapIsSorted = 0x0200 | CborValidateNoIndeterminateLength, + + CborValidateCanonicalFormat = 0x0fff, + + CborValidateMapKeysAreUnique = 0x1000 | CborValidateMapIsSorted, + CborValidateTagUse = 0x2000, + CborValidateUtf8 = 0x4000, + + CborValidateStrictMode = 0xfff00, + + CborValidateMapKeysAreString = 0x100000, + CborValidateNoUndefined = 0x200000, + CborValidateNoTags = 0x400000, + CborValidateFiniteFloatingPoint = 0x800000, + /* unused = 0x1000000, */ + /* unused = 0x2000000, */ + + CborValidateNoUnknownSimpleTypesSA = 0x4000000, + CborValidateNoUnknownSimpleTypes = 0x8000000 | CborValidateNoUnknownSimpleTypesSA, + CborValidateNoUnknownTagsSA = 0x10000000, + CborValidateNoUnknownTagsSR = 0x20000000 | CborValidateNoUnknownTagsSA, + CborValidateNoUnknownTags = 0x40000000 | CborValidateNoUnknownTagsSR, + + CborValidateCompleteData = (int)0x80000000, + + CborValidateStrictest = (int)~0U, + CborValidateBasic = 0 +}; + +CBOR_API CborError cbor_value_validate(const CborValue *it, int flags); + +/* Human-readable (dump) API */ + +enum CborPrettyFlags { + CborPrettyNumericEncodingIndicators = 0x01, + CborPrettyTextualEncodingIndicators = 0, + + CborPrettyIndicateIndetermineLength = 0x02, + CborPrettyIndicateOverlongNumbers = 0x04, + + CborPrettyShowStringFragments = 0x100, + CborPrettyMergeStringFragments = 0, + + CborPrettyDefaultFlags = CborPrettyIndicateIndetermineLength +}; + +typedef CborError (*CborStreamFunction)(void *token, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((__format__(printf, 2, 3))) +#endif +; + +CBOR_API CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags); + +/* The following API requires a hosted C implementation (uses FILE*) */ +#if !defined(__STDC_HOSTED__) || __STDC_HOSTED__-0 == 1 +CBOR_API CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags); +CBOR_API CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value); +CBOR_INLINE_API CborError cbor_value_to_pretty(FILE *out, const CborValue *value) +{ + CborValue copy = *value; + return cbor_value_to_pretty_advance_flags(out, ©, CborPrettyDefaultFlags); +} +#endif /* __STDC_HOSTED__ check */ + +#ifdef __cplusplus +} +#endif + +#endif /* CBOR_H */ + diff --git a/ext/lib/encoding/tinycbor/src/cbor_buf_reader.c b/ext/lib/encoding/tinycbor/src/cbor_buf_reader.c new file mode 100644 index 000000000000..c3e6a5732801 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_buf_reader.c @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#include "cbor_buf_reader.h" +#include "compilersupport_p.h" + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * Gets 16 bit unsigned value from the passed in ptr location, it also + * converts it to host byte order + */ +CBOR_INLINE_API uint16_t get16(const uint8_t *ptr) +{ + uint16_t result; + memcpy(&result, ptr, sizeof(result)); + return cbor_ntohs(result); +} + +/** + * Gets 32 bit unsigned value from the passed in ptr location, it also + * converts it to host byte order + */ +CBOR_INLINE_API uint32_t get32(const uint8_t *ptr) +{ + uint32_t result; + memcpy(&result, ptr, sizeof(result)); + return cbor_ntohl(result); +} + +/** + * Gets 64 bit unsigned value from the passed in ptr location, it also + * converts it to host byte order + */ +CBOR_INLINE_API uint64_t get64(const uint8_t *ptr) +{ + uint64_t result; + memcpy(&result, ptr, sizeof(result)); + return cbor_ntohll(result); +} + +/** + * Gets a string chunk from the passed in ptr location + */ +CBOR_INLINE_API uintptr_t get_string_chunk(const uint8_t *ptr) +{ + return (uintptr_t)ptr; +} + +/** + * Gets 8 bit unsigned value using the buffer pointed to by the + * decoder reader from passed in offset + */ +static uint8_t +cbuf_buf_reader_get8(struct cbor_decoder_reader *d, int offset) +{ + struct cbor_buf_reader *cb = (struct cbor_buf_reader *) d; + return cb->buffer[offset]; +} + +/** + * Gets 16 bit unsigned value using the buffer pointed to by the + * decoder reader from passed in offset + */ +static uint16_t +cbuf_buf_reader_get16(struct cbor_decoder_reader *d, int offset) +{ + struct cbor_buf_reader *cb = (struct cbor_buf_reader *) d; + return get16(cb->buffer + offset); +} + +/** + * Gets 32 bit unsigned value using the buffer pointed to by the + * decoder reader from passed in offset + */ +static uint32_t +cbuf_buf_reader_get32(struct cbor_decoder_reader *d, int offset) +{ + uint32_t val; + struct cbor_buf_reader *cb = (struct cbor_buf_reader *) d; + val = get32(cb->buffer + offset); + return val; +} + +/** + * Gets 64 bit unsigned value using the buffer pointed to by the + * decoder reader from passed in offset + */ +static uint64_t +cbuf_buf_reader_get64(struct cbor_decoder_reader *d, int offset) +{ + struct cbor_buf_reader *cb = (struct cbor_buf_reader *) d; + return get64(cb->buffer + offset); +} + +static uintptr_t +cbor_buf_reader_get_string_chunk(struct cbor_decoder_reader *d, + int offset, size_t *len) +{ + struct cbor_buf_reader *cb = (struct cbor_buf_reader *)d; + + (void)*len; + + return get_string_chunk(cb->buffer + offset); +} + +static uintptr_t +cbor_buf_reader_cmp(struct cbor_decoder_reader *d, char *dst, int src_offset, + size_t len) +{ + struct cbor_buf_reader *cb = (struct cbor_buf_reader *) d; + + return !memcmp(dst, cb->buffer + src_offset, len); +} + +static uintptr_t +cbor_buf_reader_cpy(struct cbor_decoder_reader *d, char *dst, int src_offset, + size_t len) +{ + struct cbor_buf_reader *cb = (struct cbor_buf_reader *) d; + return (uintptr_t) memcpy(dst, cb->buffer + src_offset, len); +} + +void +cbor_buf_reader_init(struct cbor_buf_reader *cb, const uint8_t *buffer, + size_t data) +{ + cb->buffer = buffer; + cb->r.get8 = &cbuf_buf_reader_get8; + cb->r.get16 = &cbuf_buf_reader_get16; + cb->r.get32 = &cbuf_buf_reader_get32; + cb->r.get64 = &cbuf_buf_reader_get64; + cb->r.cmp = &cbor_buf_reader_cmp; + cb->r.cpy = &cbor_buf_reader_cpy; + cb->r.get_string_chunk = &cbor_buf_reader_get_string_chunk; + cb->r.message_size = data; +} + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cbor_buf_reader.h b/ext/lib/encoding/tinycbor/src/cbor_buf_reader.h new file mode 100644 index 000000000000..70408fd6a3a8 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_buf_reader.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_BUF_READER_H +#define CBOR_BUF_READER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cbor_decoder_reader.h" + +struct cbor_buf_reader { + struct cbor_decoder_reader r; + const uint8_t *buffer; +}; + +void cbor_buf_reader_init(struct cbor_buf_reader *cb, const uint8_t *buffer, + size_t data); + +#ifdef __cplusplus +} +#endif + +#endif /* CBOR_BUF_READER_H */ + diff --git a/ext/lib/encoding/tinycbor/src/cbor_buf_writer.c b/ext/lib/encoding/tinycbor/src/cbor_buf_writer.c new file mode 100644 index 000000000000..fb401bcfad6c --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_buf_writer.c @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#include "cbor.h" +#include "cbor_buf_writer.h" + +CBOR_INLINE_API int +would_overflow(struct cbor_buf_writer *cb, size_t len) +{ + ptrdiff_t remaining = (ptrdiff_t)cb->end; + + if (!remaining) + return 1; + remaining -= (ptrdiff_t)cb->ptr; + remaining -= (ptrdiff_t)len; + return (remaining < 0); +} + +int +cbor_buf_writer(struct cbor_encoder_writer *arg, const char *data, int len) +{ + struct cbor_buf_writer *cb = (struct cbor_buf_writer *) arg; + + if (would_overflow(cb, len)) { + if (cb->end != NULL) { + len -= cb->end - cb->ptr; + cb->end = NULL; + cb->bytes_needed = 0; + } + + cb->bytes_needed += len; + + return CborErrorOutOfMemory; + } + + memcpy(cb->ptr, data, len); + cb->ptr += len; + cb->enc.bytes_written += len; + return CborNoError; +} + +void +cbor_buf_writer_init(struct cbor_buf_writer *cb, uint8_t *buffer, size_t size) +{ + cb->ptr = buffer; + cb->end = buffer + size; + cb->enc.bytes_written = 0; + cb->bytes_needed = 0; + cb->enc.write = cbor_buf_writer; +} + +size_t +cbor_buf_writer_buffer_size(struct cbor_buf_writer *cb, const uint8_t *buffer) +{ + return (size_t)(cb->ptr - buffer); +} + +size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *encoder) +{ + struct cbor_buf_writer *wr = (struct cbor_buf_writer *)encoder->writer; + + return wr->end ? 0 : (size_t)wr->bytes_needed; +} + +size_t cbor_encoder_get_buffer_size(const CborEncoder *encoder, const uint8_t *buffer) +{ + struct cbor_buf_writer *wr = (struct cbor_buf_writer *)encoder->writer; + + return (size_t)(wr->ptr - buffer); +} + +uint8_t *_cbor_encoder_get_buffer_pointer(const CborEncoder *encoder) +{ + struct cbor_buf_writer *wr = (struct cbor_buf_writer *)encoder->writer; + + return wr->ptr; +} + diff --git a/ext/lib/encoding/tinycbor/src/cbor_buf_writer.h b/ext/lib/encoding/tinycbor/src/cbor_buf_writer.h new file mode 100644 index 000000000000..f9f714f7fbad --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_buf_writer.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_BUF_WRITER_H +#define CBOR_BUF_WRITER_H + +#include "cbor_encoder_writer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct cbor_buf_writer { + struct cbor_encoder_writer enc; + uint8_t *ptr; + const uint8_t *end; + int bytes_needed; +}; + +struct CborEncoder; + +void cbor_buf_writer_init(struct cbor_buf_writer *cb, uint8_t *buffer, + size_t data); +size_t cbor_buf_writer_buffer_size(struct cbor_buf_writer *cb, + const uint8_t *buffer); +size_t cbor_encoder_get_extra_bytes_needed(const struct CborEncoder *encoder); +size_t cbor_encoder_get_buffer_size(const struct CborEncoder *encoder, + const uint8_t *buffer); + +#ifdef __cplusplus +} +#endif + +#endif /* CBOR_BUF_WRITER_H */ diff --git a/ext/lib/encoding/tinycbor/src/cbor_cnt_writer.h b/ext/lib/encoding/tinycbor/src/cbor_cnt_writer.h new file mode 100644 index 000000000000..7cf34649ca45 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_cnt_writer.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_CNT_WRITER_H +#define CBOR_CNT_WRITER_H + +#include "cbor.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + /* use this count writer if you want to try out a cbor encoding to see + * how long it would be (before allocating memory). This replaced the + * code in tinycbor.h that would try to do this once the encoding failed + * in a buffer. Its much easier to understand this way (for me) + */ + +struct CborCntWriter { + struct cbor_encoder_writer enc; +}; + +static inline int +cbor_cnt_writer(struct cbor_encoder_writer *arg, const char *data, int len) { + struct CborCntWriter *cb = (struct CborCntWriter *) arg; + cb->enc.bytes_written += len; + return CborNoError; +} + +static inline void +cbor_cnt_writer_init(struct CborCntWriter *cb) { + cb->enc.bytes_written = 0; + cb->enc.write = &cbor_cnt_writer; +} + +#ifdef __cplusplus +} +#endif + +#endif /* CBOR_CNT_WRITER_H */ + diff --git a/ext/lib/encoding/tinycbor/src/cbor_decoder_reader.h b/ext/lib/encoding/tinycbor/src/cbor_decoder_reader.h new file mode 100644 index 000000000000..c3ecababc803 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_decoder_reader.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_DECODER_WRITER_H +#define CBOR_DECODER_WRITER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct cbor_decoder_reader; + +typedef uint8_t (cbor_reader_get8)(struct cbor_decoder_reader *d, int offset); +typedef uint16_t (cbor_reader_get16)(struct cbor_decoder_reader *d, int offset); +typedef uint32_t (cbor_reader_get32)(struct cbor_decoder_reader *d, int offset); +typedef uint64_t (cbor_reader_get64)(struct cbor_decoder_reader *d, int offset); +typedef uintptr_t (cbor_memcmp)(struct cbor_decoder_reader *d, char *buf, int offset, size_t len); +typedef uintptr_t (cbor_memcpy)(struct cbor_decoder_reader *d, char *buf, int offset, size_t len); +typedef uintptr_t (cbor_get_string_chunk)(struct cbor_decoder_reader *d, int offset, size_t *len); + +struct cbor_decoder_reader { + cbor_reader_get8 *get8; + cbor_reader_get16 *get16; + cbor_reader_get32 *get32; + cbor_reader_get64 *get64; + cbor_memcmp *cmp; + cbor_memcpy *cpy; + cbor_get_string_chunk *get_string_chunk; + size_t message_size; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext/lib/encoding/tinycbor/src/cbor_encoder_writer.h b/ext/lib/encoding/tinycbor/src/cbor_encoder_writer.h new file mode 100644 index 000000000000..37fd3990fdd5 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbor_encoder_writer.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_ENCODER_H +#define CBOR_ENCODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct cbor_encoder_writer; + +typedef int (cbor_encoder_write)(struct cbor_encoder_writer *, const char *data, int len); + +typedef struct cbor_encoder_writer { + cbor_encoder_write *write; + int bytes_written; +} cbor_encoder_writer; + +#ifdef __cplusplus +} +#endif + +#endif /* CBOR_ENCODER_WRITER_H */ diff --git a/ext/lib/encoding/tinycbor/src/cborencoder.c b/ext/lib/encoding/tinycbor/src/cborencoder.c new file mode 100644 index 000000000000..81ac24316a6f --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborencoder.c @@ -0,0 +1,638 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" +#include "cbor_buf_writer.h" + +#include +#include + +/** + * \defgroup CborEncoding Encoding to CBOR + * \brief Group of functions used to encode data to CBOR. + * + * CborEncoder is used to encode data into a CBOR stream. The outermost + * CborEncoder is initialized by calling cbor_encoder_init(), with the buffer + * where the CBOR stream will be stored. The outermost CborEncoder is usually + * used to encode exactly one item, most often an array or map. It is possible + * to encode more than one item, but care must then be taken on the decoder + * side to ensure the state is reset after each item was decoded. + * + * Nested CborEncoder objects are created using cbor_encoder_create_array() and + * cbor_encoder_create_map(), later closed with cbor_encoder_close_container() + * or cbor_encoder_close_container_checked(). The pairs of creation and closing + * must be exactly matched and their parameters are always the same. + * + * CborEncoder writes directly to the user-supplied buffer, without extra + * buffering. CborEncoder does not allocate memory and CborEncoder objects are + * usually created on the stack of the encoding functions. + * + * The example below initializes a CborEncoder object with a buffer and encodes + * a single integer. + * + * \code + * uint8_t buf[16]; + * CborEncoder encoder; + * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * cbor_encode_int(&encoder, some_value); + * \endcode + * + * As explained before, usually the outermost CborEncoder object is used to add + * one array or map, which in turn contains multiple elements. The example + * below creates a CBOR map with one element: a key "foo" and a boolean value. + * + * \code + * uint8_t buf[16]; + * CborEncoder encoder, mapEncoder; + * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * cbor_encoder_create_map(&encoder, &mapEncoder, 1); + * cbor_encode_text_stringz(&mapEncoder, "foo"); + * cbor_encode_boolean(&mapEncoder, some_value); + * cbor_encoder_close_container(&encoder, &mapEncoder); + * \endcode + * + *

Error checking and buffer size

+ * + * All functions operating on CborEncoder return a condition of type CborError. + * If the encoding was successful, they return CborNoError. Some functions do + * extra checking on the input provided and may return some other error + * conditions (for example, cbor_encode_simple_value() checks that the type is + * of the correct type). + * + * In addition, all functions check whether the buffer has enough bytes to + * encode the item being appended. If that is not possible, they return + * CborErrorOutOfMemory. + * + * It is possible to continue with the encoding of data past the first function + * that returns CborErrorOutOfMemory. CborEncoder functions will not overrun + * the buffer, but will instead count how many more bytes are needed to + * complete the encoding. At the end, you can obtain that count by calling + * cbor_encoder_get_extra_bytes_needed(). + * + * \section1 Finalizing the encoding + * + * Once all items have been appended and the containers have all been properly + * closed, the user-supplied buffer will contain the CBOR stream and may be + * immediately used. To obtain the size of the buffer, call + * cbor_encoder_get_buffer_size() with the original buffer pointer. + * + * The example below illustrates how one can encode an item with error checking + * and then pass on the buffer for network sending. + * + * \code + * uint8_t buf[16]; + * CborError err; + * CborEncoder encoder, mapEncoder; + * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * err = cbor_encoder_create_map(&encoder, &mapEncoder, 1); + * if (!err) + * return err; + * err = cbor_encode_text_stringz(&mapEncoder, "foo"); + * if (!err) + * return err; + * err = cbor_encode_boolean(&mapEncoder, some_value); + * if (!err) + * return err; + * err = cbor_encoder_close_container_checked(&encoder, &mapEncoder); + * if (!err) + * return err; + * + * size_t len = cbor_encoder_get_buffer_size(&encoder, buf); + * send_payload(buf, len); + * return CborNoError; + * \endcode + * + * Finally, the example below illustrates expands on the one above and also + * deals with dynamically growing the buffer if the initial allocation wasn't + * big enough. Note the two places where the error checking was replaced with + * an cbor_assertion, showing where the author assumes no error can occur. + * + * \code + * uint8_t *encode_string_array(const char **strings, int n, size_t *bufsize) + * { + * CborError err; + * CborEncoder encoder, arrayEncoder; + * size_t size = 256; + * uint8_t *buf = NULL; + * + * while (1) { + * int i; + * size_t more_bytes; + * uint8_t *nbuf = realloc(buf, size); + * if (nbuf == NULL) + * goto error; + * buf = nbuf; + * + * cbor_encoder_init(&encoder, &buf, size, 0); + * err = cbor_encoder_create_array(&encoder, &arrayEncoder, n); + * cbor_assert(err); // can't fail, the buffer is always big enough + * + * for (i = 0; i < n; ++i) { + * err = cbor_encode_text_stringz(&arrayEncoder, strings[i]); + * if (err && err != CborErrorOutOfMemory) + * goto error; + * } + * + * err = cbor_encoder_close_container_checked(&encoder, &arrayEncoder); + * cbor_assert(err); // shouldn't fail! + * + * more_bytes = cbor_encoder_get_extra_bytes_needed(encoder); + * if (more_size) { + * // buffer wasn't big enough, try again + * size += more_bytes; + * continue; + * } + * + * *bufsize = cbor_encoder_get_buffer_size(encoder, buf); + * return buf; + * } + * error: + * free(buf); + * return NULL; + * } + * \endcode + */ + +/** + * \addtogroup CborEncoding + * @{ + */ + +/** + * \struct CborEncoder + * Structure used to encode to CBOR. + */ + +#ifndef CBOR_NO_DFLT_WRITER +/** + * Initializes a CborEncoder structure \a encoder by pointing it to buffer \a + * buffer of size \a size. The \a flags field is currently unused and must be + * zero. + */ +void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags) +{ + cbor_buf_writer_init(&encoder->wr, buffer, size); + + cbor_encoder_cust_writer_init(encoder, &encoder->wr.enc, flags); +} +#endif + +/** + * Initializes a CborEncoder structure \a encoder by pointing it to buffer \a + * buffer of size \a size. The \a flags field is currently unused and must be + * zero. + */ +void cbor_encoder_cust_writer_init(CborEncoder *encoder, struct cbor_encoder_writer *w, int flags) +{ + encoder->writer = w; + encoder->remaining = 2; + encoder->flags = flags; +} + + +#ifndef CBOR_NO_FLOATING_POINT +static inline void put16(void *where, uint16_t v) +{ + v = cbor_htons(v); + memcpy(where, &v, sizeof(v)); +} +#endif + +/* Note: Since this is currently only used in situations where OOM is the only + * valid error, we KNOW this to be true. Thus, this function now returns just 'true', + * but if in the future, any function starts returning a non-OOM error, this will need + * to be changed to the test. At the moment, this is done to prevent more branches + * being created in the tinycbor output */ +static inline bool isOomError(CborError err) +{ + (void) err; + return true; +} + +#ifndef CBOR_NO_FLOATING_POINT +static inline void put32(void *where, uint32_t v) +{ + v = cbor_htonl(v); + memcpy(where, &v, sizeof(v)); +} +#endif + +static inline void put64(void *where, uint64_t v) +{ + v = cbor_htonll(v); + memcpy(where, &v, sizeof(v)); +} + +static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len) +{ + return (CborError)encoder->writer->write(encoder->writer, (const char *)data, len); +} + +static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte) +{ + return append_to_buffer(encoder, &byte, 1); +} + +static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) +{ + /* Little-endian would have been so much more convenient here: + * We could just write at the beginning of buf but append_to_buffer + * only the necessary bytes. + * Since it has to be big endian, do it the other way around: + * write from the end. */ + uint64_t buf[2]; + uint8_t *const bufend = (uint8_t *)buf + sizeof(buf); + uint8_t *bufstart = bufend - 1; + put64(buf + 1, ui); /* we probably have a bunch of zeros in the beginning */ + + if (ui < Value8Bit) { + *bufstart += shiftedMajorType; + } else { + uint8_t more = 0; + if (ui > 0xffU) + ++more; + if (ui > 0xffffU) + ++more; + if (ui > 0xffffffffU) + ++more; + bufstart -= (size_t)1 << more; + *bufstart = shiftedMajorType + Value8Bit + more; + } + + return append_to_buffer(encoder, bufstart, bufend - bufstart); +} + +static inline void saturated_decrement(CborEncoder *encoder) +{ + if (encoder->remaining) + --encoder->remaining; +} + +static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) +{ + saturated_decrement(encoder); + return encode_number_no_update(encoder, ui, shiftedMajorType); +} + +/** + * Appends the unsigned 64-bit integer \a value to the CBOR stream provided by + * \a encoder. + * + * \sa cbor_encode_negative_int, cbor_encode_int + */ +CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value) +{ + return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift); +} + +/** + * Appends the negative 64-bit integer whose absolute value is \a + * absolute_value to the CBOR stream provided by \a encoder. + * + * If the value \a absolute_value is zero, this function encodes -2^64 - 1. + * + * \sa cbor_encode_uint, cbor_encode_int + */ +CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value) +{ + return encode_number(encoder, absolute_value - 1, NegativeIntegerType << MajorTypeShift); +} + +/** + * Appends the signed 64-bit integer \a value to the CBOR stream provided by + * \a encoder. + * + * \sa cbor_encode_negative_int, cbor_encode_uint + */ +CborError cbor_encode_int(CborEncoder *encoder, int64_t value) +{ + /* adapted from code in RFC 7049 appendix C (pseudocode) */ + uint64_t ui = value >> 63; /* extend sign to whole length */ + uint8_t majorType = ui & 0x20; /* extract major type */ + ui ^= value; /* complement negatives */ + return encode_number(encoder, ui, majorType); +} + +/** + * Appends the CBOR Simple Type of value \a value to the CBOR stream provided by + * \a encoder. + * + * This function may return error CborErrorIllegalSimpleType if the \a value + * variable contains a number that is not a valid simple type. + */ +CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value) +{ +#ifndef CBOR_ENCODER_NO_CHECK_USER + /* check if this is a valid simple type */ + if (value >= HalfPrecisionFloat && value <= Break) + return CborErrorIllegalSimpleType; +#endif + return encode_number(encoder, value, SimpleTypesType << MajorTypeShift); +} + +#ifndef CBOR_NO_FLOATING_POINT +/** + * Appends the floating-point value of type \a fpType and pointed to by \a + * value to the CBOR stream provided by \a encoder. The value of \a fpType must + * be one of CborHalfFloatType, CborFloatType or CborDoubleType, otherwise the + * behavior of this function is undefined. + * + * This function is useful for code that needs to pass through floating point + * values but does not wish to have the actual floating-point code. + * + * \sa cbor_encode_half_float, cbor_encode_float, cbor_encode_double + */ +CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value) +{ + uint8_t buf[1 + sizeof(uint64_t)]; + cbor_assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType); + buf[0] = fpType; + + unsigned size = 2U << (fpType - CborHalfFloatType); + if (size == 8) + put64(buf + 1, *(const uint64_t*)value); + else if (size == 4) + put32(buf + 1, *(const uint32_t*)value); + else + put16(buf + 1, *(const uint16_t*)value); + saturated_decrement(encoder); + return append_to_buffer(encoder, buf, size + 1); +} +#endif + +/** + * Appends the CBOR tag \a tag to the CBOR stream provided by \a encoder. + * + * \sa CborTag + */ +CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag) +{ + /* tags don't count towards the number of elements in an array or map */ + return encode_number_no_update(encoder, tag, TagType << MajorTypeShift); +} + +static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string) +{ + CborError err = encode_number(encoder, length, shiftedMajorType); + if (err && !isOomError(err)) + return err; + return append_to_buffer(encoder, string, length); +} + +/** + * \fn CborError cbor_encode_text_stringz(CborEncoder *encoder, const char *string) + * + * Appends the null-terminated text string \a string to the CBOR stream + * provided by \a encoder. CBOR requires that \a string be valid UTF-8, but + * TinyCBOR makes no verification of correctness. The terminating null is not + * included in the stream. + * + * \sa cbor_encode_text_string, cbor_encode_byte_string + */ + +/** + * Appends the text string \a string of length \a length to the CBOR stream + * provided by \a encoder. CBOR requires that \a string be valid UTF-8, but + * TinyCBOR makes no verification of correctness. + * + * \sa CborError cbor_encode_text_stringz, cbor_encode_byte_string + */ +CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length) +{ + return encode_string(encoder, length, ByteStringType << MajorTypeShift, string); +} + +/** + * Appends the byte string \a string of length \a length to the CBOR stream + * provided by \a encoder. CBOR byte strings are arbitrary raw data. + * + * \sa cbor_encode_text_stringz, cbor_encode_text_string + */ +CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length) +{ + return encode_string(encoder, length, TextStringType << MajorTypeShift, string); +} + +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType) +{ + CborError err; + container->writer = encoder->writer; +#ifndef CBOR_NO_DFLT_WRITER + container->wr.end = encoder->wr.end; +#endif + saturated_decrement(encoder); + container->remaining = length + 1; /* overflow ok on CborIndefiniteLength */ + + cbor_static_assert(((MapType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == CborIteratorFlag_ContainerIsMap); + cbor_static_assert(((ArrayType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == 0); + container->flags = shiftedMajorType & CborIteratorFlag_ContainerIsMap; + + if (length == CborIndefiniteLength) { + container->flags |= CborIteratorFlag_UnknownLength; + err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength); + } else { + if (shiftedMajorType & CborIteratorFlag_ContainerIsMap) + container->remaining += length; + err = encode_number_no_update(container, length, shiftedMajorType); + } + return err; +} + +/** + * Creates a CBOR array in the CBOR stream provided by \a encoder and + * initializes \a arrayEncoder so that items can be added to the array using + * the CborEncoder functions. The array must be terminated by calling either + * cbor_encoder_close_container() or cbor_encoder_close_container_checked() + * with the same \a encoder and \a arrayEncoder parameters. + * + * The number of items inserted into the array must be exactly \a length items, + * otherwise the stream is invalid. If the number of items is not known when + * creating the array, the constant \ref CborIndefiniteLength may be passed as + * length instead. + * + * \sa cbor_encoder_create_map + */ +CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length) +{ + return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift); +} + +/** + * Creates a CBOR map in the CBOR stream provided by \a encoder and + * initializes \a mapEncoder so that items can be added to the map using + * the CborEncoder functions. The map must be terminated by calling either + * cbor_encoder_close_container() or cbor_encoder_close_container_checked() + * with the same \a encoder and \a mapEncoder parameters. + * + * The number of pair of items inserted into the map must be exactly \a length + * items, otherwise the stream is invalid. If the number of items is not known + * when creating the map, the constant \ref CborIndefiniteLength may be passed as + * length instead. + * + * \b{Implementation limitation:} TinyCBOR cannot encode more than SIZE_MAX/2 + * key-value pairs in the stream. If the length \a length is larger than this + * value, this function returns error CborErrorDataTooLarge. + * + * \sa cbor_encoder_create_array + */ +CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length) +{ + if (length != CborIndefiniteLength && length > SIZE_MAX / 2) + return CborErrorDataTooLarge; + return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift); +} + +/** + * Closes the CBOR container (array or map) provided by \a containerEncoder and + * updates the CBOR stream provided by \a encoder. Both parameters must be the + * same as were passed to cbor_encoder_create_array() or + * cbor_encoder_create_map(). + * + * Since version 0.5, this function verifies that the number of items (or pair + * of items, in the case of a map) was correct. It is no longer needed to call + * cbor_encoder_close_container_checked() instead. + * + * \sa cbor_encoder_create_array(), cbor_encoder_create_map() + */ +CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder) +{ + encoder->writer = containerEncoder->writer; + + if (containerEncoder->flags & CborIteratorFlag_UnknownLength) + return append_byte_to_buffer(encoder, BreakByte); + + if (containerEncoder->remaining != 1) + return containerEncoder->remaining == 0 ? CborErrorTooManyItems : CborErrorTooFewItems; + +#ifndef CBOR_NO_DFLT_WRITER + if (!encoder->wr.end) { + return CborErrorOutOfMemory; + } +#endif + + return CborNoError; +} + +/** + * \fn CborError cbor_encode_boolean(CborEncoder *encoder, bool value) + * + * Appends the boolean value \a value to the CBOR stream provided by \a encoder. + */ + +/** + * \fn CborError cbor_encode_null(CborEncoder *encoder) + * + * Appends the CBOR type representing a null value to the CBOR stream provided + * by \a encoder. + * + * \sa cbor_encode_undefined() + */ + +/** + * \fn CborError cbor_encode_undefined(CborEncoder *encoder) + * + * Appends the CBOR type representing an undefined value to the CBOR stream + * provided by \a encoder. + * + * \sa cbor_encode_null() + */ + +/** + * \fn CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) + * + * Appends the IEEE 754 half-precision (16-bit) floating point value pointed to + * by \a value to the CBOR stream provided by \a encoder. + * + * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() + */ + +/** + * \fn CborError cbor_encode_float(CborEncoder *encoder, float value) + * + * Appends the IEEE 754 single-precision (32-bit) floating point value \a value + * to the CBOR stream provided by \a encoder. + * + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_double() + */ + +/** + * \fn CborError cbor_encode_double(CborEncoder *encoder, double value) + * + * Appends the IEEE 754 double-precision (64-bit) floating point value \a value + * to the CBOR stream provided by \a encoder. + * + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float() + */ + +/** + * \fn size_t cbor_encoder_get_buffer_size(const CborEncoder *encoder, const uint8_t *buffer) + * + * Returns the total size of the buffer starting at \a buffer after the + * encoding finished without errors. The \a encoder and \a buffer arguments + * must be the same as supplied to cbor_encoder_init(). + * + * If the encoding process had errors, the return value of this function is + * meaningless. If the only errors were CborErrorOutOfMemory, instead use + * cbor_encoder_get_extra_bytes_needed() to find out by how much to grow the + * buffer before encoding again. + * + * See \ref CborEncoding for an example of using this function. + * + * \sa cbor_encoder_init(), cbor_encoder_get_extra_bytes_needed(), CborEncoding + */ + +/** + * \fn size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *encoder) + * + * Returns how many more bytes the original buffer supplied to + * cbor_encoder_init() needs to be extended by so that no CborErrorOutOfMemory + * condition will happen for the encoding. If the buffer was big enough, this + * function returns 0. The \a encoder must be the original argument as passed + * to cbor_encoder_init(). + * + * This function is usually called after an encoding sequence ended with one or + * more CborErrorOutOfMemory errors, but no other error. If any other error + * happened, the return value of this function is meaningless. + * + * See \ref CborEncoding for an example of using this function. + * + * \sa cbor_encoder_init(), cbor_encoder_get_buffer_size(), CborEncoding + */ + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cborencoder_close_container_checked.c b/ext/lib/encoding/tinycbor/src/cborencoder_close_container_checked.c new file mode 100644 index 000000000000..5661e4d53e9d --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborencoder_close_container_checked.c @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" + +/** + * \addtogroup CborEncoding + * @{ + */ + +/** + * @deprecated + * + * Closes the CBOR container (array or map) provided by \a containerEncoder and + * updates the CBOR stream provided by \a encoder. Both parameters must be the + * same as were passed to cbor_encoder_create_array() or + * cbor_encoder_create_map(). + * + * Prior to version 0.5, cbor_encoder_close_container() did not check the + * number of items added. Since that version, it does and now + * cbor_encoder_close_container_checked() is no longer needed. + * + * \sa cbor_encoder_create_array(), cbor_encoder_create_map() + */ +CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder) +{ + return cbor_encoder_close_container(encoder, containerEncoder); +} + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cborerrorstrings.c b/ext/lib/encoding/tinycbor/src/cborerrorstrings.c new file mode 100644 index 000000000000..4aa684dd87b4 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborerrorstrings.c @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#include "cbor.h" + +#ifndef _ +# define _(msg) msg +#endif + +/** + * \enum CborError + * \ingroup CborGlobals + * The CborError enum contains the possible error values used by the CBOR encoder and decoder. + * + * TinyCBOR functions report success by returning CborNoError, or one error + * condition by returning one of the values below. One exception is the + * out-of-memory condition (CborErrorOutOfMemory), which the functions for \ref + * CborEncoding may report in bit-wise OR with other conditions. + * + * This technique allows code to determine whether the only error condition was + * a lack of buffer space, which may not be a fatal condition if the buffer can + * be resized. Additionally, the functions for \ref CborEncoding may continue + * to be used even after CborErrorOutOfMemory is returned, and instead they + * will simply calculate the extra space needed. + * + * \value CborNoError No error occurred + * \omitvalue CborUnknownError + * \value CborErrorUnknownLength Request for the length of an array, map or string whose length is not provided in the CBOR stream + * \value CborErrorAdvancePastEOF Not enough data in the stream to decode item (decoding would advance past end of stream) + * \value CborErrorIO An I/O error occurred, probably due to an out-of-memory situation + * \value CborErrorGarbageAtEnd Bytes exist past the end of the CBOR stream + * \value CborErrorUnexpectedEOF End of stream reached unexpectedly + * \value CborErrorUnexpectedBreak A CBOR break byte was found where not expected + * \value CborErrorUnknownType An unknown type (future extension to CBOR) was found in the stream + * \value CborErrorIllegalType An invalid type was found while parsing a chunked CBOR string + * \value CborErrorIllegalNumber An illegal initial byte (encoding unspecified additional information) was found + * \value CborErrorIllegalSimpleType An illegal encoding of a CBOR Simple Type of value less than 32 was found + * \omitvalue CborErrorUnknownSimpleType + * \omitvalue CborErrorUnknownTag + * \omitvalue CborErrorInappropriateTagForType + * \omitvalue CborErrorDuplicateObjectKeys + * \value CborErrorInvalidUtf8TextString Illegal UTF-8 encoding found while parsing CBOR Text String + * \value CborErrorTooManyItems Too many items were added to CBOR map or array of pre-determined length + * \value CborErrorTooFewItems Too few items were added to CBOR map or array of pre-determeined length + * \value CborErrorDataTooLarge Data item size exceeds TinyCBOR's implementation limits + * \value CborErrorNestingTooDeep Data item nesting exceeds TinyCBOR's implementation limits + * \omitvalue CborErrorUnsupportedType + * \value CborErrorJsonObjectKeyIsAggregate Conversion to JSON failed because the key in a map is a CBOR map or array + * \value CborErrorJsonObjectKeyNotString Conversion to JSON failed because the key in a map is not a text string + * \value CborErrorOutOfMemory During CBOR encoding, the buffer provided is insufficient for encoding the data item; + * in other situations, TinyCBOR failed to allocate memory + * \value CborErrorInternalError An internal error occurred in TinyCBOR + */ + +/** + * \ingroup CborGlobals + * Returns the error string corresponding to the CBOR error condition \a error. + */ +const char *cbor_error_string(CborError error) +{ + switch (error) { + case CborNoError: + return ""; + + case CborUnknownError: + return _("unknown error"); + + case CborErrorOutOfMemory: + return _("out of memory/need more memory"); + + case CborErrorUnknownLength: + return _("unknown length (attempted to get the length of a map/array/string of indeterminate length"); + + case CborErrorAdvancePastEOF: + return _("attempted to advance past EOF"); + + case CborErrorIO: + return _("I/O error"); + + case CborErrorGarbageAtEnd: + return _("garbage after the end of the content"); + + case CborErrorUnexpectedEOF: + return _("unexpected end of data"); + + case CborErrorUnexpectedBreak: + return _("unexpected 'break' byte"); + + case CborErrorUnknownType: + return _("illegal byte (encodes future extension type)"); + + case CborErrorIllegalType: + return _("mismatched string type in chunked string"); + + case CborErrorIllegalNumber: + return _("illegal initial byte (encodes unspecified additional information)"); + + case CborErrorIllegalSimpleType: + return _("illegal encoding of simple type smaller than 32"); + + case CborErrorUnknownSimpleType: + return _("unknown simple type"); + + case CborErrorUnknownTag: + return _("unknown tag"); + + case CborErrorInappropriateTagForType: + return _("inappropriate tag for type"); + + case CborErrorDuplicateObjectKeys: + return _("duplicate keys in object"); + + case CborErrorInvalidUtf8TextString: + return _("invalid UTF-8 content in string"); + + case CborErrorExcludedType: + return _("excluded type found"); + + case CborErrorExcludedValue: + return _("excluded value found"); + + case CborErrorImproperValue: + case CborErrorOverlongEncoding: + return _("value encoded in non-canonical form"); + + case CborErrorMapKeyNotString: + case CborErrorJsonObjectKeyNotString: + return _("key in map is not a string"); + + case CborErrorMapNotSorted: + return _("map is not sorted"); + + case CborErrorMapKeysNotUnique: + return _("map keys are not unique"); + + case CborErrorTooManyItems: + return _("too many items added to encoder"); + + case CborErrorTooFewItems: + return _("too few items added to encoder"); + + case CborErrorDataTooLarge: + return _("internal error: data too large"); + + case CborErrorNestingTooDeep: + return _("internal error: too many nested containers found in recursive function"); + + case CborErrorUnsupportedType: + return _("unsupported type"); + + case CborErrorJsonObjectKeyIsAggregate: + return _("conversion to JSON failed: key in object is an array or map"); + + case CborErrorJsonNotImplemented: + return _("conversion to JSON failed: open_memstream unavailable"); + + case CborErrorInternalError: + return _("internal error"); + } + return cbor_error_string(CborUnknownError); +} diff --git a/ext/lib/encoding/tinycbor/src/cborinternal_p.h b/ext/lib/encoding/tinycbor/src/cborinternal_p.h new file mode 100644 index 000000000000..ddeb2c2b783e --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborinternal_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBORINTERNAL_P_H +#define CBORINTERNAL_P_H + +#include "compilersupport_p.h" + +#ifndef CBOR_INTERNAL_API +# define CBOR_INTERNAL_API +#endif + +#ifndef CBOR_PARSER_MAX_RECURSIONS +# define CBOR_PARSER_MAX_RECURSIONS 1024 +#endif + +/* + * CBOR Major types + * Encoded in the high 3 bits of the descriptor byte + * See http://tools.ietf.org/html/rfc7049#section-2.1 + */ +typedef enum CborMajorTypes { + UnsignedIntegerType = 0U, + NegativeIntegerType = 1U, + ByteStringType = 2U, + TextStringType = 3U, + ArrayType = 4U, + MapType = 5U, /* a.k.a. object */ + TagType = 6U, + SimpleTypesType = 7U +} CborMajorTypes; + +/* + * CBOR simple and floating point types + * Encoded in the low 8 bits of the descriptor byte when the + * Major Type is 7. + */ +typedef enum CborSimpleTypes { + FalseValue = 20, + TrueValue = 21, + NullValue = 22, + UndefinedValue = 23, + SimpleTypeInNextByte = 24, /* not really a simple type */ + HalfPrecisionFloat = 25, /* ditto */ + SinglePrecisionFloat = 26, /* ditto */ + DoublePrecisionFloat = 27, /* ditto */ + Break = 31 +} CborSimpleTypes; + +enum { + SmallValueBitLength = 5U, + SmallValueMask = (1U << SmallValueBitLength) - 1, /* 31 */ + Value8Bit = 24U, + Value16Bit = 25U, + Value32Bit = 26U, + Value64Bit = 27U, + IndefiniteLength = 31U, + + MajorTypeShift = SmallValueBitLength, + MajorTypeMask = (int) (~0U << MajorTypeShift), + + BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift) +}; + +CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_extract_number(const CborParser *p, int *offset, uint64_t *len); +CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_prepare_string_iteration(CborValue *it); + +#endif /* CBORINTERNAL_P_H */ diff --git a/ext/lib/encoding/tinycbor/src/cborjson.h b/ext/lib/encoding/tinycbor/src/cborjson.h new file mode 100644 index 000000000000..8ff27b9202a0 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborjson.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBORJSON_H +#define CBORJSON_H + +#include "cbor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Conversion to JSON */ +enum CborToJsonFlags +{ + CborConvertAddMetadata = 1, + CborConvertTagsToObjects = 2, + CborConvertIgnoreTags = 0, + + CborConvertObeyByteStringTags = 0, + CborConvertByteStringsToBase64Url = 4, + + CborConvertRequireMapStringKeys = 0, + CborConvertStringifyMapKeys = 8, + + CborConvertDefaultFlags = 0 +}; + +CBOR_API CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags); +CBOR_INLINE_API CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags) +{ + CborValue copy = *value; + return cbor_value_to_json_advance(out, ©, flags); +} + +#ifdef __cplusplus +} +#endif + +#endif /* CBORJSON_H */ + diff --git a/ext/lib/encoding/tinycbor/src/cborparser.c b/ext/lib/encoding/tinycbor/src/cborparser.c new file mode 100644 index 000000000000..9ebc70563015 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborparser.c @@ -0,0 +1,1551 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" + +#include + +#include "cbor_buf_reader.h" +/** + * \defgroup CborParsing Parsing CBOR streams + * \brief Group of functions used to parse CBOR streams. + * + * TinyCBOR provides functions for pull-based stream parsing of a CBOR-encoded + * payload. The main data type for the parsing is a CborValue, which behaves + * like an iterator and can be used to extract the encoded data. It is first + * initialized with a call to cbor_parser_init() and is usually used to extract + * exactly one item, most often an array or map. + * + * Nested CborValue objects can be parsed using cbor_value_enter_container(). + * Each call to cbor_value_enter_container() must be matched by a call to + * cbor_value_leave_container(), with the exact same parameters. + * + * The example below initializes a CborParser object, begins the parsing with a + * CborValue and decodes a single integer: + * + * \code + * int extract_int(const uint8_t *buffer, size_t len) + * { + * CborParser parser; + * CborValue value; + * int result; + * cbor_parser_init(buffer, len, 0, &buffer, &value); + * cbor_value_get_int(&value, &result); + * return result; + * } + * \endcode + * + * The code above does no error checking, which means it assumes the data comes + * from a source trusted to send one properly-encoded integer. The following + * example does the exact same operation, but includes error parsing and + * returns 0 on parsing failure: + * + * \code + * int extract_int(const uint8_t *buffer, size_t len) + * { + * CborParser parser; + * CborValue value; + * int result; + * if (cbor_parser_init(buffer, len, 0, &buffer, &value) != CborNoError) + * return 0; + * if (!cbor_value_is_integer(&value) || + * cbor_value_get_int(&value, &result) != CborNoError) + * return 0; + * return result; + * } + * \endcode + * + * Note, in the example above, that one can't distinguish a parsing failure + * from an encoded value of zero. Reporting a parsing error is left as an + * exercise to the reader. + * + * The code above does not execute a range-check either: it is possible that + * the value decoded from the CBOR stream encodes a number larger than what can + * be represented in a variable of type \c{int}. If detecting that case is + * important, the code should call cbor_value_get_int_checked() instead. + * + *

Memory and parsing constraints

+ * + * TinyCBOR is designed to run with little memory and with minimal overhead. + * Except where otherwise noted, the parser functions always run on constant + * time (O(1)), do not recurse and never allocate memory (thus, stack usage is + * bounded and is O(1)). + * + *

Error handling and preconditions

+ * + * All functions operating on a CborValue return a CborError condition, with + * CborNoError standing for the normal situation in which no parsing error + * occurred. All functions may return parsing errors in case the stream cannot + * be decoded properly, be it due to corrupted data or due to reaching the end + * of the input buffer. + * + * Error conditions must not be ignored. All decoder functions have undefined + * behavior if called after an error has been reported, and may crash. + * + * Some functions are also documented to have preconditions, like + * cbor_value_get_int() requiring that the input be an integral value. + * Violation of preconditions also results in undefined behavior and the + * program may crash. + */ + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * \struct CborValue + * + * This type contains one value parsed from the CBOR stream. Each CborValue + * behaves as an iterator in a StAX-style parser. + * + * \if privatedocs + * Implementation details: the CborValue contains these fields: + * \list + * \li ptr: pointer to the actual data + * \li flags: flags from the decoder + * \li extra: partially decoded integer value (0, 1 or 2 bytes) + * \li remaining: remaining items in this collection after this item or UINT32_MAX if length is unknown + * \endlist + * \endif + */ + +CBOR_INTERNAL_API_CC CborError _cbor_value_extract_number(const CborParser *p, int *offset, uint64_t *len) +{ + uint8_t additional_information = p->d->get8(p->d, *offset) & SmallValueMask; + ++*offset; + if (additional_information < Value8Bit) { + *len = additional_information; + return CborNoError; + } + if (unlikely(additional_information > Value64Bit)) + return CborErrorIllegalNumber; + + size_t bytesNeeded = (size_t)(1 << (additional_information - Value8Bit)); + if (unlikely(bytesNeeded > (size_t)(p->end - *offset))) { + return CborErrorUnexpectedEOF; + } else if (bytesNeeded == 1) { + *len = p->d->get8(p->d, *offset); + } else if (bytesNeeded == 2) { + *len = p->d->get16(p->d, *offset); + } else if (bytesNeeded == 4) { + *len = p->d->get32(p->d, *offset); + } else { + *len = p->d->get64(p->d, *offset); + } + *offset += bytesNeeded; + return CborNoError; +} + +static CborError extract_length(const CborParser *parser, + int *offset, size_t *len) +{ + uint64_t v; + CborError err = _cbor_value_extract_number(parser, offset, &v); + if (err) { + *len = 0; + return err; + } + + *len = (size_t)v; + if (v != *len) + return CborErrorDataTooLarge; + return CborNoError; +} + +static bool is_fixed_type(uint8_t type) +{ + return type != CborTextStringType && type != CborByteStringType && type != CborArrayType && + type != CborMapType; +} + +static CborError preparse_value(CborValue *it) +{ + const CborParser *parser = it->parser; + it->type = CborInvalidType; + + /* are we at the end? */ + if (it->offset == parser->end) + return CborErrorUnexpectedEOF; + + uint8_t descriptor = parser->d->get8(parser->d, it->offset); + uint8_t type = descriptor & MajorTypeMask; + it->type = type; + it->flags = 0; + it->remainingclen = 0; + it->extra = (descriptor &= SmallValueMask); + + size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); + + if (descriptor > Value64Bit) { + if (unlikely(descriptor != IndefiniteLength)) + return type == CborSimpleType ? CborErrorUnknownType : CborErrorIllegalNumber; + if (likely(!is_fixed_type(type))) { + /* special case */ + it->flags |= CborIteratorFlag_UnknownLength; + it->type = type; + return CborNoError; + } + return type == CborSimpleType ? CborErrorUnexpectedBreak : CborErrorIllegalNumber; + } + + if (bytesNeeded + 1 > (size_t)(parser->end - it->offset)) + return CborErrorUnexpectedEOF; + + uint8_t majortype = type >> MajorTypeShift; + if (majortype == NegativeIntegerType) { + it->flags |= CborIteratorFlag_NegativeInteger; + it->type = CborIntegerType; + } else if (majortype == SimpleTypesType) { + switch (descriptor) { + case FalseValue: + it->extra = false; + it->type = CborBooleanType; + break; + + case SinglePrecisionFloat: + case DoublePrecisionFloat: + it->flags |= CborIteratorFlag_IntegerValueTooLarge; + /* fall through */ + case TrueValue: + case NullValue: + case UndefinedValue: + case HalfPrecisionFloat: + it->type = parser->d->get8(parser->d, it->offset); + break; + + case SimpleTypeInNextByte: + it->extra = parser->d->get8(parser->d, it->offset + 1); +#ifndef CBOR_PARSER_NO_STRICT_CHECKS + if (unlikely(it->extra < 32)) { + it->type = CborInvalidType; + return CborErrorIllegalSimpleType; + } +#endif + break; + + case 28: + case 29: + case 30: + case Break: + cbor_assert(false); /* these conditions can't be reached */ + return CborErrorUnexpectedBreak; + } + return CborNoError; + } + + /* try to decode up to 16 bits */ + if (descriptor < Value8Bit) + return CborNoError; + + if (descriptor == Value8Bit) + it->extra = parser->d->get8(parser->d, it->offset + 1); + else if (descriptor == Value16Bit) + it->extra = parser->d->get16(parser->d, it->offset + 1); + else + it->flags |= CborIteratorFlag_IntegerValueTooLarge; /* Value32Bit or Value64Bit */ + return CborNoError; +} + +static CborError preparse_next_value(CborValue *it) +{ + if (it->remaining != UINT32_MAX) { + /* don't decrement the item count if the current item is tag: they don't count */ + if (it->type != CborTagType && !--it->remaining) { + it->type = CborInvalidType; + return CborNoError; + } + } else if (it->remaining == UINT32_MAX && it->offset != it->parser->end && + it->parser->d->get8(it->parser->d, it->offset) == (uint8_t)BreakByte) { + /* end of map or array */ + ++it->offset; + it->type = CborInvalidType; + it->remaining = 0; + return CborNoError; + } + + return preparse_value(it); +} + +static CborError advance_internal(CborValue *it) +{ + uint64_t length; + CborError err; + err = _cbor_value_extract_number(it->parser, &it->offset, &length); + cbor_assert(err == CborNoError); + + if (it->type == CborByteStringType || it->type == CborTextStringType) { + cbor_assert(length == (size_t)length); + cbor_assert((it->flags & CborIteratorFlag_UnknownLength) == 0); + it->offset += length; + } + + return preparse_next_value(it); +} + +/** \internal + * + * Decodes the CBOR integer value when it is larger than the 16 bits available + * in value->extra. This function requires that value->flags have the + * CborIteratorFlag_IntegerValueTooLarge flag set. + * + * This function is also used to extract single- and double-precision floating + * point values (SinglePrecisionFloat == Value32Bit and DoublePrecisionFloat == + * Value64Bit). + */ +uint64_t _cbor_value_decode_int64_internal(const CborValue *value) +{ + uint8_t val = value->parser->d->get8(value->parser->d, value->offset); + + cbor_assert(value->flags & CborIteratorFlag_IntegerValueTooLarge || + value->type == CborFloatType || value->type == CborDoubleType); + + /* since the additional information can only be Value32Bit or Value64Bit, + * we just need to test for the one bit those two options differ */ + cbor_assert((val & SmallValueMask) == Value32Bit || (val & SmallValueMask) == Value64Bit); + if ((val & 1) == (Value32Bit & 1)) + return value->parser->d->get32(value->parser->d, value->offset + 1); + + cbor_assert((val & SmallValueMask) == Value64Bit); + return value->parser->d->get64(value->parser->d, value->offset + 1); +} + +#ifndef CBOR_NO_DFLT_READER +/** + * Initializes the CBOR parser for parsing \a size bytes beginning at \a + * buffer. Parsing will use flags set in \a flags. The iterator to the first + * element is returned in \a it. + * + * The \a parser structure needs to remain valid throughout the decoding + * process. It is not thread-safe to share one CborParser among multiple + * threads iterating at the same time, but the object can be copied so multiple + * threads can iterate. + */ +CborError cbor_parser_init(const uint8_t *buffer, size_t size, int flags, CborParser *parser, CborValue *it) +{ + memset(parser, 0, sizeof(*parser)); + + cbor_buf_reader_init(&parser->br, buffer, size); + + return cbor_parser_cust_reader_init(&parser->br.r, flags, parser, it); +} +#endif + +/** + * Initializes the CBOR parser for parsing. It uses the \a decoder reader. Parsing will + * use flags set in \a flags. The iterator to the first element is returned in \a it. + * + * The \a parser structure needs to remain valid throughout the decoding + * process. It is not thread-safe to share one CborParser among multiple + * threads iterating at the same time, but the object can be copied so multiple + * threads can iterate. + */ +CborError cbor_parser_cust_reader_init(struct cbor_decoder_reader *r, int flags, CborParser *parser, CborValue *it) +{ + parser->d = r; + parser->end = r->message_size; + parser->flags = flags; + it->parser = parser; + it->offset = 0; + it->remaining = 1;/* there's one type altogether, usually an array or map */ + return preparse_value(it); +} + +/** + * \fn bool cbor_value_at_end(const CborValue *it) + * + * Returns true if \a it has reached the end of the iteration, usually when + * advancing after the last item in an array or map. + * + * In the case of the outermost CborValue object, this function returns true + * after decoding a single element. A pointer to the first byte of the + * remaining data (if any) can be obtained with cbor_value_get_next_byte(). + * + * \sa cbor_value_advance(), cbor_value_is_valid(), cbor_value_get_next_byte() + */ + +/** + * \fn const uint8_t *cbor_value_get_next_byte(const CborValue *it) + * + * Returns a pointer to the next byte that would be decoded if this CborValue + * object were advanced. + * + * This function is useful if cbor_value_at_end() returns true for the + * outermost CborValue: the pointer returned is the first byte of the data + * remaining in the buffer, if any. Code can decide whether to begin decoding a + * new CBOR data stream from this point, or parse some other data appended to + * the same buffer. + * + * This function may be used even after a parsing error. If that occurred, + * then this function returns a pointer to where the parsing error occurred. + * Note that the error recovery is not precise and the pointer may not indicate + * the exact byte containing bad data. + * + * \sa cbor_value_at_end() + */ + +/** + * \fn bool cbor_value_is_valid(const CborValue *it) + * + * Returns true if the iterator \a it contains a valid value. Invalid iterators + * happen when iteration reaches the end of a container (see \ref + * cbor_value_at_end()) or when a search function resulted in no matches. + * + * \sa cbor_value_advance(), cbor_valie_at_end(), cbor_value_get_type() + */ + +/** + * Performs a basic validation of the CBOR stream pointed by \a it and returns + * the error it found. If no error was found, it returns CborNoError and the + * application can iterate over the items with certainty that no other errors + * will appear during parsing. + * + * A basic validation checks for: + * \list + * \li absence of undefined additional information bytes; + * \li well-formedness of all numbers, lengths, and simple values; + * \li string contents match reported sizes; + * \li arrays and maps contain the number of elements they are reported to have; + * \endlist + * + * For further checks, see cbor_value_validate(). + * + * This function has the same timing and memory requirements as + * cbor_value_advance(). + * + * \sa cbor_value_validate(), cbor_value_advance() + */ +CborError cbor_value_validate_basic(const CborValue *it) +{ + CborValue value = *it; + return cbor_value_advance(&value); +} + +/** + * Advances the CBOR value \a it by one fixed-size position. Fixed-size types + * are: integers, tags, simple types (including boolean, null and undefined + * values) and floating point types. + * + * If the type is not of fixed size, this function has undefined behavior. Code + * must be sure that the current type is one of the fixed-size types before + * calling this function. This function is provided because it can guarantee + * that runs in constant time (O(1)). + * + * If the caller is not able to determine whether the type is fixed or not, code + * can use the cbor_value_advance() function instead. + * + * \sa cbor_value_at_end(), cbor_value_advance(), cbor_value_enter_container(), cbor_value_leave_container() + */ +CborError cbor_value_advance_fixed(CborValue *it) +{ + cbor_assert(it->type != CborInvalidType); + cbor_assert(is_fixed_type(it->type)); + if (!it->remaining) + return CborErrorAdvancePastEOF; + return advance_internal(it); +} + +static CborError advance_recursive(CborValue *it, int nestingLevel) +{ + if (is_fixed_type(it->type)) + return advance_internal(it); + + if (!cbor_value_is_container(it)) { + size_t len = SIZE_MAX; + return _cbor_value_copy_string(it, NULL, &len, it); + } + + /* map or array */ + if (nestingLevel == 0) + return CborErrorNestingTooDeep; + + CborError err; + CborValue recursed; + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; + while (!cbor_value_at_end(&recursed)) { + err = advance_recursive(&recursed, nestingLevel - 1); + if (err) + return err; + } + return cbor_value_leave_container(it, &recursed); +} + + +/** + * Advances the CBOR value \a it by one element, skipping over containers. + * Unlike cbor_value_advance_fixed(), this function can be called on a CBOR + * value of any type. However, if the type is a container (map or array) or a + * string with a chunked payload, this function will not run in constant time + * and will recurse into itself (it will run on O(n) time for the number of + * elements or chunks and will use O(n) memory for the number of nested + * containers). + * + * The number of recursions can be limited at compile time to avoid stack + * exhaustion in constrained systems. + * + * \sa cbor_value_at_end(), cbor_value_advance_fixed(), cbor_value_enter_container(), cbor_value_leave_container() + */ +CborError cbor_value_advance(CborValue *it) +{ + cbor_assert(it->type != CborInvalidType); + if (!it->remaining) + return CborErrorAdvancePastEOF; + return advance_recursive(it, CBOR_PARSER_MAX_RECURSIONS); +} + +/** + * \fn bool cbor_value_is_tag(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR tag. + * + * \sa cbor_value_get_tag(), cbor_value_skip_tag() + */ + +/** + * \fn CborError cbor_value_get_tag(const CborValue *value, CborTag *result) + * + * Retrieves the CBOR tag value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to a CBOR tag value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_tag is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_tag() + */ + +/** + * Advances the CBOR value \a it until it no longer points to a tag. If \a it is + * already not pointing to a tag, then this function returns it unchanged. + * + * This function does not run in constant time: it will run on O(n) for n being + * the number of tags. It does use constant memory (O(1) memory requirements). + * + * \sa cbor_value_advance_fixed(), cbor_value_advance() + */ +CborError cbor_value_skip_tag(CborValue *it) +{ + while (cbor_value_is_tag(it)) { + CborError err = cbor_value_advance_fixed(it); + if (err) + return err; + } + return CborNoError; +} + +/** + * \fn bool cbor_value_is_container(const CborValue *it) + * + * Returns true if the \a it value is a container and requires recursion in + * order to decode (maps and arrays), false otherwise. + */ + +/** + * Creates a CborValue iterator pointing to the first element of the container + * represented by \a it and saves it in \a recursed. The \a it container object + * needs to be kept and passed again to cbor_value_leave_container() in order + * to continue iterating past this container. + * + * The \a it CborValue iterator must point to a container. + * + * \sa cbor_value_is_container(), cbor_value_leave_container(), cbor_value_advance() + */ +CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) +{ + CborError err; + cbor_assert(cbor_value_is_container(it)); + *recursed = *it; + + if (it->flags & CborIteratorFlag_UnknownLength) { + recursed->remaining = UINT32_MAX; + ++recursed->offset; + err = preparse_value(recursed); + if (err != CborErrorUnexpectedBreak) + return err; + /* actually, break was expected here + * it's just an empty container */ + ++recursed->offset; + } else { + uint64_t len; + err = _cbor_value_extract_number(recursed->parser, &recursed->offset, &len); + cbor_assert(err == CborNoError); + + recursed->remaining = (uint32_t)len; + if (recursed->remaining != len || len == UINT32_MAX) { + /* back track the pointer to indicate where the error occurred */ + recursed->offset = it->offset; + return CborErrorDataTooLarge; + } + if (recursed->type == CborMapType) { + /* maps have keys and values, so we need to multiply by 2 */ + if (recursed->remaining > UINT32_MAX / 2) { + /* back track the pointer to indicate where the error occurred */ + recursed->offset = it->offset; + return CborErrorDataTooLarge; + } + recursed->remaining *= 2; + } + if (len != 0) + return preparse_value(recursed); + } + + /* the case of the empty container */ + recursed->type = CborInvalidType; + recursed->remaining = 0; + return CborNoError; +} + +/** + * Updates \a it to point to the next element after the container. The \a + * recursed object needs to point to the element obtained either by advancing + * the last element of the container (via cbor_value_advance(), + * cbor_value_advance_fixed(), a nested cbor_value_leave_container(), or the \c + * next pointer from cbor_value_copy_string() or cbor_value_dup_string()). + * + * The \a it and \a recursed parameters must be the exact same as passed to + * cbor_value_enter_container(). + * + * \sa cbor_value_enter_container(), cbor_value_at_end() + */ +CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed) +{ + cbor_assert(cbor_value_is_container(it)); + cbor_assert(recursed->type == CborInvalidType); + it->offset = recursed->offset; + + return preparse_next_value(it); +} + + +/** + * \fn CborType cbor_value_get_type(const CborValue *value) + * + * Returns the type of the CBOR value that the iterator \a value points to. If + * \a value does not point to a valid value, this function returns \ref + * CborInvalidType. + * + * TinyCBOR also provides functions to test directly if a given CborValue object + * is of a given type, like cbor_value_is_text_string() and cbor_value_is_null(). + * + * \sa cbor_value_is_valid() + */ + +/** + * \fn bool cbor_value_is_null(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR null type. + * + * \sa cbor_value_is_valid(), cbor_value_is_undefined() + */ + +/** + * \fn bool cbor_value_is_undefined(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR undefined type. + * + * \sa cbor_value_is_valid(), cbor_value_is_null() + */ + +/** + * \fn bool cbor_value_is_boolean(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR boolean + * type (true or false). + * + * \sa cbor_value_is_valid(), cbor_value_get_boolean() + */ + +/** + * \fn CborError cbor_value_get_boolean(const CborValue *value, bool *result) + * + * Retrieves the boolean value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to a boolean value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_boolean is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_boolean() + */ + +/** + * \fn bool cbor_value_is_simple_type(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR Simple Type + * type (other than true, false, null and undefined). + * + * \sa cbor_value_is_valid(), cbor_value_get_simple_type() + */ + +/** + * \fn CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) + * + * Retrieves the CBOR Simple Type value that \a value points to and stores it + * in \a result. If the iterator \a value does not point to a simple_type + * value, the behavior is undefined, so checking with \ref cbor_value_get_type + * or with \ref cbor_value_is_simple_type is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_simple_type() + */ + +/** + * \fn bool cbor_value_is_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR integer + * type. + * + * \sa cbor_value_is_valid(), cbor_value_get_int, cbor_value_get_int64, cbor_value_get_uint64, cbor_value_get_raw_integer + */ + +/** + * \fn bool cbor_value_is_unsigned_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR unsigned + * integer type (positive values or zero). + * + * \sa cbor_value_is_valid(), cbor_value_get_uint64() + */ + +/** + * \fn bool cbor_value_is_negative_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR negative + * integer type. + * + * \sa cbor_value_is_valid(), cbor_value_get_int, cbor_value_get_int64, cbor_value_get_raw_integer + */ + +/** + * \fn CborError cbor_value_get_int(const CborValue *value, int *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Note that this function does not do range-checking: integral values that do + * not fit in a variable of type \c{int} are silently truncated to fit. Use + * cbor_value_get_int_checked() that is not acceptable. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * \fn CborError cbor_value_get_int64(const CborValue *value, int64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Note that this function does not do range-checking: integral values that do + * not fit in a variable of type \c{int64_t} are silently truncated to fit. Use + * cbor_value_get_int64_checked() that is not acceptable. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * \fn CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an unsigned integer + * value, the behavior is undefined, so checking with \ref cbor_value_get_type + * or with \ref cbor_value_is_unsigned_integer is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_unsigned_integer() + */ + +/** + * \fn CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * This function is provided because CBOR negative integers can assume values + * that cannot be represented with normal 64-bit integer variables. + * + * If the integer is unsigned (that is, if cbor_value_is_unsigned_integer() + * returns true), then \a result will contain the actual value. If the integer + * is negative, then \a result will contain the absolute value of that integer, + * minus one. That is, \c {actual = -result - 1}. On architectures using two's + * complement for representation of negative integers, it is equivalent to say + * that \a result will contain the bitwise negation of the actual value. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Unlike cbor_value_get_int64(), this function performs a check to see if the + * stored integer fits in \a result without data loss. If the number is outside + * the valid range for the data type, this function returns the recoverable + * error CborErrorDataTooLarge. In that case, use either + * cbor_value_get_uint64() (if the number is positive) or + * cbor_value_get_raw_integer(). + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer(), cbor_value_get_int64() + */ +CborError cbor_value_get_int64_checked(const CborValue *value, int64_t *result) +{ + cbor_assert(cbor_value_is_integer(value)); + uint64_t v = _cbor_value_extract_int64_helper(value); + + /* Check before converting, as the standard says (C11 6.3.1.3 paragraph 3): + * "[if] the new type is signed and the value cannot be represented in it; either the + * result is implementation-defined or an implementation-defined signal is raised." + * + * The range for int64_t is -2^63 to 2^63-1 (int64_t is required to be + * two's complement, C11 7.20.1.1 paragraph 3), which in CBOR is + * represented the same way, differing only on the "sign bit" (the major + * type). + */ + + if (unlikely(v > (uint64_t)INT64_MAX)) + return CborErrorDataTooLarge; + + *result = v; + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +/** + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Unlike cbor_value_get_int(), this function performs a check to see if the + * stored integer fits in \a result without data loss. If the number is outside + * the valid range for the data type, this function returns the recoverable + * error CborErrorDataTooLarge. In that case, use one of the other integer + * functions to obtain the value. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer(), cbor_value_get_int64(), + * cbor_value_get_uint64(), cbor_value_get_int64_checked(), cbor_value_get_raw_integer() + */ +CborError cbor_value_get_int_checked(const CborValue *value, int *result) +{ + cbor_assert(cbor_value_is_integer(value)); + uint64_t v = _cbor_value_extract_int64_helper(value); + + /* Check before converting, as the standard says (C11 6.3.1.3 paragraph 3): + * "[if] the new type is signed and the value cannot be represented in it; either the + * result is implementation-defined or an implementation-defined signal is raised." + * + * But we can convert from signed to unsigned without fault (paragraph 2). + * + * The range for int is implementation-defined and int is not guaranteed use + * two's complement representation (int32_t is). + */ + + if (value->flags & CborIteratorFlag_NegativeInteger) { + if (unlikely(v > (unsigned) -(INT_MIN + 1))) + return CborErrorDataTooLarge; + + *result = (int)v; + *result = -*result - 1; + } else { + if (unlikely(v > (uint64_t)INT_MAX)) + return CborErrorDataTooLarge; + + *result = (int)v; + } + return CborNoError; + +} + +/** + * \fn bool cbor_value_is_length_known(const CborValue *value) + * + * Returns true if the length of this type is known without calculation. That + * is, if the length of this CBOR string, map or array is encoded in the data + * stream, this function returns true. If the length is not encoded, it returns + * false. + * + * If the length is known, code can call cbor_value_get_string_length(), + * cbor_value_get_array_length() or cbor_value_get_map_length() to obtain the + * length. If the length is not known but is necessary, code can use the + * cbor_value_calculate_string_length() function (no equivalent function is + * provided for maps and arrays). + */ + +/** + * \fn bool cbor_value_is_text_string(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR text + * string. CBOR text strings are UTF-8 encoded and usually contain + * human-readable text. + * + * \sa cbor_value_is_valid(), cbor_value_get_string_length(), cbor_value_calculate_string_length(), + * cbor_value_copy_text_string(), cbor_value_dup_text_string() + */ + +/** + * \fn bool cbor_value_is_byte_string(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR text + * string. CBOR byte strings are binary data with no specified encoding or + * format. + * + * \sa cbor_value_is_valid(), cbor_value_get_string_length(), cbor_value_calculate_string_length(), + * cbor_value_copy_byte_string(), cbor_value_dup_byte_string() + */ + +/** + * \fn CborError cbor_value_get_string_length(const CborValue *value, size_t *length) + * + * Extracts the length of the byte or text string that \a value points to and + * stores it in \a result. If the iterator \a value does not point to a text + * string or a byte string, the behaviour is undefined, so checking with \ref + * cbor_value_get_type, with \ref cbor_value_is_text_string or \ref + * cbor_value_is_byte_string is recommended. + * + * If the length of this string is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * If the length of the string is required but the length was not encoded, use + * cbor_value_calculate_string_length(), but note that that function does not + * run in constant time. + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known(), cbor_value_calculate_string_length() + */ + +/** + * Calculates the length of the byte or text string that \a value points to and + * stores it in \a len. If the iterator \a value does not point to a text + * string or a byte string, the behaviour is undefined, so checking with \ref + * cbor_value_get_type, with \ref cbor_value_is_text_string or \ref + * cbor_value_is_byte_string is recommended. + * + * This function is different from cbor_value_get_string_length() in that it + * calculates the length even for strings sent in chunks. For that reason, this + * function may not run in constant time (it will run in O(n) time on the + * number of chunks). It does use constant memory (O(1)). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_get_string_length(), cbor_value_copy_text_string(), cbor_value_copy_byte_string(), cbor_value_is_length_known() + */ +CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len) +{ + *len = SIZE_MAX; + return _cbor_value_copy_string(value, NULL, len, NULL); +} + +static inline void prepare_string_iteration(CborValue *it) +{ + if (!cbor_value_is_length_known(it)) { + /* chunked string: we're before the first chunk; + * advance to the first chunk */ + ++it->offset; + it->flags |= CborIteratorFlag_IteratingStringChunks; + } +} + +CBOR_INTERNAL_API_CC CborError _cbor_value_prepare_string_iteration(CborValue *it) +{ + cbor_assert((it->flags & CborIteratorFlag_IteratingStringChunks) == 0); + prepare_string_iteration(it); + + /* are we at the end? */ + if (it->offset == it->parser->end) + return CborErrorUnexpectedEOF; + + return CborNoError; +} + +static const void * +get_string_chunk_update(CborValue *it, const void **bufferptr, size_t *len) +{ + *bufferptr = (const void *)it->parser->d->get_string_chunk(it->parser->d, + it->offset, len); + it->offset += *len; + it->remainingclen -= *len; + + return *bufferptr; +} + +static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len) +{ + CborError err; + + + if (it->remainingclen) { + *len = it->remainingclen; + + *bufferptr = get_string_chunk_update(it, bufferptr, len); + + return CborNoError; + } + + /* Possible states: + * length known | iterating | meaning + * no | no | before the first chunk of a chunked string + * yes | no | at a non-chunked string + * no | yes | second or later chunk + * yes | yes | after a non-chunked string + */ + if (it->flags & CborIteratorFlag_IteratingStringChunks) { + /* already iterating */ + if (cbor_value_is_length_known(it)) { + /* if the length was known, it wasn't chunked, so finish iteration */ + goto last_chunk; + } + } else { + prepare_string_iteration(it); + } + + uint8_t val; + + val = it->parser->d->get8(it->parser->d, it->offset); + + /* are we at the end? */ + if (it->offset == it->parser->end) + return CborErrorUnexpectedEOF; + + if (val == (uint8_t)BreakByte) { + /* last chunk */ + ++it->offset; +last_chunk: + *bufferptr = NULL; + *len = 0; + it->remainingclen = 0; + return preparse_next_value(it); + } else if ((val & MajorTypeMask) == it->type) { + err = extract_length(it->parser, &it->offset, len); + if (err) + return err; + + if (*len > (size_t)(it->parser->end - it->offset)) + return CborErrorUnexpectedEOF; + + it->remainingclen = *len; + + *bufferptr = get_string_chunk_update(it, bufferptr, len); + + } else { + return CborErrorIllegalType; + } + + it->flags |= CborIteratorFlag_IteratingStringChunks; + return CborNoError; +} + +/** + * \fn CborError cbor_value_get_text_string_chunk(const CborValue *value, const char **bufferptr, size_t *len, CborValue *next) + * + * Extracts one text string chunk pointed to by \a value and stores a pointer + * to the data in \a buffer and the size in \a len, which must not be null. If + * no more chunks are available, then \a bufferptr will be set to null. This + * function may be used to iterate over any string without causing its contents + * to be copied to a separate buffer, like the convenience function + * cbor_value_copy_text_string() does. + * + * It is designed to be used in code like: + * + * \code + * if (cbor_value_is_text_string(value)) { + * char *ptr; + * size_t len; + * while (1) { + * err = cbor_value_get_text_string_chunk(value, &ptr, &len, &value)); + * if (err) return err; + * if (ptr == NULL) return CborNoError; + * consume(ptr, len); + * } + * } + * \endcode + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. During iteration, the pointer must only be passed back + * again to this function; passing it to any other function in this library + * results in undefined behavior. If there are no more chunks to be read from + * \a value, then \a next will be set to the next item after this string; if \a + * value points to the last item, then \a next will be invalid. + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_dup_text_string(), cbor_value_copy_text_string(), cbor_value_caculate_string_length(), cbor_value_get_byte_string_chunk() + */ + +/** + * \fn CborError cbor_value_get_byte_string_chunk(const CborValue *value, const char **bufferptr, size_t *len, CborValue *next) + * + * Extracts one byte string chunk pointed to by \a value and stores a pointer + * to the data in \a buffer and the size in \a len, which must not be null. If + * no more chunks are available, then \a bufferptr will be set to null. This + * function may be used to iterate over any string without causing its contents + * to be copied to a separate buffer, like the convenience function + * cbor_value_copy_byte_string() does. + * + * It is designed to be used in code like: + * + * \code + * if (cbor_value_is_byte_string(value)) { + * char *ptr; + * size_t len; + * while (1) { + * err = cbor_value_get_byte_string_chunk(value, &ptr, &len, &value)); + * if (err) return err; + * if (ptr == NULL) return CborNoError; + * consume(ptr, len); + * } + * } + * \endcode + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. During iteration, the pointer must only be passed back + * again to this function; passing it to any other function in this library + * results in undefined behavior. If there are no more chunks to be read from + * \a value, then \a next will be set to the next item after this string; if \a + * value points to the last item, then \a next will be invalid. + * + * \sa cbor_value_dup_byte_string(), cbor_value_copy_byte_string(), cbor_value_caculate_string_length(), cbor_value_get_text_string_chunk() + */ + +CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, + size_t *len, CborValue *next) +{ + CborValue tmp; + if (!next) + next = &tmp; + *next = *value; + + return get_string_chunk(next, bufferptr, len); +} + +/* We return uintptr_t so that we can pass memcpy directly as the iteration + * function. The choice is to optimize for memcpy, which is used in the base + * parser API (cbor_value_copy_string), while memcmp is used in convenience API + * only. */ +typedef uintptr_t (*IterateFunction)(struct cbor_decoder_reader *d, char *dst, int src_offset, size_t len); + +static uintptr_t iterate_noop(struct cbor_decoder_reader *d, char *dst, int src_offset, size_t len) +{ + (void)d; + (void)dst; + (void)src_offset; + (void)len; + return true; +} + +static CborError iterate_string_chunks(const CborValue *value, char *buffer, size_t *buflen, + bool *result, CborValue *next, IterateFunction func) +{ + cbor_assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); + + CborError err; + CborValue tmp; + size_t total = 0; + const void *ptr; + + if (!next) + next = &tmp; + *next = *value; + *result = true; + + while (1) { + size_t newTotal; + size_t chunkLen = 0; + err = get_string_chunk(next, &ptr, &chunkLen); + if (err) + return err; + if (!ptr) + break; + + if (unlikely(add_check_overflow(total, chunkLen, &newTotal))) + return CborErrorDataTooLarge; + + if (*result && *buflen >= newTotal) + *result = !!func(value->parser->d, buffer + total, next->offset - chunkLen, chunkLen); + else + *result = false; + + total = newTotal; + } + + *buflen = total; + + return CborNoError; +} + +/** + * \fn CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, size_t *buflen, CborValue *next) + * + * Copies the string pointed by \a value into the buffer provided at \a buffer + * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not + * copy anything and will only update the \a next value. + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * If the provided buffer length was too small, this function returns an error + * condition of \ref CborErrorOutOfMemory. If you need to calculate the length + * of the string in order to preallocate a buffer, use + * cbor_value_calculate_string_length(). + * + * On success, this function sets the number of bytes copied to \c{*buflen}. If + * the buffer is large enough, this function will insert a null byte after the + * last copied byte, to facilitate manipulation of text strings. That byte is + * not included in the returned value of \c{*buflen}. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_get_text_string_chunk() cbor_value_dup_text_string(), cbor_value_copy_byte_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length() + */ + +/** + * \fn CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, size_t *buflen, CborValue *next) + * + * Copies the string pointed by \a value into the buffer provided at \a buffer + * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not + * copy anything and will only update the \a next value. + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * If the provided buffer length was too small, this function returns an error + * condition of \ref CborErrorOutOfMemory. If you need to calculate the length + * of the string in order to preallocate a buffer, use + * cbor_value_calculate_string_length(). + * + * On success, this function sets the number of bytes copied to \c{*buflen}. If + * the buffer is large enough, this function will insert a null byte after the + * last copied byte, to facilitate manipulation of null-terminated strings. + * That byte is not included in the returned value of \c{*buflen}. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \sa cbor_value_get_byte_string_chunk(), cbor_value_dup_text_string(), cbor_value_copy_text_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length() + */ + +CborError _cbor_value_copy_string(const CborValue *value, void *buffer, + size_t *buflen, CborValue *next) +{ + bool copied_all; + CborError err = iterate_string_chunks(value, (char*)buffer, buflen, &copied_all, next, + buffer ? (IterateFunction) value->parser->d->cpy : iterate_noop); + if (err) { + return err; + } + + if (!copied_all) { + return CborErrorOutOfMemory; + } + + if (buffer) { + *((uint8_t *)buffer + *buflen) = '\0'; + } + + return CborNoError; +} + +/** + * Compares the entry \a value with the string \a string and store the result + * in \a result. If the value is different from \a string \a result will + * contain \c false. + * + * The entry at \a value may be a tagged string. If \a is not a string or a + * tagged string, the comparison result will be false. + * + * CBOR requires text strings to be encoded in UTF-8, but this function does + * not validate either the strings in the stream or the string \a string to be + * matched. Moreover, comparison is done on strict codepoint comparison, + * without any Unicode normalization. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \sa cbor_value_skip_tag(), cbor_value_copy_text_string() + */ +CborError cbor_value_text_string_equals(const CborValue *value, const char *string, bool *result) +{ + CborValue copy = *value; + CborError err = cbor_value_skip_tag(©); + + if (err) + return err; + if (!cbor_value_is_text_string(©)) { + *result = false; + return CborNoError; + } + + size_t len = strlen(string); + err = iterate_string_chunks(©, CONST_CAST(char *, string), &len, + result, NULL, value->parser->d->cmp); + if (err) { + return err; + } + + if (*result && string[len] != '\0') { + *result = false; + } + + return CborNoError; +} + +/** + * \fn bool cbor_value_is_array(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR array. + * + * \sa cbor_value_is_valid(), cbor_value_is_map() + */ + +/** + * \fn CborError cbor_value_get_array_length(const CborValue *value, size_t *length) + * + * Extracts the length of the CBOR array that \a value points to and stores it + * in \a result. If the iterator \a value does not point to a CBOR array, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_array is recommended. + * + * If the length of this array is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known() + */ + +/** + * \fn bool cbor_value_is_map(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR map. + * + * \sa cbor_value_is_valid(), cbor_value_is_array() + */ + +/** + * \fn CborError cbor_value_get_map_length(const CborValue *value, size_t *length) + * + * Extracts the length of the CBOR map that \a value points to and stores it in + * \a result. If the iterator \a value does not point to a CBOR map, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_map is recommended. + * + * If the length of this map is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known() + */ + +/** + * Attempts to find the value in map \a map that corresponds to the text string + * entry \a string. If the iterator \a value does not point to a CBOR map, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_map is recommended. + * + * If the item is found, it is stored in \a result. If no item is found + * matching the key, then \a result will contain an element of type \ref + * CborInvalidType. Matching is performed using + * cbor_value_text_string_equals(), so tagged strings will also match. + * + * This function has a time complexity of O(n) where n is the number of + * elements in the map to be searched. In addition, this function is has O(n) + * memory requirement based on the number of nested containers (maps or arrays) + * found as elements of this map. + * + * \sa cbor_value_is_valid(), cbor_value_text_string_equals(), cbor_value_advance() + */ +CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element) +{ + cbor_assert(cbor_value_is_map(map)); + size_t len = strlen(string); + CborError err = cbor_value_enter_container(map, element); + if (err) + goto error; + + while (!cbor_value_at_end(element)) { + /* find the non-tag so we can compare */ + err = cbor_value_skip_tag(element); + if (err) + goto error; + if (cbor_value_is_text_string(element)) { + bool equals; + size_t dummyLen = len; + err = iterate_string_chunks(element, CONST_CAST(char *, string), &dummyLen, + &equals, element, map->parser->d->cmp); + if (err) + goto error; + if (equals && string[dummyLen] == '\0') + return preparse_value(element); + } else { + /* skip this key */ + err = cbor_value_advance(element); + if (err) + goto error; + } + + /* skip this value */ + err = cbor_value_skip_tag(element); + if (err) + goto error; + err = cbor_value_advance(element); + if (err) + goto error; + } + + /* not found */ + element->type = CborInvalidType; + return CborNoError; + +error: + element->type = CborInvalidType; + return err; +} + +/** + * \fn bool cbor_value_is_float(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * single-precision floating point (32-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_double(), cbor_value_is_half_float() + */ + +/** + * \fn CborError cbor_value_get_float(const CborValue *value, float *result) + * + * Retrieves the CBOR single-precision floating point (32-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a single-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_float is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_float(), cbor_value_get_double() + */ + +/** + * \fn bool cbor_value_is_double(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * double-precision floating point (64-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_float(), cbor_value_is_half_float() + */ + +/** + * \fn CborError cbor_value_get_double(const CborValue *value, float *result) + * + * Retrieves the CBOR double-precision floating point (64-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a double-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_double is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_double(), cbor_value_get_float() + */ + +/** + * \fn bool cbor_value_is_half_float(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * single-precision floating point (16-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_double(), cbor_value_is_float() + */ + +/** + * Retrieves the CBOR half-precision floating point (16-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a half-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_half_float is recommended. + * + * Note: since the C language does not have a standard type for half-precision + * floating point, this function takes a \c{void *} as a parameter for the + * storage area, which must be at least 16 bits wide. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_float() + */ +CborError cbor_value_get_half_float(const CborValue *value, void *result) +{ + cbor_assert(cbor_value_is_half_float(value)); + + /* size has been computed already */ + uint16_t v = value->parser->d->get16(value->parser->d, value->offset + 1); + memcpy(result, &v, sizeof(v)); + return CborNoError; +} + + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cborparser_dup_string.c b/ext/lib/encoding/tinycbor/src/cborparser_dup_string.c new file mode 100644 index 000000000000..1fda05e04660 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborparser_dup_string.c @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include +#include "compilersupport_p.h" + +/** + * \fn CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next) + * + * Allocates memory for the string pointed by \a value and copies it into this + * buffer. The pointer to the buffer is stored in \a buffer and the number of + * bytes copied is stored in \a len (those variables must not be NULL). + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * If \c malloc returns a NULL pointer, this function will return error + * condition \ref CborErrorOutOfMemory. + * + * On success, \c{*buffer} will contain a valid pointer that must be freed by + * calling \c{free()}. This is the case even for zero-length strings. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)) in addition to the + * malloc'ed block. + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_get_text_string_chunk(), cbor_value_copy_text_string(), cbor_value_dup_byte_string() + */ + +/** + * \fn CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, size_t *buflen, CborValue *next) + * + * Allocates memory for the string pointed by \a value and copies it into this + * buffer. The pointer to the buffer is stored in \a buffer and the number of + * bytes copied is stored in \a len (those variables must not be NULL). + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * If \c malloc returns a NULL pointer, this function will return error + * condition \ref CborErrorOutOfMemory. + * + * On success, \c{*buffer} will contain a valid pointer that must be freed by + * calling \c{free()}. This is the case even for zero-length strings. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)) in addition to the + * malloc'ed block. + * + * \sa cbor_value_get_text_string_chunk(), cbor_value_copy_byte_string(), cbor_value_dup_text_string() + */ +CborError _cbor_value_dup_string(const CborValue *value, void **buffer, size_t *buflen, CborValue *next) +{ + cbor_assert(buffer); + cbor_assert(buflen); + *buflen = SIZE_MAX; + CborError err = _cbor_value_copy_string(value, NULL, buflen, NULL); + if (err) + return err; + + ++*buflen; + *buffer = malloc(*buflen); + if (!*buffer) { + /* out of memory */ + return CborErrorOutOfMemory; + } + err = _cbor_value_copy_string(value, *buffer, buflen, next); + if (err) { + free(*buffer); + return err; + } + return CborNoError; +} diff --git a/ext/lib/encoding/tinycbor/src/cborpretty.c b/ext/lib/encoding/tinycbor/src/cborpretty.c new file mode 100644 index 000000000000..1a8280ee4915 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborpretty.c @@ -0,0 +1,539 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "compilersupport_p.h" +#include "cborinternal_p.h" +#include "utf8_p.h" + +#include +#include +#ifndef CBOR_NO_FLOATING_POINT +#include +#endif +#include + +/** + * \defgroup CborPretty Converting CBOR to text + * \brief Group of functions used to convert CBOR to text form. + * + * This group contains two functions that are can be used to convert one + * CborValue object to a text representation. This module attempts to follow + * the recommendations from RFC 7049 section 6 "Diagnostic Notation", though it + * has a few differences. They are noted below. + * + * TinyCBOR does not provide a way to convert from the text representation back + * to encoded form. To produce a text form meant to be parsed, CborToJson is + * recommended instead. + * + * Either of the functions in this section will attempt to convert exactly one + * CborValue object to text. Those functions may return any error documented + * for the functions for CborParsing. In addition, if the C standard library + * stream functions return with error, the text conversion will return with + * error CborErrorIO. + * + * These functions also perform UTF-8 validation in CBOR text strings. If they + * encounter a sequence of bytes that not permitted in UTF-8, they will return + * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points + * in UTF-8. + * + * \warning The output type produced by these functions is not guaranteed to + * remain stable. A future update of TinyCBOR may produce different output for + * the same input and parsers may be unable to handle them. + * + * \sa CborParsing, CborToJson, cbor_parser_init() + */ + +/** + * \addtogroup CborPretty + * @{ + *

Text format

+ * + * As described in RFC 7049 section 6 "Diagnostic Notation", the format is + * largely borrowed from JSON, but modified to suit CBOR's different data + * types. TinyCBOR makes further modifications to distinguish different, but + * similar values. + * + * CBOR values are currently encoded as follows: + * \par Integrals (unsigned and negative) + * Base-10 (decimal) text representation of the value + * \par Byte strings: + * "h'" followed by the Base16 (hex) representation of the binary data, followed by an ending quote (') + * \par Text strings: + * C-style escaped string in quotes, with C11/C++11 escaping of Unicode codepoints above U+007F. + * \par Tags: + * Tag value, with the tagged value in parentheses. No special encoding of the tagged value is performed. + * \par Simple types: + * "simple(nn)" where \c nn is the simple value + * \par Null: + * \c null + * \par Undefined: + * \c undefined + * \par Booleans: + * \c true or \c false + * \par Floating point: + * If NaN or infinite, the actual words \c NaN or \c infinite. + * Otherwise, the decimal representation with as many digits as necessary to ensure no loss of information. + * By default, float values are suffixed by "f" and half-float values suffixed by "f16" (doubles have no suffix). + * If the CborPrettyNumericEncodingIndicators flag is active, the values instead are encoded following the + * Section 6 recommended encoding indicators: float values are suffixed with "_2" and half-float with "_1". + * A dot is always present. + * \par Arrays: + * Comma-separated list of elements, enclosed in square brackets ("[" and "]"). + * \par Maps: + * Comma-separated list of key-value pairs, with the key and value separated + * by a colon (":"), enclosed in curly braces ("{" and "}"). + * + * The CborPrettyFlags enumerator contains flags to control some aspects of the + * encoding: + * \par String fragmentation + * When the CborPrettyShowStringFragments option is active, text and byte + * strings that are transmitted in fragments are shown instead inside + * parentheses ("(" and ")") with no preceding number and each fragment is + * displayed individually. If a tag precedes the string, then the output + * will contain a double set of parentheses. If the option is not active, + * the fragments are merged together and the display will not show any + * difference from a string transmitted with determinate length. + * \par Encoding indicators + * Numbers and lengths in CBOR can be encoded in multiple representations. + * If the CborPrettyIndicateOverlongNumbers option is active, numbers + * and lengths that are transmitted in a longer encoding than necessary + * will be indicated, by appending an underscore ("_") to either the + * number or the opening bracket or brace, followed by a number + * indicating the CBOR additional information: 0 for 1 byte, 1 for 2 + * bytes, 2 for 4 bytes and 3 for 8 bytes. + * If the CborPrettyIndicateIndetermineLength option is active, maps, + * arrays and strings encoded with indeterminate length will be marked by + * an underscore after the opening bracket or brace or the string (if not + * showing fragments), without a number after it. + */ + +/** + * \enum CborPrettyFlags + * The CborPrettyFlags enum contains flags that control the conversion of CBOR to text format. + * + * \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float. + * \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16"). + * \value CborPrettyIndicateIndetermineLength Indicate when a map or array has indeterminate length. + * \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed. + * \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually. + * \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry. + * \value CborPrettyDefaultFlags Default conversion flags. + */ + +static void printRecursionLimit(CborStreamFunction stream, void *out) +{ + stream(out, ""); +} + +static CborError hexDump(CborStreamFunction stream, void *out, const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + CborError err = CborNoError; + while (n-- && !err) + err = stream(out, "%02" PRIx8, *buffer++); + + return err; +} + +/* This function decodes buffer as UTF-8 and prints as escaped UTF-16. + * On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */ +static CborError utf8EscapedDump(CborStreamFunction stream, void *out, const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + const uint8_t * const end = buffer + n; + CborError err = CborNoError; + + while (buffer < end && !err) { + uint32_t uc = get_utf8(&buffer, end); + if (uc == ~0U) + return CborErrorInvalidUtf8TextString; + + if (uc < 0x80) { + /* single-byte UTF-8 */ + if (uc < 0x7f && uc >= 0x20 && uc != '\\' && uc != '"') { + err = stream(out, "%c", (char)uc); + continue; + } + + /* print as an escape sequence */ + char escaped = (char)uc; + switch (uc) { + case '"': + case '\\': + break; + case '\b': + escaped = 'b'; + break; + case '\f': + escaped = 'f'; + break; + case '\n': + escaped = 'n'; + break; + case '\r': + escaped = 'r'; + break; + case '\t': + escaped = 't'; + break; + default: + goto print_utf16; + } + err = stream(out, "\\%c", escaped); + continue; + } + + /* now print the sequence */ + if (uc > 0xffffU) { + /* needs surrogate pairs */ + err = stream(out, "\\u%04" PRIX32 "\\u%04" PRIX32, + (uc >> 10) + 0xd7c0, /* high surrogate */ + (uc % 0x0400) + 0xdc00); + } else { +print_utf16: + /* no surrogate pair needed */ + err = stream(out, "\\u%04" PRIX32, uc); + } + } + return err; +} + +static const char *resolve_indicator(const CborValue *it, int flags) +{ + static const char indicators[8][3] = { + "_0", "_1", "_2", "_3", + "", "", "", /* these are not possible */ + "_" + }; + const char *no_indicator = indicators[5]; /* empty string */ + uint8_t additional_information; + uint8_t expected_information; + uint64_t value; + CborError err; + int offset; + uint8_t val; + + if (it->offset == it->parser->end) + return NULL; /* CborErrorUnexpectedEOF */ + + val = it->parser->d->get8(it->parser->d, it->offset); + + additional_information = (val & SmallValueMask); + if (additional_information < Value8Bit) + return no_indicator; + + /* determine whether to show anything */ + if ((flags & CborPrettyIndicateIndetermineLength) && + additional_information == IndefiniteLength) + return indicators[IndefiniteLength - Value8Bit]; + if ((flags & CborPrettyIndicateOverlongNumbers) == 0) + return no_indicator; + + offset = it->offset; + + err = _cbor_value_extract_number(it->parser, &offset, &value); + if (err) + return NULL; /* CborErrorUnexpectedEOF */ + + expected_information = Value8Bit - 1; + if (value >= Value8Bit) + ++expected_information; + if (value > 0xffU) + ++expected_information; + if (value > 0xffffU) + ++expected_information; + if (value > 0xffffffffU) + ++expected_information; + return expected_information == additional_information ? + no_indicator : + indicators[additional_information - Value8Bit]; +} + +static const char *get_indicator(const CborValue *it, int flags) +{ + return resolve_indicator(it, flags); +} + +static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft); +static CborError container_to_pretty(CborStreamFunction stream, void *out, CborValue *it, CborType containerType, + int flags, int recursionsLeft) +{ + if (!recursionsLeft) { + printRecursionLimit(stream, out); + return CborNoError; /* do allow the dumping to continue */ + } + + const char *comma = ""; + CborError err = CborNoError; + while (!cbor_value_at_end(it) && !err) { + err = stream(out, "%s", comma); + comma = ", "; + + if (!err) + err = value_to_pretty(stream, out, it, flags, recursionsLeft); + + if (containerType == CborArrayType) + continue; + + /* map: that was the key, so get the value */ + if (!err) + err = stream(out, ": "); + if (!err) + err = value_to_pretty(stream, out, it, flags, recursionsLeft); + } + return err; +} + +static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft) +{ + CborError err = CborNoError; + CborType type = cbor_value_get_type(it); + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + const char *indicator = get_indicator(it, flags); + const char *space = *indicator ? " " : indicator; + + err = stream(out, "%c%s%s", type == CborArrayType ? '[' : '{', indicator, space); + if (err) + return err; + + err = cbor_value_enter_container(it, &recursed); + if (err) { + it->offset = recursed.offset; + return err; /* parse error */ + } + err = container_to_pretty(stream, out, &recursed, type, flags, recursionsLeft - 1); + if (err) { + it->offset = recursed.offset; + return err; /* parse error */ + } + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; /* parse error */ + + return stream(out, type == CborArrayType ? "]" : "}"); + } + + case CborIntegerType: { + uint64_t val; + cbor_value_get_raw_integer(it, &val); /* can't fail */ + + if (cbor_value_is_unsigned_integer(it)) { + err = stream(out, "%" PRIu64, val); + } else { + /* CBOR stores the negative number X as -1 - X + * (that is, -1 is stored as 0, -2 as 1 and so forth) */ + if (++val) { /* unsigned overflow may happen */ + err = stream(out, "-%" PRIu64, val); + } else { + /* overflown + * 0xffff`ffff`ffff`ffff + 1 = + * 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64) */ + err = stream(out, "-18446744073709551616"); + } + } + if (!err) + err = stream(out, "%s", get_indicator(it, flags)); + break; + } + + case CborByteStringType: + case CborTextStringType: { + size_t n = 0; + const void *ptr; + bool showingFragments = (flags & CborPrettyShowStringFragments) && !cbor_value_is_length_known(it); + const char *separator = ""; + char close = '\''; + char open[3] = "h'"; + const char *indicator = NULL; + + if (type == CborTextStringType) { + close = open[0] = '"'; + open[1] = '\0'; + } + + if (showingFragments) { + err = stream(out, "(_ "); + if (!err) + err = _cbor_value_prepare_string_iteration(it); + } else { + err = stream(out, "%s", open); + } + + while (!err) { + if (showingFragments || indicator == NULL) { + /* any iteration, except the second for a non-chunked string */ + indicator = resolve_indicator(it, flags); + } + + err = _cbor_value_get_string_chunk(it, &ptr, &n, it); + if (!ptr) + break; + + if (!err && showingFragments) + err = stream(out, "%s%s", separator, open); + if (!err) + err = (type == CborByteStringType ? + hexDump(stream, out, ptr, n) : + utf8EscapedDump(stream, out, ptr, n)); + if (!err && showingFragments) { + err = stream(out, "%c%s", close, indicator); + separator = ", "; + } + } + + if (!err) { + if (showingFragments) + err = stream(out, ")"); + else + err = stream(out, "%c%s", close, indicator); + } + return err; + } + + case CborTagType: { + CborTag tag; + cbor_value_get_tag(it, &tag); /* can't fail */ + err = stream(out, "%" PRIu64 "%s(", tag, get_indicator(it, flags)); + if (!err) + err = cbor_value_advance_fixed(it); + if (!err && recursionsLeft) + err = value_to_pretty(stream, out, it, flags, recursionsLeft - 1); + else if (!err) + printRecursionLimit(stream, out); + if (!err) + err = stream(out, ")"); + return err; + } + + case CborSimpleType: { + /* simple types can't fail and can't have overlong encoding */ + uint8_t simple_type; + cbor_value_get_simple_type(it, &simple_type); + err = stream(out, "simple(%" PRIu8 ")", simple_type); + break; + } + + case CborNullType: + err = stream(out, "null"); + break; + + case CborUndefinedType: + err = stream(out, "undefined"); + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); /* can't fail */ + err = stream(out, val ? "true" : "false"); + break; + } +#ifndef CBOR_NO_FLOATING_POINT + case CborDoubleType: { + const char *suffix; + double val; + int r; + if (false) { + float f; + case CborFloatType: + cbor_value_get_float(it, &f); + val = f; + suffix = flags & CborPrettyNumericEncodingIndicators ? "_2" : "f"; +#ifndef CBOR_NO_HALF_FLOAT_TYPE + } else if (false) { + uint16_t f16; + case CborHalfFloatType: + cbor_value_get_half_float(it, &f16); + val = decode_half(f16); + suffix = flags & CborPrettyNumericEncodingIndicators ? "_1" : "f16"; +#endif + } else { + cbor_value_get_double(it, &val); + suffix = ""; + } + + if ((flags & CborPrettyNumericEncodingIndicators) == 0) { + r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) + suffix = ""; + } + + uint64_t ival = (uint64_t)fabs(val); + if (ival == fabs(val)) { + /* this double value fits in a 64-bit integer, so show it as such + * (followed by a floating point suffix, to disambiguate) */ + err = stream(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix); + } else { + /* this number is definitely not a 64-bit integer */ + err = stream(out, "%." DBL_DECIMAL_DIG_STR "g%s", val, suffix); + } + break; + } +#endif + case CborInvalidType: + err = stream(out, "invalid"); + if (err) + return err; + } + + if (!err) + err = cbor_value_advance_fixed(it); + return err; +} + +/** + * Converts the current CBOR type pointed by \a value to its textual + * representation and writes it to the stream by calling the \a streamFunction. + * If an error occurs, this function returns an error code similar to + * CborParsing. + * + * The textual representation can be controlled by the \a flags parameter (see + * CborPrettyFlags for more information). + * + * If no error ocurred, this function advances \a value to the next element. + * Often, concatenating the text representation of multiple elements can be + * done by appending a comma to the output stream. + * + * The \a streamFunction function will be called with the \a token value as the + * first parameter and a printf-style format string as the second, with a variable + * number of further parameters. + * + * \sa cbor_value_to_pretty(), cbor_value_to_json_advance() + */ +CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags) +{ + return value_to_pretty(streamFunction, token, value, flags, CBOR_PARSER_MAX_RECURSIONS); +} + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cborpretty_stdio.c b/ext/lib/encoding/tinycbor/src/cborpretty_stdio.c new file mode 100644 index 000000000000..f905e86878ae --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborpretty_stdio.c @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#include "cbor.h" +#include +#include + +static CborError cbor_fprintf(void *out, const char *fmt, ...) +{ + int n; + + va_list list; + va_start(list, fmt); + n = vfprintf((FILE *)out, fmt, list); + va_end(list); + + return n < 0 ? CborErrorIO : CborNoError; +} + +/** + * \fn CborError cbor_value_to_pretty(FILE *out, const CborValue *value) + * + * Converts the current CBOR type pointed by \a value to its textual + * representation and writes it to the \a out stream. If an error occurs, this + * function returns an error code similar to CborParsing. + * + * \sa cbor_value_to_pretty_advance(), cbor_value_to_json_advance() + */ + +/** + * Converts the current CBOR type pointed by \a value to its textual + * representation and writes it to the \a out stream. If an error occurs, this + * function returns an error code similar to CborParsing. + * + * If no error ocurred, this function advances \a value to the next element. + * Often, concatenating the text representation of multiple elements can be + * done by appending a comma to the output stream. + * + * \sa cbor_value_to_pretty(), cbor_value_to_pretty_stream(), cbor_value_to_json_advance() + */ +CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value) +{ + return cbor_value_to_pretty_stream(cbor_fprintf, out, value, CborPrettyDefaultFlags); +} + +/** + * Converts the current CBOR type pointed by \a value to its textual + * representation and writes it to the \a out stream. If an error occurs, this + * function returns an error code similar to CborParsing. + * + * The textual representation can be controlled by the \a flags parameter (see + * CborPrettyFlags for more information). + * + * If no error ocurred, this function advances \a value to the next element. + * Often, concatenating the text representation of multiple elements can be + * done by appending a comma to the output stream. + * + * \sa cbor_value_to_pretty_stream(), cbor_value_to_pretty(), cbor_value_to_json_advance() + */ +CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags) +{ + return cbor_value_to_pretty_stream(cbor_fprintf, out, value, flags); +} + diff --git a/ext/lib/encoding/tinycbor/src/cbortojson.c b/ext/lib/encoding/tinycbor/src/cbortojson.c new file mode 100644 index 000000000000..2288c7cf12d1 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cbortojson.c @@ -0,0 +1,691 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#define _GNU_SOURCE 1 +#define _POSIX_C_SOURCE 200809L +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborjson.h" +#include "compilersupport_p.h" + +#include +#include +#ifndef CBOR_NO_FLOATING_POINT +#include +#endif +#include +#include +#include + +/** + * \defgroup CborToJson Converting CBOR to JSON + * \brief Group of functions used to convert CBOR to JSON. + * + * This group contains two functions that are can be used to convert one + * CborValue object to an equivalent JSON representation. This module attempts + * to follow the recommendations from RFC 7049 section 4.1 "Converting from + * CBOR to JSON", though it has a few differences. They are noted below. + * + * These functions produce a "minified" JSON output, with no spacing, + * indentation or line breaks. If those are necessary, they need to be applied + * in a post-processing phase. + * + * Note that JSON cannot support all CBOR types with fidelity, so the + * conversion is usually lossy. For that reason, TinyCBOR supports adding a set + * of metadata JSON values that can be used by a JSON-to-CBOR converter to + * restore the original data types. + * + * The TinyCBOR library does not provide a way to convert from JSON + * representation back to encoded form. However, it provides a tool called + * \c json2cbor which can be used for that purpose. That tool supports the + * metadata format that these functions may produce. + * + * Either of the functions in this section will attempt to convert exactly one + * CborValue object to JSON. Those functions may return any error documented + * for the functions for CborParsing. In addition, if the C standard library + * stream functions return with error, the text conversion will return with + * error CborErrorIO. + * + * These functions also perform UTF-8 validation in CBOR text strings. If they + * encounter a sequence of bytes that not permitted in UTF-8, they will return + * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points + * in UTF-8. + * + * \warning The metadata produced by these functions is not guaranteed to + * remain stable. A future update of TinyCBOR may produce different output for + * the same input and parsers may be unable to handle them. + * + * \sa CborParsing, CborPretty, cbor_parser_init() + */ + +/** + * \addtogroup CborToJson + * @{ + *

Conversion limitations

+ * + * When converting from CBOR to JSON, there may be information loss. This + * section lists the possible scenarios. + * + * \par Number precision: + * ALL JSON numbers, due to its JavaScript heritage, are IEEE 754 + * double-precision floating point. This means JSON is not capable of + * representing integers numbers outside the range [-(253)+1, + * 253-1] and is not capable of representing NaN or infinite. If the + * CBOR data contains a number outside the valid range, the conversion will + * lose precision. If the input was NaN or infinite, the result of the + * conversion will be "null". In addition, the distinction between half-, + * single- and double-precision is lost. + * + * \par + * If enabled, the original value and original type are stored in the metadata. + * + * \par Non-native types: + * CBOR's type system is richer than JSON's, which means some data values + * cannot be represented when converted to JSON. The conversion silently turns + * them into strings: CBOR simple types become "simple(nn)" where \c nn is the + * simple type's value, with the exception of CBOR undefined, which becomes + * "undefined", while CBOR byte strings are converted to an Base16, Base64, or + * Base64url encoding + * + * \par + * If enabled, the original type is stored in the metadata. + * + * \par Presence of tags: + * JSON has no support for tagged values, so by default tags are dropped when + * converting to JSON. However, if the CborConvertObeyByteStringTags option is + * active (default), then certain known tags are honored and are used to format + * the conversion of the tagged byte string to JSON. + * + * \par + * If the CborConvertTagsToObjects option is active, then the tag and the + * tagged value are converted to to a JSON object. Otherwise, if enabled, the + * last (innermost) tag is stored in the metadata. + * + * \par Non-string keys in maps: + * JSON requires all Object keys to be strings, while CBOR does not. By + * default, if a non-string key is found, the conversion fails with error + * CborErrorJsonObjectKeyNotString. If the CborConvertStringifyMapKeys option + * is active, then the conversion attempts to create a string representation + * using CborPretty. Note that the \c json2cbor tool is not able to parse this + * back to the original form. + * + * \par Duplicate keys in maps: + * Neither JSON nor CBOR allow duplicated keys, but current TinyCBOR does not + * validate that this is the case. If there are duplicated keys in the input, + * they will be repeated in the output, which may JSON tools may flag as + * invalid. In addition to that, if the CborConvertStringifyMapKeys option is + * active, it is possible that a non-string key in a CBOR map will be converted + * to a string form that is identical to another key. + * + * \par + * When metadata support is active, the conversion will add extra key-value + * pairs to the JSON output so it can store the metadata. It is possible that + * the keys for the metadata clash with existing keys in the JSON map. + */ + +extern FILE *open_memstream(char **bufptr, size_t *sizeptr); + +enum ConversionStatusFlags { + TypeWasNotNative = 0x100, /* anything but strings, boolean, null, arrays and maps */ + TypeWasTagged = 0x200, + NumberPrecisionWasLost = 0x400, + NumberWasNaN = 0x800, + NumberWasInfinite = 0x1000, + NumberWasNegative = 0x2000, /* always used with NumberWasInifite or NumberWasTooBig */ + + FinalTypeMask = 0xff +}; + +typedef struct ConversionStatus { + CborTag lastTag; + uint64_t originalNumber; + int flags; +} ConversionStatus; + +static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status); + +static CborError dump_bytestring_base16(char **result, CborValue *it) +{ + static const char characters[] = "0123456789abcdef"; + size_t i; + size_t n = 0; + uint8_t *buffer; + CborError err = cbor_value_calculate_string_length(it, &n); + if (err) + return err; + + /* a Base16 (hex) output is twice as big as our buffer */ + buffer = (uint8_t *)malloc(n * 2 + 1); + *result = (char *)buffer; + + /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */ + ++n; + err = cbor_value_copy_byte_string(it, buffer + n - 1, &n, it); + cbor_assert(err == CborNoError); + + for (i = 0; i < n; ++i) { + uint8_t byte = buffer[n + i]; + buffer[2*i] = characters[byte >> 4]; + buffer[2*i + 1] = characters[byte & 0xf]; + } + return CborNoError; +} + +static CborError generic_dump_base64(char **result, CborValue *it, const char alphabet[65]) +{ + size_t n = 0, i; + uint8_t *buffer, *out, *in; + CborError err = cbor_value_calculate_string_length(it, &n); + if (err) + return err; + + /* a Base64 output (untruncated) has 4 bytes for every 3 in the input */ + size_t len = (n + 5) / 3 * 4; + out = buffer = (uint8_t *)malloc(len + 1); + *result = (char *)buffer; + + /* we read our byte string at the tail end of the buffer + * so we can do an in-place conversion while iterating forwards */ + in = buffer + len - n; + + /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */ + ++n; + err = cbor_value_copy_byte_string(it, in, &n, it); + cbor_assert(err == CborNoError); + + uint_least32_t val = 0; + for (i = 0; n - i >= 3; i += 3) { + /* read 3 bytes x 8 bits = 24 bits */ + if (false) { +#ifdef __GNUC__ + } else if (i) { + __builtin_memcpy(&val, in + i - 1, sizeof(val)); + val = cbor_ntohl(val); +#endif + } else { + val = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2]; + } + + /* write 4 chars x 6 bits = 24 bits */ + *out++ = alphabet[(val >> 18) & 0x3f]; + *out++ = alphabet[(val >> 12) & 0x3f]; + *out++ = alphabet[(val >> 6) & 0x3f]; + *out++ = alphabet[val & 0x3f]; + } + + /* maybe 1 or 2 bytes left */ + if (n - i) { + /* we can read in[i + 1] even if it's past the end of the string because + * we know (by construction) that it's a NUL byte */ +#ifdef __GNUC__ + uint16_t val16; + __builtin_memcpy(&val16, in + i, sizeof(val16)); + val = cbor_ntohs(val16); +#else + val = (in[i] << 8) | in[i + 1]; +#endif + val <<= 8; + + /* the 65th character in the alphabet is our filler: either '=' or '\0' */ + out[4] = '\0'; + out[3] = alphabet[64]; + if (n - i == 2) { + /* write the third char in 3 chars x 6 bits = 18 bits */ + out[2] = alphabet[(val >> 6) & 0x3f]; + } else { + out[2] = alphabet[64]; /* filler */ + } + out[1] = alphabet[(val >> 12) & 0x3f]; + out[0] = alphabet[(val >> 18) & 0x3f]; + } else { + out[0] = '\0'; + } + + return CborNoError; +} + +static CborError dump_bytestring_base64(char **result, CborValue *it) +{ + static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef" + "ghijklmn" "opqrstuv" "wxyz0123" "456789+/" "="; + return generic_dump_base64(result, it, alphabet); +} + +static CborError dump_bytestring_base64url(char **result, CborValue *it) +{ + static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef" + "ghijklmn" "opqrstuv" "wxyz0123" "456789-_"; + return generic_dump_base64(result, it, alphabet); +} + +static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status) +{ + int flags = status->flags; + if (flags & TypeWasTagged) { + /* extract the tagged type, which may be JSON native */ + type = flags & FinalTypeMask; + flags &= ~(FinalTypeMask | TypeWasTagged); + + if (fprintf(out, "\"tag\":\"%" PRIu64 "\"%s", status->lastTag, + flags & ~TypeWasTagged ? "," : "") < 0) + return CborErrorIO; + } + + if (!flags) + return CborNoError; + + /* print at least the type */ + if (fprintf(out, "\"t\":%d", type) < 0) + return CborErrorIO; + + if (flags & NumberWasNaN) + if (fprintf(out, ",\"v\":\"nan\"") < 0) + return CborErrorIO; + if (flags & NumberWasInfinite) + if (fprintf(out, ",\"v\":\"%sinf\"", flags & NumberWasNegative ? "-" : "") < 0) + return CborErrorIO; + if (flags & NumberPrecisionWasLost) + if (fprintf(out, ",\"v\":\"%c%" PRIx64 "\"", flags & NumberWasNegative ? '-' : '+', + status->originalNumber) < 0) + return CborErrorIO; + if (type == CborSimpleType) + if (fprintf(out, ",\"v\":%d", (int)status->originalNumber) < 0) + return CborErrorIO; + return CborNoError; +} + +static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type) +{ + CborError err = CborNoError; + *type = cbor_value_get_type(it); + while (*type == CborTagType) { + cbor_value_get_tag(it, tag); /* can't fail */ + err = cbor_value_advance_fixed(it); + if (err) + return err; + + *type = cbor_value_get_type(it); + } + return err; +} + +static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +{ + CborTag tag; + CborError err; + + if (flags & CborConvertTagsToObjects) { + cbor_value_get_tag(it, &tag); /* can't fail */ + err = cbor_value_advance_fixed(it); + if (err) + return err; + + if (fprintf(out, "{\"tag%" PRIu64 "\":", tag) < 0) + return CborErrorIO; + + CborType type = cbor_value_get_type(it); + err = value_to_json(out, it, flags, type, status); + if (err) + return err; + if (flags & CborConvertAddMetadata && status->flags) { + if (fprintf(out, ",\"tag%" PRIu64 "$cbor\":{", tag) < 0 || + add_value_metadata(out, type, status) != CborNoError || + fputc('}', out) < 0) + return CborErrorIO; + } + if (fputc('}', out) < 0) + return CborErrorIO; + status->flags = TypeWasNotNative | CborTagType; + return CborNoError; + } + + CborType type; + err = find_tagged_type(it, &status->lastTag, &type); + if (err) + return err; + tag = status->lastTag; + + /* special handling of byte strings? */ + if (type == CborByteStringType && (flags & CborConvertByteStringsToBase64Url) == 0 && + (tag == CborNegativeBignumTag || tag == CborExpectedBase16Tag || tag == CborExpectedBase64Tag)) { + char *str; + char *pre = ""; + + if (tag == CborNegativeBignumTag) { + pre = "~"; + err = dump_bytestring_base64url(&str, it); + } else if (tag == CborExpectedBase64Tag) { + err = dump_bytestring_base64(&str, it); + } else { /* tag == CborExpectedBase16Tag */ + err = dump_bytestring_base16(&str, it); + } + if (err) + return err; + err = fprintf(out, "\"%s%s\"", pre, str) < 0 ? CborErrorIO : CborNoError; + free(str); + status->flags = TypeWasNotNative | TypeWasTagged | CborByteStringType; + return err; + } + + /* no special handling */ + err = value_to_json(out, it, flags, type, status); + status->flags |= TypeWasTagged | type; + return err; +} + +static CborError stringify_map_key(char **key, CborValue *it, int flags, CborType type) +{ + (void)flags; /* unused */ + (void)type; /* unused */ +#ifdef WITHOUT_OPEN_MEMSTREAM + (void)key; /* unused */ + (void)it; /* unused */ + return CborErrorJsonNotImplemented; +#else + size_t size; + + FILE *memstream = open_memstream(key, &size); + if (memstream == NULL) + return CborErrorOutOfMemory; /* could also be EMFILE, but it's unlikely */ + CborError err = cbor_value_to_pretty_advance(memstream, it); + + if (unlikely(fclose(memstream) < 0 || *key == NULL)) + return CborErrorInternalError; + return err; +#endif +} + +static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +{ + const char *comma = ""; + while (!cbor_value_at_end(it)) { + if (fprintf(out, "%s", comma) < 0) + return CborErrorIO; + comma = ","; + + CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status); + if (err) + return err; + } + return CborNoError; +} + +static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +{ + const char *comma = ""; + CborError err; + while (!cbor_value_at_end(it)) { + char *key; + if (fprintf(out, "%s", comma) < 0) + return CborErrorIO; + comma = ","; + + CborType keyType = cbor_value_get_type(it); + if (likely(keyType == CborTextStringType)) { + size_t n = 0; + err = cbor_value_dup_text_string(it, &key, &n, it); + } else if (flags & CborConvertStringifyMapKeys) { + err = stringify_map_key(&key, it, flags, keyType); + } else { + return CborErrorJsonObjectKeyNotString; + } + if (err) + return err; + + /* first, print the key */ + if (fprintf(out, "\"%s\":", key) < 0) + return CborErrorIO; + + /* then, print the value */ + CborType valueType = cbor_value_get_type(it); + err = value_to_json(out, it, flags, valueType, status); + + /* finally, print any metadata we may have */ + if (flags & CborConvertAddMetadata) { + if (!err && keyType != CborTextStringType) { + if (fprintf(out, ",\"%s$keycbordump\":true", key) < 0) + err = CborErrorIO; + } + if (!err && status->flags) { + if (fprintf(out, ",\"%s$cbor\":{", key) < 0 || + add_value_metadata(out, valueType, status) != CborNoError || + fputc('}', out) < 0) + err = CborErrorIO; + } + } + + free(key); + if (err) + return err; + } + return CborNoError; +} + +static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status) +{ + CborError err; + status->flags = 0; + + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + err = cbor_value_enter_container(it, &recursed); + if (err) { + it->offset = recursed.offset; + return err; /* parse error */ + } + if (fputc(type == CborArrayType ? '[' : '{', out) < 0) + return CborErrorIO; + + err = (type == CborArrayType) ? + array_to_json(out, &recursed, flags, status) : + map_to_json(out, &recursed, flags, status); + if (err) { + it->offset = recursed.offset; + return err; /* parse error */ + } + + if (fputc(type == CborArrayType ? ']' : '}', out) < 0) + return CborErrorIO; + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; /* parse error */ + + status->flags = 0; /* reset, there are never conversion errors for us */ + return CborNoError; + } + + case CborIntegerType: { + double num; /* JS numbers are IEEE double precision */ + uint64_t val; + cbor_value_get_raw_integer(it, &val); /* can't fail */ + num = (double)val; + + if (cbor_value_is_negative_integer(it)) { + num = -num - 1; /* convert to negative */ + if ((uint64_t)(-num - 1) != val) { + status->flags = NumberPrecisionWasLost | NumberWasNegative; + status->originalNumber = val; + } + } else { + if ((uint64_t)num != val) { + status->flags = NumberPrecisionWasLost; + status->originalNumber = val; + } + } + if (fprintf(out, "%.0f", num) < 0) /* this number has no fraction, so no decimal points please */ + return CborErrorIO; + break; + } + + case CborByteStringType: + case CborTextStringType: { + char *str; + if (type == CborByteStringType) { + err = dump_bytestring_base64url(&str, it); + status->flags = TypeWasNotNative; + } else { + size_t n = 0; + err = cbor_value_dup_text_string(it, &str, &n, it); + } + if (err) + return err; + err = (fprintf(out, "\"%s\"", str) < 0) ? CborErrorIO : CborNoError; + free(str); + return err; + } + + case CborTagType: + return tagged_value_to_json(out, it, flags, status); + + case CborSimpleType: { + uint8_t simple_type; + cbor_value_get_simple_type(it, &simple_type); /* can't fail */ + status->flags = TypeWasNotNative; + status->originalNumber = simple_type; + if (fprintf(out, "\"simple(%" PRIu8 ")\"", simple_type) < 0) + return CborErrorIO; + break; + } + + case CborNullType: + if (fprintf(out, "null") < 0) + return CborErrorIO; + break; + + case CborUndefinedType: + status->flags = TypeWasNotNative; + if (fprintf(out, "\"undefined\"") < 0) + return CborErrorIO; + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); /* can't fail */ + if (fprintf(out, val ? "true" : "false") < 0) + return CborErrorIO; + break; + } +#ifndef CBOR_NO_FLOATING_POINT + case CborDoubleType: { + double val; + if (false) { + float f; + case CborFloatType: + status->flags = TypeWasNotNative; + cbor_value_get_float(it, &f); + val = f; +#ifndef CBOR_NO_HALF_FLOAT_TYPE + } else if (false) { + uint16_t f16; + case CborHalfFloatType: + status->flags = TypeWasNotNative; + cbor_value_get_half_float(it, &f16); + val = decode_half(f16); +#endif + } else { + cbor_value_get_double(it, &val); + } + + int r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) { + if (fprintf(out, "null") < 0) + return CborErrorIO; + status->flags |= r == FP_NAN ? NumberWasNaN : + NumberWasInfinite | (val < 0 ? NumberWasNegative : 0); + } else { + uint64_t ival = (uint64_t)fabs(val); + if ((double)ival == fabs(val)) { + /* print as integer so we get the full precision */ + r = fprintf(out, "%s%" PRIu64, val < 0 ? "-" : "", ival); + status->flags |= TypeWasNotNative; /* mark this integer number as a double */ + } else { + /* this number is definitely not a 64-bit integer */ + r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g", val); + } + if (r < 0) + return CborErrorIO; + } + break; + } +#endif + + case CborInvalidType: + default: + return CborErrorUnknownType; + } + + return cbor_value_advance_fixed(it); +} + +/** + + * \enum CborToJsonFlags + * The CborToJsonFlags enum contains flags that control the conversion of CBOR to JSON. + * + * \value CborConvertAddMetadata Adds metadata to facilitate restoration of the original CBOR data. + * \value CborConvertTagsToObjects Converts CBOR tags to JSON objects + * \value CborConvertIgnoreTags (default) Ignore CBOR tags, except for byte strings + * \value CborConvertObeyByteStringTags (default) Honor formatting of CBOR byte strings if so tagged + * \value CborConvertByteStringsToBase64Url Force the conversion of all CBOR byte strings to Base64url encoding, despite any tags + * \value CborConvertRequireMapStringKeys (default) Require CBOR map keys to be strings, failing the conversion if they are not + * \value CborConvertStringifyMapKeys Convert non-string keys in CBOR maps to a string form + * \value CborConvertDefaultFlags Default conversion flags. + */ + +/** + * \fn CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags) + * + * Converts the current CBOR type pointed by \a value to JSON and writes that + * to the \a out stream. If an error occurs, this function returns an error + * code similar to CborParsing. The \a flags parameter indicates one of the + * flags from CborToJsonFlags that control the conversion. + * + * \sa cbor_value_to_json_advance(), cbor_value_to_pretty() + */ + +/** + * Converts the current CBOR type pointed by \a value to JSON and writes that + * to the \a out stream. If an error occurs, this function returns an error + * code similar to CborParsing. The \a flags parameter indicates one of the + * flags from CborToJsonFlags that control the conversion. + * + * If no error ocurred, this function advances \a value to the next element. + * + * \sa cbor_value_to_json(), cbor_value_to_pretty_advance() + */ +CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags) +{ + ConversionStatus status; + return value_to_json(out, value, flags, cbor_value_get_type(value), &status); +} + +/** @} */ diff --git a/ext/lib/encoding/tinycbor/src/cborvalidation.c b/ext/lib/encoding/tinycbor/src/cborvalidation.c new file mode 100644 index 000000000000..2553a3aa3baf --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/cborvalidation.c @@ -0,0 +1,674 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" +#include "utf8_p.h" + +#include + +#include +#ifndef CBOR_NO_FLOATING_POINT +#include +#endif + + +#ifndef CBOR_PARSER_MAX_RECURSIONS +# define CBOR_PARSER_MAX_RECURSIONS 1024 +#endif + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * \enum CborValidationFlags + * The CborValidationFlags enum contains flags that control the validation of a + * CBOR stream. + * + * \value CborValidateBasic Validates only the syntax correctedness of the stream. + * \value CborValidateCanonical Validates that the stream is in canonical format, according to + * RFC 7049 section 3.9. + * \value CborValidateStrictMode Performs strict validation, according to RFC 7049 section 3.10. + * \value CborValidateStrictest Attempt to perform the strictest validation we know of. + * + * \value CborValidateShortestIntegrals (Canonical) Validate that integral numbers and lengths are + * enconded in their shortest form possible. + * \value CborValidateShortestFloatingPoint (Canonical) Validate that floating-point numbers are encoded + * in their shortest form possible. + * \value CborValidateShortestNumbers (Canonical) Validate both integrals and floating-point numbers + * are in their shortest form possible. + * \value CborValidateNoIndeterminateLength (Canonical) Validate that no string, array or map uses + * indeterminate length encoding. + * \value CborValidateMapIsSorted (Canonical & Strict mode) Validate that map keys appear in + * sorted order. + * \value CborValidateMapKeysAreUnique (Strict mode) Validate that map keys are unique. + * \value CborValidateTagUse (Strict mode) Validate that known tags are used with the + * correct types. This does not validate that the content of + * those types is syntactically correct. + * \value CborValidateUtf8 (Strict mode) Validate that text strings are appropriately + * encoded in UTF-8. + * \value CborValidateMapKeysAreString Validate that all map keys are text strings. + * \value CborValidateNoUndefined Validate that no elements of type "undefined" are present. + * \value CborValidateNoTags Validate that no tags are used. + * \value CborValidateFiniteFloatingPoint Validate that all floating point numbers are finite (no NaN or + * infinities are allowed). + * \value CborValidateCompleteData Validate that the stream is complete and there is no more data + * in the buffer. + * \value CborValidateNoUnknownSimpleTypesSA Validate that all Standards Action simple types are registered + * with IANA. + * \value CborValidateNoUnknownSimpleTypes Validate that all simple types used are registered with IANA. + * \value CborValidateNoUnknownTagsSA Validate that all Standard Actions tags are registered with IANA. + * \value CborValidateNoUnknownTagsSR Validate that all Standard Actions and Specification Required tags + * are registered with IANA (see below for limitations). + * \value CborValidateNoUnkonwnTags Validate that all tags are registered with IANA + * (see below for limitations). + * + * \par Simple type registry + * The CBOR specification requires that registration for use of the first 19 + * simple types must be done by way of Standards Action. The rest of the simple + * types only require a specification. The official list can be obtained from + * https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml. + * + * \par + * There are no registered simple types recognized by this release of TinyCBOR + * (beyond those defined by RFC 7049). + * + * \par Tag registry + * The CBOR specification requires that registration for use of the first 23 + * tags must be done by way of Standards Action. The next up to tag 255 only + * require a specification. Finally, all other tags can be registered on a + * first-come-first-serve basis. The official list can be ontained from + * https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml. + * + * \par + * Given the variability of this list, TinyCBOR cannot recognize all tags + * registered with IANA. Instead, the implementation only recognizes tags + * that are backed by an RFC. + * + * \par + * These are the tags known to the current TinyCBOR release: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TagData ItemSemantics
0UTF-8 text stringStandard date/time string
1integerEpoch-based date/time
2byte stringPositive bignum
3byte stringNegative bignum
4arrayDecimal fraction
5arrayBigfloat
16arrayCOSE Single Recipient Encrypted Data Object (RFC 8152)
17arrayCOSE Mac w/o Recipients Object (RFC 8152)
18arrayCOSE Single Signer Data Object (RFC 8162)
21byte string, array, mapExpected conversion to base64url encoding
22byte string, array, mapExpected conversion to base64 encoding
23byte string, array, mapExpected conversion to base16 encoding
24byte stringEncoded CBOR data item
32UTF-8 text stringURI
33UTF-8 text stringbase64url
34UTF-8 text stringbase64
35UTF-8 text stringRegular expression
36UTF-8 text stringMIME message
96arrayCOSE Encrypted Data Object (RFC 8152)
97arrayCOSE MACed Data Object (RFC 8152)
98arrayCOSE Signed Data Object (RFC 8152)
55799anySelf-describe CBOR
+ */ + +struct KnownTagData { uint32_t tag; uint32_t types; }; +static const struct KnownTagData knownTagData[] = { + { 0, (uint8_t)CborTextStringType }, + { 1, (uint8_t)(CborIntegerType+1) }, + { 2, (uint8_t)CborByteStringType }, + { 3, (uint8_t)CborByteStringType }, + { 4, (uint8_t)CborArrayType }, + { 5, (uint8_t)CborArrayType }, + { 16, (uint8_t)CborArrayType }, + { 17, (uint8_t)CborArrayType }, + { 18, (uint8_t)CborArrayType }, + { 21, (uint8_t)CborByteStringType | ((uint8_t)CborArrayType << 8) | ((uint8_t)CborMapType << 16) }, + { 22, (uint8_t)CborByteStringType | ((uint8_t)CborArrayType << 8) | ((uint8_t)CborMapType << 16) }, + { 23, (uint8_t)CborByteStringType | ((uint8_t)CborArrayType << 8) | ((uint8_t)CborMapType << 16) }, + { 24, (uint8_t)CborByteStringType }, + { 32, (uint8_t)CborTextStringType }, + { 33, (uint8_t)CborTextStringType }, + { 34, (uint8_t)CborTextStringType }, + { 35, (uint8_t)CborTextStringType }, + { 36, (uint8_t)CborTextStringType }, + { 96, (uint8_t)CborArrayType }, + { 97, (uint8_t)CborArrayType }, + { 98, (uint8_t)CborArrayType }, + { 55799, 0U } +}; + +static CborError validate_value(CborValue *it, int flags, int recursionLeft); + +static inline CborError validate_utf8_string(const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + const uint8_t * const end = buffer + n; + while (buffer < end) { + uint32_t uc = get_utf8(&buffer, end); + if (uc == ~0U) + return CborErrorInvalidUtf8TextString; + } + return CborNoError; +} + +static inline CborError validate_simple_type(uint8_t simple_type, int flags) +{ + /* At current time, all known simple types are those from RFC 7049, + * which are parsed by the parser into different CBOR types. + * That means that if we've got here, the type is unknown */ + if (simple_type < 32) + return (flags & CborValidateNoUnknownSimpleTypesSA) ? CborErrorUnknownSimpleType : CborNoError; + return (flags & CborValidateNoUnknownSimpleTypes) == CborValidateNoUnknownSimpleTypes ? + CborErrorUnknownSimpleType : CborNoError; +} + +static inline CborError validate_number(const CborValue *it, CborType type, int flags) +{ + CborError err = CborNoError; + uint64_t value; + int offset; + + if ((flags & CborValidateShortestIntegrals) == 0) + return err; + if (type >= CborHalfFloatType && type <= CborDoubleType) + return err; /* checked elsewhere */ + + offset = it->offset; + err = _cbor_value_extract_number(it->parser, &offset, &value); + if (err) + return err; + + size_t bytesUsed = (size_t)(offset - it->offset - 1); + size_t bytesNeeded = 0; + if (value >= Value8Bit) + ++bytesNeeded; + if (value > 0xffU) + ++bytesNeeded; + if (value > 0xffffU) + bytesNeeded += 2; + if (value > 0xffffffffU) + bytesNeeded += 4; + if (bytesNeeded < bytesUsed) + return CborErrorOverlongEncoding; + return CborNoError; +} + +static inline CborError validate_tag(CborValue *it, CborTag tag, int flags, int recursionLeft) +{ + CborType type = cbor_value_get_type(it); + const size_t knownTagCount = sizeof(knownTagData) / sizeof(knownTagData[0]); + const struct KnownTagData *tagData = knownTagData; + const struct KnownTagData * const knownTagDataEnd = knownTagData + knownTagCount; + + if (!recursionLeft) + return CborErrorNestingTooDeep; + if (flags & CborValidateNoTags) + return CborErrorExcludedType; + + /* find the tag data, if any */ + for ( ; tagData != knownTagDataEnd; ++tagData) { + if (tagData->tag < tag) + continue; + if (tagData->tag > tag) + tagData = NULL; + break; + } + if (tagData == knownTagDataEnd) + tagData = NULL; + + if (flags & CborValidateNoUnknownTags && !tagData) { + /* tag not found */ + if (flags & CborValidateNoUnknownTagsSA && tag < 24) + return CborErrorUnknownTag; + if ((flags & CborValidateNoUnknownTagsSR) == CborValidateNoUnknownTagsSR && tag < 256) + return CborErrorUnknownTag; + if ((flags & CborValidateNoUnknownTags) == CborValidateNoUnknownTags) + return CborErrorUnknownTag; + } + + if (flags & CborValidateTagUse && tagData && tagData->types) { + uint32_t allowedTypes = tagData->types; + + /* correct Integer so it's not zero */ + if (type == CborIntegerType) + type = (CborType)(type + 1); + + while (allowedTypes) { + if ((uint8_t)(allowedTypes & 0xff) == type) + break; + allowedTypes >>= 8; + } + if (!allowedTypes) + return CborErrorInappropriateTagForType; + } + + return validate_value(it, flags, recursionLeft); +} + +#ifndef CBOR_NO_FLOATING_POINT +static inline CborError validate_floating_point(CborValue *it, CborType type, int flags) +{ + CborError err; + double val; + float valf; + uint16_t valf16; + + if (type != CborDoubleType) { + if (type == CborFloatType) { + err = cbor_value_get_float(it, &valf); + val = valf; + } else { +# ifdef CBOR_NO_HALF_FLOAT_TYPE + (void)val16; + return CborErrorUnsupportedType; +# else + err = cbor_value_get_half_float(it, &valf16); + val = decode_half(valf16); +# endif + } + } else { + err = cbor_value_get_double(it, &val); + } + cbor_assert(err == CborNoError); /* can't fail */ + + int r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) { + if (flags & CborValidateFiniteFloatingPoint) + return CborErrorExcludedValue; + if (flags & CborValidateShortestFloatingPoint) { + if (type == CborDoubleType) + return CborErrorOverlongEncoding; +# ifndef CBOR_NO_HALF_FLOAT_TYPE + if (type == CborFloatType) + return CborErrorOverlongEncoding; + if (r == FP_NAN && valf16 != 0x7e00) + return CborErrorImproperValue; + if (r == FP_INFINITE && valf16 != 0x7c00 && valf16 != 0xfc00) + return CborErrorImproperValue; +# endif + } + } + + if (flags & CborValidateShortestFloatingPoint && type > CborHalfFloatType) { + if (type == CborDoubleType) { + valf = (float)val; + if ((double)valf == val) + return CborErrorOverlongEncoding; + } +# ifndef CBOR_NO_HALF_FLOAT_TYPE + if (type == CborFloatType) { + valf16 = encode_half(valf); + if (valf == decode_half(valf16)) + return CborErrorOverlongEncoding; + } +# endif + } + + return CborNoError; +} +#endif + +static CborError validate_container(CborValue *it, int containerType, int flags, int recursionLeft) +{ + CborError err; + int previous = -1; + int previous_end = -1; + + if (!recursionLeft) + return CborErrorNestingTooDeep; + + while (!cbor_value_at_end(it)) { + int current = it->offset; + + if (containerType == CborMapType) { + if (flags & CborValidateMapKeysAreString) { + CborType type = cbor_value_get_type(it); + if (type == CborTagType) { + /* skip the tags */ + CborValue copy = *it; + err = cbor_value_skip_tag(©); + if (err) + return err; + type = cbor_value_get_type(©); + } + if (type != CborTextStringType) + return CborErrorMapKeyNotString; + } + } + + err = validate_value(it, flags, recursionLeft); + if (err) + return err; + + if (containerType != CborMapType) + continue; + + if (flags & CborValidateMapIsSorted) { + if (previous != -1) { + uint64_t len1, len2; + int offset; + + /* extract the two lengths */ + offset = previous; + _cbor_value_extract_number(it->parser, &offset, &len1); + offset = current; + _cbor_value_extract_number(it->parser, &offset, &len2); + + if (len1 > len2) + return CborErrorMapNotSorted; + if (len1 == len2) { + int bytelen1 = (previous_end - previous); + int bytelen2 = (it->offset - current); + int i; + + for (i = 0; i < (bytelen1 <= bytelen2 ? bytelen1 : bytelen2); i++) { + int r = it->parser->d->get8(it->parser->d, previous + i) - + it->parser->d->get8(it->parser->d, current + i); + + if (r < 0) { + break; + } + if (r == 0 && bytelen1 != bytelen2) + r = bytelen1 < bytelen2 ? -1 : +1; + if (r > 0) + return CborErrorMapNotSorted; + if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique) + return CborErrorMapKeysNotUnique; + } + } + } + + previous = current; + previous_end = it->offset; + } + + /* map: that was the key, so get the value */ + err = validate_value(it, flags, recursionLeft); + if (err) + return err; + } + return CborNoError; +} + +static CborError validate_value(CborValue *it, int flags, int recursionLeft) +{ + CborError err; + CborType type = cbor_value_get_type(it); + + if (cbor_value_is_length_known(it)) { + err = validate_number(it, type, flags); + if (err) + return err; + } else { + if (flags & CborValidateNoIndeterminateLength) + return CborErrorUnknownLength; + } + + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + err = cbor_value_enter_container(it, &recursed); + if (!err) + err = validate_container(&recursed, type, flags, recursionLeft - 1); + if (err) { + it->offset = recursed.offset; + return err; + } + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; + return CborNoError; + } + + case CborIntegerType: { + uint64_t val; + err = cbor_value_get_raw_integer(it, &val); + cbor_assert(err == CborNoError); /* can't fail */ + + break; + } + + case CborByteStringType: + case CborTextStringType: { + size_t n = 0; + const void *ptr; + + err = _cbor_value_prepare_string_iteration(it); + if (err) + return err; + + while (1) { + err = validate_number(it, type, flags); + if (err) + return err; + + err = _cbor_value_get_string_chunk(it, &ptr, &n, it); + if (err) + return err; + if (!ptr) + break; + + if (type == CborTextStringType && flags & CborValidateUtf8) { + err = validate_utf8_string(ptr, n); + if (err) + return err; + } + } + + return CborNoError; + } + + case CborTagType: { + CborTag tag; + err = cbor_value_get_tag(it, &tag); + cbor_assert(err == CborNoError); /* can't fail */ + + err = cbor_value_advance_fixed(it); + if (err) + return err; + err = validate_tag(it, tag, flags, recursionLeft - 1); + if (err) + return err; + + return CborNoError; + } + + case CborSimpleType: { + uint8_t simple_type; + err = cbor_value_get_simple_type(it, &simple_type); + cbor_assert(err == CborNoError); /* can't fail */ + err = validate_simple_type(simple_type, flags); + if (err) + return err; + break; + } + + case CborNullType: + case CborBooleanType: + break; + + case CborUndefinedType: + if (flags & CborValidateNoUndefined) + return CborErrorExcludedType; + break; + + case CborHalfFloatType: + case CborFloatType: + case CborDoubleType: { +#ifdef CBOR_NO_FLOATING_POINT + return CborErrorUnsupportedType; +#else + err = validate_floating_point(it, type, flags); + if (err) + return err; + break; + } +#endif /* !CBOR_NO_FLOATING_POINT */ + + case CborInvalidType: + return CborErrorUnknownType; + } + + err = cbor_value_advance_fixed(it); + return err; +} + +/** + * Performs a full validation controlled by the \a flags options of the CBOR + * stream pointed by \a it and returns the error it found. If no error was + * found, it returns CborNoError and the application can iterate over the items + * with certainty that no other errors will appear during parsing. + * + * If \a flags is CborValidateBasic, the result should be the same as + * cbor_value_validate_basic(). + * + * This function has the same timing and memory requirements as + * cbor_value_advance() and cbor_value_validate_basic(). + * + * \sa CborValidationFlags, cbor_value_validate_basic(), cbor_value_advance() + */ +CborError cbor_value_validate(const CborValue *it, int flags) +{ + CborValue value = *it; + CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS); + if (err) + return err; + if (flags & CborValidateCompleteData && it->offset != it->parser->end) + return CborErrorGarbageAtEnd; + return CborNoError; +} + +/** + * @} + */ diff --git a/ext/lib/encoding/tinycbor/src/compilersupport_p.h b/ext/lib/encoding/tinycbor/src/compilersupport_p.h new file mode 100644 index 000000000000..d21a86bcee2e --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/compilersupport_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef COMPILERSUPPORT_H +#define COMPILERSUPPORT_H + +#include "cbor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _BSD_SOURCE +# define _BSD_SOURCE +#endif +#ifndef _DEFAULT_SOURCE +# define _DEFAULT_SOURCE +#endif +#ifndef assert +# include +#endif +#include +#ifndef CBOR_NO_FLOATING_TYPE +#include +#endif +#include +#include +#include + +#ifndef __cplusplus +# include +#endif + +#ifdef __F16C__ +# include +#endif + +#if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L || __cpp_static_assert >= 200410 +# define cbor_static_assert(x) static_assert(x, #x) +#elif !defined(__cplusplus) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) && (__STDC_VERSION__ > 199901L) +# define cbor_static_assert(x) static_assert(x, #x) +#else +# define cbor_static_assert(x) ((void)sizeof(char[2*!!(x) - 1])) +#endif +#if __STDC_VERSION__ >= 199901L || defined(__cplusplus) +/* inline is a keyword */ +#else +/* use the definition from cbor.h */ +# define inline CBOR_INLINE +#endif + +#ifdef NDEBUG +# define cbor_assert(cond) do { if (!(cond)) unreachable(); } while (0) +#else +# define cbor_assert(cond) assert(cond) +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) STRINGIFY2(x) +#endif +#define STRINGIFY2(x) #x + +#if !defined(UINT32_MAX) || !defined(INT64_MAX) +/* C89? We can define UINT32_MAX portably, but not INT64_MAX */ +# error "Your system has stdint.h but that doesn't define UINT32_MAX or INT64_MAX" +#endif + +#ifndef DBL_DECIMAL_DIG +/* DBL_DECIMAL_DIG is C11 */ +# define DBL_DECIMAL_DIG 17 +#endif +#define DBL_DECIMAL_DIG_STR STRINGIFY(DBL_DECIMAL_DIG) + +#if defined(__GNUC__) && defined(__i386__) && !defined(__iamcu__) +# define CBOR_INTERNAL_API_CC __attribute__((regparm(3))) +#elif defined(_MSC_VER) && defined(_M_IX86) +# define CBOR_INTERNAL_API_CC __fastcall +#else +# define CBOR_INTERNAL_API_CC +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) || \ + (__has_builtin(__builtin_bswap64) && __has_builtin(__builtin_bswap32)) +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define cbor_ntohll __builtin_bswap64 +# define cbor_htonll __builtin_bswap64 +# define cbor_ntohl __builtin_bswap32 +# define cbor_htonl __builtin_bswap32 +# ifdef __INTEL_COMPILER +# define cbor_ntohs _bswap16 +# define cbor_htons _bswap16 +# elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 608) || __has_builtin(__builtin_bswap16) +# define cbor_ntohs __builtin_bswap16 +# define cbor_htons __builtin_bswap16 +# else +# define cbor_ntohs(x) (((uint16_t)x >> 8) | ((uint16_t)x << 8)) +# define cbor_htons cbor_ntohs +# endif +# else +# define cbor_ntohll +# define cbor_htonll +# define cbor_ntohl +# define cbor_htonl +# define cbor_ntohs +# define cbor_htons +# endif +#elif defined(__sun) +# include +#elif defined(_MSC_VER) +/* MSVC, which implies Windows, which implies little-endian and sizeof(long) == 4 */ +# include +# define cbor_ntohll _byteswap_uint64 +# define cbor_htonll _byteswap_uint64 +# define cbor_ntohl _byteswap_ulong +# define cbor_htonl _byteswap_ulong +# define cbor_ntohs _byteswap_ushort +# define cbor_htons _byteswap_ushort +#endif +#ifndef cbor_ntohs +# include +# define cbor_ntohs ntohs +# define cbor_htons htons +#endif +#ifndef cbor_ntohl +# include +# define cbor_ntohl ntohl +# define cbor_htonl htonl +#endif +#ifndef cbor_ntohll +# define cbor_ntohll ntohll +# define cbor_htonll htonll +/* ntohll isn't usually defined */ +# ifndef ntohll +# if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define ntohll +# define htonll +# elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define ntohll(x) ((ntohl((uint32_t)(x)) * UINT64_C(0x100000000)) + (ntohl((x) >> 32))) +# define htonll ntohll +# else +# error "Unable to determine byte order!" +# endif +# endif +#endif + + +#ifdef __cplusplus +# define CONST_CAST(t, v) const_cast(v) +#else +/* C-style const_cast without triggering a warning with -Wcast-qual */ +# define CONST_CAST(t, v) (t)(uintptr_t)(v) +#endif + +#ifdef __GNUC__ +#ifndef likely +# define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +# define unlikely(x) __builtin_expect(!!(x), 0) +#endif +# define unreachable() __builtin_unreachable() +#elif defined(_MSC_VER) +# define likely(x) (x) +# define unlikely(x) (x) +# define unreachable() __assume(0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +# define unreachable() do {} while (0) +#endif + +static inline bool add_check_overflow(size_t v1, size_t v2, size_t *r) +{ +#if ((defined(__GNUC__) && (__GNUC__ >= 5)) && !defined(__INTEL_COMPILER)) || __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(v1, v2, r); +#else + /* unsigned additions are well-defined */ + *r = v1 + v2; + return v1 > v1 + v2; +#endif +} + +#ifndef CBOR_NO_HALF_FLOAT_TYPE +static inline unsigned short encode_half(double val) +{ +#ifdef __F16C__ + return _cvtss_sh((float)val, 3); +#else + uint64_t v; + memcpy(&v, &val, sizeof(v)); + int sign = v >> 63 << 15; + int exp = (v >> 52) & 0x7ff; + int mant = v << 12 >> 12 >> (53-11); /* keep only the 11 most significant bits of the mantissa */ + exp -= 1023; + if (exp == 1024) { + /* infinity or NaN */ + exp = 16; + mant >>= 1; + } else if (exp >= 16) { + /* overflow, as largest number */ + exp = 15; + mant = 1023; + } else if (exp >= -14) { + /* regular normal */ + } else if (exp >= -24) { + /* subnormal */ + mant |= 1024; + mant >>= -(exp + 14); + exp = -15; + } else { + /* underflow, make zero */ + return 0; + } + + /* safe cast here as bit operations above guarantee not to overflow */ + return (unsigned short)(sign | ((exp + 15) << 10) | mant); +#endif +} + +/* this function was copied & adapted from RFC 7049 Appendix D */ +static inline double decode_half(unsigned short half) +{ +#ifdef __F16C__ + return _cvtsh_ss(half); +#else + 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 half & 0x8000 ? -val : val; +#endif +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* COMPILERSUPPORT_H */ + diff --git a/ext/lib/encoding/tinycbor/src/config.h b/ext/lib/encoding/tinycbor/src/config.h new file mode 100644 index 000000000000..50726af37a8b --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/config.h @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef TINYCBOR_CONFIG_H +#define TINYCBOR_CONFIG_H + +/** This option specifies whether a default writer exists **/ +#ifdef CONFIG_CBOR_NO_DFLT_WRITER +#define CBOR_NO_DFLT_WRITER CONFIG_CBOR_NO_DFLT_WRITER +#endif + +/** This option specifies whether a default reader exists **/ +#ifdef CONFIG_CBOR_NO_DFLT_READER +#define CBOR_NO_DFLT_READER CONFIG_CBOR_NO_DFLT_READER +#endif + +/** This option specifies whether a check user exists for a cbor encoder **/ +#ifdef CONFIG_CBOR_ENCODER_NO_CHECK_USER +#define CBOR_ENCODER_NO_CHECK_USER CONFIG_CBOR_ENCODER_NO_CHECK_USER +#endif + +/** This option specifies max recursions for the parser **/ +#ifdef CONFIG_CBOR_PARSER_MAX_RECURSIONS +#define CBOR_PARSER_MAX_RECURSIONS CONFIG_CBOR_PARSER_MAX_RECURSIONS +#endif + +/** This option enables the strict parser checks **/ +#ifdef CONFIG_CBOR_PARSER_NO_STRICT_CHECKS +#define CBOR_PARSER_NO_STRICT_CHECKS CONFIG_CBOR_PARSER_NO_STRICT_CHECKS +#endif + +/** This option enables floating point support **/ +#ifdef CONFIG_CBOR_NO_FLOATING_POINT +#define CBOR_NO_FLOATING_POINT CONFIG_CBOR_NO_FLOATING_POINT +#endif + +/** This option enables half float type support **/ +#ifdef CONFIG_CBOR_NO_HALF_FLOAT_TYPE +#define CBOR_NO_HALF_FLOAT_TYPE CONFIG_CBOR_NO_HALF_FLOAT_TYPE +#endif + +/** This option enables open memstream support **/ +#ifdef CONFIG_CBOR_WITHOUT_OPEN_MEMSTREAM +#define CBOR_WITHOUT_OPEN_MEMSTREAM CONFIG_CBOR_WITHOUT_OPEN_MEMSTREAM +#endif + +#endif /* TINYCBOR_CONFIG_H */ diff --git a/ext/lib/encoding/tinycbor/src/open_memstream.c b/ext/lib/encoding/tinycbor/src/open_memstream.c new file mode 100644 index 000000000000..e9d5449497ea --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/open_memstream.c @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#define _GNU_SOURCE 1 + +#ifndef CBOR_WITHOUT_OPEN_MEMSTREAM + +#include +#include +#include +#include +#include + +#if defined(__unix__) || defined(__APPLE__) +# include +#endif +#ifdef __APPLE__ +typedef int RetType; +typedef int LenType; +#elif __GLIBC__ +typedef ssize_t RetType; +typedef size_t LenType; +#else +# error "Cannot implement open_memstream!" +#endif + +#include "compilersupport_p.h" + +struct Buffer +{ + char **ptr; + size_t *len; + size_t alloc; +}; + +static RetType write_to_buffer(void *cookie, const char *data, LenType len) +{ + struct Buffer *b = (struct Buffer *)cookie; + char *ptr = *b->ptr; + size_t newsize; + + errno = EFBIG; + if (unlikely(add_check_overflow(*b->len, len, &newsize))) + return -1; + + if (newsize > b->alloc) { + // make room + size_t newalloc = newsize + newsize / 2 + 1; // give 50% more room + ptr = realloc(ptr, newalloc); + if (ptr == NULL) + return -1; + b->alloc = newalloc; + *b->ptr = ptr; + } + + memcpy(ptr + *b->len, data, len); + *b->len = newsize; + return len; +} + +static int close_buffer(void *cookie) +{ + struct Buffer *b = (struct Buffer *)cookie; + if (*b->ptr) + (*b->ptr)[*b->len] = '\0'; + free(b); + return 0; +} + +FILE *open_memstream(char **bufptr, size_t *lenptr) +{ + struct Buffer *b = (struct Buffer *)malloc(sizeof(struct Buffer)); + if (b == NULL) + return NULL; + b->alloc = 0; + b->len = lenptr; + b->ptr = bufptr; + *bufptr = NULL; + *lenptr = 0; + +#ifdef __APPLE__ + return funopen(b, NULL, write_to_buffer, NULL, close_buffer); +#elif __GLIBC__ + static const cookie_io_functions_t vtable = { + NULL, + write_to_buffer, + NULL, + close_buffer + }; + return fopencookie(b, "w", vtable); +#endif +} + +#endif diff --git a/ext/lib/encoding/tinycbor/src/parsetags.pl b/ext/lib/encoding/tinycbor/src/parsetags.pl new file mode 100755 index 000000000000..18be146f9b60 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/parsetags.pl @@ -0,0 +1,116 @@ +#!/usr/bin/perl -l +## Copyright (C) 2017 Intel Corporation +## +## 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. +## +use strict; +my $fname = shift @ARGV + or die("Usage: parsetags.pl tags.txt"); +open TAGS, "<", $fname + or die("Cannot open $fname: $!"); + +my %typedescriptions = ( + "Integer" => "integer", + "ByteString" => "byte string", + "TextString" => "UTF-8 text string", + "Array" => "array", + "Map" => "map", + "Tag" => "tag", # shouldn't happen + "Simple" => "any simple type", + "Boolean" => "boolean", + "Null" => "null", + "Undefined" => "undefined", + "HalfFloat" => "IEEE 754 half-precision floating point", + "Float" => "IEEE 754 single-precision floating point", + "Double" => "IEEE 754 double-precision floating point" +); + +my %tags; +while () { + s/\s*#.*$//; + next if /^$/; + chomp; + + die("Could not parse line \"$_\"") + unless /^(\d+);(\w+);([\w,]*);(.*)$/; + $tags{$1}{id} = $2; + $tags{$1}{semantic} = $4; + my @types = split(',', $3); + $tags{$1}{types} = \@types; +} +close TAGS or die; + +my @tagnumbers = sort { $a <=> $b } keys %tags; + +print "==== HTML listing ===="; +print "\n \n \n \n \n "; +for my $n (@tagnumbers) { + print " "; + print " "; + + my @types = @{$tags{$n}{types}}; + @types = map { $typedescriptions{$_}; } @types; + unshift @types, "any" + if (scalar @types == 0); + printf " \n", join(', ', @types); + printf " \n", $tags{$n}{semantic}; + print " "; +} +print "
TagData ItemSemantics
$n%s%s
"; + +print "\n==== enum listing for cbor.h ====\n"; +printf "typedef enum CborKnownTags {"; +my $comma = ""; +for my $n (@tagnumbers) { + printf "%s\n Cbor%sTag%s = %d", $comma, + $tags{$n}{id}, + ' ' x (23 - length($tags{$n}{id})), + $n; + $comma = ","; +} +print "\n} CborKnownTags;"; +print "\n/* #define the constants so we can check with #ifdef */"; +for my $n (@tagnumbers) { + printf "#define Cbor%sTag Cbor%sTag\n", $tags{$n}{id}, $tags{$n}{id}; +} + +print "\n==== search table ====\n"; +print "struct KnownTagData { uint32_t tag; uint32_t types; };"; +printf "static const struct KnownTagData knownTagData[] = {"; +$comma = ""; +for my $n (@tagnumbers) { + my @types = @{$tags{$n}{types}}; + + my $typemask; + my $shift = 0; + for my $type (@types) { + die("Too many match types for tag $n") if $shift == 32; + my $actualtype = "Cbor${type}Type"; + $actualtype = "($actualtype+1)" if $type eq "Integer"; + $typemask .= " | " if $typemask ne ""; + $typemask .= "((uint8_t)$actualtype << $shift)" if $shift; + $typemask .= "(uint8_t)$actualtype" unless $shift; + $shift += 8; + } + $typemask = "0U" if $typemask eq ""; + + printf "%s\n { %d, %s }", $comma, $n, $typemask; + $comma = ","; +} +print "\n};"; diff --git a/ext/lib/encoding/tinycbor/src/tags.txt b/ext/lib/encoding/tinycbor/src/tags.txt new file mode 100644 index 000000000000..ef78cfb5a262 --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/tags.txt @@ -0,0 +1,23 @@ +# Tag number; Tag ID; Applicable types (comma-separated); Semantics +0;DateTimeString;TextString;Standard date/time string +1;UnixTime_t;Integer;Epoch-based date/time +2;PositiveBignum;ByteString;Positive bignum +3;NegativeBignum;ByteString;Negative bignum +4;Decimal;Array;Decimal fraction +5;Bigfloat;Array;Bigfloat +16;COSE_Encrypt0;Array;COSE Single Recipient Encrypted Data Object (RFC 8152) +17;COSE_Mac0;Array;COSE Mac w/o Recipients Object (RFC 8152) +18;COSE_Sign1;Array;COSE Single Signer Data Object (RFC 8162) +21;ExpectedBase64url;ByteString,Array,Map;Expected conversion to base64url encoding +22;ExpectedBase64;ByteString,Array,Map;Expected conversion to base64 encoding +23;ExpectedBase16;ByteString,Array,Map;Expected conversion to base16 encoding +24;EncodedCbor;ByteString;Encoded CBOR data item +32;Url;TextString;URI +33;Base64url;TextString;base64url +34;Base64;TextString;base64 +35;RegularExpression;TextString;Regular expression +36;MimeMessage;TextString;MIME message +96;COSE_Encrypt;Array;COSE Encrypted Data Object (RFC 8152) +97;COSE_Mac;Array;COSE MACed Data Object (RFC 8152) +98;COSE_Sign;Array;COSE Signed Data Object (RFC 8152) +55799;Signature;;Self-describe CBOR diff --git a/ext/lib/encoding/tinycbor/src/tinycbor-version.h b/ext/lib/encoding/tinycbor/src/tinycbor-version.h new file mode 100644 index 000000000000..19936e535faa --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/tinycbor-version.h @@ -0,0 +1,3 @@ +#define TINYCBOR_VERSION_MAJOR 0 +#define TINYCBOR_VERSION_MINOR 5 +#define TINYCBOR_VERSION_PATCH 0 diff --git a/ext/lib/encoding/tinycbor/src/utf8_p.h b/ext/lib/encoding/tinycbor/src/utf8_p.h new file mode 100644 index 000000000000..661c428eb06c --- /dev/null +++ b/ext/lib/encoding/tinycbor/src/utf8_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** 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. +** +****************************************************************************/ + +#ifndef CBOR_UTF8_H +#define CBOR_UTF8_H + +#include "compilersupport_p.h" + +#include + +static inline uint32_t get_utf8(const uint8_t **buffer, const uint8_t *end) +{ + uint32_t uc; + ptrdiff_t n = end - *buffer; + if (n == 0) + return ~0U; + + uc = *(*buffer)++; + if (uc < 0x80) { + /* single-byte UTF-8 */ + return uc; + } + + /* multi-byte UTF-8, decode it */ + int charsNeeded; + uint32_t min_uc; + if (unlikely(uc <= 0xC1)) + return ~0U; + if (uc < 0xE0) { + /* two-byte UTF-8 */ + charsNeeded = 2; + min_uc = 0x80; + uc &= 0x1f; + } else if (uc < 0xF0) { + /* three-byte UTF-8 */ + charsNeeded = 3; + min_uc = 0x800; + uc &= 0x0f; + } else if (uc < 0xF5) { + /* four-byte UTF-8 */ + charsNeeded = 4; + min_uc = 0x10000; + uc &= 0x07; + } else { + return ~0U; + } + + if (n < charsNeeded - 1) + return ~0U; + + /* first continuation character */ + uint8_t b = *(*buffer)++; + if ((b & 0xc0) != 0x80) + return ~0U; + uc <<= 6; + uc |= b & 0x3f; + + if (charsNeeded > 2) { + /* second continuation character */ + b = *(*buffer)++; + if ((b & 0xc0) != 0x80) + return ~0U; + uc <<= 6; + uc |= b & 0x3f; + + if (charsNeeded > 3) { + /* third continuation character */ + b = *(*buffer)++; + if ((b & 0xc0) != 0x80) + return ~0U; + uc <<= 6; + uc |= b & 0x3f; + } + } + + /* overlong sequence? surrogate pair? out or range? */ + if (uc < min_uc || uc - 0xd800U < 2048U || uc > 0x10ffff) + return ~0U; + + return uc; +} + +#endif // CBOR_UTF8_H