diff --git a/.appveyor.yml b/.appveyor.yml index eb704417..ef47797e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,35 +1,28 @@ -version: 0.5.4-build-{build} +version: 0.6-build-{build} pull_requests: do_not_increment_build_number: true image: -- Visual Studio 2015 - 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 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.12\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\5.15\msvc2019_64) & set CFLAGS=/arch:AVX2 + 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: - cmd: >- - nmake -f Makefile.nmake -nologo CFLAGS="%CFLAGS% -W3 -Os -MDd" + nmake -f Makefile.nmake -nologo CFLAGS="-W3 -Os -MDd" 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 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/.travis.yml b/.travis.yml index a935ac99..4df21cb7 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 @@ -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_ENCODER_WRITER_CONTROL=-1 -DCBOR_PARSER_READER_CONTROL=-1" lib/libtinycbor.a - size lib/libtinycbor.a | tee sizes - make -s clean 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/Makefile b/Makefile index f3ff00b3..948e3faf 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 @@ -234,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/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/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/VERSION b/VERSION index 7d856835..a918a2aa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.5.4 +0.6.0 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/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 diff --git a/src/cbor.h b/src/cbor.h index 313d6833..be5bbc77 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 @@ -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, @@ -189,6 +190,7 @@ typedef enum CborError { CborErrorDataTooLarge = 1024, CborErrorNestingTooDeep, CborErrorUnsupportedType, + CborErrorUnimplementedValidation, /* errors in converting to JSON */ CborErrorJsonObjectKeyIsAggregate = 1280, @@ -202,13 +204,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; }; @@ -216,7 +234,9 @@ 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 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); @@ -237,6 +257,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) @@ -261,30 +282,64 @@ 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 */ +enum CborParserGlobalFlags +{ + CborParserFlag_ExternalSource = 0x01 +}; + enum CborParserIteratorFlags { - CborIteratorFlag_IntegerValueTooLarge = 0x01, - CborIteratorFlag_NegativeInteger = 0x02, - CborIteratorFlag_IteratingStringChunks = 0x02, - CborIteratorFlag_UnknownLength = 0x04, + /* 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 }; +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; @@ -292,14 +347,17 @@ 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_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_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) @@ -454,6 +512,49 @@ 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, + 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 */ @@ -493,7 +594,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_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; } @@ -520,6 +630,7 @@ CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double * } /* Validation API */ +#ifndef CBOR_NO_VALIDATION_API enum CborValidationFlags { /* Bit mapping: @@ -563,8 +674,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, @@ -599,6 +712,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 diff --git a/src/cborencoder.c b/src/cborencoder.c index 692ff620..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) @@ -382,7 +411,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) { @@ -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; @@ -589,13 +621,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 +648,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/cborerrorstrings.c b/src/cborerrorstrings.c index 3fe3a982..44f766a3 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 @@ -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"); @@ -169,6 +172,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 35b7a79e..16269e63 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 @@ -106,6 +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 @@ -154,10 +161,156 @@ 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); -CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, - size_t *len, CborValue *next); +static inline void copy_current_position(CborValue *dst, const CborValue *src) +{ + /* 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->source.end - it->source.ptr) >= n; +} + +static inline void advance_bytes(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_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) +{ + 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__ +__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; +} + +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); +} + +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 60ce5748..74d91a30 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 @@ -142,68 +142,20 @@ * \endif */ -static inline uint16_t get16(const uint8_t *ptr) +static uint64_t extract_number_and_advance(CborValue *it) { - uint16_t result; - memcpy(&result, ptr, sizeof(result)); - return cbor_ntohs(result); -} + /* 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); -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; -} + read_bytes_unchecked(it, &descriptor, 0, 1); + descriptor &= SmallValueMask; -static CborError extract_length(const CborParser *parser, const uint8_t **ptr, size_t *len) -{ - uint64_t v; - CborError err = _cbor_value_extract_number(ptr, parser->end, &v); - if (err) { - *len = 0; - return err; - } + size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); + advance_bytes(it, bytesNeeded + 1); - *len = (size_t)v; - if (v != *len) - return CborErrorDataTooLarge; - return CborNoError; + return v; } static bool is_fixed_type(uint8_t type) @@ -218,17 +170,16 @@ static CborError preparse_value(CborValue *it) /* flags to keep */ FlagsToKeep = CborIteratorFlag_ContainerIsMap | CborIteratorFlag_NextIsMapKey }; - const CborParser *parser = it->parser; - it->type = CborInvalidType; + uint8_t descriptor; /* are we at the end? */ - if (it->ptr == parser->end) + it->type = CborInvalidType; + it->flags &= FlagsToKeep; + if (!read_bytes(it, &descriptor, 0, 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 +195,28 @@ 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; + + it->extra = 0; + + /* read up to 16 bits into it->extra */ + 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); + it->extra = cbor_ntohs(it->extra); + } else { + cbor_static_assert(CborIteratorFlag_IntegerValueTooLarge == (Value32Bit & 3)); + cbor_static_assert((CborIteratorFlag_IntegerValueIs64Bit | + CborIteratorFlag_IntegerValueTooLarge) == (Value64Bit & 3)); + it->flags |= (descriptor & 3); + } + } uint8_t majortype = type >> MajorTypeShift; if (majortype == NegativeIntegerType) { @@ -266,11 +237,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; @@ -286,34 +256,24 @@ 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 && it->ptr != it->parser->end && *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; it->type = CborInvalidType; it->remaining = 0; + it->flags |= CborIteratorFlag_UnknownLength; /* leave_container must consume the Break */ return CborNoError; } @@ -329,6 +289,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; } } @@ -341,14 +302,12 @@ 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); - 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); cbor_assert((it->flags & CborIteratorFlag_UnknownLength) == 0); - it->ptr += length; + advance_bytes(it, length); } return preparse_next_value(it); @@ -368,15 +327,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 */ - cbor_assert((*value->ptr & SmallValueMask) == Value32Bit || (*value->ptr & SmallValueMask) == Value64Bit); - if ((*value->ptr & 1) == (Value32Bit & 1)) - return get32(value->ptr + 1); - - cbor_assert((*value->ptr & SmallValueMask) == Value64Bit); - return get64(value->ptr + 1); + return read_uint32(value, 1); } /** @@ -392,15 +346,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) * @@ -431,9 +396,21 @@ 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() */ +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) * @@ -609,23 +586,21 @@ 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); - cbor_assert(err == CborNoError); + uint64_t len = extract_number_and_advance(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; @@ -656,7 +631,10 @@ 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); + if (recursed->flags & CborIteratorFlag_UnknownLength) + advance_bytes(it, 1); return preparse_next_value(it); } @@ -991,79 +969,185 @@ 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 */ - ++it->ptr; - it->flags |= CborIteratorFlag_IteratingStringChunks; + advance_bytes(it, 1); } + + 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 (it->ptr == it->parser->end) - 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) { - CborError err; + uint8_t descriptor; + size_t bytesNeeded = 1; - /* 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); - } + if (cbor_value_is_length_known(it) && (it->flags & CborIteratorFlag_BeforeFirstStringChunk) == 0) + return CborErrorNoMoreStringChunks; /* are we at the end? */ - if (it->ptr == it->parser->end) + if (!read_bytes(it, &descriptor, 0, 1)) return CborErrorUnexpectedEOF; - if (*it->ptr == BreakByte) { - /* last chunk */ - ++it->ptr; -last_chunk: - *bufferptr = NULL; - *len = 0; - return preparse_next_value(it); - } else if ((uint8_t)(*it->ptr & MajorTypeMask) == it->type) { - err = extract_length(it->parser, &it->ptr, len); - if (err) - return err; - if (*len > (size_t)(it->parser->end - it->ptr)) - return CborErrorUnexpectedEOF; + if (descriptor == BreakByte) + return CborErrorNoMoreStringChunks; + if ((descriptor & MajorTypeMask) != it->type) + return CborErrorIllegalType; - *bufferptr = it->ptr; - it->ptr += *len; + /* find the string length */ + descriptor &= SmallValueMask; + if (descriptor < Value8Bit) { + *len = descriptor; + } else if (unlikely(descriptor > Value64Bit)) { + return CborErrorIllegalNumber; } else { - return CborErrorIllegalType; + 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; } - 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; } -CborError CBOR_INTERNAL_API_CC -_cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, - size_t *len, CborValue *next) +/** + * \fn CborError cbor_value_get_text_string_chunk(const CborValue *value, const char **bufferptr, size_t *len, CborValue *next) + * + * Extracts one text string chunk pointed to by \a value and stores a pointer + * to the data in \a buffer and the size in \a len, which must not be null. If + * no more chunks are available, then \a bufferptr will be set to null. This + * function may be used to iterate over any string without causing its contents + * to be copied to a separate buffer, like the convenience function + * cbor_value_copy_text_string() does. + * + * It is designed to be used in code like: + * + * \code + * if (cbor_value_is_text_string(value)) { + * char *ptr; + * size_t len; + * while (1) { + * err = cbor_value_get_text_string_chunk(value, &ptr, &len, &value)); + * if (err) return err; + * if (ptr == NULL) return CborNoError; + * consume(ptr, len); + * } + * } + * \endcode + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. During iteration, the pointer must only be passed back + * again to this function; passing it to any other function in this library + * results in undefined behavior. If there are no more chunks to be read from + * \a value, then \a next will be set to the next item after this string; if \a + * value points to the last item, then \a next will be invalid. + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_dup_text_string(), cbor_value_copy_text_string(), cbor_value_caculate_string_length(), cbor_value_get_byte_string_chunk() + */ + +/** + * \fn CborError cbor_value_get_byte_string_chunk(const CborValue *value, const char **bufferptr, size_t *len, CborValue *next) + * + * Extracts one byte string chunk pointed to by \a value and stores a pointer + * to the data in \a buffer and the size in \a len, which must not be null. If + * no more chunks are available, then \a bufferptr will be set to null. This + * function may be used to iterate over any string without causing its contents + * to be copied to a separate buffer, like the convenience function + * cbor_value_copy_byte_string() does. + * + * It is designed to be used in code like: + * + * \code + * if (cbor_value_is_byte_string(value)) { + * char *ptr; + * size_t len; + * while (1) { + * err = cbor_value_get_byte_string_chunk(value, &ptr, &len, &value)); + * if (err) return err; + * if (ptr == NULL) return CborNoError; + * consume(ptr, len); + * } + * } + * \endcode + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. During iteration, the pointer must only be passed back + * again to this function; passing it to any other function in this library + * results in undefined behavior. If there are no more chunks to be read from + * \a value, then \a next will be set to the next item after this string; if \a + * value points to the last item, then \a next will be invalid. + * + * \sa cbor_value_dup_byte_string(), cbor_value_copy_byte_string(), cbor_value_caculate_string_length(), cbor_value_get_text_string_chunk() + */ + +CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, + size_t *len, CborValue *next) { CborValue tmp; if (!next) @@ -1110,14 +1194,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; @@ -1136,7 +1224,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); } /** @@ -1423,6 +1511,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 @@ -1433,17 +1523,7 @@ 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); - memcpy(result, &v, sizeof(v)); - return CborNoError; -} /** @} */ diff --git a/src/cborparser_float.c b/src/cborparser_float.c new file mode 100644 index 00000000..426cd7d4 --- /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; + CborError err = cbor_value_get_half_float(value, &v); + cbor_assert(err == CborNoError); + + *result = (float)decode_half((unsigned short)v); + + return CborNoError; +} +#endif diff --git a/src/cborpretty.c b/src/cborpretty.c index b0a3db8c..49d3cae7 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); @@ -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); @@ -407,25 +407,24 @@ 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 */ - indicator = resolve_indicator(it->ptr, it->parser->end, flags); + indicator = resolve_indicator(it, flags); } 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/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 6c9d383c..4c11fb13 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 @@ -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; @@ -380,7 +378,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) { @@ -473,34 +471,23 @@ 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) { - uint64_t len1, len2; - const uint8_t *ptr; + size_t bytelen1 = (size_t)(previous_end - previous); + size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - 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; - previous_end = it->ptr; + previous_end = cbor_value_get_next_byte(it); } /* map: that was the key, so get the value */ @@ -534,7 +521,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); @@ -556,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); @@ -660,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; } 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 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 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) 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 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/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 4a1b1467..31c29152 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 @@ -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(); @@ -54,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(); } @@ -69,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) { @@ -272,204 +185,125 @@ 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; + + encodeOne(input, fn_under_test, buffer, error); + if (QTest::currentTestFailed()) + return; - buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); + QCOMPARE(error, CborNoError); QCOMPARE(buffer, output); } -void addColumns() +void compare(const QVariant &input, const QByteArray &output) +{ + compare(input, encodeVariant, output); +} + +void tst_Encoder::floatAsHalfFloat_data() { - QTest::addColumn("output"); - QTest::addColumn("input"); + addHalfFloat(); } -void addFixedData() +void tst_Encoder::floatAsHalfFloat() { - // 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 + QFETCH(unsigned, rawInput); + QFETCH(double, floatInput); + QFETCH(QByteArray, output); - 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()); + 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 addStringsData() +void tst_Encoder::halfFloat() { - // 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')); + 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 addArraysAndMaps() +void tst_Encoder::floatAsHalfFloatNaN() { - 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})}}); + 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() @@ -635,6 +469,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); 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 1890234a..91a65a00 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 @@ -38,7 +38,6 @@ # include #endif -Q_DECLARE_METATYPE(CborError) namespace QTest { template<> char *toString(const CborError &err) @@ -56,6 +55,8 @@ private slots: // parsing API void integers_data(); void integers(); + void halfFloat_data(); + void halfFloat(); void fixed_data(); void fixed(); void strings_data(); @@ -83,6 +84,11 @@ private slots: void mapsAndArrays_data() { arrays_data(); } void mapsAndArrays(); + void readerApi_data() { arrays_data(); } + void readerApi(); + void reparse_data(); + void reparse(); + // chunked string API void chunkedString_data(); void chunkedString(); @@ -104,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(); @@ -225,9 +231,34 @@ CborError parseOne(CborValue *it, QString *parsed) return cbor_value_to_pretty_stream(qstring_printf, parsed, it, flags); } -template QByteArray raw(const char (&data)[N]) +CborError parseOneChunk(CborValue *it, QString *parsed) { - return QByteArray::fromRawData(data, N - 1); + 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; } void tst_Parser::initParserEmpty() @@ -238,13 +269,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) { @@ -304,151 +328,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() { @@ -495,6 +375,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(); @@ -509,114 +463,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(); @@ -633,14 +485,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(); @@ -853,25 +697,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(); @@ -932,38 +757,101 @@ void tst_Parser::mapsAndArrays() "{_ 1: [_ " + expected + "], \"Hello\": {_ " + expected + ": (_ )}}"); } +struct Input { + QByteArray data; + int consumed; +}; + +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); + }, + /* 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); + }, + /* advance_bytes = */ [](void *token, size_t len) { + auto input = static_cast(token); + input->consumed += int(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)) + return CborErrorUnexpectedEOF; + input->consumed += int(offset); + *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(&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; + 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"); - 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, @@ -981,8 +869,24 @@ static void chunkedStringTest(const QByteArray &data, const QString &concatenate CborValue copy = value; - Q_UNUSED(chunks); // for future API - QCOMPARE(cbor_value_advance(&value), CborNoError); + err = cbor_value_begin_string_iteration(&value); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + forever { + QString decoded; + err = parseOneChunk(&value, &decoded); + 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 { @@ -1433,248 +1337,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(); @@ -2096,7 +1758,7 @@ void tst_Parser::strictValidation() QCOMPARE(err, expectedError); } -void tst_Parser::resumeParsing_data() +void tst_Parser::incompleteData_data() { addColumns(); addFixedData(); @@ -2105,7 +1767,7 @@ void tst_Parser::resumeParsing_data() addMapMixedData(); } -void tst_Parser::resumeParsing() +void tst_Parser::incompleteData() { QFETCH(QByteArray, data); QFETCH(QString, expected); 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/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..6772ea25 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,40 @@ +# /**************************************************************************** +# ** +# ** 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 +) + +target_link_libraries(json2cbor + PRIVATE + libcjson.a + intel::tinycbor +) 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);