From 4509c99a7762e3411b05a8c6fc62985893c84cc7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 24 Dec 2017 18:37:46 -0200 Subject: [PATCH 01/35] Update version number for TinyCBOR 0.6 Signed-off-by: Thiago Macieira --- .appveyor.yml | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 47273355..42a5a5bb 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,4 @@ -version: 0.5.1-build-{build} +version: 0.6-build-{build} pull_requests: do_not_increment_build_number: true image: diff --git a/VERSION b/VERSION index 4b9fcbec..a918a2aa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.1 +0.6.0 From 3c4c1038f794c8c6007c06f7c21162ce7e28af3e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 31 Jan 2018 18:24:27 -0800 Subject: [PATCH 02/35] One more place to update the version number in Signed-off-by: Thiago Macieira --- src/tinycbor-version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tinycbor-version.h b/src/tinycbor-version.h index b5a7050e..c26560cc 100644 --- a/src/tinycbor-version.h +++ b/src/tinycbor-version.h @@ -1,3 +1,3 @@ #define TINYCBOR_VERSION_MAJOR 0 -#define TINYCBOR_VERSION_MINOR 5 -#define TINYCBOR_VERSION_PATCH 1 +#define TINYCBOR_VERSION_MINOR 6 +#define TINYCBOR_VERSION_PATCH 0 From edd2b9e1fd2844bac22e1b850ccedee7a15c8905 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 26 Jan 2018 20:39:44 -0800 Subject: [PATCH 03/35] Revert "Parser: remove the cbor_value_get_xxxx_string_chunk() API" This reverts commit 6587c3e1d25670d2da2514a6f1261dd409c854f0. Bringing the API back in 0.6, but it'll change before the release. Signed-off-by: Thiago Macieira --- src/cbor.h | 15 +++++++ src/cborinternal_p.h | 3 -- src/cborparser.c | 82 ++++++++++++++++++++++++++++++++++++- tests/parser/tst_parser.cpp | 45 +++++++++++++++++++- 4 files changed, 139 insertions(+), 6 deletions(-) diff --git a/src/cbor.h b/src/cbor.h index 31d19713..24c6df69 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -452,6 +452,21 @@ CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uin 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 */ diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index faa76829..06fa6a2e 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -85,8 +85,5 @@ enum { CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len); CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_prepare_string_iteration(CborValue *it); -CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, - size_t *len, CborValue *next); - #endif /* CBORINTERNAL_P_H */ diff --git a/src/cborparser.c b/src/cborparser.c index 9cdc6849..099cdc8f 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -1038,7 +1038,87 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t return CborNoError; } -CBOR_INTERNAL_API_CC +/** + * \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) { diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index 740e8ea4..6dd4a353 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -120,6 +120,36 @@ CborError parseOne(CborValue *it, QString *parsed) return cbor_value_to_pretty_stream(qstring_printf, parsed, it, flags); } +CborError parseOneChunk(CborValue *it, QString *parsed) +{ + CborError err; + CborType ourType = cbor_value_get_type(it); + if (ourType == CborByteStringType) { + const uint8_t *bytes; + size_t len; + err = cbor_value_get_byte_string_chunk(it, &bytes, &len, it); + if (err) + return err; + + if (bytes) + *parsed = QString::fromLatin1("h'" + + QByteArray::fromRawData(reinterpret_cast(bytes), len).toHex() + + '\''); + } else if (ourType == CborTextStringType) { + const char *text; + size_t len; + err = cbor_value_get_text_string_chunk(it, &text, &len, it); + if (err) + return err; + + if (text) + *parsed = '"' + QString::fromUtf8(text, len) + '"'; + } else { + Q_ASSERT(false); + } + return err; +} + template QByteArray raw(const char (&data)[N]) { return QByteArray::fromRawData(data, N - 1); @@ -879,8 +909,19 @@ static void chunkedStringTest(const QByteArray &data, const QString &concatenate CborValue copy = value; - Q_UNUSED(chunks); // for future API - QCOMPARE(cbor_value_advance(&value), CborNoError); + forever { + QString decoded; + err = parseOneChunk(&value, &decoded); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + + if (decoded.isEmpty()) + break; // last chunk + + QVERIFY2(!chunks.isEmpty(), "Too many chunks"); + QString expected = chunks.takeFirst(); + QCOMPARE(decoded, expected); + } + QVERIFY2(chunks.isEmpty(), "Too few chunks"); // compare to the concatenated data { From d87c786fa39b3cf99254113cdcae5ac2be0e61fe Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 22:30:38 -0800 Subject: [PATCH 04/35] Validator: simplify (and correct) the map sorting verification We don't need to compare the map lengths directly. The memcmp is sufficient, since the source data is big endian. Of course, verifying for sorting requires the map has known length. Signed-off-by: Thiago Macieira --- src/cborvalidation.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/cborvalidation.c b/src/cborvalidation.c index ee9369b8..4455bc85 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -472,29 +472,16 @@ static CborError validate_container(CborValue *it, int containerType, int flags, if (flags & CborValidateMapIsSorted) { if (previous) { - uint64_t len1, len2; - const uint8_t *ptr; + size_t bytelen1 = (size_t)(previous_end - previous); + size_t bytelen2 = (size_t)(it->ptr - current); + int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2); - /* extract the two lengths */ - ptr = previous; - _cbor_value_extract_number(&ptr, it->parser->end, &len1); - ptr = current; - _cbor_value_extract_number(&ptr, it->parser->end, &len2); - - if (len1 > len2) + if (r == 0 && bytelen1 != bytelen2) + r = bytelen1 < bytelen2 ? -1 : +1; + if (r > 0) return CborErrorMapNotSorted; - if (len1 == len2) { - size_t bytelen1 = (size_t)(previous_end - previous); - size_t bytelen2 = (size_t)(it->ptr - current); - int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2); - - if (r == 0 && bytelen1 != bytelen2) - r = bytelen1 < bytelen2 ? -1 : +1; - if (r > 0) - return CborErrorMapNotSorted; - if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique) - return CborErrorMapKeysNotUnique; - } + if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique) + return CborErrorMapKeysNotUnique; } previous = current; From aa3731592c784ce91e7fcafa66665c147b18be25 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 1 Jul 2018 07:51:43 -0700 Subject: [PATCH 05/35] Add a way to disable the declaration of some API Because of all the inline functions, #include'ing without linking in all .c files may result in undefined symbol linker errors. Signed-off-by: Thiago Macieira --- src/cbor.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cbor.h b/src/cbor.h index 9ab90268..ecd09d0c 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -216,6 +216,7 @@ typedef struct CborEncoder CborEncoder; static const size_t CborIndefiniteLength = SIZE_MAX; +#ifndef CBOR_NO_ENCODER_API CBOR_API void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags); CBOR_API CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value); CBOR_API CborError cbor_encode_int(CborEncoder *encoder, int64_t value); @@ -261,6 +262,7 @@ CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *en { return encoder->end ? 0 : (size_t)encoder->data.bytes_needed; } +#endif /* CBOR_NO_ENCODER_API */ /* Parser API */ @@ -291,6 +293,7 @@ struct CborValue }; typedef struct CborValue CborValue; +#ifndef CBOR_NO_PARSER_API CBOR_API CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it); CBOR_API CborError cbor_value_validate_basic(const CborValue *it); @@ -534,6 +537,7 @@ CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double * } /* Validation API */ +#ifndef CBOR_NO_VALIDATION_API enum CborValidationFlags { /* Bit mapping: @@ -577,8 +581,10 @@ enum CborValidationFlags { }; CBOR_API CborError cbor_value_validate(const CborValue *it, uint32_t flags); +#endif /* CBOR_NO_VALIDATION_API */ /* Human-readable (dump) API */ +#ifndef CBOR_NO_PRETTY_API enum CborPrettyFlags { CborPrettyNumericEncodingIndicators = 0x01, @@ -613,6 +619,10 @@ CBOR_INLINE_API CborError cbor_value_to_pretty(FILE *out, const CborValue *value } #endif /* __STDC_HOSTED__ check */ +#endif /* CBOR_NO_PRETTY_API */ + +#endif /* CBOR_NO_PARSER_API */ + #ifdef __cplusplus } #endif From f1db0abccdad483506a662240edd0c3a272ad802 Mon Sep 17 00:00:00 2001 From: phirsov <41143811+phirsov@users.noreply.github.com> Date: Mon, 21 Jan 2019 21:06:49 +0300 Subject: [PATCH 06/35] enhancement #149 implemented: access half-precision floating point data as single float Motivation: half-precision floating point format is used to minimize storage and traffic mostly. Application level manipulates with single and double precision usually. So, two routines added to public API to encode/decode given single precision value in the half precision format Signed-off-by: S.Phirsov Signed-off-by: Thiago Macieira --- Makefile | 2 + Makefile.nmake | 4 + src/cbor.h | 3 + src/cborencoder.c | 18 +++- src/cborencoder_float.c | 42 +++++++++ src/cborparser.c | 23 ++++- src/cborparser_float.c | 54 ++++++++++++ tests/cpp/tst_cpp.cpp | 2 + tests/encoder/tst_encoder.cpp | 159 ++++++++++++++++++++++++++++++++-- tests/parser/tst_parser.cpp | 76 ++++++++++++++++ 10 files changed, 370 insertions(+), 13 deletions(-) create mode 100644 src/cborencoder_float.c create mode 100644 src/cborparser_float.c diff --git a/Makefile b/Makefile index 06aabfba..5944018a 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,9 @@ TINYCBOR_FREESTANDING_SOURCES = \ src/cborerrorstrings.c \ src/cborencoder.c \ src/cborencoder_close_container_checked.c \ + src/cborencoder_float.c \ src/cborparser.c \ + src/cborparser_float.c \ src/cborpretty.c \ # CBORDUMP_SOURCES = tools/cbordump/cbordump.c diff --git a/Makefile.nmake b/Makefile.nmake index 04b58ab4..6afe517d 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -5,8 +5,10 @@ TINYCBOR_SOURCES = \ src\cborerrorstrings.c \ src\cborencoder.c \ src\cborencoder_close_container_checked.c \ + src\cborencoder_float.c \ src\cborparser.c \ src\cborparser_dup_string.c \ + src\cborparser_float.c \ src\cborpretty.c \ src\cborpretty_stdio.c \ src\cborvalidation.c @@ -14,8 +16,10 @@ TINYCBOR_OBJS = \ src\cborerrorstrings.obj \ src\cborencoder.obj \ src\cborencoder_close_container_checked.obj \ + src\cborencoder_float.obj \ src\cborparser.obj \ src\cborparser_dup_string.obj \ + src\cborparser_float.obj \ src\cborpretty.obj \ src\cborpretty_stdio.obj \ src\cborvalidation.obj diff --git a/src/cbor.h b/src/cbor.h index ecd09d0c..eb21c7d9 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -238,6 +238,7 @@ CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder) CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) { return cbor_encode_floating_point(encoder, CborHalfFloatType, value); } +CBOR_API CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float 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) @@ -510,7 +511,9 @@ CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *s /* Floating point */ CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) { return value->type == CborHalfFloatType; } +CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value); CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result); +CBOR_API CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result); CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) { return value->type == CborFloatType; } diff --git a/src/cborencoder.c b/src/cborencoder.c index adb92fd8..fdcaae44 100644 --- a/src/cborencoder.c +++ b/src/cborencoder.c @@ -382,7 +382,7 @@ CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value) * 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 + * \sa cbor_encode_half_float, cbor_encode_float_as_half_float, cbor_encode_float, cbor_encode_double */ CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value) { @@ -589,13 +589,25 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder * * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() */ +/** + * \fn CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value) + * + * Convert the IEEE 754 single-precision (32-bit) floating point value \a value + * to the IEEE 754 half-precision (16-bit) floating point value and append it + * to the CBOR stream provided by \a encoder. + * The \a value should be in the range of the IEEE 754 half-precision floating point type, + * INFINITY, -INFINITY, or NAN, otherwise the behavior of this function is undefined. + * + * \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() + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_double() */ /** @@ -604,7 +616,7 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder * * 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() + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_float() */ /** diff --git a/src/cborencoder_float.c b/src/cborencoder_float.c new file mode 100644 index 00000000..6f168379 --- /dev/null +++ b/src/cborencoder_float.c @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2019 S.Phirsov +** +** 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 "cborinternal_p.h" + +#ifndef CBOR_NO_HALF_FLOAT_TYPE +CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value) +{ + uint16_t v = (uint16_t)encode_half(value); + + return cbor_encode_floating_point(encoder, CborHalfFloatType, &v); +} +#endif diff --git a/src/cborparser.c b/src/cborparser.c index 346057f3..4ed25984 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -1494,17 +1494,32 @@ CborError cbor_value_map_find_value(const CborValue *map, const char *string, Cb * 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() + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float() */ CborError cbor_value_get_half_float(const CborValue *value, void *result) { uint16_t v; - cbor_assert(cbor_value_is_half_float(value)); - /* size has been computed already */ - v = get16(value->ptr + 1); + v = _cbor_value_get_half_float_helper(value); memcpy(result, &v, sizeof(v)); + return CborNoError; } +/** \internal + * + * Retrieves the CBOR half-precision floating point value binary + * representation as 16-bit unsigned integer. + * The result can be used as-is, e.g. to copy bitwise into the + * system-dependent half-precision floating point type, or it can be + * converted to the C language standard floating point type + * (float or double). + */ +CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value) +{ + cbor_assert(cbor_value_is_half_float(value)); + /* size has been computed already */ + return get16(value->ptr + 1); +} + /** @} */ diff --git a/src/cborparser_float.c b/src/cborparser_float.c new file mode 100644 index 00000000..739d3488 --- /dev/null +++ b/src/cborparser_float.c @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2019 S.Phirsov +** +** 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 "cborinternal_p.h" + +#ifndef CBOR_NO_HALF_FLOAT_TYPE +/** + * Retrieves the CBOR half-precision floating point (16-bit) value that \a + * value points to, converts it to the float and store 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. + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float(), cbor_value_get_float() + */ +CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result) +{ + uint16_t v; + + v = _cbor_value_get_half_float_helper(value); + + *result = (float)decode_half((unsigned short)v); + + return CborNoError; +} +#endif diff --git a/tests/cpp/tst_cpp.cpp b/tests/cpp/tst_cpp.cpp index dcf3d1fc..7f0eefc0 100644 --- a/tests/cpp/tst_cpp.cpp +++ b/tests/cpp/tst_cpp.cpp @@ -23,9 +23,11 @@ ****************************************************************************/ #include "../../src/cborencoder.c" +#include "../../src/cborencoder_float.c" #include "../../src/cborerrorstrings.c" #include "../../src/cborparser.c" #include "../../src/cborparser_dup_string.c" +#include "../../src/cborparser_float.c" #include "../../src/cborvalidation.c" #include diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index 4a1b1467..e3d83b84 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp @@ -41,6 +41,13 @@ class tst_Encoder : public QObject { Q_OBJECT private slots: + void floatAsHalfFloat_data(); + void floatAsHalfFloat(); + void halfFloat_data(); + void halfFloat(); + void floatAsHalfFloatCloseToZero_data(); + void floatAsHalfFloatCloseToZero(); + void floatAsHalfFloatNaN(); void fixed_data(); void fixed(); void strings_data(); @@ -272,21 +279,40 @@ CborError encodeVariant(CborEncoder *encoder, const QVariant &v) return CborErrorUnknownType; } -void compare(const QVariant &input, const QByteArray &output) +template +void encodeOne(Input input, FnUnderTest fn_under_test, QByteArray &buffer, CborError &error) { - QByteArray buffer(output.length(), Qt::Uninitialized); uint8_t *bufptr = reinterpret_cast(buffer.data()); CborEncoder encoder; cbor_encoder_init(&encoder, bufptr, buffer.length(), 0); - QCOMPARE(encodeVariant(&encoder, input), CborNoError); - QCOMPARE(encoder.remaining, size_t(1)); - QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0)); + error = fn_under_test(&encoder, input); + + if (error == CborNoError) { + QCOMPARE(encoder.remaining, size_t(1)); + QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0)); + + buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + } +} + +template +void compare(Input input, FnUnderTest fn_under_test, const QByteArray &output) +{ + QByteArray buffer(output.length(), Qt::Uninitialized); + CborError error; - buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + encodeOne(input, fn_under_test, buffer, error); + + QCOMPARE(error, CborNoError); QCOMPARE(buffer, output); } +void compare(const QVariant &input, const QByteArray &output) +{ + compare(input, encodeVariant, output); +} + void addColumns() { QTest::addColumn("output"); @@ -472,6 +498,127 @@ void addArraysAndMaps() QTest::newRow("map-1(2):3(4)") << raw("\xa1\xc1\2\xc3\4") << make_map({{QVariant::fromValue(Tag{1, 2}), QVariant::fromValue(Tag{3, 4})}}); } +static void addHalfFloat() +{ + QTest::addColumn("output"); + QTest::addColumn("rawInput"); + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf(); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf(); + + QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN(); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN(); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN(); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN(); +} + +void tst_Encoder::floatAsHalfFloat_data() +{ + addHalfFloat(); +} + +void tst_Encoder::floatAsHalfFloat() +{ + QFETCH(unsigned, rawInput); + QFETCH(double, floatInput); + QFETCH(QByteArray, output); + + if (rawInput == 0U || rawInput == 0x8000U) + QSKIP("zero values are out of scope of this test case", QTest::SkipSingle); + + if (qIsNaN(floatInput)) + QSKIP("NaN values are out of scope of this test case", QTest::SkipSingle); + + output.prepend('\xf9'); + + compare((float)floatInput, cbor_encode_float_as_half_float, output); +} + +void tst_Encoder::halfFloat_data() +{ + addHalfFloat(); +} + +void tst_Encoder::halfFloat() +{ + QFETCH(unsigned, rawInput); + QFETCH(QByteArray, output); + + uint16_t v = (uint16_t)rawInput; + output.prepend('\xf9'); + + compare(&v, cbor_encode_half_float, output); +} + +void tst_Encoder::floatAsHalfFloatCloseToZero_data() +{ + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << 0.0; + QTest::newRow("-0") << -0.0; + + QTest::newRow("below min.denorm") << ldexp(1.0, -14) * ldexp(1.0, -11); + QTest::newRow("above -min.denorm") << ldexp(-1.0, -14) * ldexp(1.0, -11); +} + +void tst_Encoder::floatAsHalfFloatCloseToZero() +{ + QFETCH(double, floatInput); + + QByteArray buffer(4, Qt::Uninitialized); + CborError error; + + encodeOne((float)floatInput, cbor_encode_float_as_half_float, buffer, error); + + QCOMPARE(error, CborNoError); + + QVERIFY2( + buffer == raw("\xf9\x00\x00") || buffer == raw("\xf9\x80\x00"), + "Got value " + QByteArray::number(floatInput) + " encoded to: " + buffer); +} + +void tst_Encoder::floatAsHalfFloatNaN() +{ + QByteArray buffer(4, Qt::Uninitialized); + CborError error; + + encodeOne(myNaNf(), cbor_encode_float_as_half_float, buffer, error); + + QCOMPARE(error, CborNoError); + QCOMPARE(buffer.size(), 3); + + uint8_t ini_byte = (uint8_t)buffer[0], + exp = (uint8_t)buffer[1] & 0x7cU, + manth = (uint8_t)buffer[1] & 0x03U, + mantl = (uint8_t)buffer[2]; + + QCOMPARE((unsigned)ini_byte, 0xf9U); + QCOMPARE((unsigned)exp, 0x7cU); + QVERIFY((manth | mantl) != 0); +} + void tst_Encoder::fixed_data() { addColumns(); diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index 79b7523f..725e413f 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -45,6 +45,8 @@ private slots: // parsing API void integers_data(); void integers(); + void halfFloat_data(); + void halfFloat(); void fixed_data(); void fixed(); void strings_data(); @@ -422,6 +424,80 @@ void tst_Parser::integers() QCOMPARE(err, inIntRange ? CborNoError : CborErrorDataTooLarge); } +static void addHalfFloat() +{ + QTest::addColumn("data"); + QTest::addColumn("expectedRaw"); + QTest::addColumn("expectedValue"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << double(INFINITY); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << double(-INFINITY); + + QTest::newRow("nan") << raw("\x7c\x01") << 0x7c01U << double(NAN); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << double(NAN); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << double(NAN); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << double(NAN); +} + +void tst_Parser::halfFloat_data() +{ + addHalfFloat(); +} + +void tst_Parser::halfFloat() +{ + QFETCH(QByteArray, data); + QFETCH(unsigned, expectedRaw); + QFETCH(double, expectedValue); + + CborParser parser; + CborValue first; + + data.prepend('\xf9'); + + CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + QVERIFY(cbor_value_is_half_float(&first)); + + uint16_t raw; + cbor_value_get_half_float(&first, &raw); + QCOMPARE(raw, uint16_t(expectedRaw)); + + float value; + cbor_value_get_half_float_as_float(&first, &value); + + const double epsilon = ldexp(1.0, -25); + + if (qIsNaN(expectedValue)) { + QVERIFY(qIsNaN(value)); + } else if (qIsInf(expectedValue)) { + QVERIFY(value == (float)expectedValue); + } else { + QVERIFY(qAbs(value - (float)expectedValue) < epsilon); + } +} + void tst_Parser::fixed_data() { addColumns(); From e52b1d5bec6f3d929997d9d1996156056476e65d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 4 Apr 2019 10:15:47 -0700 Subject: [PATCH 07/35] Tests: Catch an earlier QCOMPARE failure in compare() QCOMPARE macro has a return in case of failure. If a test fails inside the encodeOne function, we log that error, but were proceeding to perform more tests (which could fail again and produce more errors). Signed-off-by: Thiago Macieira --- tests/encoder/tst_encoder.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index e3d83b84..7955e11a 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp @@ -303,6 +303,8 @@ void compare(Input input, FnUnderTest fn_under_test, const QByteArray &output) CborError error; encodeOne(input, fn_under_test, buffer, error); + if (QTest::currentTestFailed()) + return; QCOMPARE(error, CborNoError); QCOMPARE(buffer, output); From dbf8f131146b0754f8c5affca73044fc151322b8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 3 Sep 2021 11:48:37 -0700 Subject: [PATCH 08/35] Update references of 'master' to 'main' --- .travis.yml | 4 ++-- scripts/update-docs.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a935ac99..6e010ce8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,8 @@ env: - BUILD_DOCS=false jobs: include: - - # only build docs on master - if: branch = master + - # only build docs on main + if: branch = main env: BUILD_DOCS=true language: cpp diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index dc5f1a20..19acfaea 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -1,7 +1,7 @@ #!/bin/sh -ex tuple="$TRAVIS_BRANCH${TRAVIS_TAG:+tag:$TRAVIS_TAG},$TRAVIS_PULL_REQUEST" case "$tuple" in - dev,false|master,false|tag:*) + dev,false|main,false|tag:*) ;; *) exit 0 From cb372527dfbf84ca8bcef7a6fd50a06bcfa480fc Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 24 Dec 2017 18:37:46 -0200 Subject: [PATCH 09/35] Update version number for TinyCBOR 0.6 Signed-off-by: Thiago Macieira --- .appveyor.yml | 2 +- VERSION | 2 +- src/tinycbor-version.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index eb704417..9e5ff270 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,4 @@ -version: 0.5.4-build-{build} +version: 0.6-build-{build} pull_requests: do_not_increment_build_number: true image: diff --git a/VERSION b/VERSION index 7d856835..a918a2aa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.4 +0.6.0 diff --git a/src/tinycbor-version.h b/src/tinycbor-version.h index 270c915d..c26560cc 100644 --- a/src/tinycbor-version.h +++ b/src/tinycbor-version.h @@ -1,3 +1,3 @@ #define TINYCBOR_VERSION_MAJOR 0 -#define TINYCBOR_VERSION_MINOR 5 -#define TINYCBOR_VERSION_PATCH 4 +#define TINYCBOR_VERSION_MINOR 6 +#define TINYCBOR_VERSION_PATCH 0 From 814ec02bb502045923b6a1da5d3cd41acf57a52a Mon Sep 17 00:00:00 2001 From: phirsov <41143811+phirsov@users.noreply.github.com> Date: Mon, 21 Jan 2019 21:06:49 +0300 Subject: [PATCH 10/35] enhancement #149 implemented: access half-precision floating point data as single float Motivation: half-precision floating point format is used to minimize storage and traffic mostly. Application level manipulates with single and double precision usually. So, two routines added to public API to encode/decode given single precision value in the half precision format Signed-off-by: S.Phirsov Signed-off-by: Thiago Macieira --- Makefile | 2 + Makefile.nmake | 4 + src/cbor.h | 3 + src/cborencoder.c | 18 +++- src/cborencoder_float.c | 42 +++++++++ src/cborparser.c | 23 ++++- src/cborparser_float.c | 54 ++++++++++++ tests/cpp/tst_cpp.cpp | 2 + tests/encoder/tst_encoder.cpp | 159 ++++++++++++++++++++++++++++++++-- tests/parser/tst_parser.cpp | 76 ++++++++++++++++ 10 files changed, 370 insertions(+), 13 deletions(-) create mode 100644 src/cborencoder_float.c create mode 100644 src/cborparser_float.c diff --git a/Makefile b/Makefile index f3ff00b3..d2cfe447 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,9 @@ TINYCBOR_FREESTANDING_SOURCES = \ src/cborerrorstrings.c \ src/cborencoder.c \ src/cborencoder_close_container_checked.c \ + src/cborencoder_float.c \ src/cborparser.c \ + src/cborparser_float.c \ src/cborpretty.c \ # CBORDUMP_SOURCES = tools/cbordump/cbordump.c diff --git a/Makefile.nmake b/Makefile.nmake index 04b58ab4..6afe517d 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -5,8 +5,10 @@ TINYCBOR_SOURCES = \ src\cborerrorstrings.c \ src\cborencoder.c \ src\cborencoder_close_container_checked.c \ + src\cborencoder_float.c \ src\cborparser.c \ src\cborparser_dup_string.c \ + src\cborparser_float.c \ src\cborpretty.c \ src\cborpretty_stdio.c \ src\cborvalidation.c @@ -14,8 +16,10 @@ TINYCBOR_OBJS = \ src\cborerrorstrings.obj \ src\cborencoder.obj \ src\cborencoder_close_container_checked.obj \ + src\cborencoder_float.obj \ src\cborparser.obj \ src\cborparser_dup_string.obj \ + src\cborparser_float.obj \ src\cborpretty.obj \ src\cborpretty_stdio.obj \ src\cborvalidation.obj diff --git a/src/cbor.h b/src/cbor.h index 313d6833..7afcf934 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -237,6 +237,7 @@ CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder) CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) { return cbor_encode_floating_point(encoder, CborHalfFloatType, value); } +CBOR_API CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float 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) @@ -493,7 +494,9 @@ CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *s /* Floating point */ CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) { return value->type == CborHalfFloatType; } +CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value); CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result); +CBOR_API CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result); CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) { return value->type == CborFloatType; } diff --git a/src/cborencoder.c b/src/cborencoder.c index 692ff620..645e4069 100644 --- a/src/cborencoder.c +++ b/src/cborencoder.c @@ -382,7 +382,7 @@ CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value) * 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 + * \sa cbor_encode_half_float, cbor_encode_float_as_half_float, cbor_encode_float, cbor_encode_double */ CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value) { @@ -589,13 +589,25 @@ CborError cbor_encoder_close_container(CborEncoder *parentEncoder, const CborEnc * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() */ +/** + * \fn CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value) + * + * Convert the IEEE 754 single-precision (32-bit) floating point value \a value + * to the IEEE 754 half-precision (16-bit) floating point value and append it + * to the CBOR stream provided by \a encoder. + * The \a value should be in the range of the IEEE 754 half-precision floating point type, + * INFINITY, -INFINITY, or NAN, otherwise the behavior of this function is undefined. + * + * \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() + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_double() */ /** @@ -604,7 +616,7 @@ CborError cbor_encoder_close_container(CborEncoder *parentEncoder, const CborEnc * 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() + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_float() */ /** diff --git a/src/cborencoder_float.c b/src/cborencoder_float.c new file mode 100644 index 00000000..6f168379 --- /dev/null +++ b/src/cborencoder_float.c @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2019 S.Phirsov +** +** 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 "cborinternal_p.h" + +#ifndef CBOR_NO_HALF_FLOAT_TYPE +CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value) +{ + uint16_t v = (uint16_t)encode_half(value); + + return cbor_encode_floating_point(encoder, CborHalfFloatType, &v); +} +#endif diff --git a/src/cborparser.c b/src/cborparser.c index 60ce5748..1da0754a 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -1433,17 +1433,32 @@ CborError cbor_value_map_find_value(const CborValue *map, const char *string, Cb * 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() + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float() */ CborError cbor_value_get_half_float(const CborValue *value, void *result) { uint16_t v; - cbor_assert(cbor_value_is_half_float(value)); - /* size has been computed already */ - v = get16(value->ptr + 1); + v = _cbor_value_get_half_float_helper(value); memcpy(result, &v, sizeof(v)); + return CborNoError; } +/** \internal + * + * Retrieves the CBOR half-precision floating point value binary + * representation as 16-bit unsigned integer. + * The result can be used as-is, e.g. to copy bitwise into the + * system-dependent half-precision floating point type, or it can be + * converted to the C language standard floating point type + * (float or double). + */ +CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value) +{ + cbor_assert(cbor_value_is_half_float(value)); + /* size has been computed already */ + return get16(value->ptr + 1); +} + /** @} */ diff --git a/src/cborparser_float.c b/src/cborparser_float.c new file mode 100644 index 00000000..739d3488 --- /dev/null +++ b/src/cborparser_float.c @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2019 S.Phirsov +** +** 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 "cborinternal_p.h" + +#ifndef CBOR_NO_HALF_FLOAT_TYPE +/** + * Retrieves the CBOR half-precision floating point (16-bit) value that \a + * value points to, converts it to the float and store 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. + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float(), cbor_value_get_float() + */ +CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result) +{ + uint16_t v; + + v = _cbor_value_get_half_float_helper(value); + + *result = (float)decode_half((unsigned short)v); + + return CborNoError; +} +#endif diff --git a/tests/cpp/tst_cpp.cpp b/tests/cpp/tst_cpp.cpp index dcf3d1fc..7f0eefc0 100644 --- a/tests/cpp/tst_cpp.cpp +++ b/tests/cpp/tst_cpp.cpp @@ -23,9 +23,11 @@ ****************************************************************************/ #include "../../src/cborencoder.c" +#include "../../src/cborencoder_float.c" #include "../../src/cborerrorstrings.c" #include "../../src/cborparser.c" #include "../../src/cborparser_dup_string.c" +#include "../../src/cborparser_float.c" #include "../../src/cborvalidation.c" #include diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index 4a1b1467..e3d83b84 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp @@ -41,6 +41,13 @@ class tst_Encoder : public QObject { Q_OBJECT private slots: + void floatAsHalfFloat_data(); + void floatAsHalfFloat(); + void halfFloat_data(); + void halfFloat(); + void floatAsHalfFloatCloseToZero_data(); + void floatAsHalfFloatCloseToZero(); + void floatAsHalfFloatNaN(); void fixed_data(); void fixed(); void strings_data(); @@ -272,21 +279,40 @@ CborError encodeVariant(CborEncoder *encoder, const QVariant &v) return CborErrorUnknownType; } -void compare(const QVariant &input, const QByteArray &output) +template +void encodeOne(Input input, FnUnderTest fn_under_test, QByteArray &buffer, CborError &error) { - QByteArray buffer(output.length(), Qt::Uninitialized); uint8_t *bufptr = reinterpret_cast(buffer.data()); CborEncoder encoder; cbor_encoder_init(&encoder, bufptr, buffer.length(), 0); - QCOMPARE(encodeVariant(&encoder, input), CborNoError); - QCOMPARE(encoder.remaining, size_t(1)); - QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0)); + error = fn_under_test(&encoder, input); + + if (error == CborNoError) { + QCOMPARE(encoder.remaining, size_t(1)); + QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0)); + + buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + } +} + +template +void compare(Input input, FnUnderTest fn_under_test, const QByteArray &output) +{ + QByteArray buffer(output.length(), Qt::Uninitialized); + CborError error; - buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + encodeOne(input, fn_under_test, buffer, error); + + QCOMPARE(error, CborNoError); QCOMPARE(buffer, output); } +void compare(const QVariant &input, const QByteArray &output) +{ + compare(input, encodeVariant, output); +} + void addColumns() { QTest::addColumn("output"); @@ -472,6 +498,127 @@ void addArraysAndMaps() QTest::newRow("map-1(2):3(4)") << raw("\xa1\xc1\2\xc3\4") << make_map({{QVariant::fromValue(Tag{1, 2}), QVariant::fromValue(Tag{3, 4})}}); } +static void addHalfFloat() +{ + QTest::addColumn("output"); + QTest::addColumn("rawInput"); + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf(); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf(); + + QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN(); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN(); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN(); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN(); +} + +void tst_Encoder::floatAsHalfFloat_data() +{ + addHalfFloat(); +} + +void tst_Encoder::floatAsHalfFloat() +{ + QFETCH(unsigned, rawInput); + QFETCH(double, floatInput); + QFETCH(QByteArray, output); + + if (rawInput == 0U || rawInput == 0x8000U) + QSKIP("zero values are out of scope of this test case", QTest::SkipSingle); + + if (qIsNaN(floatInput)) + QSKIP("NaN values are out of scope of this test case", QTest::SkipSingle); + + output.prepend('\xf9'); + + compare((float)floatInput, cbor_encode_float_as_half_float, output); +} + +void tst_Encoder::halfFloat_data() +{ + addHalfFloat(); +} + +void tst_Encoder::halfFloat() +{ + QFETCH(unsigned, rawInput); + QFETCH(QByteArray, output); + + uint16_t v = (uint16_t)rawInput; + output.prepend('\xf9'); + + compare(&v, cbor_encode_half_float, output); +} + +void tst_Encoder::floatAsHalfFloatCloseToZero_data() +{ + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << 0.0; + QTest::newRow("-0") << -0.0; + + QTest::newRow("below min.denorm") << ldexp(1.0, -14) * ldexp(1.0, -11); + QTest::newRow("above -min.denorm") << ldexp(-1.0, -14) * ldexp(1.0, -11); +} + +void tst_Encoder::floatAsHalfFloatCloseToZero() +{ + QFETCH(double, floatInput); + + QByteArray buffer(4, Qt::Uninitialized); + CborError error; + + encodeOne((float)floatInput, cbor_encode_float_as_half_float, buffer, error); + + QCOMPARE(error, CborNoError); + + QVERIFY2( + buffer == raw("\xf9\x00\x00") || buffer == raw("\xf9\x80\x00"), + "Got value " + QByteArray::number(floatInput) + " encoded to: " + buffer); +} + +void tst_Encoder::floatAsHalfFloatNaN() +{ + QByteArray buffer(4, Qt::Uninitialized); + CborError error; + + encodeOne(myNaNf(), cbor_encode_float_as_half_float, buffer, error); + + QCOMPARE(error, CborNoError); + QCOMPARE(buffer.size(), 3); + + uint8_t ini_byte = (uint8_t)buffer[0], + exp = (uint8_t)buffer[1] & 0x7cU, + manth = (uint8_t)buffer[1] & 0x03U, + mantl = (uint8_t)buffer[2]; + + QCOMPARE((unsigned)ini_byte, 0xf9U); + QCOMPARE((unsigned)exp, 0x7cU); + QVERIFY((manth | mantl) != 0); +} + void tst_Encoder::fixed_data() { addColumns(); diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index 1890234a..336907ef 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -56,6 +56,8 @@ private slots: // parsing API void integers_data(); void integers(); + void halfFloat_data(); + void halfFloat(); void fixed_data(); void fixed(); void strings_data(); @@ -495,6 +497,80 @@ void tst_Parser::integers() QCOMPARE(err, inIntRange ? CborNoError : CborErrorDataTooLarge); } +static void addHalfFloat() +{ + QTest::addColumn("data"); + QTest::addColumn("expectedRaw"); + QTest::addColumn("expectedValue"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << double(INFINITY); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << double(-INFINITY); + + QTest::newRow("nan") << raw("\x7c\x01") << 0x7c01U << double(NAN); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << double(NAN); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << double(NAN); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << double(NAN); +} + +void tst_Parser::halfFloat_data() +{ + addHalfFloat(); +} + +void tst_Parser::halfFloat() +{ + QFETCH(QByteArray, data); + QFETCH(unsigned, expectedRaw); + QFETCH(double, expectedValue); + + CborParser parser; + CborValue first; + + data.prepend('\xf9'); + + CborError err = cbor_parser_init(reinterpret_cast(data.constData()), data.length(), 0, &parser, &first); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + QVERIFY(cbor_value_is_half_float(&first)); + + uint16_t raw; + cbor_value_get_half_float(&first, &raw); + QCOMPARE(raw, uint16_t(expectedRaw)); + + float value; + cbor_value_get_half_float_as_float(&first, &value); + + const double epsilon = ldexp(1.0, -25); + + if (qIsNaN(expectedValue)) { + QVERIFY(qIsNaN(value)); + } else if (qIsInf(expectedValue)) { + QVERIFY(value == (float)expectedValue); + } else { + QVERIFY(qAbs(value - (float)expectedValue) < epsilon); + } +} + void tst_Parser::fixed_data() { addColumns(); From 9ed9d03a69813a75ed8911b6fbd87272b04dee8f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 3 Sep 2021 12:00:13 -0700 Subject: [PATCH 11/35] Update the qmake buildsystem source files The listing was outdated. Signed-off-by: Thiago Macieira --- src/src.pri | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/src.pri b/src/src.pri index 01887aa4..07495326 100644 --- a/src/src.pri +++ b/src/src.pri @@ -1,14 +1,24 @@ SOURCES += \ $$PWD/cborencoder.c \ $$PWD/cborencoder_close_container_checked.c \ + $$PWD/cborencoder_float.c \ $$PWD/cborerrorstrings.c \ $$PWD/cborparser.c \ $$PWD/cborparser_dup_string.c \ + $$PWD/cborparser_float.c \ $$PWD/cborpretty.c \ + $$PWD/cborpretty_stdio.c \ $$PWD/cbortojson.c \ $$PWD/cborvalidation.c \ -HEADERS += $$PWD/cbor.h $$PWD/tinycbor-version.h +HEADERS += \ + $$PWD/cbor.h \ + $$PWD/cborinternal_p.h \ + $$PWD/cborjson.h \ + $$PWD/compilersupport_p.h \ + $$PWD/tinycbor-version.h \ + $$PWD/utf8_p.h \ + QMAKE_CFLAGS *= $$QMAKE_CFLAGS_SPLIT_SECTIONS QMAKE_LFLAGS *= $$QMAKE_LFLAGS_GCSECTIONS From e2a4ed135c4d9101c4df83f2dd033cd249b6ef07 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 3 Sep 2021 12:16:29 -0700 Subject: [PATCH 12/35] Build system: Add a few -Werror for sane C development --- Makefile | 5 ++++- src/tinycbor.pro | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d2cfe447..948e3faf 100644 --- a/Makefile +++ b/Makefile @@ -236,7 +236,10 @@ tag: distcheck .SECONDARY: cflags := $(CPPFLAGS) -I$(SRCDIR)src -cflags += -std=gnu99 $(CFLAGS) +cflags += -std=gnu99 $(CFLAGS) \ + -Werror=incompatible-pointer-types \ + -Werror=implicit-function-declaration \ + -Werror=int-conversion %.o: %.c @test -d $(@D) || $(MKDIR) $(@D) $(CC) $(cflags) $($(basename $(notdir $@))_CCFLAGS) -c -o $@ $< diff --git a/src/tinycbor.pro b/src/tinycbor.pro index 980dfd1f..2ba508a6 100644 --- a/src/tinycbor.pro +++ b/src/tinycbor.pro @@ -1,6 +1,10 @@ TEMPLATE = lib -CONFIG += static +CONFIG += static warn_on CONFIG -= qt DESTDIR = ../lib +!msvc:QMAKE_CFLAGS += \ + -Werror=incompatible-pointer-types \ + -Werror=implicit-function-declaration \ + -Werror=int-conversion include(src.pri) From bf919a2ebd557a20f979a43aa898b13d3a428462 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 3 Sep 2021 12:44:04 -0700 Subject: [PATCH 13/35] AppVeyor: update to use more recent Qt and MSVC The last version of Qt to support MSVC 2015 is no longer maintained, so I'm skipping that. I'm therefore rededicating MSVC 2017 for 32-bit. There's also no more no-tests build. --- .appveyor.yml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 59137207..ef47797e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,18 +2,13 @@ version: 0.6-build-{build} pull_requests: do_not_increment_build_number: true image: -- Visual Studio 2015 -- Visual Studio 2013 - Visual Studio 2017 +- Visual Studio 2019 install: - cmd: >- - set tests=1 + if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86) & (set QTDIR=C:\Qt\5.13\msvc2017) - if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64) & (set tests=0) - - if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86) & (set QTDIR=C:\Qt\5.9\msvc2015) - - if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64) & (set QTDIR=C:\Qt\5.9\msvc2017_64) + if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64) & (set QTDIR=C:\Qt\6.1\msvc2019_64) set path=%PATH%;%QTDIR%\bin build_script: @@ -22,14 +17,12 @@ build_script: cd tests - if /i "%tests%"=="1" qmake CONFIG-=release CONFIG+=debug + qmake CONFIG-=release CONFIG+=debug - if /i "%tests%"=="1" nmake -nologo -s + nmake -nologo -s test_script: - cmd: >- - if /i "%tests%"=="1" nmake -s -nologo TESTARGS=-silent check - - if /i "%tests%"=="0" echo Tests skipped. + nmake -s -nologo TESTARGS=-silent check artifacts: - path: lib\tinycbor.lib deploy: off From f798bc073da64c6e02a5c4d508ec8a50f436566b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 22:30:38 -0800 Subject: [PATCH 14/35] Validator: simplify (and correct) the map sorting verification We don't need to compare the map lengths directly. The memcmp is sufficient, since the source data is big endian. Of course, verifying for sorting requires the map has known length. Signed-off-by: Thiago Macieira --- src/cborvalidation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cborvalidation.c b/src/cborvalidation.c index 93784a4f..49641b4a 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation +** Copyright (C) 2021 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 From 6f001e6f3509e31a5aafb617ac855e4a77409fad Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 1 Jul 2018 07:51:43 -0700 Subject: [PATCH 15/35] Add a way to disable the declaration of some API Because of all the inline functions, #include'ing without linking in all .c files may result in undefined symbol linker errors. Signed-off-by: Thiago Macieira --- src/cbor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cbor.h b/src/cbor.h index 327bff0e..c9d7c87a 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation +** Copyright (C) 2021 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 From 2d8e73ba6c10770bbbc771a9627ac73d59ee7aea Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 4 Apr 2019 10:15:47 -0700 Subject: [PATCH 16/35] Tests: Catch an earlier QCOMPARE failure in compare() QCOMPARE macro has a return in case of failure. If a test fails inside the encodeOne function, we log that error, but were proceeding to perform more tests (which could fail again and produce more errors). Signed-off-by: Thiago Macieira --- tests/encoder/tst_encoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index 7955e11a..b5513101 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation +** Copyright (C) 2021 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 From 5521ccf2f718b3900b02bbca49e72941c6e3d2ad Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 17:43:47 -0800 Subject: [PATCH 17/35] Parser: centralize checking of the available buffer size As noted in the comment, we need to be sure we don't allow a length too big from the stream to overflow and become smaller than the number of bytes we're looking for. This is also the first step in creating an API that reads from something other than a linear buffer. Signed-off-by: Thiago Macieira --- src/cborinternal_p.h | 10 +++++++++- src/cborparser.c | 25 +++++++++++++------------ src/cborvalidation.c | 2 +- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index b9623002..55386da5 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 Intel Corporation +** Copyright (C) 2021 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 @@ -157,4 +157,12 @@ enum { CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len); CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it); +static inline bool can_read_bytes(const CborValue *it, size_t n) +{ + /* Convert the pointer subtraction to size_t since end >= ptr + * (this prevents issues with (ptrdiff_t)n becoming negative). + */ + return (size_t)(it->parser->end - it->ptr) >= n; +} + #endif /* CBORINTERNAL_P_H */ diff --git a/src/cborparser.c b/src/cborparser.c index 840eb18c..9605a0fd 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 Intel Corporation +** Copyright (C) 2021 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 @@ -218,17 +218,15 @@ static CborError preparse_value(CborValue *it) /* flags to keep */ FlagsToKeep = CborIteratorFlag_ContainerIsMap | CborIteratorFlag_NextIsMapKey }; - const CborParser *parser = it->parser; - it->type = CborInvalidType; - /* are we at the end? */ - if (it->ptr == parser->end) + it->type = CborInvalidType; + it->flags &= FlagsToKeep; + if (!can_read_bytes(it, 1)) return CborErrorUnexpectedEOF; uint8_t descriptor = *it->ptr; uint8_t type = descriptor & MajorTypeMask; it->type = type; - it->flags &= FlagsToKeep; it->extra = (descriptor &= SmallValueMask); if (descriptor > Value64Bit) { @@ -244,8 +242,11 @@ static CborError preparse_value(CborValue *it) } size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); - if (bytesNeeded + 1 > (size_t)(parser->end - it->ptr)) - return CborErrorUnexpectedEOF; + + if (bytesNeeded) { + if (!can_read_bytes(it, bytesNeeded + 1)) + return CborErrorUnexpectedEOF; + } uint8_t majortype = type >> MajorTypeShift; if (majortype == NegativeIntegerType) { @@ -304,7 +305,7 @@ static CborError preparse_value(CborValue *it) static CborError preparse_next_value_nodecrement(CborValue *it) { - if (it->remaining == UINT32_MAX && it->ptr != it->parser->end && *it->ptr == (uint8_t)BreakByte) { + if (it->remaining == UINT32_MAX && can_read_bytes(it, 1) && *it->ptr == (uint8_t)BreakByte) { /* end of map or array */ if ((it->flags & CborIteratorFlag_ContainerIsMap && it->flags & CborIteratorFlag_NextIsMapKey) || it->type == CborTagType) { @@ -1007,7 +1008,7 @@ CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *i prepare_string_iteration(it); /* are we at the end? */ - if (it->ptr == it->parser->end) + if (!can_read_bytes(it, 1)) return CborErrorUnexpectedEOF; return CborNoError; } @@ -1034,7 +1035,7 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t } /* are we at the end? */ - if (it->ptr == it->parser->end) + if (!can_read_bytes(it, 1)) return CborErrorUnexpectedEOF; if (*it->ptr == BreakByte) { @@ -1048,7 +1049,7 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t err = extract_length(it->parser, &it->ptr, len); if (err) return err; - if (*len > (size_t)(it->parser->end - it->ptr)) + if (!can_read_bytes(it, *len)) return CborErrorUnexpectedEOF; *bufferptr = it->ptr; diff --git a/src/cborvalidation.c b/src/cborvalidation.c index 49641b4a..44361d89 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -647,7 +647,7 @@ CborError cbor_value_validate(const CborValue *it, uint32_t flags) CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS); if (err) return err; - if (flags & CborValidateCompleteData && it->ptr != it->parser->end) + if (flags & CborValidateCompleteData && can_read_bytes(it, 1)) return CborErrorGarbageAtEnd; return CborNoError; } From b8ea2eb29ac38f17e960f0d31730b11c55c10bdd Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 18:06:56 -0800 Subject: [PATCH 18/35] Parser: create a function that centralizes reading from the buffer This commit does not change all the readers yet, this is just the first step. As an interesting side-effect, we ended up reading the half-float into it->extra and need not re-read it. The cbor_value_get_half_float() function can be inlined in a later commit. Signed-off-by: Thiago Macieira --- src/cborinternal_p.h | 20 +++++++++++++++ src/cborparser.c | 60 +++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index 55386da5..5a597eef 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -165,4 +165,24 @@ static inline bool can_read_bytes(const CborValue *it, size_t n) return (size_t)(it->parser->end - it->ptr) >= n; } +static inline void advance_bytes(CborValue *it, size_t n) +{ + it->ptr += n; +} + +static inline void *read_bytes_unchecked(const CborValue *it, void *dst, size_t offset, size_t n) +{ + return memcpy(dst, it->ptr + offset, n); +} + +#ifdef __GNUC__ +__attribute__((warn_unused_result)) +#endif +static inline void *read_bytes(const CborValue *it, void *dst, size_t offset, size_t n) +{ + if (can_read_bytes(it, offset + n)) + return read_bytes_unchecked(it, dst, offset, n); + return NULL; +} + #endif /* CBORINTERNAL_P_H */ diff --git a/src/cborparser.c b/src/cborparser.c index 9605a0fd..fc93c65e 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -218,13 +218,14 @@ static CborError preparse_value(CborValue *it) /* flags to keep */ FlagsToKeep = CborIteratorFlag_ContainerIsMap | CborIteratorFlag_NextIsMapKey }; + uint8_t descriptor; + /* are we at the end? */ it->type = CborInvalidType; it->flags &= FlagsToKeep; - if (!can_read_bytes(it, 1)) + if (!read_bytes(it, &descriptor, 0, 1)) return CborErrorUnexpectedEOF; - uint8_t descriptor = *it->ptr; uint8_t type = descriptor & MajorTypeMask; it->type = type; it->extra = (descriptor &= SmallValueMask); @@ -246,6 +247,17 @@ static CborError preparse_value(CborValue *it) if (bytesNeeded) { if (!can_read_bytes(it, bytesNeeded + 1)) return CborErrorUnexpectedEOF; + + it->extra = 0; + + /* read up to 16 bits into it->extra */ + if (bytesNeeded <= 2) { + read_bytes_unchecked(it, &it->extra, 1, bytesNeeded); + if (bytesNeeded == 2) + it->extra = cbor_ntohs(it->extra); + } else { + it->flags |= CborIteratorFlag_IntegerValueTooLarge; /* Value32Bit or Value64Bit */ + } } uint8_t majortype = type >> MajorTypeShift; @@ -267,11 +279,10 @@ static CborError preparse_value(CborValue *it) case NullValue: case UndefinedValue: case HalfPrecisionFloat: - it->type = *it->ptr; + read_bytes_unchecked(it, &it->type, 0, 1); break; case SimpleTypeInNextByte: - it->extra = (uint8_t)it->ptr[1]; #ifndef CBOR_PARSER_NO_STRICT_CHECKS if (unlikely(it->extra < 32)) { it->type = CborInvalidType; @@ -287,32 +298,22 @@ static CborError preparse_value(CborValue *it) 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 = (uint8_t)it->ptr[1]; - else if (descriptor == Value16Bit) - it->extra = get16(it->ptr + 1); - else - it->flags |= CborIteratorFlag_IntegerValueTooLarge; /* Value32Bit or Value64Bit */ return CborNoError; } static CborError preparse_next_value_nodecrement(CborValue *it) { - if (it->remaining == UINT32_MAX && can_read_bytes(it, 1) && *it->ptr == (uint8_t)BreakByte) { + uint8_t byte; + if (it->remaining == UINT32_MAX && read_bytes(it, &byte, 0, 1) && byte == (uint8_t)BreakByte) { /* end of map or array */ if ((it->flags & CborIteratorFlag_ContainerIsMap && it->flags & CborIteratorFlag_NextIsMapKey) || it->type == CborTagType) { /* but we weren't expecting it! */ return CborErrorUnexpectedBreak; } - ++it->ptr; + advance_bytes(it, 1); it->type = CborInvalidType; it->remaining = 0; return CborNoError; @@ -349,7 +350,7 @@ static CborError advance_internal(CborValue *it) if (it->type == CborByteStringType || it->type == CborTextStringType) { cbor_assert(length == (size_t)length); cbor_assert((it->flags & CborIteratorFlag_UnknownLength) == 0); - it->ptr += length; + advance_bytes(it, length); } return preparse_next_value(it); @@ -372,11 +373,13 @@ uint64_t _cbor_value_decode_int64_internal(const CborValue *value) /* 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((*value->ptr & SmallValueMask) == Value32Bit || (*value->ptr & SmallValueMask) == Value64Bit); - if ((*value->ptr & 1) == (Value32Bit & 1)) + uint8_t byte; + read_bytes_unchecked(value, &byte, 0, 1); + cbor_assert((byte & SmallValueMask) == Value32Bit || (byte & SmallValueMask) == Value64Bit); + if ((byte & 1) == (Value32Bit & 1)) return get32(value->ptr + 1); - cbor_assert((*value->ptr & SmallValueMask) == Value64Bit); + cbor_assert((byte & SmallValueMask) == Value64Bit); return get64(value->ptr + 1); } @@ -610,7 +613,7 @@ CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) if (it->flags & CborIteratorFlag_UnknownLength) { recursed->remaining = UINT32_MAX; - ++recursed->ptr; + advance_bytes(recursed, 1); } else { uint64_t len; CborError err = _cbor_value_extract_number(&recursed->ptr, recursed->parser->end, &len); @@ -997,7 +1000,7 @@ 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->ptr; + advance_bytes(it, 1); it->flags |= CborIteratorFlag_IteratingStringChunks; } } @@ -1035,17 +1038,18 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t } /* are we at the end? */ - if (!can_read_bytes(it, 1)) + uint8_t descriptor; + if (!read_bytes(it, &descriptor, 0, 1)) return CborErrorUnexpectedEOF; - if (*it->ptr == BreakByte) { + if (descriptor == BreakByte) { /* last chunk */ - ++it->ptr; + advance_bytes(it, 1); last_chunk: *bufferptr = NULL; *len = 0; return preparse_next_value(it); - } else if ((uint8_t)(*it->ptr & MajorTypeMask) == it->type) { + } else if ((descriptor & MajorTypeMask) == it->type) { err = extract_length(it->parser, &it->ptr, len); if (err) return err; @@ -1053,7 +1057,7 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t return CborErrorUnexpectedEOF; *bufferptr = it->ptr; - it->ptr += *len; + advance_bytes(it, *len); } else { return CborErrorIllegalType; } From 14299a2be199047ba39349f2de3c7df0c2740143 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 20 Dec 2017 16:52:27 -0600 Subject: [PATCH 19/35] Parser: Save the fact that we found a 64-bit number We don't need to skimp on bits in the CborValue::flags, so save the fact that the preparser found a 64-bit number in there. This saves us from having to re-read the descriptor byte again in _cbor_value_decode_int64_internal(). Signed-off-by: Thiago Macieira --- src/cbor.h | 9 +++++---- src/cborinternal_p.h | 28 ++++++++++++++++++++++++++++ src/cborparser.c | 18 +++++++----------- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/cbor.h b/src/cbor.h index c9d7c87a..8465ced8 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -269,10 +269,11 @@ CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *en enum CborParserIteratorFlags { - CborIteratorFlag_IntegerValueTooLarge = 0x01, - CborIteratorFlag_NegativeInteger = 0x02, - CborIteratorFlag_IteratingStringChunks = 0x02, - CborIteratorFlag_UnknownLength = 0x04, + CborIteratorFlag_IntegerValueIs64Bit = 0x01, + CborIteratorFlag_IntegerValueTooLarge = 0x02, + CborIteratorFlag_NegativeInteger = 0x04, + CborIteratorFlag_IteratingStringChunks = 0x08, + CborIteratorFlag_UnknownLength = 0x10, CborIteratorFlag_ContainerIsMap = 0x20, CborIteratorFlag_NextIsMapKey = 0x40 }; diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index 5a597eef..49c166c3 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -185,4 +185,32 @@ static inline void *read_bytes(const CborValue *it, void *dst, size_t offset, si return NULL; } +static inline uint16_t read_uint8(const CborValue *it, size_t offset) +{ + uint8_t result; + read_bytes_unchecked(it, &result, offset, sizeof(result)); + return result; +} + +static inline uint16_t read_uint16(const CborValue *it, size_t offset) +{ + uint16_t result; + read_bytes_unchecked(it, &result, offset, sizeof(result)); + return cbor_ntohs(result); +} + +static inline uint32_t read_uint32(const CborValue *it, size_t offset) +{ + uint32_t result; + read_bytes_unchecked(it, &result, offset, sizeof(result)); + return cbor_ntohl(result); +} + +static inline uint64_t read_uint64(const CborValue *it, size_t offset) +{ + uint64_t result; + read_bytes_unchecked(it, &result, offset, sizeof(result)); + return cbor_ntohll(result); +} + #endif /* CBORINTERNAL_P_H */ diff --git a/src/cborparser.c b/src/cborparser.c index fc93c65e..5d017d11 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -256,7 +256,10 @@ static CborError preparse_value(CborValue *it) if (bytesNeeded == 2) it->extra = cbor_ntohs(it->extra); } else { - it->flags |= CborIteratorFlag_IntegerValueTooLarge; /* Value32Bit or Value64Bit */ + cbor_static_assert(CborIteratorFlag_IntegerValueTooLarge == (Value32Bit & 3)); + cbor_static_assert((CborIteratorFlag_IntegerValueIs64Bit | + CborIteratorFlag_IntegerValueTooLarge) == (Value64Bit & 3)); + it->flags |= (descriptor & 3); } } @@ -370,17 +373,10 @@ uint64_t _cbor_value_decode_int64_internal(const CborValue *value) { cbor_assert(value->flags & CborIteratorFlag_IntegerValueTooLarge || value->type == CborFloatType || value->type == CborDoubleType); + if (value->flags & CborIteratorFlag_IntegerValueIs64Bit) + return read_uint64(value, 1); - /* since the additional information can only be Value32Bit or Value64Bit, - * we just need to test for the one bit those two options differ */ - uint8_t byte; - read_bytes_unchecked(value, &byte, 0, 1); - cbor_assert((byte & SmallValueMask) == Value32Bit || (byte & SmallValueMask) == Value64Bit); - if ((byte & 1) == (Value32Bit & 1)) - return get32(value->ptr + 1); - - cbor_assert((byte & SmallValueMask) == Value64Bit); - return get64(value->ptr + 1); + return read_uint32(value, 1); } /** From 0b4f7f66f96c163e68fc05d66dd2db74b2edfa46 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Dec 2017 12:48:08 -0200 Subject: [PATCH 20/35] Parser: inline the cbor_value_get_half_float() function It was originally non-inline because I had thought of doing conversions from half-float to float and onwards to double, but I never actually made that in the API. Instead, even the get_float() and get_double(), we only memcpy anyway and leave it up to the upper layer to convert, as needed. This change triggered an use-when-uninitialised false positive warning that I needed to work around in the validator. Signed-off-by: Thiago Macieira --- src/cbor.h | 11 +++++++++-- src/cborparser.c | 27 ++------------------------- src/cborparser_float.c | 4 ++-- src/cborvalidation.c | 2 +- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/cbor.h b/src/cbor.h index 8465ced8..2ae51228 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -513,9 +513,16 @@ CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *s /* Floating point */ CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) { return value->type == CborHalfFloatType; } -CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value); -CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result); CBOR_API CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result); +CBOR_INLINE_API CborError cbor_value_get_half_float(const CborValue *value, void *result) +{ + assert(cbor_value_is_half_float(value)); + assert((value->flags & CborIteratorFlag_IntegerValueTooLarge) == 0); + + /* size has already been computed */ + memcpy(result, &value->extra, sizeof(value->extra)); + return CborNoError; +} CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) { return value->type == CborFloatType; } diff --git a/src/cborparser.c b/src/cborparser.c index 5d017d11..544a94de 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -1504,6 +1504,8 @@ CborError cbor_value_map_find_value(const CborValue *map, const char *string, Cb */ /** + * \fn CborError cbor_value_get_half_float(const CborValue *value, void *result) + * * 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 @@ -1516,30 +1518,5 @@ CborError cbor_value_map_find_value(const CborValue *map, const char *string, Cb * * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float() */ -CborError cbor_value_get_half_float(const CborValue *value, void *result) -{ - uint16_t v; - - v = _cbor_value_get_half_float_helper(value); - memcpy(result, &v, sizeof(v)); - - return CborNoError; -} - -/** \internal - * - * Retrieves the CBOR half-precision floating point value binary - * representation as 16-bit unsigned integer. - * The result can be used as-is, e.g. to copy bitwise into the - * system-dependent half-precision floating point type, or it can be - * converted to the C language standard floating point type - * (float or double). - */ -CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value) -{ - cbor_assert(cbor_value_is_half_float(value)); - /* size has been computed already */ - return get16(value->ptr + 1); -} /** @} */ diff --git a/src/cborparser_float.c b/src/cborparser_float.c index 739d3488..426cd7d4 100644 --- a/src/cborparser_float.c +++ b/src/cborparser_float.c @@ -44,8 +44,8 @@ CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result) { uint16_t v; - - v = _cbor_value_get_half_float_helper(value); + CborError err = cbor_value_get_half_float(value, &v); + cbor_assert(err == CborNoError); *result = (float)decode_half((unsigned short)v); diff --git a/src/cborvalidation.c b/src/cborvalidation.c index 44361d89..b1e23e18 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -380,7 +380,7 @@ static inline CborError validate_floating_point(CborValue *it, CborType type, ui int r; double val; float valf; - uint16_t valf16; + uint16_t valf16 = 0x7c01; /* dummy value, an infinity */ if (type != CborDoubleType) { if (type == CborFloatType) { From a8515c4e11d55835c388ba22f3805c42bf46a371 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 19:51:47 -0800 Subject: [PATCH 21/35] Parser: use read_bytes() in the extract_number function The extract_length() function is only used in string context, so rename accordingly. Signed-off-by: Thiago Macieira --- src/cborparser.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/cborparser.c b/src/cborparser.c index 544a94de..46e668b8 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -191,10 +191,43 @@ CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, c return CborNoError; } -static CborError extract_length(const CborParser *parser, const uint8_t **ptr, size_t *len) +static CborError extract_number_and_advance(CborValue *it, uint64_t *len) +{ + uint8_t additional_information; + size_t bytesNeeded = 0; + + /* We've already verified that there's at least one byte to be read */ + read_bytes_unchecked(it, &additional_information, 0, 1); + additional_information &= SmallValueMask; + if (additional_information < Value8Bit) { + *len = additional_information; + } else if (unlikely(additional_information > Value64Bit)) { + return CborErrorIllegalNumber; + } else { + bytesNeeded = (size_t)(1 << (additional_information - Value8Bit)); + if (!can_read_bytes(it, 1 + bytesNeeded)) + return CborErrorUnexpectedEOF; + if (additional_information <= Value16Bit) { + if (additional_information == Value16Bit) + *len = read_uint16(it, 1); + else + *len = read_uint8(it, 1); + } else { + if (additional_information == Value32Bit) + *len = read_uint32(it, 1); + else + *len = read_uint64(it, 1); + } + } + + advance_bytes(it, bytesNeeded + 1); + return CborNoError; +} + +static CborError extract_string_length(CborValue *it, size_t *len) { uint64_t v; - CborError err = _cbor_value_extract_number(ptr, parser->end, &v); + CborError err = extract_number_and_advance(it, &v); if (err) { *len = 0; return err; @@ -347,7 +380,7 @@ static CborError preparse_next_value(CborValue *it) static CborError advance_internal(CborValue *it) { uint64_t length; - CborError err = _cbor_value_extract_number(&it->ptr, it->parser->end, &length); + CborError err = extract_number_and_advance(it, &length); cbor_assert(err == CborNoError); if (it->type == CborByteStringType || it->type == CborTextStringType) { @@ -612,7 +645,7 @@ CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) advance_bytes(recursed, 1); } else { uint64_t len; - CborError err = _cbor_value_extract_number(&recursed->ptr, recursed->parser->end, &len); + CborError err = extract_number_and_advance(recursed, &len); cbor_assert(err == CborNoError); recursed->remaining = (uint32_t)len; @@ -1046,7 +1079,7 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len = 0; return preparse_next_value(it); } else if ((descriptor & MajorTypeMask) == it->type) { - err = extract_length(it->parser, &it->ptr, len); + err = extract_string_length(it, len); if (err) return err; if (!can_read_bytes(it, *len)) From eddbf5b17639792e235c66c548b89523b41bf041 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 19:44:10 -0800 Subject: [PATCH 22/35] Parser: rework the two extract_{number,length} internal functions The extract_length() was the only case that called extract_number_and_advance() without verifying we had a proper number. This commit reworks the implementation so extract_number_and_advance() reuses the number previously read by preparse_value(), and extract_length() gets inlined to the only place that uses it. Signed-off-by: Thiago Macieira --- src/cborparser.c | 108 +++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/src/cborparser.c b/src/cborparser.c index 46e668b8..a1c24fa6 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -191,52 +191,20 @@ CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, c return CborNoError; } -static CborError extract_number_and_advance(CborValue *it, uint64_t *len) +static uint64_t extract_number_and_advance(CborValue *it) { - uint8_t additional_information; - size_t bytesNeeded = 0; + /* This function is only called after we've verified that the number + * here is valid, so we can just use _cbor_value_extract_int64_helper. */ + uint8_t descriptor; + uint64_t v = _cbor_value_extract_int64_helper(it); - /* We've already verified that there's at least one byte to be read */ - read_bytes_unchecked(it, &additional_information, 0, 1); - additional_information &= SmallValueMask; - if (additional_information < Value8Bit) { - *len = additional_information; - } else if (unlikely(additional_information > Value64Bit)) { - return CborErrorIllegalNumber; - } else { - bytesNeeded = (size_t)(1 << (additional_information - Value8Bit)); - if (!can_read_bytes(it, 1 + bytesNeeded)) - return CborErrorUnexpectedEOF; - if (additional_information <= Value16Bit) { - if (additional_information == Value16Bit) - *len = read_uint16(it, 1); - else - *len = read_uint8(it, 1); - } else { - if (additional_information == Value32Bit) - *len = read_uint32(it, 1); - else - *len = read_uint64(it, 1); - } - } + read_bytes_unchecked(it, &descriptor, 0, 1); + descriptor &= SmallValueMask; + size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); advance_bytes(it, bytesNeeded + 1); - return CborNoError; -} - -static CborError extract_string_length(CborValue *it, size_t *len) -{ - uint64_t v; - CborError err = extract_number_and_advance(it, &v); - if (err) { - *len = 0; - return err; - } - *len = (size_t)v; - if (v != *len) - return CborErrorDataTooLarge; - return CborNoError; + return v; } static bool is_fixed_type(uint8_t type) @@ -379,9 +347,7 @@ static CborError preparse_next_value(CborValue *it) static CborError advance_internal(CborValue *it) { - uint64_t length; - CborError err = extract_number_and_advance(it, &length); - cbor_assert(err == CborNoError); + uint64_t length = extract_number_and_advance(it); if (it->type == CborByteStringType || it->type == CborTextStringType) { cbor_assert(length == (size_t)length); @@ -644,9 +610,7 @@ CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) recursed->remaining = UINT32_MAX; advance_bytes(recursed, 1); } else { - uint64_t len; - CborError err = extract_number_and_advance(recursed, &len); - cbor_assert(err == CborNoError); + uint64_t len = extract_number_and_advance(recursed); recursed->remaining = (uint32_t)len; if (recursed->remaining != len || len == UINT32_MAX) { @@ -1047,8 +1011,6 @@ CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *i static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len) { - CborError err; - /* Possible states: * length known | iterating | meaning * no | no | before the first chunk of a chunked string @@ -1079,14 +1041,50 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len = 0; return preparse_next_value(it); } else if ((descriptor & MajorTypeMask) == it->type) { - err = extract_string_length(it, len); - if (err) - return err; - if (!can_read_bytes(it, *len)) + /* find the string length */ + size_t bytesNeeded = 1; + + descriptor &= SmallValueMask; + if (descriptor < Value8Bit) { + *len = descriptor; + } else if (unlikely(descriptor > Value64Bit)) { + return CborErrorIllegalNumber; + } else { + uint64_t val; + bytesNeeded = (size_t)(1 << (descriptor - Value8Bit)); + if (!can_read_bytes(it, 1 + bytesNeeded)) + return CborErrorUnexpectedEOF; + + if (descriptor <= Value16Bit) { + if (descriptor == Value16Bit) + val = read_uint16(it, 1); + else + val = read_uint8(it, 1); + } else { + if (descriptor == Value32Bit) + val = read_uint32(it, 1); + else + val = read_uint64(it, 1); + } + + *len = val; + if (*len != val) + return CborErrorDataTooLarge; + + ++bytesNeeded; + } + + *bufferptr = it->ptr + bytesNeeded; + if (*len != (size_t)*len) + return CborErrorDataTooLarge; + + if (add_check_overflow(bytesNeeded, *len, &bytesNeeded)) + return CborErrorUnexpectedEOF; + + if (!can_read_bytes(it, bytesNeeded)) return CborErrorUnexpectedEOF; - *bufferptr = it->ptr; - advance_bytes(it, *len); + advance_bytes(it, bytesNeeded); } else { return CborErrorIllegalType; } From 5d871b201a3bb7f68dde8ab1030282a2a34165de Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 22:43:49 -0800 Subject: [PATCH 23/35] Validator & Pretty: Remove the last uses of _cbor_value_extract_number Signed-off-by: Thiago Macieira --- src/cborinternal_p.h | 35 ++++++++++++++++++++++++++++++- src/cborparser.c | 49 -------------------------------------------- src/cborpretty.c | 14 ++++++------- src/cborvalidation.c | 4 +--- 4 files changed, 42 insertions(+), 60 deletions(-) diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index 49c166c3..be51981f 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -154,7 +154,6 @@ enum { BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift) }; -CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len); CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it); static inline bool can_read_bytes(const CborValue *it, size_t n) @@ -213,4 +212,38 @@ static inline uint64_t read_uint64(const CborValue *it, size_t offset) return cbor_ntohll(result); } +static inline CborError extract_number_checked(const CborValue *it, uint64_t *value, size_t *bytesUsed) +{ + uint8_t descriptor; + size_t bytesNeeded = 0; + + /* We've already verified that there's at least one byte to be read */ + read_bytes_unchecked(it, &descriptor, 0, 1); + descriptor &= SmallValueMask; + if (descriptor < Value8Bit) { + *value = descriptor; + } else if (unlikely(descriptor > Value64Bit)) { + return CborErrorIllegalNumber; + } else { + bytesNeeded = (size_t)(1 << (descriptor - Value8Bit)); + if (!can_read_bytes(it, 1 + bytesNeeded)) + return CborErrorUnexpectedEOF; + if (descriptor <= Value16Bit) { + if (descriptor == Value16Bit) + *value = read_uint16(it, 1); + else + *value = read_uint8(it, 1); + } else { + if (descriptor == Value32Bit) + *value = read_uint32(it, 1); + else + *value = read_uint64(it, 1); + } + } + + if (bytesUsed) + *bytesUsed = bytesNeeded; + return CborNoError; +} + #endif /* CBORINTERNAL_P_H */ diff --git a/src/cborparser.c b/src/cborparser.c index a1c24fa6..4532c35b 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -142,55 +142,6 @@ * \endif */ -static inline uint16_t get16(const uint8_t *ptr) -{ - uint16_t result; - memcpy(&result, ptr, sizeof(result)); - return cbor_ntohs(result); -} - -static inline uint32_t get32(const uint8_t *ptr) -{ - uint32_t result; - memcpy(&result, ptr, sizeof(result)); - return cbor_ntohl(result); -} - -static inline uint64_t get64(const uint8_t *ptr) -{ - uint64_t result; - memcpy(&result, ptr, sizeof(result)); - return cbor_ntohll(result); -} - -CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len) -{ - size_t bytesNeeded; - uint8_t additional_information = **ptr & SmallValueMask; - ++*ptr; - if (additional_information < Value8Bit) { - *len = additional_information; - return CborNoError; - } - if (unlikely(additional_information > Value64Bit)) - return CborErrorIllegalNumber; - - bytesNeeded = (size_t)(1 << (additional_information - Value8Bit)); - if (unlikely(bytesNeeded > (size_t)(end - *ptr))) { - return CborErrorUnexpectedEOF; - } else if (bytesNeeded == 1) { - *len = (uint8_t)(*ptr)[0]; - } else if (bytesNeeded == 2) { - *len = get16(*ptr); - } else if (bytesNeeded == 4) { - *len = get32(*ptr); - } else { - *len = get64(*ptr); - } - *ptr += bytesNeeded; - return CborNoError; -} - static uint64_t extract_number_and_advance(CborValue *it) { /* This function is only called after we've verified that the number diff --git a/src/cborpretty.c b/src/cborpretty.c index b0a3db8c..d3a38784 100644 --- a/src/cborpretty.c +++ b/src/cborpretty.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation +** Copyright (C) 2021 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 @@ -255,7 +255,7 @@ static CborError utf8EscapedDump(CborStreamFunction stream, void *out, const voi return err; } -static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int flags) +static const char *resolve_indicator(const CborValue *it, int flags) { static const char indicators[8][3] = { "_0", "_1", "_2", "_3", @@ -268,10 +268,10 @@ static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int uint64_t value; CborError err; - if (ptr == end) + if (!read_bytes(it, &additional_information, 0, 1)) return NULL; /* CborErrorUnexpectedEOF */ - additional_information = (*ptr & SmallValueMask); + additional_information &= SmallValueMask; if (additional_information < Value8Bit) return no_indicator; @@ -282,7 +282,7 @@ static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int if ((flags & CborPrettyIndicateOverlongNumbers) == 0) return no_indicator; - err = _cbor_value_extract_number(&ptr, end, &value); + err = extract_number_checked(it, &value, NULL); if (err) return NULL; /* CborErrorUnexpectedEOF */ @@ -302,7 +302,7 @@ static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int static const char *get_indicator(const CborValue *it, int flags) { - return resolve_indicator(it->ptr, it->parser->end, flags); + return resolve_indicator(it, flags); } static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft); @@ -418,7 +418,7 @@ static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue while (!err) { if (showingFragments || indicator == NULL) { /* any iteration, except the second for a non-chunked string */ - indicator = resolve_indicator(it->ptr, it->parser->end, flags); + indicator = resolve_indicator(it, flags); } err = _cbor_value_get_string_chunk(it, &ptr, &n, it); diff --git a/src/cborvalidation.c b/src/cborvalidation.c index b1e23e18..8469205a 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -293,7 +293,6 @@ static inline CborError validate_simple_type(uint8_t simple_type, uint32_t flags static inline CborError validate_number(const CborValue *it, CborType type, uint32_t flags) { CborError err = CborNoError; - const uint8_t *ptr = it->ptr; size_t bytesUsed, bytesNeeded; uint64_t value; @@ -302,11 +301,10 @@ static inline CborError validate_number(const CborValue *it, CborType type, uint if (type >= CborHalfFloatType && type <= CborDoubleType) return err; /* checked elsewhere */ - err = _cbor_value_extract_number(&ptr, it->parser->end, &value); + err = extract_number_checked(it, &value, &bytesUsed); if (err) return err; - bytesUsed = (size_t)(ptr - it->ptr - 1); bytesNeeded = 0; if (value >= Value8Bit) ++bytesNeeded; From d503c111a23021ce934e674c303fb6260b61224d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 22:54:46 -0800 Subject: [PATCH 24/35] Pretty & Validation: remove the last direct accesses to CborValue::ptr Signed-off-by: Thiago Macieira --- src/cborinternal_p.h | 5 +++++ src/cborparser.c | 6 +++--- src/cborpretty.c | 4 ++-- src/cbortojson.c | 7 ++++--- src/cborvalidation.c | 6 +++--- tests/tojson/tst_tojson.cpp | 6 +++--- tools/cbordump/cbordump.c | 4 ++-- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index be51981f..5b116142 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -156,6 +156,11 @@ enum { CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it); +static inline void copy_current_position(CborValue *dst, const CborValue *src) +{ + dst->ptr = src->ptr; +} + static inline bool can_read_bytes(const CborValue *it, size_t n) { /* Convert the pointer subtraction to size_t since end >= ptr diff --git a/src/cborparser.c b/src/cborparser.c index 4532c35b..d8d37402 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -566,14 +566,14 @@ CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) recursed->remaining = (uint32_t)len; if (recursed->remaining != len || len == UINT32_MAX) { /* back track the pointer to indicate where the error occurred */ - recursed->ptr = it->ptr; + copy_current_position(recursed, it); 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->ptr = it->ptr; + copy_current_position(recursed, it); return CborErrorDataTooLarge; } recursed->remaining *= 2; @@ -604,7 +604,7 @@ CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed) { cbor_assert(cbor_value_is_container(it)); cbor_assert(recursed->type == CborInvalidType); - it->ptr = recursed->ptr; + copy_current_position(it, recursed); return preparse_next_value(it); } diff --git a/src/cborpretty.c b/src/cborpretty.c index d3a38784..ea3ae80d 100644 --- a/src/cborpretty.c +++ b/src/cborpretty.c @@ -354,12 +354,12 @@ static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue err = cbor_value_enter_container(it, &recursed); if (err) { - it->ptr = recursed.ptr; + copy_current_position(it, &recursed); return err; /* parse error */ } err = container_to_pretty(stream, out, &recursed, type, flags, recursionsLeft - 1); if (err) { - it->ptr = recursed.ptr; + copy_current_position(it, &recursed); return err; /* parse error */ } err = cbor_value_leave_container(it, &recursed); diff --git a/src/cbortojson.c b/src/cbortojson.c index 4b11d319..856d9268 100644 --- a/src/cbortojson.c +++ b/src/cbortojson.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 Intel Corporation +** Copyright (C) 2021 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 @@ -34,6 +34,7 @@ #include "cborjson.h" #include "cborinternal_p.h" #include "compilersupport_p.h" +#include "cborinternal_p.h" #include #include @@ -507,7 +508,7 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ CborValue recursed; err = cbor_value_enter_container(it, &recursed); if (err) { - it->ptr = recursed.ptr; + copy_current_position(it, &recursed); return err; /* parse error */ } if (fputc(type == CborArrayType ? '[' : '{', out) < 0) @@ -517,7 +518,7 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ array_to_json(out, &recursed, flags, status) : map_to_json(out, &recursed, flags, status); if (err) { - it->ptr = recursed.ptr; + copy_current_position(it, &recursed); return err; /* parse error */ } diff --git a/src/cborvalidation.c b/src/cborvalidation.c index 8469205a..a59a4614 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -473,7 +473,7 @@ static CborError validate_container(CborValue *it, int containerType, uint32_t f if (flags & CborValidateMapIsSorted) { if (previous) { size_t bytelen1 = (size_t)(previous_end - previous); - size_t bytelen2 = (size_t)(it->ptr - current); + size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - current); int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2); if (r == 0 && bytelen1 != bytelen2) @@ -485,7 +485,7 @@ static CborError validate_container(CborValue *it, int containerType, uint32_t f } previous = current; - previous_end = it->ptr; + previous_end = cbor_value_get_next_byte(it); } /* map: that was the key, so get the value */ @@ -519,7 +519,7 @@ static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft if (!err) err = validate_container(&recursed, type, flags, recursionLeft - 1); if (err) { - it->ptr = recursed.ptr; + copy_current_position(it, &recursed); return err; } err = cbor_value_leave_container(it, &recursed); diff --git a/tests/tojson/tst_tojson.cpp b/tests/tojson/tst_tojson.cpp index 8bf24efb..89b5d129 100644 --- a/tests/tojson/tst_tojson.cpp +++ b/tests/tojson/tst_tojson.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 Intel Corporation +** Copyright (C) 2021 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 @@ -225,7 +225,7 @@ void compareOne_real(const QByteArray &data, const QString &expected, int flags, QCOMPARE(decoded, expected); // check that we consumed everything - QCOMPARE((void*)first.ptr, (void*)data.constEnd()); + QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd()); compareFailed = false; } @@ -665,7 +665,7 @@ void compareMetaData(QByteArray data, const QString &expected, int otherFlags = "\"; decoded stream:\n" + decoded.toLatin1()); // check that we consumed everything - QCOMPARE((void*)first.ptr, (void*)data.constEnd()); + QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd()); } QVERIFY(decoded.startsWith("{\"v\":")); diff --git a/tools/cbordump/cbordump.c b/tools/cbordump/cbordump.c index 97adef3f..26268132 100644 --- a/tools/cbordump/cbordump.c +++ b/tools/cbordump/cbordump.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 Intel Corporation +** Copyright (C) 2021 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 @@ -79,7 +79,7 @@ void dumpFile(FILE *in, const char *fname, bool printJosn, int flags) if (!err) puts(""); } - if (!err && value.ptr != buffer + buflen) + if (!err && cbor_value_get_next_byte(&value) != buffer + buflen) err = CborErrorGarbageAtEnd; if (err) printerror(err, fname); From 95129b84ed56b5b2bb562dc51bc0a8f95f57fb69 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 23 Jan 2018 21:58:08 -0800 Subject: [PATCH 25/35] Parser: let cbor_value_leave_container() consume the Break Instead of consuming it at the end of the last element of a map or array of unknown length. This allows us to obtain the pointer to or offset of the Break byte. Signed-off-by: Thiago Macieira --- src/cborparser.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cborparser.c b/src/cborparser.c index d8d37402..405a7d76 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -268,9 +268,9 @@ static CborError preparse_next_value_nodecrement(CborValue *it) /* but we weren't expecting it! */ return CborErrorUnexpectedBreak; } - advance_bytes(it, 1); it->type = CborInvalidType; it->remaining = 0; + it->flags |= CborIteratorFlag_UnknownLength; /* leave_container must consume the Break */ return CborNoError; } @@ -286,6 +286,7 @@ static CborError preparse_next_value(CborValue *it) if (it->remaining != UINT32_MAX) { if (itemCounts && --it->remaining == 0) { it->type = CborInvalidType; + it->flags &= ~CborIteratorFlag_UnknownLength; /* no Break to consume */ return CborNoError; } } @@ -604,7 +605,10 @@ CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed) { cbor_assert(cbor_value_is_container(it)); cbor_assert(recursed->type == CborInvalidType); + copy_current_position(it, recursed); + if (recursed->flags & CborIteratorFlag_UnknownLength) + advance_bytes(it, 1); return preparse_next_value(it); } From 34c84520a764dfe5bac6725aa95e9871baf0bbc6 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 23:45:04 -0800 Subject: [PATCH 26/35] WIP Initial API for delegated streaming in Signed-off-by: Thiago Macieira --- .travis.yml | 2 +- src/cbor.h | 30 +++++++++++++--- src/cborerrorstrings.c | 5 ++- src/cborinternal_p.h | 68 ++++++++++++++++++++++++++++++++++--- src/cborparser.c | 33 ++++++++++++------ src/cborvalidation.c | 2 ++ tests/parser/tst_parser.cpp | 53 ++++++++++++++++++++++++++++- 7 files changed, 171 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e010ce8..21701a30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,7 +75,7 @@ script: - make -s -f Makefile.configure configure | tee .config - make -k CFLAGS="$CFLAGS -march=native -g1 -Wall -Wextra -Werror" - CPPFLAGS="-DNDEBUG" + CPPFLAGS="-DNDEBUG -DCBOR_PARSER_READER_CONTROL=-1" lib/libtinycbor.a - size lib/libtinycbor.a | tee sizes - make -s clean diff --git a/src/cbor.h b/src/cbor.h index 2ae51228..04181f6a 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -189,6 +189,7 @@ typedef enum CborError { CborErrorDataTooLarge = 1024, CborErrorNestingTooDeep, CborErrorUnsupportedType, + CborErrorUnimplementedValidation, /* errors in converting to JSON */ CborErrorJsonObjectKeyIsAggregate = 1280, @@ -267,6 +268,11 @@ CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *en /* Parser API */ +enum CborParserGlobalFlags +{ + CborParserFlag_ExternalSource = 0x01 +}; + enum CborParserIteratorFlags { CborIteratorFlag_IntegerValueIs64Bit = 0x01, @@ -278,17 +284,32 @@ enum CborParserIteratorFlags CborIteratorFlag_NextIsMapKey = 0x40 }; +struct CborValue; +struct CborParserOperations +{ + bool (*can_read_bytes)(void *token, size_t len); + void *(*read_bytes)(void *token, void *dst, size_t offset, size_t len); + void (*advance_bytes)(void *token, size_t len); + CborError (*transfer_string)(void *token, const void **userptr, size_t offset, size_t len); +}; + struct CborParser { - const uint8_t *end; - uint32_t flags; + union { + const uint8_t *end; + const struct CborParserOperations *ops; + } source; + enum CborParserGlobalFlags flags; }; typedef struct CborParser CborParser; struct CborValue { const CborParser *parser; - const uint8_t *ptr; + union { + const uint8_t *ptr; + void *token; + } source; uint32_t remaining; uint16_t extra; uint8_t type; @@ -298,13 +319,14 @@ typedef struct CborValue CborValue; #ifndef CBOR_NO_PARSER_API CBOR_API CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it); +CBOR_API CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborParser *parser, CborValue *it, void *token); 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_INLINE_API const uint8_t *cbor_value_get_next_byte(const CborValue *it) -{ return it->ptr; } +{ return it->source.ptr; } 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) diff --git a/src/cborerrorstrings.c b/src/cborerrorstrings.c index 3fe3a982..994c6a4c 100644 --- a/src/cborerrorstrings.c +++ b/src/cborerrorstrings.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation +** Copyright (C) 2021 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 @@ -169,6 +169,9 @@ const char *cbor_error_string(CborError error) case CborErrorUnsupportedType: return _("unsupported type"); + case CborErrorUnimplementedValidation: + return _("validation not implemented for the current parser state"); + case CborErrorJsonObjectKeyIsAggregate: return _("conversion to JSON failed: key in object is an array or map"); diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index 5b116142..f3747e6b 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -106,6 +106,9 @@ static inline double decode_half(unsigned short half) # define CBOR_PARSER_MAX_RECURSIONS 1024 #endif +#ifndef CBOR_PARSER_READER_CONTROL +# define CBOR_PARSER_READER_CONTROL 0 +#endif /* * CBOR Major types * Encoded in the high 3 bits of the descriptor byte @@ -158,25 +161,82 @@ CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iter static inline void copy_current_position(CborValue *dst, const CborValue *src) { - dst->ptr = src->ptr; + /* This "if" is here for pedantry only: the two branches should perform + * the same memory operation. */ + if (src->parser->flags & CborParserFlag_ExternalSource) + dst->source.token = src->source.token; + else + dst->source.ptr = src->source.ptr; } static inline bool can_read_bytes(const CborValue *it, size_t n) { + if (CBOR_PARSER_READER_CONTROL >= 0) { + if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) { +#ifdef CBOR_PARSER_CAN_READ_BYTES_FUNCTION + return CBOR_PARSER_CAN_READ_BYTES_FUNCTION(it->source.token, n); +#else + return it->parser->source.ops->can_read_bytes(it->source.token, n); +#endif + } + } + /* Convert the pointer subtraction to size_t since end >= ptr * (this prevents issues with (ptrdiff_t)n becoming negative). */ - return (size_t)(it->parser->end - it->ptr) >= n; + return (size_t)(it->parser->source.end - it->source.ptr) >= n; } static inline void advance_bytes(CborValue *it, size_t n) { - it->ptr += n; + if (CBOR_PARSER_READER_CONTROL >= 0) { + if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) { +#ifdef CBOR_PARSER_ADVANCE_BYTES_FUNCTION + CBOR_PARSER_ADVANCE_BYTES_FUNCTION(it->source.token, n); +#else + it->parser->source.ops->advance_bytes(it->source.token, n); +#endif + return; + } + } + + it->source.ptr += n; +} + +static inline CborError transfer_string(CborValue *it, const void **ptr, size_t offset, size_t len) +{ + if (CBOR_PARSER_READER_CONTROL >= 0) { + if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) { +#ifdef CBOR_PARSER_TRANSFER_STRING_FUNCTION + return CBOR_PARSER_TRANSFER_STRING_FUNCTION(it->source.token, ptr, offset, len); +#else + return it->parser->source.ops->transfer_string(it->source.token, ptr, offset, len); +#endif + } + } + + it->source.ptr += offset; + if (can_read_bytes(it, len)) { + *CONST_CAST(const void **, ptr) = it->source.ptr; + it->source.ptr += len; + return CborNoError; + } + return CborErrorUnexpectedEOF; } static inline void *read_bytes_unchecked(const CborValue *it, void *dst, size_t offset, size_t n) { - return memcpy(dst, it->ptr + offset, n); + if (CBOR_PARSER_READER_CONTROL >= 0) { + if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) { +#ifdef CBOR_PARSER_READ_BYTES_FUNCTION + return CBOR_PARSER_READ_BYTES_FUNCTION(it->source.token, dst, offset, n); +#else + return it->parser->source.ops->read_bytes(it->source.token, dst, offset, n); +#endif + } + } + + return memcpy(dst, it->source.ptr + offset, n); } #ifdef __GNUC__ diff --git a/src/cborparser.c b/src/cborparser.c index 405a7d76..92810b31 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -343,15 +343,26 @@ uint64_t _cbor_value_decode_int64_internal(const CborValue *value) CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it) { memset(parser, 0, sizeof(*parser)); - parser->end = buffer + size; - parser->flags = flags; + parser->source.end = buffer + size; + parser->flags = (enum CborParserGlobalFlags)flags; it->parser = parser; - it->ptr = buffer; + it->source.ptr = buffer; it->remaining = 1; /* there's one type altogether, usually an array or map */ it->flags = 0; return preparse_value(it); } +CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborParser *parser, CborValue *it, void *token) +{ + memset(parser, 0, sizeof(*parser)); + parser->source.ops = ops; + parser->flags = CborParserFlag_ExternalSource; + it->parser = parser; + it->source.token = token; + it->remaining = 1; + return preparse_value(it); +} + /** * \fn bool cbor_value_at_end(const CborValue *it) * @@ -382,6 +393,11 @@ CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, C * Note that the error recovery is not precise and the pointer may not indicate * the exact byte containing bad data. * + * This function makes sense only when using a linear buffer (that is, when the + * parser is initialize by cbor_parser_init()). If using an external source, + * this function may return garbage; instead, consult the external source itself + * to find out more details about the presence of more data. + * * \sa cbor_value_at_end() */ @@ -1029,17 +1045,12 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t ++bytesNeeded; } - *bufferptr = it->ptr + bytesNeeded; if (*len != (size_t)*len) return CborErrorDataTooLarge; - if (add_check_overflow(bytesNeeded, *len, &bytesNeeded)) - return CborErrorUnexpectedEOF; - - if (!can_read_bytes(it, bytesNeeded)) - return CborErrorUnexpectedEOF; - - advance_bytes(it, bytesNeeded); + CborError err = transfer_string(it, bufferptr, bytesNeeded, *len); + if (err) + return err; } else { return CborErrorIllegalType; } diff --git a/src/cborvalidation.c b/src/cborvalidation.c index a59a4614..321374e3 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -471,6 +471,8 @@ static CborError validate_container(CborValue *it, int containerType, uint32_t f continue; if (flags & CborValidateMapIsSorted) { + if (it->parser->flags & CborParserFlag_ExternalSource) + return CborErrorUnimplementedValidation; if (previous) { size_t bytelen1 = (size_t)(previous_end - previous); size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - current); diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index 891264f0..29e6c046 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 Intel Corporation +** Copyright (C) 2021 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 @@ -85,6 +85,9 @@ private slots: void mapsAndArrays_data() { arrays_data(); } void mapsAndArrays(); + void readerApi_data() { arrays_data(); } + void readerApi(); + // chunked string API void chunkedString_data(); void chunkedString(); @@ -1038,6 +1041,54 @@ void tst_Parser::mapsAndArrays() "{_ 1: [_ " + expected + "], \"Hello\": {_ " + expected + ": (_ )}}"); } +void tst_Parser::readerApi() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + + struct Input { + QByteArray data; + int consumed; + } input = { data, 0 }; + + CborParserOperations ops; + ops.can_read_bytes = [](void *token, size_t len) { + auto input = static_cast(token); + return input->data.size() - input->consumed >= int(len); + }; + ops.read_bytes = [](void *token, void *dst, size_t offset, size_t len) { + auto input = static_cast(token); + return memcpy(dst, input->data.constData() + input->consumed + offset, len); + }; + ops.advance_bytes = [](void *token, size_t len) { + auto input = static_cast(token); + input->consumed += int(len); + }; + ops.transfer_string = [](void *token, const void **userptr, size_t offset, size_t len) { + // ### + auto input = static_cast(token); + if (input->data.size() - input->consumed < int(len + offset)) + return CborErrorUnexpectedEOF; + input->consumed += int(offset); + *userptr = input->data.constData() + input->consumed; + input->consumed += int(len); + return CborNoError; + }; + + CborParser parser; + CborValue first; + CborError err = cbor_parser_init_reader(&ops, &parser, &first, &input); + QCOMPARE(err, CborNoError); + + QString decoded; + err = parseOne(&first, &decoded); + QCOMPARE(err, CborNoError); + QCOMPARE(decoded, expected); + + // check we consumed everything + QCOMPARE(input.consumed, data.size()); +} + void tst_Parser::chunkedString_data() { QTest::addColumn("data"); From e26ff9a2dc3d0383e09f24b5920766e2ea4ca4d4 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 14:03:03 -0800 Subject: [PATCH 27/35] WIP Initial API for delegated streaming out Signed-off-by: Thiago Macieira --- .travis.yml | 2 +- src/cbor.h | 19 +++++++++++++- src/cborencoder.c | 48 +++++++++++++++++++++++++++++------ src/cborinternal_p.h | 4 +++ tests/encoder/tst_encoder.cpp | 45 ++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 21701a30..4df21cb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -75,7 +75,7 @@ script: - make -s -f Makefile.configure configure | tee .config - make -k CFLAGS="$CFLAGS -march=native -g1 -Wall -Wextra -Werror" - CPPFLAGS="-DNDEBUG -DCBOR_PARSER_READER_CONTROL=-1" + CPPFLAGS="-DNDEBUG -DCBOR_ENCODER_WRITER_CONTROL=-1 -DCBOR_PARSER_READER_CONTROL=-1" lib/libtinycbor.a - size lib/libtinycbor.a | tee sizes - make -s clean diff --git a/src/cbor.h b/src/cbor.h index 04181f6a..703ad42a 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -203,13 +203,29 @@ typedef enum CborError { CBOR_API const char *cbor_error_string(CborError error); /* Encoder API */ + +typedef enum CborEncoderAppendType +{ + CborEncoderAppendCborData = 0, + CborEncoderAppendStringData = 1 +} CborEncoderAppendType; + +typedef CborError (*CborEncoderWriteFunction)(void *, const void *, size_t, CborEncoderAppendType); + +enum CborEncoderFlags +{ + CborIteratorFlag_WriterFunction = 0x01, + CborIteratorFlag_ContainerIsMap_ = 0x20 +}; + struct CborEncoder { union { uint8_t *ptr; ptrdiff_t bytes_needed; + CborEncoderWriteFunction writer; } data; - const uint8_t *end; + uint8_t *end; size_t remaining; int flags; }; @@ -219,6 +235,7 @@ static const size_t CborIndefiniteLength = SIZE_MAX; #ifndef CBOR_NO_ENCODER_API CBOR_API void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags); +CBOR_API void cbor_encoder_init_writer(CborEncoder *encoder, CborEncoderWriteFunction writer, void *); 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); diff --git a/src/cborencoder.c b/src/cborencoder.c index 645e4069..a51f4451 100644 --- a/src/cborencoder.c +++ b/src/cborencoder.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation +** Copyright (C) 2021 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 @@ -208,6 +208,18 @@ void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int f encoder->flags = flags; } +void cbor_encoder_init_writer(CborEncoder *encoder, CborEncoderWriteFunction writer, void *token) +{ +#ifdef CBOR_ENCODER_WRITE_FUNCTION + (void) writer; +#else + encoder->data.writer = writer; +#endif + encoder->end = (uint8_t *)token; + encoder->remaining = 2; + encoder->flags = CborIteratorFlag_WriterFunction; +} + static inline void put16(void *where, uint16_t v) { uint16_t v_be = cbor_htons(v); @@ -221,8 +233,12 @@ static inline void put16(void *where, uint16_t v) * being created in the tinycbor output */ static inline bool isOomError(CborError err) { - (void) err; - return true; + if (CBOR_ENCODER_WRITER_CONTROL < 0) + return true; + + /* CborErrorOutOfMemory is the only negative error code, intentionally + * so we can write the test like this */ + return (int)err < 0; } static inline void put32(void *where, uint32_t v) @@ -253,8 +269,20 @@ static inline void advance_ptr(CborEncoder *encoder, size_t n) encoder->data.bytes_needed += n; } -static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len) +static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len, + CborEncoderAppendType appendType) { + if (CBOR_ENCODER_WRITER_CONTROL >= 0) { + if (encoder->flags & CborIteratorFlag_WriterFunction || CBOR_ENCODER_WRITER_CONTROL != 0) { +# ifdef CBOR_ENCODER_WRITE_FUNCTION + return CBOR_ENCODER_WRITE_FUNCTION(encoder->end, data, len, appendType); +# else + return encoder->data.writer(encoder->end, data, len, appendType); +# endif + } + } + +#if CBOR_ENCODER_WRITER_CONTROL <= 0 if (would_overflow(encoder, len)) { if (encoder->end != NULL) { len -= encoder->end - encoder->data.ptr; @@ -268,12 +296,13 @@ static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, memcpy(encoder->data.ptr, data, len); encoder->data.ptr += len; +#endif return CborNoError; } static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte) { - return append_to_buffer(encoder, &byte, 1); + return append_to_buffer(encoder, &byte, 1, CborEncoderAppendCborData); } static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) @@ -302,7 +331,7 @@ static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t u *bufstart = shiftedMajorType + Value8Bit + more; } - return append_to_buffer(encoder, bufstart, bufend - bufstart); + return append_to_buffer(encoder, bufstart, bufend - bufstart, CborEncoderAppendCborData); } static inline void saturated_decrement(CborEncoder *encoder) @@ -399,7 +428,7 @@ CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, cons else put16(buf + 1, *(const uint16_t*)value); saturated_decrement(encoder); - return append_to_buffer(encoder, buf, size + 1); + return append_to_buffer(encoder, buf, size + 1, CborEncoderAppendCborData); } /** @@ -418,7 +447,7 @@ static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shif CborError err = encode_number(encoder, length, shiftedMajorType); if (err && !isOomError(err)) return err; - return append_to_buffer(encoder, string, length); + return append_to_buffer(encoder, string, length, CborEncoderAppendStringData); } /** @@ -466,9 +495,12 @@ static CborError create_container(CborEncoder *encoder, CborEncoder *container, saturated_decrement(encoder); container->remaining = length + 1; /* overflow ok on CborIndefiniteLength */ + cbor_static_assert((int)CborIteratorFlag_ContainerIsMap_ == (int)CborIteratorFlag_ContainerIsMap); cbor_static_assert(((MapType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == CborIteratorFlag_ContainerIsMap); cbor_static_assert(((ArrayType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == 0); container->flags = shiftedMajorType & CborIteratorFlag_ContainerIsMap; + if (CBOR_ENCODER_WRITER_CONTROL == 0) + container->flags |= encoder->flags & CborIteratorFlag_WriterFunction; if (length == CborIndefiniteLength) { container->flags |= CborIteratorFlag_UnknownLength; diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index f3747e6b..2343ce76 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -106,9 +106,13 @@ static inline double decode_half(unsigned short half) # define CBOR_PARSER_MAX_RECURSIONS 1024 #endif +#ifndef CBOR_ENCODER_WRITER_CONTROL +# define CBOR_ENCODER_WRITER_CONTROL 0 +#endif #ifndef CBOR_PARSER_READER_CONTROL # define CBOR_PARSER_READER_CONTROL 0 #endif + /* * CBOR Major types * Encoded in the high 3 bits of the descriptor byte diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index b5513101..ecebc6ee 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp @@ -61,6 +61,10 @@ private slots: void maps_data() { tags_data(); } void maps(); + void writerApi_data() { tags_data(); } + void writerApi(); + void writerApiFail_data() { tags_data(); } + void writerApiFail(); void shortBuffer_data() { tags_data(); } void shortBuffer(); void tooShortArrays_data() { tags_data(); } @@ -784,6 +788,47 @@ void tst_Encoder::maps() if (QTest::currentTestFailed()) return; } +void tst_Encoder::writerApi() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + + // instead of writing to a QByteArray like all other tests, write to a QBuffer + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + auto callback = [](void *token, const void *data, size_t len, CborEncoderAppendType) { + auto buffer = static_cast(token); + buffer->write(static_cast(data), len); + return CborNoError; + }; + + CborEncoder encoder; + cbor_encoder_init_writer(&encoder, callback, &buffer); + QCOMPARE(encodeVariant(&encoder, input), CborNoError); + + buffer.reset(); + QCOMPARE(buffer.readAll(), output); +} + +void tst_Encoder::writerApiFail() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + + // same as above, but we'll produce an error during writing and we expect + // it to be returned + int callCount = 0; + auto callback = [](void *token, const void *, size_t, CborEncoderAppendType) { + ++*static_cast(token); + return CborErrorIO; + }; + + CborEncoder encoder; + cbor_encoder_init_writer(&encoder, callback, &callCount); + QCOMPARE(encodeVariant(&encoder, input), CborErrorIO); + QCOMPARE(callCount, 1); +} + void tst_Encoder::shortBuffer() { QFETCH(QVariant, input); From 87a7a9308c66c571243c9803eaf14f8a22cc4873 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sun, 17 Dec 2017 15:26:18 -0800 Subject: [PATCH 28/35] Move the testdata out to a separate .cpp so they can be reused Signed-off-by: Thiago Macieira --- tests/encoder/data.cpp | 346 +++++++++++++++++++ tests/encoder/tst_encoder.cpp | 321 +----------------- tests/parser/data.cpp | 607 ++++++++++++++++++++++++++++++++++ tests/parser/tst_parser.cpp | 561 +------------------------------ 4 files changed, 956 insertions(+), 879 deletions(-) create mode 100644 tests/encoder/data.cpp create mode 100644 tests/parser/data.cpp diff --git a/tests/encoder/data.cpp b/tests/encoder/data.cpp new file mode 100644 index 00000000..6dca49d4 --- /dev/null +++ b/tests/encoder/data.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 + +static float myNaNf() +{ + uint32_t v = 0x7fc00000; + float f; + memcpy(&f, &v, sizeof(f)); + Q_ASSERT(qIsNaN(f)); + return f; +} + +static float myInff() +{ + uint32_t v = 0x7f800000; + float f; + memcpy(&f, &v, sizeof(f)); + Q_ASSERT(qIsInf(f)); + return f; +} + +static float myNInff() +{ + uint32_t v = 0xff800000; + float f; + memcpy(&f, &v, sizeof(f)); + Q_ASSERT(qIsInf(f)); + return f; +} + +static double myNaN() +{ + uint64_t v = UINT64_C(0x7ff8000000000000); + double f; + memcpy(&f, &v, sizeof(f)); + Q_ASSERT(qIsNaN(f)); + return f; +} + +static double myInf() +{ + uint64_t v = UINT64_C(0x7ff0000000000000); + double f; + memcpy(&f, &v, sizeof(f)); + Q_ASSERT(qIsInf(f)); + return f; +} + +static double myNInf() +{ + uint64_t v = UINT64_C(0xfff0000000000000); + double f; + memcpy(&f, &v, sizeof(f)); + Q_ASSERT(qIsInf(f)); + return f; +} + +template QByteArray raw(const char (&data)[N]) +{ + return QByteArray::fromRawData(data, N - 1); +} + +struct NegativeInteger { quint64 abs; }; +Q_DECLARE_METATYPE(NegativeInteger) + +struct SimpleType { uint8_t type; }; +Q_DECLARE_METATYPE(SimpleType) + +struct Float16Standin { uint16_t val; }; +Q_DECLARE_METATYPE(Float16Standin) + +struct Tag { CborTag tag; QVariant tagged; }; +Q_DECLARE_METATYPE(Tag) + +template +QVariant make_list(const Args &... args) +{ + return QVariantList{args...}; +} + +typedef QVector> Map; +Q_DECLARE_METATYPE(Map) +QVariant make_map(const std::initializer_list> &list) +{ + return QVariant::fromValue(Map(list)); +} + +struct IndeterminateLengthArray : QVariantList { using QVariantList::QVariantList; }; +struct IndeterminateLengthMap : Map { using Map::Map; }; +Q_DECLARE_METATYPE(IndeterminateLengthArray) +Q_DECLARE_METATYPE(IndeterminateLengthMap) + +QVariant make_ilarray(const std::initializer_list &list) +{ + return QVariant::fromValue(IndeterminateLengthArray(list)); +} + +QVariant make_ilmap(const std::initializer_list> &list) +{ + return QVariant::fromValue(IndeterminateLengthMap(list)); +} + +void addHalfFloat() +{ + QTest::addColumn("output"); + QTest::addColumn("rawInput"); + QTest::addColumn("floatInput"); + + QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; + QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; + + QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); + QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); + + QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); + QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); + + QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); + QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); + + QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; + QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; + + QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; + QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; + + QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); + QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); + + QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf(); + QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf(); + + QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN(); + QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN(); + QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN(); + QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN(); +} + +void addColumns() +{ + QTest::addColumn("output"); + QTest::addColumn("input"); +} + +void addFixedData() +{ + // unsigned integers + QTest::newRow("0U") << raw("\x00") << QVariant(0U); + QTest::newRow("1U") << raw("\x01") << QVariant(1U); + QTest::newRow("10U") << raw("\x0a") << QVariant(10U); + QTest::newRow("23U") << raw("\x17") << QVariant(23U); + QTest::newRow("24U") << raw("\x18\x18") << QVariant(24U); + QTest::newRow("255U") << raw("\x18\xff") << QVariant(255U); + QTest::newRow("256U") << raw("\x19\x01\x00") << QVariant(256U); + QTest::newRow("65535U") << raw("\x19\xff\xff") << QVariant(65535U); + QTest::newRow("65536U") << raw("\x1a\0\1\x00\x00") << QVariant(65536U); + QTest::newRow("4294967295U") << raw("\x1a\xff\xff\xff\xff") << QVariant(4294967295U); + QTest::newRow("4294967296U") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_UINT64_C(4294967296)); + QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") + << QVariant(std::numeric_limits::max()); + + // signed integers containing non-negative numbers + QTest::newRow("0") << raw("\x00") << QVariant(0); + QTest::newRow("1") << raw("\x01") << QVariant(1); + QTest::newRow("10") << raw("\x0a") << QVariant(10); + QTest::newRow("23") << raw("\x17") << QVariant(23); + QTest::newRow("24") << raw("\x18\x18") << QVariant(24); + QTest::newRow("255") << raw("\x18\xff") << QVariant(255); + QTest::newRow("256") << raw("\x19\x01\x00") << QVariant(256); + QTest::newRow("65535") << raw("\x19\xff\xff") << QVariant(65535); + QTest::newRow("65536") << raw("\x1a\0\1\x00\x00") << QVariant(65536); + QTest::newRow("4294967295") << raw("\x1a\xff\xff\xff\xff") << QVariant(Q_INT64_C(4294967295)); + QTest::newRow("4294967296") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(4294967296)); + + // signed integers containing negative numbers + QTest::newRow("-1") << raw("\x20") << QVariant(-1); + QTest::newRow("-2") << raw("\x21") << QVariant(-2); + QTest::newRow("-24") << raw("\x37") << QVariant(-24); + QTest::newRow("-25") << raw("\x38\x18") << QVariant(-25); + QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << QVariant(-256); + QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << QVariant(-257); + QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << QVariant(-65536); + QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << QVariant(-65537); + QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << QVariant(Q_INT64_C(-4294967296)); + QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(-4294967297)); + + // negative integers + auto neg = [](quint64 v) { return QVariant::fromValue({v}); }; + QTest::newRow("negative1") << raw("\x20") << neg(1); + QTest::newRow("negative2") << raw("\x21") << neg(2); + QTest::newRow("negative24") << raw("\x37") << neg(24); + QTest::newRow("negative25") << raw("\x38\x18") << neg(25); + QTest::newRow("negativeUINT8_MAX") << raw("\x38\xff") << neg(256); + QTest::newRow("negativeUINT8_MAX-1") << raw("\x39\x01\x00") << neg(257); + QTest::newRow("negativeUINT16_MAX") << raw("\x39\xff\xff") << neg(65536); + QTest::newRow("negativeUINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << neg(65537); + QTest::newRow("negativeUINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << neg(Q_UINT64_C(4294967296)); + QTest::newRow("negativeUINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << neg(Q_UINT64_C(4294967297)); + QTest::newRow("negativeUINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") + << neg(std::numeric_limits::max()); + QTest::newRow("negativeUINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") << neg(0); + + QTest::newRow("simple0") << raw("\xe0") << QVariant::fromValue(SimpleType{0}); + QTest::newRow("simple19") << raw("\xf3") << QVariant::fromValue(SimpleType{19}); + QTest::newRow("false") << raw("\xf4") << QVariant(false); + QTest::newRow("true") << raw("\xf5") << QVariant(true); + QTest::newRow("null") << raw("\xf6") << QVariant::fromValue(nullptr); + QTest::newRow("undefined") << raw("\xf7") << QVariant(); + QTest::newRow("simple32") << raw("\xf8\x20") << QVariant::fromValue(SimpleType{32}); + QTest::newRow("simple255") << raw("\xf8\xff") << QVariant::fromValue(SimpleType{255}); + + // floating point +#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) + QTest::newRow("0.f16") << raw("\xf9\0\0") << QVariant::fromValue(Float16Standin{0x0000}); +#else + QTest::newRow("0.f16") << raw("\xf9\0\0") << QVariant::fromValue(qfloat16(0)); + QTest::newRow("-1.f16") << raw("\xf9\xbc\0") << QVariant::fromValue(qfloat16(-1)); + QTest::newRow("1.5f16") << raw("\xf9\x3e\0") << QVariant::fromValue(qfloat16(1.5)); + QTest::newRow("nan_f16") << raw("\xf9\x7e\0") << QVariant::fromValue(myNaNf()); + QTest::newRow("-inf_f16") << raw("\xf9\xfc\0") << QVariant::fromValue(myNInff()); + QTest::newRow("+inf_f16") << raw("\xf9\x7c\0") << QVariant::fromValue(myInff()); +#endif + + QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << QVariant::fromValue(0.f); + QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << QVariant(0.); + QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << QVariant::fromValue(-1.f); + QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << QVariant(-1.); + QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << QVariant::fromValue(16777215.f); + QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << QVariant::fromValue(16777215.); + QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << QVariant(-16777215.f); + QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << QVariant::fromValue(-16777215.); + + QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << QVariant::fromValue(myNaNf()); + QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << QVariant(myNaN()); + QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << QVariant::fromValue(myNInff()); + QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << QVariant(myNInf()); + QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << QVariant::fromValue(myInff()); + QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << QVariant(myInf()); +} + +void addStringsData() +{ + // byte strings + QTest::newRow("emptybytestring") << raw("\x40") << QVariant(QByteArray("")); + QTest::newRow("bytestring1") << raw("\x41 ") << QVariant(QByteArray(" ")); + QTest::newRow("bytestring1-nul") << raw("\x41\0") << QVariant(QByteArray("", 1)); + QTest::newRow("bytestring5") << raw("\x45Hello") << QVariant(QByteArray("Hello")); + QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234") + << QVariant(QByteArray("123456789012345678901234")); + QTest::newRow("bytestring256") << raw("\x59\1\0") + QByteArray(256, '3') + << QVariant(QByteArray(256, '3')); + + // text strings + QTest::newRow("emptytextstring") << raw("\x60") << QVariant(""); + QTest::newRow("textstring1") << raw("\x61 ") << QVariant(" "); + QTest::newRow("textstring1-nul") << raw("\x61\0") << QVariant(QString::fromLatin1("", 1)); + QTest::newRow("textstring5") << raw("\x65Hello") << QVariant("Hello"); + QTest::newRow("textstring24") << raw("\x78\x18""123456789012345678901234") + << QVariant("123456789012345678901234"); + QTest::newRow("textstring256") << raw("\x79\1\0") + QByteArray(256, '3') + << QVariant(QString(256, '3')); +} + +void addArraysAndMaps() +{ + QTest::newRow("emptyarray") << raw("\x80") << make_list(); + QTest::newRow("emptymap") << raw("\xa0") << make_map({}); + + QTest::newRow("array-0") << raw("\x81\0") << make_list(0); + QTest::newRow("array-{0-0}") << raw("\x82\0\0") << make_list(0, 0); + QTest::newRow("array-Hello") << raw("\x81\x65Hello") << make_list("Hello"); + QTest::newRow("array-array-0") << raw("\x81\x81\0") << make_list(make_list(0)); + QTest::newRow("array-array-{0-0}") << raw("\x81\x82\0\0") << make_list(make_list(0, 0)); + QTest::newRow("array-array-0-0") << raw("\x82\x81\0\0") << make_list(make_list(0),0); + QTest::newRow("array-array-Hello") << raw("\x81\x81\x65Hello") << make_list(make_list("Hello")); + + QTest::newRow("map-0:0") << raw("\xa1\0\0") << make_map({{0,0}}); + QTest::newRow("map-0:0-1:1") << raw("\xa2\0\0\1\1") << make_map({{0,0}, {1,1}}); + QTest::newRow("map-0:{map-0:0-1:1}") << raw("\xa1\0\xa2\0\0\1\1") << make_map({{0, make_map({{0,0}, {1,1}})}}); + + QTest::newRow("array-map1") << raw("\x81\xa1\0\0") << make_list(make_map({{0,0}})); + QTest::newRow("array-map2") << raw("\x82\xa1\0\0\xa1\1\1") << make_list(make_map({{0,0}}), make_map({{1,1}})); + + QTest::newRow("map-array1") << raw("\xa1\x62oc\x81\0") << make_map({{"oc", make_list(0)}}); + QTest::newRow("map-array2") << raw("\xa1\x62oc\x84\0\1\2\3") << make_map({{"oc", make_list(0, 1, 2, 3)}}); + QTest::newRow("map-array3") << raw("\xa2\x62oc\x82\0\1\2\3") << make_map({{"oc", make_list(0, 1)}, {2, 3}}); + + // indeterminate length + QTest::newRow("_emptyarray") << raw("\x9f\xff") << QVariant::fromValue(IndeterminateLengthArray{}); + QTest::newRow("_emptymap") << raw("\xbf\xff") << make_ilmap({}); + + QTest::newRow("_array-0") << raw("\x9f\0\xff") << make_ilarray({0}); + QTest::newRow("_array-{0-0}") << raw("\x9f\0\0\xff") << make_ilarray({0, 0}); + QTest::newRow("_array-Hello") << raw("\x9f\x65Hello\xff") << make_ilarray({"Hello"}); + QTest::newRow("_array-array-0") << raw("\x9f\x81\0\xff") << make_ilarray({make_list(0)}); + QTest::newRow("_array-_array-0") << raw("\x9f\x9f\0\xff\xff") << make_ilarray({make_ilarray({0})}); + QTest::newRow("_array-_array-{0-0}") << raw("\x9f\x9f\0\0\xff\xff") << make_ilarray({make_ilarray({0, 0})}); + QTest::newRow("_array-_array-0-0") << raw("\x9f\x9f\0\xff\0\xff") << make_ilarray({make_ilarray({0}),0}); + QTest::newRow("_array-_array-Hello") << raw("\x9f\x9f\x65Hello\xff\xff") << make_ilarray({make_ilarray({"Hello"})}); + + QTest::newRow("_map-0:0") << raw("\xbf\0\0\xff") << make_ilmap({{0,0}}); + QTest::newRow("_map-0:0-1:1") << raw("\xbf\0\0\1\1\xff") << make_ilmap({{0,0}, {1,1}}); + QTest::newRow("_map-0:{map-0:0-1:1}") << raw("\xbf\0\xa2\0\0\1\1\xff") << make_ilmap({{0, make_map({{0,0}, {1,1}})}}); + QTest::newRow("_map-0:{_map-0:0-1:1}") << raw("\xbf\0\xbf\0\0\1\1\xff\xff") << make_ilmap({{0, make_ilmap({{0,0}, {1,1}})}}); + + QTest::newRow("_array-map1") << raw("\x9f\xa1\0\0\xff") << make_ilarray({make_map({{0,0}})}); + QTest::newRow("_array-_map1") << raw("\x9f\xbf\0\0\xff\xff") << make_ilarray({make_ilmap({{0,0}})}); + QTest::newRow("_array-map2") << raw("\x9f\xa1\0\0\xa1\1\1\xff") << make_ilarray({make_map({{0,0}}), make_map({{1,1}})}); + QTest::newRow("_array-_map2") << raw("\x9f\xbf\0\0\xff\xbf\1\1\xff\xff") << make_ilarray({make_ilmap({{0,0}}), make_ilmap({{1,1}})}); + + QTest::newRow("_map-array1") << raw("\xbf\x62oc\x81\0\xff") << make_ilmap({{"oc", make_list(0)}}); + QTest::newRow("_map-_array1") << raw("\xbf\x62oc\x9f\0\xff\xff") << make_ilmap({{"oc", make_ilarray({0})}}); + QTest::newRow("_map-array2") << raw("\xbf\x62oc\x84\0\1\2\3\xff") << make_ilmap({{"oc", make_list(0, 1, 2, 3)}}); + QTest::newRow("_map-_array2") << raw("\xbf\x62oc\x9f\0\1\2\3\xff\xff") << make_ilmap({{"oc", make_ilarray({0, 1, 2, 3})}}); + QTest::newRow("_map-array3") << raw("\xbf\x62oc\x82\0\1\2\3\xff") << make_ilmap({{"oc", make_list(0, 1)}, {2, 3}}); + QTest::newRow("_map-_array3") << raw("\xbf\x62oc\x9f\0\1\xff\2\3\xff") << make_ilmap({{"oc", make_ilarray({0, 1})}, {2, 3}}); + + // tagged + QTest::newRow("array-1(0)") << raw("\x81\xc1\0") << make_list(QVariant::fromValue(Tag{1, 0})); + QTest::newRow("array-1(map)") << raw("\x81\xc1\xa0") << make_list(QVariant::fromValue(Tag{1, make_map({})})); + QTest::newRow("map-1(2):3(4)") << raw("\xa1\xc1\2\xc3\4") << make_map({{QVariant::fromValue(Tag{1, 2}), QVariant::fromValue(Tag{3, 4})}}); +} + diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index ecebc6ee..31c29152 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp @@ -80,105 +80,7 @@ private slots: }; #include "tst_encoder.moc" - -static float myNaNf() -{ - uint32_t v = 0x7fc00000; - float f; - memcpy(&f, &v, sizeof(f)); - Q_ASSERT(qIsNaN(f)); - return f; -} - -static float myInff() -{ - uint32_t v = 0x7f800000; - float f; - memcpy(&f, &v, sizeof(f)); - Q_ASSERT(qIsInf(f)); - return f; -} - -static float myNInff() -{ - uint32_t v = 0xff800000; - float f; - memcpy(&f, &v, sizeof(f)); - Q_ASSERT(qIsInf(f)); - return f; -} - -static double myNaN() -{ - uint64_t v = UINT64_C(0x7ff8000000000000); - double f; - memcpy(&f, &v, sizeof(f)); - Q_ASSERT(qIsNaN(f)); - return f; -} - -static double myInf() -{ - uint64_t v = UINT64_C(0x7ff0000000000000); - double f; - memcpy(&f, &v, sizeof(f)); - Q_ASSERT(qIsInf(f)); - return f; -} - -static double myNInf() -{ - uint64_t v = UINT64_C(0xfff0000000000000); - double f; - memcpy(&f, &v, sizeof(f)); - Q_ASSERT(qIsInf(f)); - return f; -} - -template QByteArray raw(const char (&data)[N]) -{ - return QByteArray::fromRawData(data, N - 1); -} - -struct NegativeInteger { quint64 abs; }; -Q_DECLARE_METATYPE(NegativeInteger) - -struct SimpleType { uint8_t type; }; -Q_DECLARE_METATYPE(SimpleType) - -struct Float16Standin { uint16_t val; }; -Q_DECLARE_METATYPE(Float16Standin) - -struct Tag { CborTag tag; QVariant tagged; }; -Q_DECLARE_METATYPE(Tag) - -template -QVariant make_list(const Args &... args) -{ - return QVariantList{args...}; -} - -typedef QVector> Map; -Q_DECLARE_METATYPE(Map) -QVariant make_map(const std::initializer_list> &list) -{ - return QVariant::fromValue(Map(list)); -} - -struct IndeterminateLengthArray : QVariantList { using QVariantList::QVariantList; }; -struct IndeterminateLengthMap : Map { using Map::Map; }; -Q_DECLARE_METATYPE(IndeterminateLengthArray) -Q_DECLARE_METATYPE(IndeterminateLengthMap) - -QVariant make_ilarray(const std::initializer_list &list) -{ - return QVariant::fromValue(IndeterminateLengthArray(list)); -} - -QVariant make_ilmap(const std::initializer_list> &list) -{ - return QVariant::fromValue(IndeterminateLengthMap(list)); -} +#include "data.cpp" static inline bool isOomError(CborError err) { @@ -319,227 +221,6 @@ void compare(const QVariant &input, const QByteArray &output) compare(input, encodeVariant, output); } -void addColumns() -{ - QTest::addColumn("output"); - QTest::addColumn("input"); -} - -void addFixedData() -{ - // unsigned integers - QTest::newRow("0U") << raw("\x00") << QVariant(0U); - QTest::newRow("1U") << raw("\x01") << QVariant(1U); - QTest::newRow("10U") << raw("\x0a") << QVariant(10U); - QTest::newRow("23U") << raw("\x17") << QVariant(23U); - QTest::newRow("24U") << raw("\x18\x18") << QVariant(24U); - QTest::newRow("255U") << raw("\x18\xff") << QVariant(255U); - QTest::newRow("256U") << raw("\x19\x01\x00") << QVariant(256U); - QTest::newRow("65535U") << raw("\x19\xff\xff") << QVariant(65535U); - QTest::newRow("65536U") << raw("\x1a\0\1\x00\x00") << QVariant(65536U); - QTest::newRow("4294967295U") << raw("\x1a\xff\xff\xff\xff") << QVariant(4294967295U); - QTest::newRow("4294967296U") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_UINT64_C(4294967296)); - QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") - << QVariant(std::numeric_limits::max()); - - // signed integers containing non-negative numbers - QTest::newRow("0") << raw("\x00") << QVariant(0); - QTest::newRow("1") << raw("\x01") << QVariant(1); - QTest::newRow("10") << raw("\x0a") << QVariant(10); - QTest::newRow("23") << raw("\x17") << QVariant(23); - QTest::newRow("24") << raw("\x18\x18") << QVariant(24); - QTest::newRow("255") << raw("\x18\xff") << QVariant(255); - QTest::newRow("256") << raw("\x19\x01\x00") << QVariant(256); - QTest::newRow("65535") << raw("\x19\xff\xff") << QVariant(65535); - QTest::newRow("65536") << raw("\x1a\0\1\x00\x00") << QVariant(65536); - QTest::newRow("4294967295") << raw("\x1a\xff\xff\xff\xff") << QVariant(Q_INT64_C(4294967295)); - QTest::newRow("4294967296") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(4294967296)); - - // signed integers containing negative numbers - QTest::newRow("-1") << raw("\x20") << QVariant(-1); - QTest::newRow("-2") << raw("\x21") << QVariant(-2); - QTest::newRow("-24") << raw("\x37") << QVariant(-24); - QTest::newRow("-25") << raw("\x38\x18") << QVariant(-25); - QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << QVariant(-256); - QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << QVariant(-257); - QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << QVariant(-65536); - QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << QVariant(-65537); - QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << QVariant(Q_INT64_C(-4294967296)); - QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(-4294967297)); - - // negative integers - auto neg = [](quint64 v) { return QVariant::fromValue({v}); }; - QTest::newRow("negative1") << raw("\x20") << neg(1); - QTest::newRow("negative2") << raw("\x21") << neg(2); - QTest::newRow("negative24") << raw("\x37") << neg(24); - QTest::newRow("negative25") << raw("\x38\x18") << neg(25); - QTest::newRow("negativeUINT8_MAX") << raw("\x38\xff") << neg(256); - QTest::newRow("negativeUINT8_MAX-1") << raw("\x39\x01\x00") << neg(257); - QTest::newRow("negativeUINT16_MAX") << raw("\x39\xff\xff") << neg(65536); - QTest::newRow("negativeUINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << neg(65537); - QTest::newRow("negativeUINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << neg(Q_UINT64_C(4294967296)); - QTest::newRow("negativeUINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << neg(Q_UINT64_C(4294967297)); - QTest::newRow("negativeUINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") - << neg(std::numeric_limits::max()); - QTest::newRow("negativeUINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") << neg(0); - - QTest::newRow("simple0") << raw("\xe0") << QVariant::fromValue(SimpleType{0}); - QTest::newRow("simple19") << raw("\xf3") << QVariant::fromValue(SimpleType{19}); - QTest::newRow("false") << raw("\xf4") << QVariant(false); - QTest::newRow("true") << raw("\xf5") << QVariant(true); - QTest::newRow("null") << raw("\xf6") << QVariant::fromValue(nullptr); - QTest::newRow("undefined") << raw("\xf7") << QVariant(); - QTest::newRow("simple32") << raw("\xf8\x20") << QVariant::fromValue(SimpleType{32}); - QTest::newRow("simple255") << raw("\xf8\xff") << QVariant::fromValue(SimpleType{255}); - - // floating point -#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) - QTest::newRow("0.f16") << raw("\xf9\0\0") << QVariant::fromValue(Float16Standin{0x0000}); -#else - QTest::newRow("0.f16") << raw("\xf9\0\0") << QVariant::fromValue(qfloat16(0)); - QTest::newRow("-1.f16") << raw("\xf9\xbc\0") << QVariant::fromValue(qfloat16(-1)); - QTest::newRow("1.5f16") << raw("\xf9\x3e\0") << QVariant::fromValue(qfloat16(1.5)); - QTest::newRow("nan_f16") << raw("\xf9\x7e\0") << QVariant::fromValue(myNaNf()); - QTest::newRow("-inf_f16") << raw("\xf9\xfc\0") << QVariant::fromValue(myNInff()); - QTest::newRow("+inf_f16") << raw("\xf9\x7c\0") << QVariant::fromValue(myInff()); -#endif - - QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << QVariant::fromValue(0.f); - QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << QVariant(0.); - QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << QVariant::fromValue(-1.f); - QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << QVariant(-1.); - QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << QVariant::fromValue(16777215.f); - QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << QVariant::fromValue(16777215.); - QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << QVariant(-16777215.f); - QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << QVariant::fromValue(-16777215.); - - QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << QVariant::fromValue(myNaNf()); - QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << QVariant(myNaN()); - QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << QVariant::fromValue(myNInff()); - QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << QVariant(myNInf()); - QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << QVariant::fromValue(myInff()); - QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << QVariant(myInf()); -} - -void addStringsData() -{ - // byte strings - QTest::newRow("emptybytestring") << raw("\x40") << QVariant(QByteArray("")); - QTest::newRow("bytestring1") << raw("\x41 ") << QVariant(QByteArray(" ")); - QTest::newRow("bytestring1-nul") << raw("\x41\0") << QVariant(QByteArray("", 1)); - QTest::newRow("bytestring5") << raw("\x45Hello") << QVariant(QByteArray("Hello")); - QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234") - << QVariant(QByteArray("123456789012345678901234")); - QTest::newRow("bytestring256") << raw("\x59\1\0") + QByteArray(256, '3') - << QVariant(QByteArray(256, '3')); - - // text strings - QTest::newRow("emptytextstring") << raw("\x60") << QVariant(""); - QTest::newRow("textstring1") << raw("\x61 ") << QVariant(" "); - QTest::newRow("textstring1-nul") << raw("\x61\0") << QVariant(QString::fromLatin1("", 1)); - QTest::newRow("textstring5") << raw("\x65Hello") << QVariant("Hello"); - QTest::newRow("textstring24") << raw("\x78\x18""123456789012345678901234") - << QVariant("123456789012345678901234"); - QTest::newRow("textstring256") << raw("\x79\1\0") + QByteArray(256, '3') - << QVariant(QString(256, '3')); -} - -void addArraysAndMaps() -{ - QTest::newRow("emptyarray") << raw("\x80") << make_list(); - QTest::newRow("emptymap") << raw("\xa0") << make_map({}); - - QTest::newRow("array-0") << raw("\x81\0") << make_list(0); - QTest::newRow("array-{0-0}") << raw("\x82\0\0") << make_list(0, 0); - QTest::newRow("array-Hello") << raw("\x81\x65Hello") << make_list("Hello"); - QTest::newRow("array-array-0") << raw("\x81\x81\0") << make_list(make_list(0)); - QTest::newRow("array-array-{0-0}") << raw("\x81\x82\0\0") << make_list(make_list(0, 0)); - QTest::newRow("array-array-0-0") << raw("\x82\x81\0\0") << make_list(make_list(0),0); - QTest::newRow("array-array-Hello") << raw("\x81\x81\x65Hello") << make_list(make_list("Hello")); - - QTest::newRow("map-0:0") << raw("\xa1\0\0") << make_map({{0,0}}); - QTest::newRow("map-0:0-1:1") << raw("\xa2\0\0\1\1") << make_map({{0,0}, {1,1}}); - QTest::newRow("map-0:{map-0:0-1:1}") << raw("\xa1\0\xa2\0\0\1\1") << make_map({{0, make_map({{0,0}, {1,1}})}}); - - QTest::newRow("array-map1") << raw("\x81\xa1\0\0") << make_list(make_map({{0,0}})); - QTest::newRow("array-map2") << raw("\x82\xa1\0\0\xa1\1\1") << make_list(make_map({{0,0}}), make_map({{1,1}})); - - QTest::newRow("map-array1") << raw("\xa1\x62oc\x81\0") << make_map({{"oc", make_list(0)}}); - QTest::newRow("map-array2") << raw("\xa1\x62oc\x84\0\1\2\3") << make_map({{"oc", make_list(0, 1, 2, 3)}}); - QTest::newRow("map-array3") << raw("\xa2\x62oc\x82\0\1\2\3") << make_map({{"oc", make_list(0, 1)}, {2, 3}}); - - // indeterminate length - QTest::newRow("_emptyarray") << raw("\x9f\xff") << QVariant::fromValue(IndeterminateLengthArray{}); - QTest::newRow("_emptymap") << raw("\xbf\xff") << make_ilmap({}); - - QTest::newRow("_array-0") << raw("\x9f\0\xff") << make_ilarray({0}); - QTest::newRow("_array-{0-0}") << raw("\x9f\0\0\xff") << make_ilarray({0, 0}); - QTest::newRow("_array-Hello") << raw("\x9f\x65Hello\xff") << make_ilarray({"Hello"}); - QTest::newRow("_array-array-0") << raw("\x9f\x81\0\xff") << make_ilarray({make_list(0)}); - QTest::newRow("_array-_array-0") << raw("\x9f\x9f\0\xff\xff") << make_ilarray({make_ilarray({0})}); - QTest::newRow("_array-_array-{0-0}") << raw("\x9f\x9f\0\0\xff\xff") << make_ilarray({make_ilarray({0, 0})}); - QTest::newRow("_array-_array-0-0") << raw("\x9f\x9f\0\xff\0\xff") << make_ilarray({make_ilarray({0}),0}); - QTest::newRow("_array-_array-Hello") << raw("\x9f\x9f\x65Hello\xff\xff") << make_ilarray({make_ilarray({"Hello"})}); - - QTest::newRow("_map-0:0") << raw("\xbf\0\0\xff") << make_ilmap({{0,0}}); - QTest::newRow("_map-0:0-1:1") << raw("\xbf\0\0\1\1\xff") << make_ilmap({{0,0}, {1,1}}); - QTest::newRow("_map-0:{map-0:0-1:1}") << raw("\xbf\0\xa2\0\0\1\1\xff") << make_ilmap({{0, make_map({{0,0}, {1,1}})}}); - QTest::newRow("_map-0:{_map-0:0-1:1}") << raw("\xbf\0\xbf\0\0\1\1\xff\xff") << make_ilmap({{0, make_ilmap({{0,0}, {1,1}})}}); - - QTest::newRow("_array-map1") << raw("\x9f\xa1\0\0\xff") << make_ilarray({make_map({{0,0}})}); - QTest::newRow("_array-_map1") << raw("\x9f\xbf\0\0\xff\xff") << make_ilarray({make_ilmap({{0,0}})}); - QTest::newRow("_array-map2") << raw("\x9f\xa1\0\0\xa1\1\1\xff") << make_ilarray({make_map({{0,0}}), make_map({{1,1}})}); - QTest::newRow("_array-_map2") << raw("\x9f\xbf\0\0\xff\xbf\1\1\xff\xff") << make_ilarray({make_ilmap({{0,0}}), make_ilmap({{1,1}})}); - - QTest::newRow("_map-array1") << raw("\xbf\x62oc\x81\0\xff") << make_ilmap({{"oc", make_list(0)}}); - QTest::newRow("_map-_array1") << raw("\xbf\x62oc\x9f\0\xff\xff") << make_ilmap({{"oc", make_ilarray({0})}}); - QTest::newRow("_map-array2") << raw("\xbf\x62oc\x84\0\1\2\3\xff") << make_ilmap({{"oc", make_list(0, 1, 2, 3)}}); - QTest::newRow("_map-_array2") << raw("\xbf\x62oc\x9f\0\1\2\3\xff\xff") << make_ilmap({{"oc", make_ilarray({0, 1, 2, 3})}}); - QTest::newRow("_map-array3") << raw("\xbf\x62oc\x82\0\1\2\3\xff") << make_ilmap({{"oc", make_list(0, 1)}, {2, 3}}); - QTest::newRow("_map-_array3") << raw("\xbf\x62oc\x9f\0\1\xff\2\3\xff") << make_ilmap({{"oc", make_ilarray({0, 1})}, {2, 3}}); - - // tagged - QTest::newRow("array-1(0)") << raw("\x81\xc1\0") << make_list(QVariant::fromValue(Tag{1, 0})); - QTest::newRow("array-1(map)") << raw("\x81\xc1\xa0") << make_list(QVariant::fromValue(Tag{1, make_map({})})); - QTest::newRow("map-1(2):3(4)") << raw("\xa1\xc1\2\xc3\4") << make_map({{QVariant::fromValue(Tag{1, 2}), QVariant::fromValue(Tag{3, 4})}}); -} - -static void addHalfFloat() -{ - QTest::addColumn("output"); - QTest::addColumn("rawInput"); - QTest::addColumn("floatInput"); - - QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; - QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; - - QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); - QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); - - QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); - QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); - - QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); - QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); - - QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; - QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; - - QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; - QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; - - QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); - QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); - - QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf(); - QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf(); - - QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN(); - QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN(); - QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN(); - QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN(); -} - void tst_Encoder::floatAsHalfFloat_data() { addHalfFloat(); diff --git a/tests/parser/data.cpp b/tests/parser/data.cpp new file mode 100644 index 00000000..f701a5a5 --- /dev/null +++ b/tests/parser/data.cpp @@ -0,0 +1,607 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 +#include +#include + +Q_DECLARE_METATYPE(CborError) + +template QByteArray raw(const char (&data)[N]) +{ + return QByteArray::fromRawData(data, N - 1); +} + +void addIntegers() +{ + QTest::addColumn("data"); + QTest::addColumn("expectedRaw"); + QTest::addColumn("expectedValue"); + QTest::addColumn("isNegative"); + QTest::addColumn("inInt64Range"); + + // unsigned integers + QTest::newRow("0") << raw("\x00") << Q_UINT64_C(0) << Q_INT64_C(0) << false << true; + QTest::newRow("1") << raw("\x01") << Q_UINT64_C(1) << Q_INT64_C(1) << false << true; + QTest::newRow("10") << raw("\x0a") << Q_UINT64_C(10) << Q_INT64_C(10) << false << true; + QTest::newRow("23") << raw("\x17") << Q_UINT64_C(23) << Q_INT64_C(23) << false << true; + QTest::newRow("24") << raw("\x18\x18") << Q_UINT64_C(24) << Q_INT64_C(24) << false << true; + QTest::newRow("UINT8_MAX") << raw("\x18\xff") << Q_UINT64_C(255) << Q_INT64_C(255) << false << true; + QTest::newRow("UINT8_MAX+1") << raw("\x19\x01\x00") << Q_UINT64_C(256) << Q_INT64_C(256) << false << true; + QTest::newRow("UINT16_MAX") << raw("\x19\xff\xff") << Q_UINT64_C(65535) << Q_INT64_C(65535) << false << true; + QTest::newRow("UINT16_MAX+1") << raw("\x1a\0\1\x00\x00") << Q_UINT64_C(65536) << Q_INT64_C(65536) << false << true; + QTest::newRow("UINT32_MAX") << raw("\x1a\xff\xff\xff\xff") << Q_UINT64_C(4294967295) << Q_INT64_C(4294967295) << false << true; + QTest::newRow("UINT32_MAX+1") << raw("\x1b\0\0\0\1\0\0\0\0") << Q_UINT64_C(4294967296) << Q_INT64_C(4294967296) << false << true; + QTest::newRow("INT64_MAX") << raw("\x1b" "\x7f\xff\xff\xff" "\xff\xff\xff\xff") + << quint64(std::numeric_limits::max()) + << std::numeric_limits::max() << false << true; + QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") + << std::numeric_limits::max() << qint64(-123456) << false << false; + + // negative integers + QTest::newRow("-1") << raw("\x20") << Q_UINT64_C(0) << Q_INT64_C(-1) << true << true; + QTest::newRow("-2") << raw("\x21") << Q_UINT64_C(1) << Q_INT64_C(-2) << true << true; + QTest::newRow("-24") << raw("\x37") << Q_UINT64_C(23) << Q_INT64_C(-24) << true << true; + QTest::newRow("-25") << raw("\x38\x18") << Q_UINT64_C(24) << Q_INT64_C(-25) << true << true; + QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << Q_UINT64_C(255) << Q_INT64_C(-256) << true << true; + QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << Q_UINT64_C(256) << Q_INT64_C(-257) << true << true; + QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << Q_UINT64_C(65535) << Q_INT64_C(-65536) << true << true; + QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << Q_UINT64_C(65536) << Q_INT64_C(-65537) << true << true; + QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << Q_UINT64_C(4294967295) << Q_INT64_C(-4294967296) << true << true; + QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << Q_UINT64_C(4294967296) << Q_INT64_C(-4294967297) << true << true; + QTest::newRow("INT64_MIN+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe") + << quint64(std::numeric_limits::max() - 1) + << (std::numeric_limits::min() + 1) + << true << true; + QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") + << quint64(std::numeric_limits::max()) + << std::numeric_limits::min() + << true << true; + QTest::newRow("INT64_MIN-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << Q_UINT64_C(9223372036854775808) << qint64(-123456) << true << false; + QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") + << (std::numeric_limits::max() - 1) << qint64(-123456) << true << false; + QTest::newRow("-UINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") + << std::numeric_limits::max() << qint64(-123456) << true << false; +} + +void addColumns() +{ + QTest::addColumn("data"); + QTest::addColumn("expected"); + QTest::addColumn("n"); // some aux integer, not added in all columns +} + +void addFixedData() +{ + // unsigned integers + QTest::newRow("0") << raw("\x00") << "0"; + QTest::newRow("1") << raw("\x01") << "1"; + QTest::newRow("10") << raw("\x0a") << "10"; + QTest::newRow("23") << raw("\x17") << "23"; + QTest::newRow("24") << raw("\x18\x18") << "24"; + QTest::newRow("UINT8_MAX") << raw("\x18\xff") << "255"; + QTest::newRow("UINT8_MAX+1") << raw("\x19\x01\x00") << "256"; + QTest::newRow("UINT16_MAX") << raw("\x19\xff\xff") << "65535"; + QTest::newRow("UINT16_MAX+1") << raw("\x1a\0\1\x00\x00") << "65536"; + QTest::newRow("UINT32_MAX") << raw("\x1a\xff\xff\xff\xff") << "4294967295"; + QTest::newRow("UINT32_MAX+1") << raw("\x1b\0\0\0\1\0\0\0\0") << "4294967296"; + QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") + << QString::number(std::numeric_limits::max()); + + // negative integers + QTest::newRow("-1") << raw("\x20") << "-1"; + QTest::newRow("-2") << raw("\x21") << "-2"; + QTest::newRow("-24") << raw("\x37") << "-24"; + QTest::newRow("-25") << raw("\x38\x18") << "-25"; + QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << "-256"; + QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << "-257"; + QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << "-65536"; + QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << "-65537"; + QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << "-4294967296"; + QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << "-4294967297"; + QTest::newRow("INT64_MIN+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe") + << QString::number(std::numeric_limits::min() + 1); + QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") + << QString::number(std::numeric_limits::min()); + QTest::newRow("INT64_MIN-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << "-9223372036854775809"; + QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") + << '-' + QString::number(std::numeric_limits::max()); + QTest::newRow("-UINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") + << "-18446744073709551616"; + + // overlongs + QTest::newRow("0*1") << raw("\x18\x00") << "0_0"; + QTest::newRow("0*2") << raw("\x19\x00\x00") << "0_1"; + QTest::newRow("0*4") << raw("\x1a\0\0\0\0") << "0_2"; + QTest::newRow("0*8") << raw("\x1b\0\0\0\0\0\0\0\0") << "0_3"; + QTest::newRow("-1*1") << raw("\x38\x00") << "-1_0"; + QTest::newRow("-1*2") << raw("\x39\x00\x00") << "-1_1"; + QTest::newRow("-1*4") << raw("\x3a\0\0\0\0") << "-1_2"; + QTest::newRow("-1*8") << raw("\x3b\0\0\0\0\0\0\0\0") << "-1_3"; + + QTest::newRow("simple0") << raw("\xe0") << "simple(0)"; + QTest::newRow("simple19") << raw("\xf3") << "simple(19)"; + QTest::newRow("false") << raw("\xf4") << "false"; + QTest::newRow("true") << raw("\xf5") << "true"; + QTest::newRow("null") << raw("\xf6") << "null"; + QTest::newRow("undefined") << raw("\xf7") << "undefined"; + QTest::newRow("simple32") << raw("\xf8\x20") << "simple(32)"; + QTest::newRow("simple255") << raw("\xf8\xff") << "simple(255)"; + + // floating point + + QTest::newRow("0.f16") << raw("\xf9\0\0") << "0.f16"; + QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "0.f"; + QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "0."; + QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "-1.f16"; + QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "-1.f"; + QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "-1."; + QTest::newRow("65504.f16") << raw("\xf9\x7b\xff") << "65504.f16"; + QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "16777215.f"; + QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "16777215."; + QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << "-16777215.f"; + QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "-16777215."; + + QTest::newRow("0.5f16") << raw("\xf9\x38\0") << "0.5f16"; + QTest::newRow("0.5f") << raw("\xfa\x3f\0\0\0") << "0.5f"; + QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << "0.5"; + QTest::newRow("2.f16^11-1") << raw("\xf9\x67\xff") << "2047.f16"; + QTest::newRow("2.f^24-1") << raw("\xfa\x4b\x7f\xff\xff") << "16777215.f"; + QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "9007199254740991."; + QTest::newRow("2.f^64-epsilon") << raw("\xfa\x5f\x7f\xff\xff") << "18446742974197923840.f"; + QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "18446744073709549568."; + QTest::newRow("2.f^64") << raw("\xfa\x5f\x80\0\0") << "1.8446744073709552e+19f"; + QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << "1.8446744073709552e+19"; + + QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "nan"; + QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "nan"; + QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "nan"; + QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "-inf"; + QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "-inf"; + QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "-inf"; + QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "inf"; + QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "inf"; + QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "inf"; + +} + +void addNonChunkedStringsData() +{ + // byte strings + QTest::newRow("emptybytestring") << raw("\x40") << "h''"; + QTest::newRow("bytestring1") << raw("\x41 ") << "h'20'"; + QTest::newRow("bytestring1-nul") << raw("\x41\0") << "h'00'"; + QTest::newRow("bytestring5") << raw("\x45Hello") << "h'48656c6c6f'"; + QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234") + << "h'313233343536373839303132333435363738393031323334'"; + QTest::newRow("bytestring256") << raw("\x59\1\0") + QByteArray(256, '3') + << "h'" + QString(256 * 2, '3') + '\''; + + // text strings + QTest::newRow("emptytextstring") << raw("\x60") << "\"\""; + QTest::newRow("textstring1") << raw("\x61 ") << "\" \""; + QTest::newRow("textstring1-nul") << raw("\x61\0") << "\"\\u0000\""; + QTest::newRow("textstring5") << raw("\x65Hello") << "\"Hello\""; + QTest::newRow("textstring24") << raw("\x78\x18""123456789012345678901234") + << "\"123456789012345678901234\""; + QTest::newRow("textstring256") << raw("\x79\1\0") + QByteArray(256, '3') + << '"' + QString(256, '3') + '"'; + + // some strings with UTF-8 content + // we had a bug in the pretty dumper - see issue #54 + QTest::newRow("textstringutf8-2char") << raw("\x62\xc2\xa0") << "\"\\u00A0\""; + QTest::newRow("textstringutf8-2char2") << raw("\x64\xc2\xa0\xc2\xa9") << "\"\\u00A0\\u00A9\""; + QTest::newRow("textstringutf8-3char") << raw("\x63\xe2\x88\x80") << "\"\\u2200\""; + QTest::newRow("textstringutf8-4char") << raw("\x64\xf0\x90\x88\x83") << "\"\\uD800\\uDE03\""; + + // strings with overlong length + QTest::newRow("emptybytestring*1") << raw("\x58\x00") << "h''_0"; + QTest::newRow("emptytextstring*1") << raw("\x78\x00") << "\"\"_0"; + QTest::newRow("emptybytestring*2") << raw("\x59\x00\x00") << "h''_1"; + QTest::newRow("emptytextstring*2") << raw("\x79\x00\x00") << "\"\"_1"; + QTest::newRow("emptybytestring*4") << raw("\x5a\0\0\0\0") << "h''_2"; + QTest::newRow("emptytextstring*4") << raw("\x7a\0\0\0\0") << "\"\"_2"; + QTest::newRow("emptybytestring*8") << raw("\x5b\0\0\0\0\0\0\0\0") << "h''_3"; + QTest::newRow("emptytextstring*8") << raw("\x7b\0\0\0\0\0\0\0\0") << "\"\"_3"; + QTest::newRow("bytestring5*1") << raw("\x58\x05Hello") << "h'48656c6c6f'_0"; + QTest::newRow("textstring5*1") << raw("\x78\x05Hello") << "\"Hello\"_0"; + QTest::newRow("bytestring5*2") << raw("\x59\0\5Hello") << "h'48656c6c6f'_1"; + QTest::newRow("textstring5*2") << raw("\x79\0\x05Hello") << "\"Hello\"_1"; + QTest::newRow("bytestring5*4") << raw("\x5a\0\0\0\5Hello") << "h'48656c6c6f'_2"; + QTest::newRow("textstring5*4") << raw("\x7a\0\0\0\x05Hello") << "\"Hello\"_2"; + QTest::newRow("bytestring5*8") << raw("\x5b\0\0\0\0\0\0\0\5Hello") << "h'48656c6c6f'_3"; + QTest::newRow("textstring5*8") << raw("\x7b\0\0\0\0\0\0\0\x05Hello") << "\"Hello\"_3"; + +} + +void addStringsData() +{ + addNonChunkedStringsData(); + + // strings with undefined length + QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "(_ )"; + QTest::newRow("_emptytextstring") << raw("\x7f\xff") << "(_ )"; + QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "(_ h'')"; + QTest::newRow("_emptytextstring2") << raw("\x7f\x60\xff") << "(_ \"\")"; + QTest::newRow("_emptybytestring2*1") << raw("\x5f\x58\x00\xff") << "(_ h''_0)"; + QTest::newRow("_emptytextstring2*1") << raw("\x7f\x78\x00\xff") << "(_ \"\"_0)"; + QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "(_ h'', h'')"; + QTest::newRow("_emptytextstring3") << raw("\x7f\x60\x60\xff") << "(_ \"\", \"\")"; + QTest::newRow("_emptybytestring3*2") << raw("\x5f\x59\x00\x00\x40\xff") << "(_ h''_1, h'')"; + QTest::newRow("_emptytextstring3*2") << raw("\x7f\x79\x00\x00\x60\xff") << "(_ \"\"_1, \"\")"; + QTest::newRow("_bytestring5x2") << raw("\x5f\x43Hel\x42lo\xff") << "(_ h'48656c', h'6c6f')"; + QTest::newRow("_textstring5x2") << raw("\x7f\x63Hel\x62lo\xff") << "(_ \"Hel\", \"lo\")"; + QTest::newRow("_bytestring5x2*8*4") << raw("\x5f\x5b\0\0\0\0\0\0\0\3Hel\x5a\0\0\0\2lo\xff") << "(_ h'48656c'_3, h'6c6f'_2)"; + QTest::newRow("_textstring5x2*8*4") << raw("\x7f\x7b\0\0\0\0\0\0\0\3Hel\x7a\0\0\0\2lo\xff") << "(_ \"Hel\"_3, \"lo\"_2)"; + QTest::newRow("_bytestring5x5") << raw("\x5f\x41H\x41""e\x41l\x41l\x41o\xff") << "(_ h'48', h'65', h'6c', h'6c', h'6f')"; + QTest::newRow("_textstring5x5") << raw("\x7f\x61H\x61""e\x61l\x61l\x61o\xff") << "(_ \"H\", \"e\", \"l\", \"l\", \"o\")"; + QTest::newRow("_bytestring5x6") << raw("\x5f\x41H\x41""e\x40\x41l\x41l\x41o\xff") << "(_ h'48', h'65', h'', h'6c', h'6c', h'6f')"; + QTest::newRow("_textstring5x6") << raw("\x7f\x61H\x61""e\x61l\x60\x61l\x61o\xff") << "(_ \"H\", \"e\", \"l\", \"\", \"l\", \"o\")"; +} + +void addTagsData() +{ + // since parseOne() works recursively for tags, we can't test lone tags + QTest::newRow("tag0") << raw("\xc0\x00") << "0(0)"; + QTest::newRow("tag1") << raw("\xc1\x00") << "1(0)"; + QTest::newRow("tag24") << raw("\xd8\x18\x00") << "24(0)"; + QTest::newRow("tag255") << raw("\xd8\xff\x00") << "255(0)"; + QTest::newRow("tag256") << raw("\xd9\1\0\x00") << "256(0)"; + QTest::newRow("tag65535") << raw("\xd9\xff\xff\x00") << "65535(0)"; + QTest::newRow("tag65536") << raw("\xda\0\1\0\0\x00") << "65536(0)"; + QTest::newRow("tagUINT32_MAX-1") << raw("\xda\xff\xff\xff\xff\x00") << "4294967295(0)"; + QTest::newRow("tagUINT32_MAX") << raw("\xdb\0\0\0\1\0\0\0\0\x00") << "4294967296(0)"; + QTest::newRow("tagUINT64_MAX") << raw("\xdb" "\xff\xff\xff\xff" "\xff\xff\xff\xff" "\x00") + << QString::number(std::numeric_limits::max()) + "(0)"; + + // overlong tags + QTest::newRow("tag0*1") << raw("\xd8\0\x00") << "0_0(0)"; + QTest::newRow("tag0*2") << raw("\xd9\0\0\x00") << "0_1(0)"; + QTest::newRow("tag0*4") << raw("\xda\0\0\0\0\x00") << "0_2(0)"; + QTest::newRow("tag0*8") << raw("\xdb\0\0\0\0\0\0\0\0\x00") << "0_3(0)"; + + // tag other things + QTest::newRow("unixtime") << raw("\xc1\x1a\x55\x4b\xbf\xd3") << "1(1431027667)"; + QTest::newRow("rfc3339date") << raw("\xc0\x78\x19" "2015-05-07 12:41:07-07:00") + << "0(\"2015-05-07 12:41:07-07:00\")"; + QTest::newRow("tag6+false") << raw("\xc6\xf4") << "6(false)"; + QTest::newRow("tag25+true") << raw("\xd8\x19\xf5") << "25(true)"; + QTest::newRow("tag256+null") << raw("\xd9\1\0\xf6") << "256(null)"; + QTest::newRow("tag65536+simple32") << raw("\xda\0\1\0\0\xf8\x20") << "65536(simple(32))"; + QTest::newRow("float+unixtime") << raw("\xc1\xfa\x4e\xaa\x97\x80") << "1(1431027712.f)"; + QTest::newRow("double+unixtime") << raw("\xc1\xfb" "\x41\xd5\x52\xef" "\xf4\xc7\xce\xfe") + << "1(1431027667.1220088)"; +} + +void addEmptyContainersData() +{ + QTest::newRow("emptyarray") << raw("\x80") << "[]" << 0; + QTest::newRow("emptymap") << raw("\xa0") << "{}" << 0; + QTest::newRow("_emptyarray") << raw("\x9f\xff") << "[_ ]" << -1; + QTest::newRow("_emptymap") << raw("\xbf\xff") << "{_ }" << -1; +} + +void addMapMixedData() +{ + QTest::newRow("map-0-24") << raw("\xa1\0\x18\x18") << "{0: 24}" << 1; + QTest::newRow("map-0*1-24") << raw("\xa1\x18\0\x18\x18") << "{0_0: 24}" << 1; + QTest::newRow("map-0*1-24*2") << raw("\xa1\x18\0\x19\0\x18") << "{0_0: 24_1}" << 1; + QTest::newRow("map-0*4-24*2") << raw("\xa1\x1a\0\0\0\0\x19\0\x18") << "{0_2: 24_1}" << 1; + QTest::newRow("map-24-0") << raw("\xa1\x18\x18\0") << "{24: 0}" << 1; + QTest::newRow("map-24-0*1") << raw("\xa1\x18\x18\x18\0") << "{24: 0_0}" << 1; + QTest::newRow("map-255-65535") << raw("\xa1\x18\xff\x19\xff\xff") << "{255: 65535}" << 1; + + QTest::newRow("_map-0-24") << raw("\xbf\0\x18\x18\xff") << "{_ 0: 24}" << 1; + QTest::newRow("_map-0*1-24") << raw("\xbf\x18\0\x18\x18\xff") << "{_ 0_0: 24}" << 1; + QTest::newRow("_map-0*1-24*2") << raw("\xbf\x18\0\x19\0\x18\xff") << "{_ 0_0: 24_1}" << 1; + QTest::newRow("_map-0*4-24*2") << raw("\xbf\x1a\0\0\0\0\x19\0\x18\xff") << "{_ 0_2: 24_1}" << 1; + QTest::newRow("_map-24-0") << raw("\xbf\x18\x18\0\xff") << "{_ 24: 0}" << 1; + QTest::newRow("_map-24-0*1") << raw("\xbf\x18\x18\x18\0\xff") << "{_ 24: 0_0}" << 1; + QTest::newRow("_map-255-65535") << raw("\xbf\x18\xff\x19\xff\xff\xff") << "{_ 255: 65535}" << 1; +} + +void addChunkedStringData() +{ + QTest::addColumn("data"); + QTest::addColumn("concatenated"); + QTest::addColumn("chunks"); + + // non-chunked: + QTest::newRow("emptybytestring") << raw("\x40") << "h''" << QStringList{"h''"}; + QTest::newRow("bytestring1") << raw("\x41 ") << "h'20'" << QStringList{"h'20'"}; + QTest::newRow("emptytextstring") << raw("\x60") << "\"\"" << QStringList{"\"\""}; + QTest::newRow("textstring1") << raw("\x61 ") << "\" \"" << QStringList{"\" \""}; + + // empty chunked: + QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "h''" << QStringList{}; + QTest::newRow("_emptytextstring") << raw("\x7f\xff") << "\"\"" << QStringList{}; + QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "h''" << QStringList{"h''"}; + QTest::newRow("_emptytextstring2") << raw("\x7f\x60\xff") << "\"\"" << QStringList{"\"\""}; + QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "h''" << QStringList{"h''", "h''"}; + QTest::newRow("_emptytextstring3") << raw("\x7f\x60\x60\xff") << "\"\"" << QStringList{"\"\"", "\"\""}; + + // regular chunks + QTest::newRow("_bytestring1") << raw("\x5f\x41 \xff") << "h'20'" << QStringList{"h'20'"}; + QTest::newRow("_bytestring2") << raw("\x5f\x41 \x41z\xff") << "h'207a'" << QStringList{"h'20'", "h'7a'"}; + QTest::newRow("_bytestring3") << raw("\x5f\x41 \x58\x18""123456789012345678901234\x41z\xff") + << "h'203132333435363738393031323334353637383930313233347a'" + << QStringList{"h'20'", "h'313233343536373839303132333435363738393031323334'", "h'7a'"}; + + QTest::newRow("_textstring1") << raw("\x7f\x61 \xff") << "\" \"" << QStringList{"\" \""}; + QTest::newRow("_textstring2") << raw("\x7f\x61 \x61z\xff") << "\" z\"" << QStringList{"\" \"", "\"z\""}; + QTest::newRow("_textstring3") << raw("\x7f\x61 \x78\x18""123456789012345678901234\x61z\xff") + << "\" 123456789012345678901234z\"" + << QStringList{"\" \"", "\"123456789012345678901234\"", "\"z\""}; +} + +void addValidationColumns() +{ + QTest::addColumn("data"); + QTest::addColumn("flags"); // future + QTest::addColumn("expectedError"); +} + +void addValidationData(size_t minInvalid = ~size_t(0)) +{ + // illegal numbers are future extension points + QTest::newRow("illegal-number-in-unsigned-1") << raw("\x81\x1c") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-unsigned-2") << raw("\x81\x1d") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-unsigned-3") << raw("\x81\x1e") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-unsigned-4") << raw("\x81\x1f") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-negative-1") << raw("\x81\x3c") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-negative-2") << raw("\x81\x3d") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-negative-3") << raw("\x81\x3e") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-negative-4") << raw("\x81\x3f") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-bytearray-length-1") << raw("\x81\x5c") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-bytearray-length-2") << raw("\x81\x5d") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-bytearray-length-3") << raw("\x81\x5e") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-string-length-1") << raw("\x81\x7c") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-string-length-2") << raw("\x81\x7d") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-string-length-3") << raw("\x81\x7e") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-array-length-1") << raw("\x81\x9c") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-array-length-2") << raw("\x81\x9d") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-array-length-3") << raw("\x81\x9e") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-map-length-1") << raw("\x81\xbc") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-map-length-2") << raw("\x81\xbd") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-map-length-3") << raw("\x81\xbe") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-tag-1") << raw("\x81\xdc") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-tag-2") << raw("\x81\xdd") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-tag-3") << raw("\x81\xde") << 0 << CborErrorIllegalNumber; + QTest::newRow("illegal-number-in-tag-4") << raw("\x81\xdf") << 0 << CborErrorIllegalNumber; + + QTest::newRow("unsigned-too-short-1-0") << raw("\x81\x18") << 0 << CborErrorUnexpectedEOF; // requires 1 byte, 0 given + QTest::newRow("unsigned-too-short-2-0") << raw("\x81\x19") << 0 << CborErrorUnexpectedEOF; // requires 2 bytes, 0 given + QTest::newRow("unsigned-too-short-2-1") << raw("\x81\x19\x01") << 0 << CborErrorUnexpectedEOF; // etc + QTest::newRow("unsigned-too-short-4-0") << raw("\x81\x1a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("unsigned-too-short-4-3") << raw("\x81\x1a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("unsigned-too-short-8-0") << raw("\x81\x1b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("unsigned-too-short-8-7") << raw("\x81\x1b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-1-0") << raw("\x81\x38") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-2-0") << raw("\x81\x39") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-2-1") << raw("\x81\x39\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-4-0") << raw("\x81\x3a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-4-3") << raw("\x81\x3a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-8-0") << raw("\x81\x3b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("negative-length-too-short-8-7") << raw("\x81\x3b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-1-0") << raw("\x81\x58") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-2-0") << raw("\x81\x59") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-2-1") << raw("\x81\x59\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-4-0") << raw("\x81\x5a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-4-3") << raw("\x81\x5a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-8-0") << raw("\x81\x5b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-length-too-short-8-7") << raw("\x81\x5b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-1-0") << raw("\x81\x78") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-2-0") << raw("\x81\x79") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-2-1") << raw("\x81\x79\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-4-0") << raw("\x81\x7a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-4-3") << raw("\x81\x7a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-8-0") << raw("\x81\x7b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-length-too-short-8-7") << raw("\x81\x7b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-1-0") << raw("\x81\x5f\x58") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-2-0") << raw("\x81\x5f\x59") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-2-1") << raw("\x81\x5f\x59\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-4-0") << raw("\x81\x5f\x5a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-4-3") << raw("\x81\x5f\x5a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-8-0") << raw("\x81\x5f\x5b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-length-too-short-8-7") << raw("\x81\x5f\x5b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-1-0") << raw("\x81\x7f\x78") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-2-0") << raw("\x81\x7f\x79") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-2-1") << raw("\x81\x7f\x79\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-4-0") << raw("\x81\x7f\x7a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-4-3") << raw("\x81\x7f\x7a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-8-0") << raw("\x81\x7f\x7b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-length-too-short-8-7") << raw("\x81\x7f\x7b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-1-0") << raw("\x81\x5f\x40\x58") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-2-0") << raw("\x81\x5f\x40\x59") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-2-1") << raw("\x81\x5f\x40\x59\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-4-0") << raw("\x81\x5f\x40\x5a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-4-3") << raw("\x81\x5f\x40\x5a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-8-0") << raw("\x81\x5f\x40\x5b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-2-length-too-short-8-7") << raw("\x81\x5f\x40\x5b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-1-0") << raw("\x81\x7f\x60\x78") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-2-0") << raw("\x81\x7f\x60\x79") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-2-1") << raw("\x81\x7f\x60\x79\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-4-0") << raw("\x81\x7f\x60\x7a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-4-3") << raw("\x81\x7f\x60\x7a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-8-0") << raw("\x81\x7f\x60\x7b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-2-length-too-short-8-7") << raw("\x81\x7f\x60\x7b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-1-0") << raw("\x81\x98") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-2-0") << raw("\x81\x99") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-2-1") << raw("\x81\x99\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-4-0") << raw("\x81\x9a") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-4-3") << raw("\x81\x9a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-8-0") << raw("\x81\x9b") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-length-too-short-8-7") << raw("\x81\x9b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-1-0") << raw("\x81\xb8") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-2-0") << raw("\x81\xb9") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-2-1") << raw("\x81\xb9\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-4-0") << raw("\x81\xba") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-4-3") << raw("\x81\xba\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-8-0") << raw("\x81\xbb") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-length-too-short-8-7") << raw("\x81\xbb\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-1-0") << raw("\x81\xd8") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-2-0") << raw("\x81\xd9") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-2-1") << raw("\x81\xd9\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-4-0") << raw("\x81\xda") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-4-3") << raw("\x81\xda\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-8-0") << raw("\x81\xdb") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("tag-too-short-8-7") << raw("\x81\xdb\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("fp16-too-short1") << raw("\x81\xf9") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("fp16-too-short2") << raw("\x81\xf9\x00") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("float-too-short1") << raw("\x81\xfa") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("float-too-short2") << raw("\x81\xfa\0\0\0") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("double-too-short1") << raw("\x81\xfb") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("double-too-short2") << raw("\x81\xfb\0\0\0\0\0\0\0") << 0 << CborErrorUnexpectedEOF; + + QTest::newRow("bytearray-too-short1") << raw("\x81\x42z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-too-short2") << raw("\x81\x58\x02z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-too-short3") << raw("\x81\x5a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-too-short4") << raw("\x81\x5b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-too-short1") << raw("\x81\x62z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-too-short2") << raw("\x81\x78\x02z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-too-short3") << raw("\x81\x7a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-too-short4") << raw("\x81\x7b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short1") << raw("\x81\x5f\x42z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short2") << raw("\x81\x5f\x58\x02z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short3") << raw("\x81\x5f\x5a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short4") << raw("\x81\x5f\x5b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short1") << raw("\x81\x7f\x62z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short2") << raw("\x81\x7f\x78\x02z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short3") << raw("\x81\x7f\x7a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short4") << raw("\x81\x7f\x7b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short1x2") << raw("\x81\x5f\x40\x42z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short2x2") << raw("\x81\x5f\x40\x58\x02z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short3x2") << raw("\x81\x5f\x40\x5a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-chunked-too-short4x2") << raw("\x81\x5f\x40\x5b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short1x2") << raw("\x81\x7f\x60\x62z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short2x2") << raw("\x81\x7f\x60\x78\x02z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short3x2") << raw("\x81\x7f\x60\x7a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-chunked-too-short4x2") << raw("\x81\x7f\x60\x7b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; + + QTest::newRow("bytearray-no-break1") << raw("\x81\x5f") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("bytearray-no-break2") << raw("\x81\x5f\x40") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-no-break1") << raw("\x81\x7f") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("string-no-break2") << raw("\x81\x7f\x60") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-no-break1") << raw("\x81\x9f") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("array-no-break2") << raw("\x81\x9f\0") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-no-break1") << raw("\x81\xbf") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-no-break2") << raw("\x81\xbf\0\0") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("map-break-after-key") << raw("\x81\xbf\0\xff") << 0 << CborErrorUnexpectedBreak; + QTest::newRow("map-break-after-second-key") << raw("\x81\xbf\x64xyzw\x04\x00\xff") << 0 << CborErrorUnexpectedBreak; + QTest::newRow("map-break-after-value-tag") << raw("\x81\xbf\0\xc0\xff") << 0 << CborErrorUnexpectedBreak; + QTest::newRow("map-break-after-value-tag2") << raw("\x81\xbf\0\xd8\x20\xff") << 0 << CborErrorUnexpectedBreak; + + // check for pointer additions wrapping over the limit of the address space + auto wraparoundError = [minInvalid](uint64_t encodedSize) { + if (encodedSize > minInvalid) + return CborErrorDataTooLarge; + return CborErrorUnexpectedEOF; + }; + constexpr uint64_t FourGB = UINT32_MAX + UINT64_C(1); + // on 32-bit systems, this is a -1 + QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + // on 32-bit systems, a 4GB addition could be dropped + QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + // on 64-bit systems, this could be a -1 + QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + + // ditto on chunks + QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX); + // on 32-bit systems, a 4GB addition could be dropped + QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB); + // on 64-bit systems, this could be a -1 + QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 + << wraparoundError(UINT64_MAX); + + QTest::newRow("eof-after-array") << raw("\x81") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-array-element") << raw("\x81\x82\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-object") << raw("\x81\xa1") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-object2") << raw("\x81\xb8\x20") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-object-key") << raw("\x81\xa1\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-object-value") << raw("\x81\xa2\x01\x01") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-tag") << raw("\x81\xc0") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("eof-after-tag2") << raw("\x81\xd8\x20") << 0 << CborErrorUnexpectedEOF; + + // major type 7 has future types + QTest::newRow("future-type-28") << raw("\x81\xfc") << 0 << CborErrorUnknownType; + QTest::newRow("future-type-29") << raw("\x81\xfd") << 0 << CborErrorUnknownType; + QTest::newRow("future-type-30") << raw("\x81\xfe") << 0 << CborErrorUnknownType; + QTest::newRow("unexpected-break") << raw("\x81\xff") << 0 << CborErrorUnexpectedBreak; + QTest::newRow("illegal-simple-0") << raw("\x81\xf8\0") << 0 << CborErrorIllegalSimpleType; + QTest::newRow("illegal-simple-31") << raw("\x81\xf8\x1f") << 0 << CborErrorIllegalSimpleType; + + // not only too big (UINT_MAX or UINT_MAX+1 in size), but also incomplete + if (sizeof(size_t) < sizeof(uint64_t)) { + QTest::newRow("bytearray-too-big1") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; + QTest::newRow("string-too-big1") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; + } + QTest::newRow("array-too-big1") << raw("\x81\x9a\xff\xff\xff\xff\0\0\0\0") << 0 << CborErrorDataTooLarge; + QTest::newRow("array-too-big2") << raw("\x81\x9b\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; + QTest::newRow("object-too-big1") << raw("\x81\xba\xff\xff\xff\xff\0\0\0\0") << 0 << CborErrorDataTooLarge; + QTest::newRow("object-too-big2") << raw("\x81\xbb\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; + + QTest::newRow("no-break-for-array0") << raw("\x81\x9f") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("no-break-for-array1") << raw("\x81\x9f\x01") << 0 << CborErrorUnexpectedEOF; + + QTest::newRow("no-break-string0") << raw("\x81\x7f") << 0 << CborErrorUnexpectedEOF; + QTest::newRow("no-break-string1") << raw("\x81\x7f\x61Z") << 0 << CborErrorUnexpectedEOF; + + QTest::newRow("nested-indefinite-length-bytearrays") << raw("\x81\x5f\x5f\xff\xff") << 0 << CborErrorIllegalNumber; + QTest::newRow("nested-indefinite-length-strings") << raw("\x81\x7f\x7f\xff\xff") << 0 << CborErrorIllegalNumber; + + QTest::newRow("string-chunk-unsigned") << raw("\x81\x7f\0\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-negative") << raw("\x81\x7f\x20\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-bytearray") << raw("\x81\x7f\x40\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-array") << raw("\x81\x7f\x80\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-map") << raw("\x81\x7f\xa0\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-tag") << raw("\x81\x7f\xc0\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-tagged-string") << raw("\x81\x7f\xc0\x60\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-simple0") << raw("\x81\x7f\xe0\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-false") << raw("\x81\x7f\xf4\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-true") << raw("\x81\x7f\xf5\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-null") << raw("\x81\x7f\xf6\xff") << 0 << CborErrorIllegalType; + QTest::newRow("string-chunk-undefined") << raw("\x81\x7f\xf7\xff") << 0 << CborErrorIllegalType; + + QTest::newRow("bytearray-chunk-string") << raw("\x81\x5f\x60\xff") << 0 << CborErrorIllegalType; + QTest::newRow("bytearray-chunk-tagged-bytearray") << raw("\x81\x7f\xc0\x40\xff") << 0 << CborErrorIllegalType; + + // RFC 7049 Section 2.2.2 "Indefinite-Length Byte Strings and Text Strings" says + // Text strings with indefinite lengths act the same as byte strings + // with indefinite lengths, except that all their chunks MUST be + // definite-length text strings. Note that this implies that the bytes + // of a single UTF-8 character cannot be spread between chunks: a new + // chunk can only be started at a character boundary. + // This test technically tests the dumper, not the parser. + QTest::newRow("string-utf8-chunk-split") << raw("\x81\x7f\x61\xc2\x61\xa0\xff") << 0 << CborErrorInvalidUtf8TextString; +} diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index 29e6c046..3a0d766c 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -38,7 +38,6 @@ # include #endif -Q_DECLARE_METATYPE(CborError) namespace QTest { template<> char *toString(const CborError &err) @@ -260,11 +259,6 @@ CborError parseOneChunk(CborValue *it, QString *parsed) return err; } -template QByteArray raw(const char (&data)[N]) -{ - return QByteArray::fromRawData(data, N - 1); -} - void tst_Parser::initParserEmpty() { CborParser parser; @@ -273,13 +267,6 @@ void tst_Parser::initParserEmpty() QCOMPARE(err, CborErrorUnexpectedEOF); } -void addColumns() -{ - QTest::addColumn("data"); - QTest::addColumn("expected"); - QTest::addColumn("n"); // some aux integer, not added in all columns -} - bool compareFailed = true; void compareOne_real(const QByteArray &data, const QString &expected, int line, int n = -1) { @@ -339,151 +326,7 @@ void compareOne_real(const QByteArray &data, const QString &expected, int line, #define compareOne(data, expected) compareOne_real(data, expected, __LINE__) #define compareOneSize(n, data, expected) compareOne_real(data, expected, __LINE__, n) -void addFixedData() -{ - // unsigned integers - QTest::newRow("0") << raw("\x00") << "0"; - QTest::newRow("1") << raw("\x01") << "1"; - QTest::newRow("10") << raw("\x0a") << "10"; - QTest::newRow("23") << raw("\x17") << "23"; - QTest::newRow("24") << raw("\x18\x18") << "24"; - QTest::newRow("UINT8_MAX") << raw("\x18\xff") << "255"; - QTest::newRow("UINT8_MAX+1") << raw("\x19\x01\x00") << "256"; - QTest::newRow("UINT16_MAX") << raw("\x19\xff\xff") << "65535"; - QTest::newRow("UINT16_MAX+1") << raw("\x1a\0\1\x00\x00") << "65536"; - QTest::newRow("UINT32_MAX") << raw("\x1a\xff\xff\xff\xff") << "4294967295"; - QTest::newRow("UINT32_MAX+1") << raw("\x1b\0\0\0\1\0\0\0\0") << "4294967296"; - QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") - << QString::number(std::numeric_limits::max()); - - // negative integers - QTest::newRow("-1") << raw("\x20") << "-1"; - QTest::newRow("-2") << raw("\x21") << "-2"; - QTest::newRow("-24") << raw("\x37") << "-24"; - QTest::newRow("-25") << raw("\x38\x18") << "-25"; - QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << "-256"; - QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << "-257"; - QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << "-65536"; - QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << "-65537"; - QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << "-4294967296"; - QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << "-4294967297"; - QTest::newRow("INT64_MIN+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe") - << QString::number(std::numeric_limits::min() + 1); - QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") - << QString::number(std::numeric_limits::min()); - QTest::newRow("INT64_MIN-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << "-9223372036854775809"; - QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") - << '-' + QString::number(std::numeric_limits::max()); - QTest::newRow("-UINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") - << "-18446744073709551616"; - - // overlongs - QTest::newRow("0*1") << raw("\x18\x00") << "0_0"; - QTest::newRow("0*2") << raw("\x19\x00\x00") << "0_1"; - QTest::newRow("0*4") << raw("\x1a\0\0\0\0") << "0_2"; - QTest::newRow("0*8") << raw("\x1b\0\0\0\0\0\0\0\0") << "0_3"; - QTest::newRow("-1*1") << raw("\x38\x00") << "-1_0"; - QTest::newRow("-1*2") << raw("\x39\x00\x00") << "-1_1"; - QTest::newRow("-1*4") << raw("\x3a\0\0\0\0") << "-1_2"; - QTest::newRow("-1*8") << raw("\x3b\0\0\0\0\0\0\0\0") << "-1_3"; - - QTest::newRow("simple0") << raw("\xe0") << "simple(0)"; - QTest::newRow("simple19") << raw("\xf3") << "simple(19)"; - QTest::newRow("false") << raw("\xf4") << "false"; - QTest::newRow("true") << raw("\xf5") << "true"; - QTest::newRow("null") << raw("\xf6") << "null"; - QTest::newRow("undefined") << raw("\xf7") << "undefined"; - QTest::newRow("simple32") << raw("\xf8\x20") << "simple(32)"; - QTest::newRow("simple255") << raw("\xf8\xff") << "simple(255)"; - - // floating point - - QTest::newRow("0.f16") << raw("\xf9\0\0") << "0.f16"; - QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "0.f"; - QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "0."; - QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "-1.f16"; - QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "-1.f"; - QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "-1."; - QTest::newRow("65504.f16") << raw("\xf9\x7b\xff") << "65504.f16"; - QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "16777215.f"; - QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "16777215."; - QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << "-16777215.f"; - QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "-16777215."; - - QTest::newRow("0.5f16") << raw("\xf9\x38\0") << "0.5f16"; - QTest::newRow("0.5f") << raw("\xfa\x3f\0\0\0") << "0.5f"; - QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << "0.5"; - QTest::newRow("2.f16^11-1") << raw("\xf9\x67\xff") << "2047.f16"; - QTest::newRow("2.f^24-1") << raw("\xfa\x4b\x7f\xff\xff") << "16777215.f"; - QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "9007199254740991."; - QTest::newRow("2.f^64-epsilon") << raw("\xfa\x5f\x7f\xff\xff") << "18446742974197923840.f"; - QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "18446744073709549568."; - QTest::newRow("2.f^64") << raw("\xfa\x5f\x80\0\0") << "1.8446744073709552e+19f"; - QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << "1.8446744073709552e+19"; - - QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "nan"; - QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "nan"; - QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "nan"; - QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "-inf"; - QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "-inf"; - QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "-inf"; - QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "inf"; - QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "inf"; - QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "inf"; - -} - -static void addIntegers() -{ - QTest::addColumn("data"); - QTest::addColumn("expectedRaw"); - QTest::addColumn("expectedValue"); - QTest::addColumn("isNegative"); - QTest::addColumn("inInt64Range"); - - // unsigned integers - QTest::newRow("0") << raw("\x00") << Q_UINT64_C(0) << Q_INT64_C(0) << false << true; - QTest::newRow("1") << raw("\x01") << Q_UINT64_C(1) << Q_INT64_C(1) << false << true; - QTest::newRow("10") << raw("\x0a") << Q_UINT64_C(10) << Q_INT64_C(10) << false << true; - QTest::newRow("23") << raw("\x17") << Q_UINT64_C(23) << Q_INT64_C(23) << false << true; - QTest::newRow("24") << raw("\x18\x18") << Q_UINT64_C(24) << Q_INT64_C(24) << false << true; - QTest::newRow("UINT8_MAX") << raw("\x18\xff") << Q_UINT64_C(255) << Q_INT64_C(255) << false << true; - QTest::newRow("UINT8_MAX+1") << raw("\x19\x01\x00") << Q_UINT64_C(256) << Q_INT64_C(256) << false << true; - QTest::newRow("UINT16_MAX") << raw("\x19\xff\xff") << Q_UINT64_C(65535) << Q_INT64_C(65535) << false << true; - QTest::newRow("UINT16_MAX+1") << raw("\x1a\0\1\x00\x00") << Q_UINT64_C(65536) << Q_INT64_C(65536) << false << true; - QTest::newRow("UINT32_MAX") << raw("\x1a\xff\xff\xff\xff") << Q_UINT64_C(4294967295) << Q_INT64_C(4294967295) << false << true; - QTest::newRow("UINT32_MAX+1") << raw("\x1b\0\0\0\1\0\0\0\0") << Q_UINT64_C(4294967296) << Q_INT64_C(4294967296) << false << true; - QTest::newRow("INT64_MAX") << raw("\x1b" "\x7f\xff\xff\xff" "\xff\xff\xff\xff") - << quint64(std::numeric_limits::max()) - << std::numeric_limits::max() << false << true; - QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") - << std::numeric_limits::max() << qint64(-123456) << false << false; - - // negative integers - QTest::newRow("-1") << raw("\x20") << Q_UINT64_C(0) << Q_INT64_C(-1) << true << true; - QTest::newRow("-2") << raw("\x21") << Q_UINT64_C(1) << Q_INT64_C(-2) << true << true; - QTest::newRow("-24") << raw("\x37") << Q_UINT64_C(23) << Q_INT64_C(-24) << true << true; - QTest::newRow("-25") << raw("\x38\x18") << Q_UINT64_C(24) << Q_INT64_C(-25) << true << true; - QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << Q_UINT64_C(255) << Q_INT64_C(-256) << true << true; - QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << Q_UINT64_C(256) << Q_INT64_C(-257) << true << true; - QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << Q_UINT64_C(65535) << Q_INT64_C(-65536) << true << true; - QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << Q_UINT64_C(65536) << Q_INT64_C(-65537) << true << true; - QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << Q_UINT64_C(4294967295) << Q_INT64_C(-4294967296) << true << true; - QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << Q_UINT64_C(4294967296) << Q_INT64_C(-4294967297) << true << true; - QTest::newRow("INT64_MIN+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe") - << quint64(std::numeric_limits::max() - 1) - << (std::numeric_limits::min() + 1) - << true << true; - QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") - << quint64(std::numeric_limits::max()) - << std::numeric_limits::min() - << true << true; - QTest::newRow("INT64_MIN-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << Q_UINT64_C(9223372036854775808) << qint64(-123456) << true << false; - QTest::newRow("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") - << (std::numeric_limits::max() - 1) << qint64(-123456) << true << false; - QTest::newRow("-UINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") - << std::numeric_limits::max() << qint64(-123456) << true << false; -} +#include "data.cpp" void tst_Parser::integers_data() { @@ -618,114 +461,12 @@ void tst_Parser::fixed() compareOne(data, expected); } -void addStringsData() -{ - // byte strings - QTest::newRow("emptybytestring") << raw("\x40") << "h''"; - QTest::newRow("bytestring1") << raw("\x41 ") << "h'20'"; - QTest::newRow("bytestring1-nul") << raw("\x41\0") << "h'00'"; - QTest::newRow("bytestring5") << raw("\x45Hello") << "h'48656c6c6f'"; - QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234") - << "h'313233343536373839303132333435363738393031323334'"; - QTest::newRow("bytestring256") << raw("\x59\1\0") + QByteArray(256, '3') - << "h'" + QString(256 * 2, '3') + '\''; - - // text strings - QTest::newRow("emptytextstring") << raw("\x60") << "\"\""; - QTest::newRow("textstring1") << raw("\x61 ") << "\" \""; - QTest::newRow("textstring1-nul") << raw("\x61\0") << "\"\\u0000\""; - QTest::newRow("textstring5") << raw("\x65Hello") << "\"Hello\""; - QTest::newRow("textstring24") << raw("\x78\x18""123456789012345678901234") - << "\"123456789012345678901234\""; - QTest::newRow("textstring256") << raw("\x79\1\0") + QByteArray(256, '3') - << '"' + QString(256, '3') + '"'; - - // some strings with UTF-8 content - // we had a bug in the pretty dumper - see issue #54 - QTest::newRow("textstringutf8-2char") << raw("\x62\xc2\xa0") << "\"\\u00A0\""; - QTest::newRow("textstringutf8-2char2") << raw("\x64\xc2\xa0\xc2\xa9") << "\"\\u00A0\\u00A9\""; - QTest::newRow("textstringutf8-3char") << raw("\x63\xe2\x88\x80") << "\"\\u2200\""; - QTest::newRow("textstringutf8-4char") << raw("\x64\xf0\x90\x88\x83") << "\"\\uD800\\uDE03\""; - - // strings with overlong length - QTest::newRow("emptybytestring*1") << raw("\x58\x00") << "h''_0"; - QTest::newRow("emptytextstring*1") << raw("\x78\x00") << "\"\"_0"; - QTest::newRow("emptybytestring*2") << raw("\x59\x00\x00") << "h''_1"; - QTest::newRow("emptytextstring*2") << raw("\x79\x00\x00") << "\"\"_1"; - QTest::newRow("emptybytestring*4") << raw("\x5a\0\0\0\0") << "h''_2"; - QTest::newRow("emptytextstring*4") << raw("\x7a\0\0\0\0") << "\"\"_2"; - QTest::newRow("emptybytestring*8") << raw("\x5b\0\0\0\0\0\0\0\0") << "h''_3"; - QTest::newRow("emptytextstring*8") << raw("\x7b\0\0\0\0\0\0\0\0") << "\"\"_3"; - QTest::newRow("bytestring5*1") << raw("\x58\x05Hello") << "h'48656c6c6f'_0"; - QTest::newRow("textstring5*1") << raw("\x78\x05Hello") << "\"Hello\"_0"; - QTest::newRow("bytestring5*2") << raw("\x59\0\5Hello") << "h'48656c6c6f'_1"; - QTest::newRow("textstring5*2") << raw("\x79\0\x05Hello") << "\"Hello\"_1"; - QTest::newRow("bytestring5*4") << raw("\x5a\0\0\0\5Hello") << "h'48656c6c6f'_2"; - QTest::newRow("textstring5*4") << raw("\x7a\0\0\0\x05Hello") << "\"Hello\"_2"; - QTest::newRow("bytestring5*8") << raw("\x5b\0\0\0\0\0\0\0\5Hello") << "h'48656c6c6f'_3"; - QTest::newRow("textstring5*8") << raw("\x7b\0\0\0\0\0\0\0\x05Hello") << "\"Hello\"_3"; - - // strings with undefined length - QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "(_ )"; - QTest::newRow("_emptytextstring") << raw("\x7f\xff") << "(_ )"; - QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "(_ h'')"; - QTest::newRow("_emptytextstring2") << raw("\x7f\x60\xff") << "(_ \"\")"; - QTest::newRow("_emptybytestring2*1") << raw("\x5f\x58\x00\xff") << "(_ h''_0)"; - QTest::newRow("_emptytextstring2*1") << raw("\x7f\x78\x00\xff") << "(_ \"\"_0)"; - QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "(_ h'', h'')"; - QTest::newRow("_emptytextstring3") << raw("\x7f\x60\x60\xff") << "(_ \"\", \"\")"; - QTest::newRow("_emptybytestring3*2") << raw("\x5f\x59\x00\x00\x40\xff") << "(_ h''_1, h'')"; - QTest::newRow("_emptytextstring3*2") << raw("\x7f\x79\x00\x00\x60\xff") << "(_ \"\"_1, \"\")"; - QTest::newRow("_bytestring5x2") << raw("\x5f\x43Hel\x42lo\xff") << "(_ h'48656c', h'6c6f')"; - QTest::newRow("_textstring5x2") << raw("\x7f\x63Hel\x62lo\xff") << "(_ \"Hel\", \"lo\")"; - QTest::newRow("_bytestring5x2*8*4") << raw("\x5f\x5b\0\0\0\0\0\0\0\3Hel\x5a\0\0\0\2lo\xff") << "(_ h'48656c'_3, h'6c6f'_2)"; - QTest::newRow("_textstring5x2*8*4") << raw("\x7f\x7b\0\0\0\0\0\0\0\3Hel\x7a\0\0\0\2lo\xff") << "(_ \"Hel\"_3, \"lo\"_2)"; - QTest::newRow("_bytestring5x5") << raw("\x5f\x41H\x41""e\x41l\x41l\x41o\xff") << "(_ h'48', h'65', h'6c', h'6c', h'6f')"; - QTest::newRow("_textstring5x5") << raw("\x7f\x61H\x61""e\x61l\x61l\x61o\xff") << "(_ \"H\", \"e\", \"l\", \"l\", \"o\")"; - QTest::newRow("_bytestring5x6") << raw("\x5f\x41H\x41""e\x40\x41l\x41l\x41o\xff") << "(_ h'48', h'65', h'', h'6c', h'6c', h'6f')"; - QTest::newRow("_textstring5x6") << raw("\x7f\x61H\x61""e\x61l\x60\x61l\x61o\xff") << "(_ \"H\", \"e\", \"l\", \"\", \"l\", \"o\")"; -} - void tst_Parser::strings_data() { addColumns(); addStringsData(); } -void addTagsData() -{ - // since parseOne() works recursively for tags, we can't test lone tags - QTest::newRow("tag0") << raw("\xc0\x00") << "0(0)"; - QTest::newRow("tag1") << raw("\xc1\x00") << "1(0)"; - QTest::newRow("tag24") << raw("\xd8\x18\x00") << "24(0)"; - QTest::newRow("tag255") << raw("\xd8\xff\x00") << "255(0)"; - QTest::newRow("tag256") << raw("\xd9\1\0\x00") << "256(0)"; - QTest::newRow("tag65535") << raw("\xd9\xff\xff\x00") << "65535(0)"; - QTest::newRow("tag65536") << raw("\xda\0\1\0\0\x00") << "65536(0)"; - QTest::newRow("tagUINT32_MAX-1") << raw("\xda\xff\xff\xff\xff\x00") << "4294967295(0)"; - QTest::newRow("tagUINT32_MAX") << raw("\xdb\0\0\0\1\0\0\0\0\x00") << "4294967296(0)"; - QTest::newRow("tagUINT64_MAX") << raw("\xdb" "\xff\xff\xff\xff" "\xff\xff\xff\xff" "\x00") - << QString::number(std::numeric_limits::max()) + "(0)"; - - // overlong tags - QTest::newRow("tag0*1") << raw("\xd8\0\x00") << "0_0(0)"; - QTest::newRow("tag0*2") << raw("\xd9\0\0\x00") << "0_1(0)"; - QTest::newRow("tag0*4") << raw("\xda\0\0\0\0\x00") << "0_2(0)"; - QTest::newRow("tag0*8") << raw("\xdb\0\0\0\0\0\0\0\0\x00") << "0_3(0)"; - - // tag other things - QTest::newRow("unixtime") << raw("\xc1\x1a\x55\x4b\xbf\xd3") << "1(1431027667)"; - QTest::newRow("rfc3339date") << raw("\xc0\x78\x19" "2015-05-07 12:41:07-07:00") - << "0(\"2015-05-07 12:41:07-07:00\")"; - QTest::newRow("tag6+false") << raw("\xc6\xf4") << "6(false)"; - QTest::newRow("tag25+true") << raw("\xd8\x19\xf5") << "25(true)"; - QTest::newRow("tag256+null") << raw("\xd9\1\0\xf6") << "256(null)"; - QTest::newRow("tag65536+simple32") << raw("\xda\0\1\0\0\xf8\x20") << "65536(simple(32))"; - QTest::newRow("float+unixtime") << raw("\xc1\xfa\x4e\xaa\x97\x80") << "1(1431027712.f)"; - QTest::newRow("double+unixtime") << raw("\xc1\xfb" "\x41\xd5\x52\xef" "\xf4\xc7\xce\xfe") - << "1(1431027667.1220088)"; -} - void tst_Parser::tags_data() { addColumns(); @@ -742,14 +483,6 @@ void tst_Parser::tagTags() compareOne("\xd9\xd9\xf7" "\xd9\xd9\xf7" + data, "55799(55799(" + expected + "))"); } -void addEmptyContainersData() -{ - QTest::newRow("emptyarray") << raw("\x80") << "[]" << 0; - QTest::newRow("emptymap") << raw("\xa0") << "{}" << 0; - QTest::newRow("_emptyarray") << raw("\x9f\xff") << "[_ ]" << -1; - QTest::newRow("_emptymap") << raw("\xbf\xff") << "{_ }" << -1; -} - void tst_Parser::emptyContainers_data() { addColumns(); @@ -962,25 +695,6 @@ void tst_Parser::nestedMaps() if (compareFailed) return; } -void addMapMixedData() -{ - QTest::newRow("map-0-24") << raw("\xa1\0\x18\x18") << "{0: 24}" << 1; - QTest::newRow("map-0*1-24") << raw("\xa1\x18\0\x18\x18") << "{0_0: 24}" << 1; - QTest::newRow("map-0*1-24*2") << raw("\xa1\x18\0\x19\0\x18") << "{0_0: 24_1}" << 1; - QTest::newRow("map-0*4-24*2") << raw("\xa1\x1a\0\0\0\0\x19\0\x18") << "{0_2: 24_1}" << 1; - QTest::newRow("map-24-0") << raw("\xa1\x18\x18\0") << "{24: 0}" << 1; - QTest::newRow("map-24-0*1") << raw("\xa1\x18\x18\x18\0") << "{24: 0_0}" << 1; - QTest::newRow("map-255-65535") << raw("\xa1\x18\xff\x19\xff\xff") << "{255: 65535}" << 1; - - QTest::newRow("_map-0-24") << raw("\xbf\0\x18\x18\xff") << "{_ 0: 24}" << 1; - QTest::newRow("_map-0*1-24") << raw("\xbf\x18\0\x18\x18\xff") << "{_ 0_0: 24}" << 1; - QTest::newRow("_map-0*1-24*2") << raw("\xbf\x18\0\x19\0\x18\xff") << "{_ 0_0: 24_1}" << 1; - QTest::newRow("_map-0*4-24*2") << raw("\xbf\x1a\0\0\0\0\x19\0\x18\xff") << "{_ 0_2: 24_1}" << 1; - QTest::newRow("_map-24-0") << raw("\xbf\x18\x18\0\xff") << "{_ 24: 0}" << 1; - QTest::newRow("_map-24-0*1") << raw("\xbf\x18\x18\x18\0\xff") << "{_ 24: 0_0}" << 1; - QTest::newRow("_map-255-65535") << raw("\xbf\x18\xff\x19\xff\xff\xff") << "{_ 255: 65535}" << 1; -} - void tst_Parser::mapMixed_data() { addColumns(); @@ -1091,36 +805,7 @@ void tst_Parser::readerApi() void tst_Parser::chunkedString_data() { - QTest::addColumn("data"); - QTest::addColumn("concatenated"); - QTest::addColumn("chunks"); - - // non-chunked: - QTest::newRow("emptybytestring") << raw("\x40") << "h''" << QStringList{"h''"}; - QTest::newRow("bytestring1") << raw("\x41 ") << "h'20'" << QStringList{"h'20'"}; - QTest::newRow("emptytextstring") << raw("\x60") << "\"\"" << QStringList{"\"\""}; - QTest::newRow("textstring1") << raw("\x61 ") << "\" \"" << QStringList{"\" \""}; - - // empty chunked: - QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "h''" << QStringList{}; - QTest::newRow("_emptytextstring") << raw("\x7f\xff") << "\"\"" << QStringList{}; - QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "h''" << QStringList{"h''"}; - QTest::newRow("_emptytextstring2") << raw("\x7f\x60\xff") << "\"\"" << QStringList{"\"\""}; - QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "h''" << QStringList{"h''", "h''"}; - QTest::newRow("_emptytextstring3") << raw("\x7f\x60\x60\xff") << "\"\"" << QStringList{"\"\"", "\"\""}; - - // regular chunks - QTest::newRow("_bytestring1") << raw("\x5f\x41 \xff") << "h'20'" << QStringList{"h'20'"}; - QTest::newRow("_bytestring2") << raw("\x5f\x41 \x41z\xff") << "h'207a'" << QStringList{"h'20'", "h'7a'"}; - QTest::newRow("_bytestring3") << raw("\x5f\x41 \x58\x18""123456789012345678901234\x41z\xff") - << "h'203132333435363738393031323334353637383930313233347a'" - << QStringList{"h'20'", "h'313233343536373839303132333435363738393031323334'", "h'7a'"}; - - QTest::newRow("_textstring1") << raw("\x7f\x61 \xff") << "\" \"" << QStringList{"\" \""}; - QTest::newRow("_textstring2") << raw("\x7f\x61 \x61z\xff") << "\" z\"" << QStringList{"\" \"", "\"z\""}; - QTest::newRow("_textstring3") << raw("\x7f\x61 \x78\x18""123456789012345678901234\x61z\xff") - << "\" 123456789012345678901234z\"" - << QStringList{"\" \"", "\"123456789012345678901234\"", "\"z\""}; + addChunkedStringData(); } static void chunkedStringTest(const QByteArray &data, const QString &concatenated, @@ -1601,248 +1286,6 @@ void tst_Parser::checkedIntegers() } } -static void addValidationColumns() -{ - QTest::addColumn("data"); - QTest::addColumn("flags"); // future - QTest::addColumn("expectedError"); -} - -static void addValidationData() -{ - // illegal numbers are future extension points - QTest::newRow("illegal-number-in-unsigned-1") << raw("\x81\x1c") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-unsigned-2") << raw("\x81\x1d") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-unsigned-3") << raw("\x81\x1e") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-unsigned-4") << raw("\x81\x1f") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-negative-1") << raw("\x81\x3c") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-negative-2") << raw("\x81\x3d") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-negative-3") << raw("\x81\x3e") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-negative-4") << raw("\x81\x3f") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-bytearray-length-1") << raw("\x81\x5c") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-bytearray-length-2") << raw("\x81\x5d") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-bytearray-length-3") << raw("\x81\x5e") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-string-length-1") << raw("\x81\x7c") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-string-length-2") << raw("\x81\x7d") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-string-length-3") << raw("\x81\x7e") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-array-length-1") << raw("\x81\x9c") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-array-length-2") << raw("\x81\x9d") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-array-length-3") << raw("\x81\x9e") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-map-length-1") << raw("\x81\xbc") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-map-length-2") << raw("\x81\xbd") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-map-length-3") << raw("\x81\xbe") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-tag-1") << raw("\x81\xdc") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-tag-2") << raw("\x81\xdd") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-tag-3") << raw("\x81\xde") << 0 << CborErrorIllegalNumber; - QTest::newRow("illegal-number-in-tag-4") << raw("\x81\xdf") << 0 << CborErrorIllegalNumber; - - QTest::newRow("unsigned-too-short-1-0") << raw("\x81\x18") << 0 << CborErrorUnexpectedEOF; // requires 1 byte, 0 given - QTest::newRow("unsigned-too-short-2-0") << raw("\x81\x19") << 0 << CborErrorUnexpectedEOF; // requires 2 bytes, 0 given - QTest::newRow("unsigned-too-short-2-1") << raw("\x81\x19\x01") << 0 << CborErrorUnexpectedEOF; // etc - QTest::newRow("unsigned-too-short-4-0") << raw("\x81\x1a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("unsigned-too-short-4-3") << raw("\x81\x1a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("unsigned-too-short-8-0") << raw("\x81\x1b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("unsigned-too-short-8-7") << raw("\x81\x1b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-1-0") << raw("\x81\x38") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-2-0") << raw("\x81\x39") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-2-1") << raw("\x81\x39\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-4-0") << raw("\x81\x3a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-4-3") << raw("\x81\x3a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-8-0") << raw("\x81\x3b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("negative-length-too-short-8-7") << raw("\x81\x3b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-1-0") << raw("\x81\x58") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-2-0") << raw("\x81\x59") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-2-1") << raw("\x81\x59\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-4-0") << raw("\x81\x5a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-4-3") << raw("\x81\x5a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-8-0") << raw("\x81\x5b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-length-too-short-8-7") << raw("\x81\x5b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-1-0") << raw("\x81\x78") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-2-0") << raw("\x81\x79") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-2-1") << raw("\x81\x79\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-4-0") << raw("\x81\x7a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-4-3") << raw("\x81\x7a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-8-0") << raw("\x81\x7b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-length-too-short-8-7") << raw("\x81\x7b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-1-0") << raw("\x81\x5f\x58") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-2-0") << raw("\x81\x5f\x59") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-2-1") << raw("\x81\x5f\x59\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-4-0") << raw("\x81\x5f\x5a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-4-3") << raw("\x81\x5f\x5a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-8-0") << raw("\x81\x5f\x5b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-length-too-short-8-7") << raw("\x81\x5f\x5b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-1-0") << raw("\x81\x7f\x78") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-2-0") << raw("\x81\x7f\x79") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-2-1") << raw("\x81\x7f\x79\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-4-0") << raw("\x81\x7f\x7a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-4-3") << raw("\x81\x7f\x7a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-8-0") << raw("\x81\x7f\x7b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-length-too-short-8-7") << raw("\x81\x7f\x7b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-1-0") << raw("\x81\x5f\x40\x58") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-2-0") << raw("\x81\x5f\x40\x59") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-2-1") << raw("\x81\x5f\x40\x59\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-4-0") << raw("\x81\x5f\x40\x5a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-4-3") << raw("\x81\x5f\x40\x5a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-8-0") << raw("\x81\x5f\x40\x5b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-2-length-too-short-8-7") << raw("\x81\x5f\x40\x5b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-1-0") << raw("\x81\x7f\x60\x78") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-2-0") << raw("\x81\x7f\x60\x79") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-2-1") << raw("\x81\x7f\x60\x79\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-4-0") << raw("\x81\x7f\x60\x7a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-4-3") << raw("\x81\x7f\x60\x7a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-8-0") << raw("\x81\x7f\x60\x7b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-2-length-too-short-8-7") << raw("\x81\x7f\x60\x7b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-1-0") << raw("\x81\x98") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-2-0") << raw("\x81\x99") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-2-1") << raw("\x81\x99\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-4-0") << raw("\x81\x9a") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-4-3") << raw("\x81\x9a\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-8-0") << raw("\x81\x9b") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-length-too-short-8-7") << raw("\x81\x9b\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-1-0") << raw("\x81\xb8") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-2-0") << raw("\x81\xb9") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-2-1") << raw("\x81\xb9\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-4-0") << raw("\x81\xba") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-4-3") << raw("\x81\xba\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-8-0") << raw("\x81\xbb") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-length-too-short-8-7") << raw("\x81\xbb\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-1-0") << raw("\x81\xd8") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-2-0") << raw("\x81\xd9") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-2-1") << raw("\x81\xd9\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-4-0") << raw("\x81\xda") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-4-3") << raw("\x81\xda\x01\x02\x03") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-8-0") << raw("\x81\xdb") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("tag-too-short-8-7") << raw("\x81\xdb\1\2\3\4\5\6\7") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("fp16-too-short1") << raw("\x81\xf9") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("fp16-too-short2") << raw("\x81\xf9\x00") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("float-too-short1") << raw("\x81\xfa") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("float-too-short2") << raw("\x81\xfa\0\0\0") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("double-too-short1") << raw("\x81\xfb") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("double-too-short2") << raw("\x81\xfb\0\0\0\0\0\0\0") << 0 << CborErrorUnexpectedEOF; - - QTest::newRow("bytearray-too-short1") << raw("\x81\x42z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-too-short2") << raw("\x81\x58\x02z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-too-short3") << raw("\x81\x5a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-too-short4") << raw("\x81\x5b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-too-short1") << raw("\x81\x62z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-too-short2") << raw("\x81\x78\x02z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-too-short3") << raw("\x81\x7a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-too-short4") << raw("\x81\x7b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short1") << raw("\x81\x5f\x42z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short2") << raw("\x81\x5f\x58\x02z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short3") << raw("\x81\x5f\x5a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short4") << raw("\x81\x5f\x5b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short1") << raw("\x81\x7f\x62z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short2") << raw("\x81\x7f\x78\x02z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short3") << raw("\x81\x7f\x7a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short4") << raw("\x81\x7f\x7b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short1x2") << raw("\x81\x5f\x40\x42z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short2x2") << raw("\x81\x5f\x40\x58\x02z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short3x2") << raw("\x81\x5f\x40\x5a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-chunked-too-short4x2") << raw("\x81\x5f\x40\x5b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short1x2") << raw("\x81\x7f\x60\x62z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short2x2") << raw("\x81\x7f\x60\x78\x02z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short3x2") << raw("\x81\x7f\x60\x7a\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunked-too-short4x2") << raw("\x81\x7f\x60\x7b\0\0\0\0\0\0\0\2z") << 0 << CborErrorUnexpectedEOF; - - QTest::newRow("bytearray-no-break1") << raw("\x81\x5f") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("bytearray-no-break2") << raw("\x81\x5f\x40") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-no-break1") << raw("\x81\x7f") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-no-break2") << raw("\x81\x7f\x60") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-no-break1") << raw("\x81\x9f") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("array-no-break2") << raw("\x81\x9f\0") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-no-break1") << raw("\x81\xbf") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-no-break2") << raw("\x81\xbf\0\0") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("map-break-after-key") << raw("\x81\xbf\0\xff") << 0 << CborErrorUnexpectedBreak; - QTest::newRow("map-break-after-second-key") << raw("\x81\xbf\x64xyzw\x04\x00\xff") << 0 << CborErrorUnexpectedBreak; - QTest::newRow("map-break-after-value-tag") << raw("\x81\xbf\0\xc0\xff") << 0 << CborErrorUnexpectedBreak; - QTest::newRow("map-break-after-value-tag2") << raw("\x81\xbf\0\xd8\x20\xff") << 0 << CborErrorUnexpectedBreak; - - // check for pointer additions wrapping over the limit of the address space - CborError tooLargeOn32bit = (sizeof(void *) == 4) ? CborErrorDataTooLarge : CborErrorUnexpectedEOF; - // on 32-bit systems, this is a -1 - QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - // on 32-bit systems, a 4GB addition could be dropped - QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - // on 64-bit systems, this could be a -1 - QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - - // ditto on chunks - QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF; - // on 32-bit systems, a 4GB addition could be dropped - QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit; - // on 64-bit systems, this could be a -1 - QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit; - - QTest::newRow("eof-after-array") << raw("\x81") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-array-element") << raw("\x81\x82\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-object") << raw("\x81\xa1") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-object2") << raw("\x81\xb8\x20") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-object-key") << raw("\x81\xa1\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-object-value") << raw("\x81\xa2\x01\x01") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-tag") << raw("\x81\xc0") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("eof-after-tag2") << raw("\x81\xd8\x20") << 0 << CborErrorUnexpectedEOF; - - // major type 7 has future types - QTest::newRow("future-type-28") << raw("\x81\xfc") << 0 << CborErrorUnknownType; - QTest::newRow("future-type-29") << raw("\x81\xfd") << 0 << CborErrorUnknownType; - QTest::newRow("future-type-30") << raw("\x81\xfe") << 0 << CborErrorUnknownType; - QTest::newRow("unexpected-break") << raw("\x81\xff") << 0 << CborErrorUnexpectedBreak; - QTest::newRow("illegal-simple-0") << raw("\x81\xf8\0") << 0 << CborErrorIllegalSimpleType; - QTest::newRow("illegal-simple-31") << raw("\x81\xf8\x1f") << 0 << CborErrorIllegalSimpleType; - - // not only too big (UINT_MAX or UINT_MAX+1 in size), but also incomplete - if (sizeof(size_t) < sizeof(uint64_t)) { - QTest::newRow("bytearray-too-big1") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; - QTest::newRow("string-too-big1") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; - } - QTest::newRow("array-too-big1") << raw("\x81\x9a\xff\xff\xff\xff\0\0\0\0") << 0 << CborErrorDataTooLarge; - QTest::newRow("array-too-big2") << raw("\x81\x9b\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; - QTest::newRow("object-too-big1") << raw("\x81\xba\xff\xff\xff\xff\0\0\0\0") << 0 << CborErrorDataTooLarge; - QTest::newRow("object-too-big2") << raw("\x81\xbb\0\0\0\1\0\0\0\0") << 0 << CborErrorDataTooLarge; - - QTest::newRow("no-break-for-array0") << raw("\x81\x9f") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("no-break-for-array1") << raw("\x81\x9f\x01") << 0 << CborErrorUnexpectedEOF; - - QTest::newRow("no-break-string0") << raw("\x81\x7f") << 0 << CborErrorUnexpectedEOF; - QTest::newRow("no-break-string1") << raw("\x81\x7f\x61Z") << 0 << CborErrorUnexpectedEOF; - - QTest::newRow("nested-indefinite-length-bytearrays") << raw("\x81\x5f\x5f\xff\xff") << 0 << CborErrorIllegalNumber; - QTest::newRow("nested-indefinite-length-strings") << raw("\x81\x7f\x7f\xff\xff") << 0 << CborErrorIllegalNumber; - - QTest::newRow("string-chunk-unsigned") << raw("\x81\x7f\0\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-negative") << raw("\x81\x7f\x20\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-bytearray") << raw("\x81\x7f\x40\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-array") << raw("\x81\x7f\x80\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-map") << raw("\x81\x7f\xa0\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-tag") << raw("\x81\x7f\xc0\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-tagged-string") << raw("\x81\x7f\xc0\x60\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-simple0") << raw("\x81\x7f\xe0\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-false") << raw("\x81\x7f\xf4\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-true") << raw("\x81\x7f\xf5\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-null") << raw("\x81\x7f\xf6\xff") << 0 << CborErrorIllegalType; - QTest::newRow("string-chunk-undefined") << raw("\x81\x7f\xf7\xff") << 0 << CborErrorIllegalType; - - QTest::newRow("bytearray-chunk-string") << raw("\x81\x5f\x60\xff") << 0 << CborErrorIllegalType; - QTest::newRow("bytearray-chunk-tagged-bytearray") << raw("\x81\x7f\xc0\x40\xff") << 0 << CborErrorIllegalType; - - // RFC 7049 Section 2.2.2 "Indefinite-Length Byte Strings and Text Strings" says - // Text strings with indefinite lengths act the same as byte strings - // with indefinite lengths, except that all their chunks MUST be - // definite-length text strings. Note that this implies that the bytes - // of a single UTF-8 character cannot be spread between chunks: a new - // chunk can only be started at a character boundary. - // This test technically tests the dumper, not the parser. - QTest::newRow("string-utf8-chunk-split") << raw("\x81\x7f\x61\xc2\x61\xa0\xff") << 0 << CborErrorInvalidUtf8TextString; -} - void tst_Parser::validation_data() { addValidationColumns(); From 5159ec39dfdd2165d93c6c5d08a1c1264865a9ef Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 22 Dec 2017 21:53:38 -0200 Subject: [PATCH 29/35] Parser: add a way to parse the current element again We need to re-parse if the input buffer was too short to read the current element's information. When that happens, the current element will be CborInvalidType, so we can't easily resume. Signed-off-by: Thiago Macieira --- src/cbor.h | 1 + src/cborparser.c | 7 +++ tests/parser/tst_parser.cpp | 92 +++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/cbor.h b/src/cbor.h index 703ad42a..47b88af5 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -344,6 +344,7 @@ CBOR_INLINE_API bool cbor_value_at_end(const CborValue *it) { return it->remaining == 0; } CBOR_INLINE_API const uint8_t *cbor_value_get_next_byte(const CborValue *it) { return it->source.ptr; } +CBOR_API CborError cbor_value_reparse(CborValue *it); 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) diff --git a/src/cborparser.c b/src/cborparser.c index 92810b31..54ce9533 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -401,6 +401,13 @@ CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborPa * \sa cbor_value_at_end() */ +CborError cbor_value_reparse(CborValue *it) +{ + if (it->flags & CborIteratorFlag_IteratingStringChunks) + return CborNoError; + return preparse_next_value_nodecrement(it); +} + /** * \fn bool cbor_value_is_valid(const CborValue *it) * diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index 3a0d766c..bc8f5e5c 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -86,6 +86,8 @@ private slots: void readerApi_data() { arrays_data(); } void readerApi(); + void reparse_data(); + void reparse(); // chunked string API void chunkedString_data(); @@ -108,8 +110,8 @@ private slots: void validation(); void strictValidation_data(); void strictValidation(); - void resumeParsing_data(); - void resumeParsing(); + void incompleteData_data(); + void incompleteData(); void endPointer_data(); void endPointer(); void recursionLimit_data(); @@ -755,30 +757,25 @@ void tst_Parser::mapsAndArrays() "{_ 1: [_ " + expected + "], \"Hello\": {_ " + expected + ": (_ )}}"); } -void tst_Parser::readerApi() -{ - QFETCH(QByteArray, data); - QFETCH(QString, expected); - - struct Input { - QByteArray data; - int consumed; - } input = { data, 0 }; +struct Input { + QByteArray data; + int consumed; +}; - CborParserOperations ops; - ops.can_read_bytes = [](void *token, size_t len) { +static const CborParserOperations byteArrayOps = { + /* can_read_bytes = */ [](void *token, size_t len) { auto input = static_cast(token); return input->data.size() - input->consumed >= int(len); - }; - ops.read_bytes = [](void *token, void *dst, size_t offset, size_t len) { + }, + /* read_bytes = */ [](void *token, void *dst, size_t offset, size_t len) { auto input = static_cast(token); return memcpy(dst, input->data.constData() + input->consumed + offset, len); - }; - ops.advance_bytes = [](void *token, size_t len) { + }, + /* advance_bytes = */ [](void *token, size_t len) { auto input = static_cast(token); input->consumed += int(len); - }; - ops.transfer_string = [](void *token, const void **userptr, size_t offset, size_t len) { + }, + /* transfer_string = */ [](void *token, const void **userptr, size_t offset, size_t len) { // ### auto input = static_cast(token); if (input->data.size() - input->consumed < int(len + offset)) @@ -787,11 +784,60 @@ void tst_Parser::readerApi() *userptr = input->data.constData() + input->consumed; input->consumed += int(len); return CborNoError; - }; + } +}; + +void tst_Parser::readerApi() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + + Input input = { data, 0 }; CborParser parser; CborValue first; - CborError err = cbor_parser_init_reader(&ops, &parser, &first, &input); + CborError err = cbor_parser_init_reader(&byteArrayOps, &parser, &first, &input); + QCOMPARE(err, CborNoError); + + QString decoded; + err = parseOne(&first, &decoded); + QCOMPARE(err, CborNoError); + QCOMPARE(decoded, expected); + + // check we consumed everything + QCOMPARE(input.consumed, data.size()); +} + +void tst_Parser::reparse_data() +{ + // only one-item rows + addColumns(); + addFixedData(); +} + +void tst_Parser::reparse() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + + Input input = { QByteArray(), 0 }; + CborParser parser; + CborValue first; + CborError err = cbor_parser_init_reader(&byteArrayOps, &parser, &first, &input); + QCOMPARE(err, CborErrorUnexpectedEOF); + + for (int i = 0; i < data.size(); ++i) { + input.data = data.left(i); + err = cbor_value_reparse(&first); + if (err != CborErrorUnexpectedEOF) + qDebug() << "At" << i; + QCOMPARE(err, CborErrorUnexpectedEOF); + QCOMPARE(input.consumed, 0); + } + + // now it should work + input.data = data; + err = cbor_value_reparse(&first); QCOMPARE(err, CborNoError); QString decoded; @@ -1707,7 +1753,7 @@ void tst_Parser::strictValidation() QCOMPARE(err, expectedError); } -void tst_Parser::resumeParsing_data() +void tst_Parser::incompleteData_data() { addColumns(); addFixedData(); @@ -1716,7 +1762,7 @@ void tst_Parser::resumeParsing_data() addMapMixedData(); } -void tst_Parser::resumeParsing() +void tst_Parser::incompleteData() { QFETCH(QByteArray, data); QFETCH(QString, expected); From 8adc3cf3f4e39659750b36dcce77c30133b1c05c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 5 Feb 2018 21:19:37 -0800 Subject: [PATCH 30/35] Parser: modify the zero-copy string API Instead of just one function (_cbor_value_get_string_chunk), we now have _cbor_value_begin_string_iteration, _cbor_value_finish_string_iteration, _cbor_value_get_string_chunk_size, and _cbor_value_get_string_chunk. The "begin" function positions the pointer at the first chunk. That's what makes "get_size" possible, since it doesn't need to check for any state. The "finish" funcntion allows the caller to distinguish an error parsing the string from an error parsing the next value. Signed-off-by: Thiago Macieira --- src/cbor.h | 41 ++++++++++ src/cborerrorstrings.c | 3 + src/cborinternal_p.h | 2 - src/cborparser.c | 148 ++++++++++++++++++------------------ src/cborpretty.c | 15 ++-- src/cborvalidation.c | 12 +-- tests/parser/tst_parser.cpp | 11 ++- 7 files changed, 139 insertions(+), 93 deletions(-) diff --git a/src/cbor.h b/src/cbor.h index 47b88af5..be5bbc77 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -166,6 +166,7 @@ typedef enum CborError { CborErrorIllegalType, /* type not allowed here */ CborErrorIllegalNumber, CborErrorIllegalSimpleType, /* types of value less than 32 encoded in two bytes */ + CborErrorNoMoreStringChunks, /* parser errors in strict mode parsing only */ CborErrorUnknownSimpleType = 512, @@ -292,11 +293,23 @@ enum CborParserGlobalFlags enum CborParserIteratorFlags { + /* used for all types, but not during string chunk iteration + * (values are static-asserted, don't change) */ CborIteratorFlag_IntegerValueIs64Bit = 0x01, CborIteratorFlag_IntegerValueTooLarge = 0x02, + + /* used only for CborIntegerType */ CborIteratorFlag_NegativeInteger = 0x04, + + /* used only during string iteration */ + CborIteratorFlag_BeforeFirstStringChunk = 0x04, CborIteratorFlag_IteratingStringChunks = 0x08, + + /* used for arrays, maps and strings, including during chunk iteration */ CborIteratorFlag_UnknownLength = 0x10, + + /* used for maps, but must be kept for all types + * (ContainerIsMap value must be CborMapType - CborArrayType) */ CborIteratorFlag_ContainerIsMap = 0x20, CborIteratorFlag_NextIsMapKey = 0x40 }; @@ -499,6 +512,34 @@ CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uin return _cbor_value_dup_string(value, (void **)buffer, buflen, next); } +CBOR_PRIVATE_API CborError _cbor_value_get_string_chunk_size(const CborValue *value, size_t *len); +CBOR_INLINE_API CborError cbor_value_get_string_chunk_size(const CborValue *value, size_t *len) +{ + assert(value->flags & CborIteratorFlag_IteratingStringChunks); + return _cbor_value_get_string_chunk_size(value, len); +} + +CBOR_INLINE_API bool cbor_value_string_iteration_at_end(const CborValue *value) +{ + size_t dummy; + return cbor_value_get_string_chunk_size(value, &dummy) == CborErrorNoMoreStringChunks; +} + +CBOR_PRIVATE_API CborError _cbor_value_begin_string_iteration(CborValue *value); +CBOR_INLINE_API CborError cbor_value_begin_string_iteration(CborValue *value) +{ + assert(cbor_value_is_text_string(value) || cbor_value_is_byte_string(value)); + assert(!(value->flags & CborIteratorFlag_IteratingStringChunks)); + return _cbor_value_begin_string_iteration(value); +} + +CBOR_PRIVATE_API CborError _cbor_value_finish_string_iteration(CborValue *value); +CBOR_INLINE_API CborError cbor_value_finish_string_iteration(CborValue *value) +{ + assert(cbor_value_string_iteration_at_end(value)); + return _cbor_value_finish_string_iteration(value); +} + 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, diff --git a/src/cborerrorstrings.c b/src/cborerrorstrings.c index 994c6a4c..44f766a3 100644 --- a/src/cborerrorstrings.c +++ b/src/cborerrorstrings.c @@ -119,6 +119,9 @@ const char *cbor_error_string(CborError error) case CborErrorIllegalSimpleType: return _("illegal encoding of simple type smaller than 32"); + case CborErrorNoMoreStringChunks: + return _("no more byte or text strings available"); + case CborErrorUnknownSimpleType: return _("unknown simple type"); diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index 2343ce76..16269e63 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h @@ -161,8 +161,6 @@ enum { BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift) }; -CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it); - static inline void copy_current_position(CborValue *dst, const CborValue *src) { /* This "if" is here for pedantry only: the two branches should perform diff --git a/src/cborparser.c b/src/cborparser.c index 54ce9533..c6a1b762 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -966,103 +966,99 @@ CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len return _cbor_value_copy_string(value, NULL, len, NULL); } -static inline void prepare_string_iteration(CborValue *it) +CborError _cbor_value_begin_string_iteration(CborValue *it) { + it->flags |= CborIteratorFlag_IteratingStringChunks | + CborIteratorFlag_BeforeFirstStringChunk; if (!cbor_value_is_length_known(it)) { /* chunked string: we're before the first chunk; * advance to the first chunk */ advance_bytes(it, 1); - it->flags |= CborIteratorFlag_IteratingStringChunks; } + + return CborNoError; } -CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it) +CborError _cbor_value_finish_string_iteration(CborValue *it) { - cbor_assert((it->flags & CborIteratorFlag_IteratingStringChunks) == 0); - prepare_string_iteration(it); + if (!cbor_value_is_length_known(it)) + advance_bytes(it, 1); /* skip the Break */ - /* are we at the end? */ - if (!can_read_bytes(it, 1)) - return CborErrorUnexpectedEOF; - return CborNoError; + return preparse_next_value(it); } -static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len) +static CborError get_string_chunk_size(const CborValue *it, size_t *offset, size_t *len) { - /* 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 descriptor; + size_t bytesNeeded = 1; + + if (cbor_value_is_length_known(it) && (it->flags & CborIteratorFlag_BeforeFirstStringChunk) == 0) + return CborErrorNoMoreStringChunks; /* are we at the end? */ - uint8_t descriptor; if (!read_bytes(it, &descriptor, 0, 1)) return CborErrorUnexpectedEOF; - if (descriptor == BreakByte) { - /* last chunk */ - advance_bytes(it, 1); -last_chunk: - *bufferptr = NULL; - *len = 0; - return preparse_next_value(it); - } else if ((descriptor & MajorTypeMask) == it->type) { - /* find the string length */ - size_t bytesNeeded = 1; - - descriptor &= SmallValueMask; - if (descriptor < Value8Bit) { - *len = descriptor; - } else if (unlikely(descriptor > Value64Bit)) { - return CborErrorIllegalNumber; - } else { - uint64_t val; - bytesNeeded = (size_t)(1 << (descriptor - Value8Bit)); - if (!can_read_bytes(it, 1 + bytesNeeded)) - return CborErrorUnexpectedEOF; - - if (descriptor <= Value16Bit) { - if (descriptor == Value16Bit) - val = read_uint16(it, 1); - else - val = read_uint8(it, 1); - } else { - if (descriptor == Value32Bit) - val = read_uint32(it, 1); - else - val = read_uint64(it, 1); - } + if (descriptor == BreakByte) + return CborErrorNoMoreStringChunks; + if ((descriptor & MajorTypeMask) != it->type) + return CborErrorIllegalType; - *len = val; - if (*len != val) - return CborErrorDataTooLarge; + /* find the string length */ + descriptor &= SmallValueMask; + if (descriptor < Value8Bit) { + *len = descriptor; + } else if (unlikely(descriptor > Value64Bit)) { + return CborErrorIllegalNumber; + } else { + uint64_t val; + bytesNeeded = (size_t)(1 << (descriptor - Value8Bit)); + if (!can_read_bytes(it, 1 + bytesNeeded)) + return CborErrorUnexpectedEOF; - ++bytesNeeded; + if (descriptor <= Value16Bit) { + if (descriptor == Value16Bit) + val = read_uint16(it, 1); + else + val = read_uint8(it, 1); + } else { + if (descriptor == Value32Bit) + val = read_uint32(it, 1); + else + val = read_uint64(it, 1); } - if (*len != (size_t)*len) + *len = val; + if (*len != val) return CborErrorDataTooLarge; - CborError err = transfer_string(it, bufferptr, bytesNeeded, *len); - if (err) - return err; - } else { - return CborErrorIllegalType; + ++bytesNeeded; } - it->flags |= CborIteratorFlag_IteratingStringChunks; + *offset = bytesNeeded; + return CborNoError; +} + +CborError _cbor_value_get_string_chunk_size(const CborValue *value, size_t *len) +{ + size_t offset; + return get_string_chunk_size(value, &offset, len); +} + +static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len) +{ + size_t offset; + CborError err = get_string_chunk_size(it, &offset, len); + if (err) + return err; + + /* we're good, transfer the string now */ + err = transfer_string(it, bufferptr, offset, *len); + if (err) + return err; + + /* we've iterated at least once */ + it->flags &= ~CborIteratorFlag_BeforeFirstStringChunk; return CborNoError; } @@ -1195,14 +1191,18 @@ static CborError iterate_string_chunks(const CborValue *value, char *buffer, siz *next = *value; *result = true; + err = _cbor_value_begin_string_iteration(next); + if (err) + return err; + while (1) { size_t newTotal; size_t chunkLen; err = get_string_chunk(next, &ptr, &chunkLen); + if (err == CborErrorNoMoreStringChunks) + break; if (err) return err; - if (!ptr) - break; if (unlikely(add_check_overflow(total, chunkLen, &newTotal))) return CborErrorDataTooLarge; @@ -1221,7 +1221,7 @@ static CborError iterate_string_chunks(const CborValue *value, char *buffer, siz *result = !!func(buffer + total, nul, 1); } *buflen = total; - return CborNoError; + return _cbor_value_finish_string_iteration(next); } /** diff --git a/src/cborpretty.c b/src/cborpretty.c index ea3ae80d..49d3cae7 100644 --- a/src/cborpretty.c +++ b/src/cborpretty.c @@ -407,14 +407,13 @@ static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue open[1] = '\0'; } - if (showingFragments) { + if (showingFragments) err = stream(out, "(_ "); - if (!err) - err = _cbor_value_prepare_string_iteration(it); - } else { + else err = stream(out, "%s", open); - } + if (!err) + err = cbor_value_begin_string_iteration(it); while (!err) { if (showingFragments || indicator == NULL) { /* any iteration, except the second for a non-chunked string */ @@ -422,10 +421,10 @@ static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue } err = _cbor_value_get_string_chunk(it, &ptr, &n, it); - if (err) - return err; - if (!ptr) + if (err == CborErrorNoMoreStringChunks) { + err = cbor_value_finish_string_iteration(it); break; + } if (!err && showingFragments) err = stream(out, "%s%s", separator, open); diff --git a/src/cborvalidation.c b/src/cborvalidation.c index 321374e3..4c11fb13 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c @@ -543,24 +543,24 @@ static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft size_t n = 0; const void *ptr; - err = _cbor_value_prepare_string_iteration(it); + err = cbor_value_begin_string_iteration(it); if (err) return err; while (1) { CborValue next; err = _cbor_value_get_string_chunk(it, &ptr, &n, &next); - if (err) - return err; - if (ptr) { + if (!err) { err = validate_number(it, type, flags); if (err) return err; } *it = next; - if (!ptr) - break; + if (err == CborErrorNoMoreStringChunks) + return cbor_value_finish_string_iteration(it); + if (err) + return err; if (type == CborTextStringType && flags & CborValidateUtf8) { err = validate_utf8_string(ptr, n); diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index bc8f5e5c..91a65a00 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp @@ -869,18 +869,23 @@ static void chunkedStringTest(const QByteArray &data, const QString &concatenate CborValue copy = value; + err = cbor_value_begin_string_iteration(&value); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); forever { QString decoded; err = parseOneChunk(&value, &decoded); - QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - - if (decoded.isEmpty()) + if (err == CborErrorNoMoreStringChunks) break; // last chunk + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + QVERIFY2(!chunks.isEmpty(), "Too many chunks"); QString expected = chunks.takeFirst(); QCOMPARE(decoded, expected); } + + err = cbor_value_finish_string_iteration(&value); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(chunks.isEmpty(), "Too few chunks"); // compare to the concatenated data From d393c16f3eb30d0c47e6f9d92db62272f0ec4dc7 Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Thu, 11 Apr 2019 16:13:51 +0300 Subject: [PATCH 31/35] Parser: fix reading it->extra on big endian when bytesNeeded == 1 Signed-off-by: Dmitry Shachnev --- src/cborparser.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cborparser.c b/src/cborparser.c index c6a1b762..74d91a30 100644 --- a/src/cborparser.c +++ b/src/cborparser.c @@ -203,10 +203,13 @@ static CborError preparse_value(CborValue *it) it->extra = 0; /* read up to 16 bits into it->extra */ - if (bytesNeeded <= 2) { + if (bytesNeeded == 1) { + uint8_t extra; + read_bytes_unchecked(it, &extra, 1, bytesNeeded); + it->extra = extra; + } else if (bytesNeeded == 2) { read_bytes_unchecked(it, &it->extra, 1, bytesNeeded); - if (bytesNeeded == 2) - it->extra = cbor_ntohs(it->extra); + it->extra = cbor_ntohs(it->extra); } else { cbor_static_assert(CborIteratorFlag_IntegerValueTooLarge == (Value32Bit & 3)); cbor_static_assert((CborIteratorFlag_IntegerValueIs64Bit | From 10f739921db946aa05420d7284f0cf7998df4828 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 3 Sep 2021 13:19:19 -0700 Subject: [PATCH 32/35] .gitignore: ignore the c90 test too --- tests/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/.gitignore b/tests/.gitignore index e65577d2..f5db2a82 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,6 +5,8 @@ release target_wrapper.* # The executables +c90/c90 +c90/c90.exe cpp/cpp cpp/cpp.exe encoder/encoder From c223ee66d626fe4f16ca7e1e15728c7268c81690 Mon Sep 17 00:00:00 2001 From: Dane Walton Date: Tue, 9 Feb 2021 12:57:41 -0800 Subject: [PATCH 33/35] add CMake support --- .gitignore | 2 ++ CMakeLists.txt | 55 +++++++++++++++++++++++++++++++++++++++++ README => README.md | 18 +++++++++++--- examples/CMakeLists.txt | 34 +++++++++++++++++++++++++ examples/README.md | 15 +++++++++++ examples/testcbor | 1 + tools/CMakeLists.txt | 48 +++++++++++++++++++++++++++++++++++ 7 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 CMakeLists.txt rename README => README.md (55%) create mode 100644 examples/CMakeLists.txt create mode 100644 examples/README.md create mode 100644 examples/testcbor create mode 100644 tools/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 3272de33..f27c5d8f 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,5 @@ src/cjson src/doxygen.log !/Makefile .config +build/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..d9776cbc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +# /**************************************************************************** +# ** +# ** 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. +# ** +# ****************************************************************************/ + +project(intel_tinycbor LANGUAGES C) + +cmake_minimum_required(VERSION 3.10) + +option(build_tools "Build the cbor tools. Note: This will install the cjson library as a dependency" OFF) + +add_library(tinycbor + ${CMAKE_CURRENT_LIST_DIR}/src/cborencoder.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborencoder_close_container_checked.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborerrorstrings.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborparser.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborparser_dup_string.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborpretty.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborpretty_stdio.c + ${CMAKE_CURRENT_LIST_DIR}/src/cbortojson.c + ${CMAKE_CURRENT_LIST_DIR}/src/cborvalidation.c + ${CMAKE_CURRENT_LIST_DIR}/src/open_memstream.c +) + +target_include_directories(tinycbor + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src +) + +add_library(intel::tinycbor ALIAS tinycbor) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples) + +if(${build_tools}) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tools) +endif() diff --git a/README b/README.md similarity index 55% rename from README rename to README.md index 167efa06..4276e7f3 100644 --- a/README +++ b/README.md @@ -3,11 +3,23 @@ Concise Binary Object Representation (CBOR) Library To build TinyCBOR: - make +```bash +make +``` If you want to change the compiler or pass extra compiler flags: - make CC=clang CFLAGS="-m32 -Oz" LDFLAGS="-m32" +```bash +make CC=clang CFLAGS="-m32 -Oz" LDFLAGS="-m32" +``` -Documentation: https://intel.github.io/tinycbor/current/ +You may also use CMake to build and integrate with your project: + +```bash +mkdir build +cd build +cmake .. +cmake --build . +``` +Documentation: https://intel.github.io/tinycbor/current/ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..f6f5adf5 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,34 @@ +# /**************************************************************************** +# ** +# ** 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. +# ** +# ****************************************************************************/ + +cmake_minimum_required(VERSION 3.10) + +add_executable(simplereader + ${CMAKE_CURRENT_LIST_DIR}/simplereader.c +) + +target_link_libraries(simplereader + PRIVATE + intel::tinycbor +) diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..7ca2e5af --- /dev/null +++ b/examples/README.md @@ -0,0 +1,15 @@ +# tinyCBOR Example + +This directory has sample CBOR data (`testcbor`) that can be used with the example. The CBOR data resembles the following CBOR (based on [cbor.me diagnostic notation](http://cbor.me/)): + +``` +[ 1, [2, 3, 4]] +``` + +To run the example, be sure the test data is in the same directory as the example executable and add `testcbor` as an argument to the executable. + +Example: + +```bash +./examples/simplereader testcbor +``` diff --git a/examples/testcbor b/examples/testcbor new file mode 100644 index 00000000..78b7d881 --- /dev/null +++ b/examples/testcbor @@ -0,0 +1 @@ +‚ƒ \ No newline at end of file diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..8d42d06e --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,48 @@ +# /**************************************************************************** +# ** +# ** 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. +# ** +# ****************************************************************************/ + +include(ExternalProject) + +add_executable(json2cbor + ${CMAKE_CURRENT_LIST_DIR}/json2cbor/json2cbor.c +) + +ExternalProject_Add(cjson + GIT_REPOSITORY https://github.com/DaveGamble/cJSON.git + CMAKE_ARGS -DBUILD_SHARED_AND_STATIC_LIBS=ON +) + +find_library(CJSON_LIB + NAME + libcjson.a + HINTS + "/usr/lib" + "/usr/local/lib" +) + +target_link_libraries(json2cbor + PRIVATE + ${CJSON_LIB} + intel::tinycbor +) From b422a415f3de9174381ee9da4b26a9b5260b59cf Mon Sep 17 00:00:00 2001 From: Dane Walton Date: Wed, 10 Feb 2021 12:49:44 -0800 Subject: [PATCH 34/35] update to evaluate lib finding at compile time --- tools/CMakeLists.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8d42d06e..6772ea25 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -33,16 +33,8 @@ ExternalProject_Add(cjson CMAKE_ARGS -DBUILD_SHARED_AND_STATIC_LIBS=ON ) -find_library(CJSON_LIB - NAME - libcjson.a - HINTS - "/usr/lib" - "/usr/local/lib" -) - target_link_libraries(json2cbor PRIVATE - ${CJSON_LIB} + libcjson.a intel::tinycbor ) From 9ded7dc7d3319b200d4411d884f5f6706ab84d71 Mon Sep 17 00:00:00 2001 From: Dane Walton Date: Wed, 22 Sep 2021 09:46:53 -0700 Subject: [PATCH 35/35] remove test cbor --- examples/testcbor | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/testcbor diff --git a/examples/testcbor b/examples/testcbor deleted file mode 100644 index 78b7d881..00000000 --- a/examples/testcbor +++ /dev/null @@ -1 +0,0 @@ -‚ƒ \ No newline at end of file