diff --git a/.appveyor.yml b/.appveyor.yml index 59137207..ef47797e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,18 +2,13 @@ version: 0.6-build-{build} pull_requests: do_not_increment_build_number: true image: -- Visual Studio 2015 -- Visual Studio 2013 - Visual Studio 2017 +- Visual Studio 2019 install: - cmd: >- - set tests=1 + if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86) & (set QTDIR=C:\Qt\5.13\msvc2017) - if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64) & (set tests=0) - - if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" (call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86) & (set QTDIR=C:\Qt\5.9\msvc2015) - - if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64) & (set QTDIR=C:\Qt\5.9\msvc2017_64) + if /i "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" (call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64) & (set QTDIR=C:\Qt\6.1\msvc2019_64) set path=%PATH%;%QTDIR%\bin build_script: @@ -22,14 +17,12 @@ build_script: cd tests - if /i "%tests%"=="1" qmake CONFIG-=release CONFIG+=debug + qmake CONFIG-=release CONFIG+=debug - if /i "%tests%"=="1" nmake -nologo -s + nmake -nologo -s test_script: - cmd: >- - if /i "%tests%"=="1" nmake -s -nologo TESTARGS=-silent check - - if /i "%tests%"=="0" echo Tests skipped. + nmake -s -nologo TESTARGS=-silent check artifacts: - path: lib\tinycbor.lib deploy: off diff --git a/.travis.yml b/.travis.yml index 7ca287dd..4df21cb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,11 @@ +env: + - BUILD_DOCS=false +jobs: + include: + - # only build docs on main + if: branch = main + env: BUILD_DOCS=true + language: cpp matrix: include: @@ -67,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 @@ -81,4 +89,4 @@ script: (cd tests && make TESTARGS=-silent check -k TESTRUNNER=`which valgrind 2>/dev/null`) - make -s clean - - ./scripts/update-docs.sh + - ! [ $BUILD_DOCS ] || ./scripts/update-docs.sh diff --git a/Makefile b/Makefile index d2cfe447..948e3faf 100644 --- a/Makefile +++ b/Makefile @@ -236,7 +236,10 @@ tag: distcheck .SECONDARY: cflags := $(CPPFLAGS) -I$(SRCDIR)src -cflags += -std=gnu99 $(CFLAGS) +cflags += -std=gnu99 $(CFLAGS) \ + -Werror=incompatible-pointer-types \ + -Werror=implicit-function-declaration \ + -Werror=int-conversion %.o: %.c @test -d $(@D) || $(MKDIR) $(@D) $(CC) $(cflags) $($(basename $(notdir $@))_CCFLAGS) -c -o $@ $< diff --git a/examples/bufferedreader.c b/examples/bufferedreader.c new file mode 100644 index 00000000..a4a87a4a --- /dev/null +++ b/examples/bufferedreader.c @@ -0,0 +1,783 @@ +/* vim: set sw=4 ts=4 et tw=78: */ + +/** + * \brief An example of a buffered CBOR file reader using low-level + * POSIX file I/O as might be implemented in a microcontroller + * RTOS. + * + * \author Stuart Longland + * + * \copyright tinycbor project contributors + * + * \file bufferedwriter.c + */ + +/* Includes for POSIX low-level file I/O */ +#include +#include +#include +#include + +/* Sanity check routine */ +#include + +/* Pull in "standard" integer types */ +#include + +/* Pull in definitions for printf and errno */ +#include +#include +#include + +/* For example usage */ +#include + +#include "../src/cbor.h" + +/** + * Context for the file reader. This stores the file descriptor, a pointer to + * the read buffer, and context pointers. The assumption here is that the + * CBOR document being read is less than 64KiB (65536 bytes) in size. + */ +struct filereader +{ + /** + * Read buffer. This must be allocated by the caller, and sized + * appropriately since the buffer must be big enough to accommodate + * entire string chunks embedded in the CBOR document. + */ + uint8_t* buffer; + + /** + * File descriptor, returned by the `open` system call. + */ + int fd; + + /** + * Size of the file in bytes. + */ + uint16_t file_sz; + + /** + * Size of the read buffer in bytes. + */ + uint16_t buffer_sz; + + /** + * Read position within the file. This basically describes where + * `buffer[0]` came from in the source file. + */ + uint16_t pos; + + /** + * Number of bytes stored in the buffer presently. + */ + uint16_t used_sz; + + /** + * Block size. When reading from the file, we round up to whole multiples + * of this block size to improve I/O efficiency. + */ + uint16_t block_sz; +}; + +/* Implementation routines */ + +/** + * Return the nearest (earlier) position that is on a block boundary. + */ +static uint16_t filereader_get_block_pos( + const struct filereader * const context, + uint16_t pos +) { + return context->block_sz * (pos / context->block_sz); +} + +/** + * Retrieve a pointer to the region defined by \a pos and \a sz. + * + * \retval NULL The region is not contained in the buffer. + */ +static uint8_t *filereader_get_ptr( + struct filereader *context, uint16_t pos, uint16_t sz +) { + /* Sanity check, disallow `sz` bigger than buffer content */ + if (sz > context->used_sz) { + return NULL; + } + + /* Is `pos` off the start of the buffer? */ + if (pos < context->pos) { + return NULL; + } + + /* Is `pos + sz` off the end of the buffer? */ + if ((pos + sz) > (context->pos + context->used_sz)) { + return NULL; + } + + /* We should be good */ + return &(context->buffer[pos - context->pos]); +} + +/** + * Copy the data from the requested position in the file to the buffer. + */ +static int filereader_read( + struct filereader *context, uint16_t pos, uint16_t sz, uint8_t *wptr +) { + /* Seek to the required file position */ + off_t seek_res = lseek(context->fd, pos, SEEK_SET); + if (seek_res != pos) { + /* We failed */ + return -errno; + } + + /* Perform the read */ + ssize_t read_res = read(context->fd, wptr, sz); + if (read_res != sz) { + /* Truncated read */ + return -errno; + } + + return sz; +} + +/** + * Prepend \arg sz bytes from the file to the buffer, shifting the + * read window back \arg sz bytes. + * + * \param context Reader context + * \param sz Number of bytes to read + * + * \retval ≥0 Number of bytes read into the buffer. + * \retval <0 Read failure, value is `errno` negated. + */ +static int filereader_prepend_buffer(struct filereader *context, uint16_t sz) { + /* Compute read position */ + const uint16_t pos = context->pos - sz; + + /* Shuffle existing data forward by sz bytes */ + memmove( + &(context->buffer[sz]), context->buffer, + context->buffer_sz - sz + ); + + /* Copy the data in */ + int read_res = filereader_read(context, pos, sz, context->buffer); + if (read_res >= 0) { + context->pos = pos; + if ((context->used_sz + sz) < context->buffer_sz) { + context->used_sz += sz; + } + } + + return read_res; +} + +/** + * Append \arg sz bytes from the file to the buffer, shifting the + * read window forward \arg sz bytes. + * + * \param context Reader context + * \param sz Number of bytes to read + * + * \retval ≥0 Number of bytes read into the buffer. + * \retval <0 Read failure, value is `errno` negated. + */ +static int filereader_append_buffer(struct filereader *context, uint16_t sz) { + /* Compute read position */ + const uint16_t pos = context->pos + context->used_sz; + + /* Is there room? */ + if ((context->buffer_sz - context->used_sz) < sz) { + /* Shuffle existing data forward by sz bytes */ + memmove( + context->buffer, &(context->buffer[sz]), + context->buffer_sz - sz + ); + context->pos += sz; + context->used_sz -= sz; + } + + /* Copy the data in */ + int read_res = filereader_read( + context, pos, sz, + &(context->buffer[context->used_sz]) + ); + if (read_res >= 0) { + context->used_sz += sz; + } + + return read_res; +} + +/** + * Read data from the file and place it in the buffer, shuffling + * existing data around as required. + * + * \param context Reader context + * \param pos Position in the file to start reading + * \param sz Number of bytes to read + * + * \retval ≥0 Number of bytes read into the buffer. + * \retval <0 Read failure, value is `errno` negated. + */ +static int filereader_load_buffer( + struct filereader *context, uint16_t pos, uint16_t sz +) { + /* Compute the end position (not-inclusive) */ + uint16_t end = pos + sz; + + /* Is this in the buffer already? */ + if ( + (pos < context->pos) + || (end > (context->pos + context->used_sz)) + ) { + /* Make a note of the current buffer state */ + uint16_t buffer_end = context->pos + context->used_sz; + uint16_t buffer_rem = context->buffer_sz - context->used_sz; + + /* Our buffer write position */ + uint8_t* wptr = context->buffer; + + /* + * Dumb approach for now, replace the entire buffer. Round + * the start and end points to block boundaries for efficiency. + */ + pos = filereader_get_block_pos(context, pos); + end = filereader_get_block_pos(context, end + (context->block_sz - 1)); + + /* Clamp the end position to the file size */ + if (end > context->file_sz) { + end = context->file_sz; + } + + /* Compute new rounded size, then clamp to buffer size */ + sz = end - pos; + if (sz > context->buffer_sz) { + sz = context->buffer_sz; + } + + /* Can we re-use existing data? */ + if ( + (pos >= context->pos) + && (pos < buffer_end) + && (end > buffer_end) + ) { + return filereader_append_buffer(context, end - buffer_end); + } else if ( + (pos < context->pos) + && (end >= context->pos) + && (end <= buffer_end) + ) { + return filereader_prepend_buffer(context, context->pos - pos); + } else { + /* Nope, read the lot in */ + const uint16_t file_rem = context->file_sz - pos; + if (file_rem < context->buffer_sz) { + sz = file_rem; + } else { + sz = context->buffer_sz; + } + + int read_res = filereader_read( + context, pos, sz, + context->buffer + ); + if (read_res >= 0) { + context->pos = pos; + context->used_sz = sz; + } + return read_res; + } + } else { + /* Nothing to do, we have the required data already */ + return 0; + } +} + +/** + * Try to read the data into the buffer, then return a pointer to it. + * + * \retval NULL The region could not be loaded into the buffer. + */ +static uint8_t *filereader_fetch_ptr( + struct filereader *context, uint16_t pos, uint16_t sz +) { + /* Ensure the data we need is present */ + if (filereader_load_buffer(context, pos, sz) < 0) { + /* We failed */ + return NULL; + } else { + return filereader_get_ptr(context, pos, sz); + } +} + +/** + * Fetch the reader context from the CborValue + */ +static struct filereader *filereader_get_context(const CborValue * const value) { + return (struct filereader*)(value->parser->data.ctx); +} + +/** + * Fetch the CborValue read position + */ +static uint16_t filereader_get_pos(const CborValue * const value) { + return (uint16_t)(uintptr_t)(value->source.token); +} + +/** + * Set the CborValue read position + */ +static void filereader_set_pos(CborValue * const value, uint16_t new_pos) { + value->source.token = (void*)(uintptr_t)new_pos; +} + +/** + * Return `true` if there is at least \a len bytes that can be read from + * the file at this moment in time. + */ +static bool filereader_impl_can_read_bytes( + const struct CborValue *value, + size_t len +) { + const struct filereader *context = filereader_get_context(value); + const uint16_t pos = filereader_get_pos(value); + + return ((size_t)pos + len) <= context->file_sz; +} + +/** + * Read the bytes from the buffer without advancing the read pointer. + */ +static void* filereader_impl_read_bytes( + const struct CborValue *value, + void* dst, size_t offset, size_t len +) { + struct filereader *context = filereader_get_context(value); + + /* Determine read position factoring in offset */ + const uint16_t pos = filereader_get_pos(value) + offset; + + /* Fetch the data from the file */ + const uint8_t* ptr = filereader_fetch_ptr(context, pos, (uint16_t)len); + if (ptr != NULL) { + return memcpy(dst, ptr, len); + } else { + /* We could not read the data */ + return NULL; + } +} + +/** + * Advance the pointer by the requested amount. + */ +static void filereader_impl_advance_bytes(struct CborValue *value, size_t len) { + filereader_set_pos(value, filereader_get_pos(value) + (uint16_t)len); +} + +/** + * Retrieve a pointer to the string defined by the given offset and length. + */ +CborError filereader_impl_transfer_string( + struct CborValue *value, + const void **userptr, size_t offset, size_t len +) { + struct filereader *context = filereader_get_context(value); + + /* Determine read position factoring in offset */ + const uint16_t pos = filereader_get_pos(value) + offset; + + /* Fetch the data from the file */ + const uint8_t* ptr = filereader_fetch_ptr(context, pos, (uint16_t)len); + if (ptr != NULL) { + /* All good, advance the cursor past the data and return the pointer */ + filereader_set_pos(value, pos + len); + *userptr = (void*)ptr; + return CborNoError; + } else { + /* We could not read the data */ + return CborErrorIO; + } +} + +/** + * Implementation of the CBOR File Reader operations. + */ +static const struct CborParserOperations filereader_ops = { + .can_read_bytes = filereader_impl_can_read_bytes, + .read_bytes = filereader_impl_read_bytes, + .advance_bytes = filereader_impl_advance_bytes, + .transfer_string = filereader_impl_transfer_string +}; + +/** + * Open a CBOR file for reading. + * + * \param[inout] parser CBOR parser object to initialise. + * \param[inout] value Root CBOR cursor object to initialise. + * + * \param[inout] context The file reader context. This must exist + * for the duration the file is open. + * + * \param[inout] buffer Read buffer allocated by the caller where + * the read data will be stored. + * + * \param[in] buffer_sz Size of the read buffer. + * + * \param[in] path The path to the file being read. + * + * \param[in] flags `open` flags. `O_RDONLY` is logic-ORed + * with this value, but the user may provide + * other options here. + * + * \param[in] block_sz Size of read blocks. Where possible, + * reads will be rounded up and aligned with + * blocks of this size for efficiency. Set + * to 0 to default to `buffer_sz / 2`. + * + * \retval CborErrorIO The `open` call failed for some reason, + * see the POSIX standard `errno` variable + * for why. + * + * \retval CborErrorDataTooLarge The CBOR document is too big to be + * handled by this reader. + * + * \retval CborNoError CBOR encoder initialised successfully. + */ +CborError filereader_open( + CborParser * const parser, + CborValue * const value, + struct filereader * const context, + uint8_t *buffer, + uint16_t buffer_sz, + const char* path, + int flags, + uint16_t block_sz +) +{ + CborError error = CborNoError; + struct stat path_stat; + + /* Determine the file size */ + if (stat(path, &path_stat) < 0) { + /* stat fails */ + error = CborErrorIO; + } else { + context->fd = open(path, O_RDONLY | flags); + if (context->fd < 0) { + /* Open fails */ + error = CborErrorIO; + } else { + /* Sanity check document size */ + if (path_stat.st_size > UINT16_MAX) { + error = CborErrorDataTooLarge; + } else { + /* Initialise structure */ + context->pos = 0; + context->used_sz = 0; + context->buffer = buffer; + context->buffer_sz = buffer_sz; + context->file_sz = (uint16_t)path_stat.st_size; + + if (block_sz == 0) { + block_sz = buffer_sz / 2; + } + context->block_sz = block_sz; + + /* Fill the initial buffer */ + if (filereader_load_buffer(context, 0, buffer_sz) >= 0) { + /* Initialise the CBOR parser */ + error = cbor_parser_init_reader( + &filereader_ops, parser, value, (void*)context + ); + } + } + + if (error != CborNoError) { + /* Close the file, if we can */ + assert(close(context->fd) == 0); + context->fd = -1; + } + } + } + + return error; +} + +/** + * Close the file reader. + * + * \param[inout] context File reader context to close. + * + * \retval CborErrorIO The `close` call failed for some + * reason, see the POSIX standard + * `errno` variable for why. + * + * \retval CborNoError File closed, `fd` should be set to -1. + */ +CborError filereader_close(struct filereader * const context) +{ + CborError error = CborNoError; + + /* Try to close the file */ + if (close(context->fd) < 0) { + /* Close fails! */ + error = CborErrorIO; + } else { + context->fd = -1; + } + + return error; +} + +/* --- Example usage of the above reader --- */ + +/** + * Indent the output text to the level specified. Taken from `simplereader.c` + */ +static void indent(int nestingLevel) +{ + while (nestingLevel--) + printf(" "); +} + +/** + * Dump the raw bytes given. Taken from `simplereader.c` + */ +static void dumpbytes(const uint8_t *buf, size_t len) +{ + while (len--) + printf("%02X ", *buf++); +} + +/** + * Recursively dump the CBOR data structure. Taken from `simplereader.c` + */ +static CborError dumprecursive(CborValue *it, int nestingLevel) +{ + while (!cbor_value_at_end(it)) { + CborError err; + CborType type = cbor_value_get_type(it); + + indent(nestingLevel); + switch (type) { + case CborArrayType: + case CborMapType: { + // recursive type + CborValue recursed; + assert(cbor_value_is_container(it)); + puts(type == CborArrayType ? "Array[" : "Map["); + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; // parse error + err = dumprecursive(&recursed, nestingLevel + 1); + if (err) + return err; // parse error + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; // parse error + indent(nestingLevel); + puts("]"); + continue; + } + + case CborIntegerType: { + int64_t val; + cbor_value_get_int64(it, &val); // can't fail + printf("%lld\n", (long long)val); + break; + } + + case CborByteStringType: { + uint8_t *buf; + size_t n; + err = cbor_value_dup_byte_string(it, &buf, &n, it); + if (err) + return err; // parse error + dumpbytes(buf, n); + printf("\n"); + free(buf); + continue; + } + + case CborTextStringType: { + char *buf; + size_t n; + err = cbor_value_dup_text_string(it, &buf, &n, it); + if (err) + return err; // parse error + puts(buf); + free(buf); + continue; + } + + case CborTagType: { + CborTag tag; + cbor_value_get_tag(it, &tag); // can't fail + printf("Tag(%lld)\n", (long long)tag); + break; + } + + case CborSimpleType: { + uint8_t type; + cbor_value_get_simple_type(it, &type); // can't fail + printf("simple(%u)\n", type); + break; + } + + case CborNullType: + puts("null"); + break; + + case CborUndefinedType: + puts("undefined"); + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); // can't fail + puts(val ? "true" : "false"); + break; + } + + case CborDoubleType: { + double val; + if (false) { + float f; + case CborFloatType: + cbor_value_get_float(it, &f); + val = f; + } else { + cbor_value_get_double(it, &val); + } + printf("%g\n", val); + break; + } + case CborHalfFloatType: { + uint16_t val; + cbor_value_get_half_float(it, &val); + printf("__f16(%04x)\n", val); + break; + } + + case CborInvalidType: + assert(false); // can't happen + break; + } + + err = cbor_value_advance_fixed(it); + if (err) + return err; + } + return CborNoError; +} + +/** + * Print the error encountered. If the error is `CborErrorIO`, also check + * the global `errno` variable and print the resultant error seen. + * + * \param[in] error CBORError constant + */ +void print_err(CborError error) +{ + if (error == CborErrorIO) { + printf("IO: %s\n", strerror(errno)); + } else { + printf("%s\n", cbor_error_string(error)); + } +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf( + "Usage: %s [buffer_sz [block_sz]]\n", + argv[0] + ); + return 1; + } else { + struct filereader context; + CborParser parser; + CborValue value; + CborError error; + + uint16_t buffer_sz = 64; + uint16_t block_sz = 0; + + if (argc > 2) { + /* buffer_sz given */ + char *endptr = NULL; + unsigned long long_buffer_sz = strtoul(argv[2], &endptr, 0); + + if (!endptr || *endptr) { + printf("Invalid buffer size %s\n", argv[2]); + return 1; + } + + if (long_buffer_sz > UINT16_MAX) { + printf("Buffer size (%lu bytes) too big\n", long_buffer_sz); + return 1; + } + + buffer_sz = (uint16_t)long_buffer_sz; + + if (argc > 3) { + /* block_sz given */ + char *endptr = NULL; + unsigned long long_block_sz = strtoul(argv[3], &endptr, 0); + + if (!endptr || *endptr) { + printf("Invalid block size %s\n", argv[3]); + return 1; + } + + if (long_block_sz > buffer_sz) { + printf("Block size (%lu bytes) too big\n", long_block_sz); + return 1; + } + + block_sz = (uint16_t)long_block_sz; + } + } + + /* Allocate the buffer on the stack */ + uint8_t buffer[buffer_sz]; + + /* Open the file for writing, create if needed */ + error = filereader_open( + &parser, /* CBOR context */ + &value, /* CBOR cursor */ + &context, /* Reader context */ + buffer, buffer_sz, /* Reader buffer & size */ + argv[1], /* File name */ + 0, /* Open flags */ + block_sz /* Block size */ + ); + + if (error != CborNoError) { + printf("Failed to open %s for reading: ", argv[1]); + print_err(error); + } else { + error = dumprecursive(&value, 0); + if (error != CborNoError) { + printf("Failed to read file: "); + print_err(error); + } + + error = filereader_close(&context); + if (error != CborNoError) { + printf("Failed to close file: "); + print_err(error); + } + } + + if (error != CborNoError) { + return 2; + } else { + return 0; + } + } +} diff --git a/examples/bufferedwriter.c b/examples/bufferedwriter.c new file mode 100644 index 00000000..747fbbb9 --- /dev/null +++ b/examples/bufferedwriter.c @@ -0,0 +1,711 @@ +/* vim: set sw=4 ts=4 et tw=78: */ + +/** + * \brief An example of a buffered CBOR file writer using low-level + * POSIX file I/O as might be implemented in a microcontroller + * RTOS. + * + * \author Stuart Longland + * + * \copyright tinycbor project contributors + * + * \file bufferedwriter.c + */ + +/* Includes for POSIX low-level file I/O */ +#include +#include +#include + +/* Pull in "standard" integer types */ +#include + +/* Pull in definitions for printf and errno */ +#include +#include +#include + +/* For example usage */ +#include + +#include "../src/cbor.h" + +/** + * File writer buffer size. This should be tuned to balance memory usage and + * performance. Most interfaces, bigger writes are more efficient, but on a + * small MCU, memory may be tight. + * + * We're using `uint8_t` to represent our buffer position, so this must be + * strictly less than 256 bytes unless you change it in \ref filewriter (and + * in \ref filewriter_writer_impl) below. + */ +#define FILEWRITER_BUFFER_SZ (64) + +/** + * Context for the file writer. This stores the file descriptor, the write + * buffer, and a counter indicating our position within it. + */ +struct filewriter +{ + /** + * Write buffer. `tinycbor` writes will be initially buffered here, and + * the buffer will automatically be flushed: + * - when the buffer position counter reaches \ref FILEWRITER_BUFFER_SZ + * - when the file is closed with \ref filewriter_close + */ + uint8_t buffer[FILEWRITER_BUFFER_SZ]; + + /** + * File descriptor, returned by the `open` system call. + */ + int fd; + + /** + * Position within the buffer. When less than \ref FILEWRITER_BUFFER_SZ + * this indicates the position for new data. If new data arrives and + * this value is equal to \ref FILEWRITER_BUFFER_SZ, the buffer will be + * flushed first before writing. + */ + uint8_t pos; +}; + +/* Forward declaration, we'll cover this later */ +static CborError filewriter_writer_impl( + void* token, const void* data, size_t len, CborEncoderAppendType append +); + +/** + * Open a CBOR file for writing. + * + * \param[inout] encoder CBOR encoder object to initialise. + * + * \param[inout] context The file writer context. This must exist + * for the duration the file is open. + * + * \param[in] path The path to the file being written. + * + * \param[in] flags `open` flags. `O_WRONLY` is logic-ORed + * with this value, but the user may provide + * other options here. + * + * \param[in] mode Mode bits to set on the created file. + * + * \retval CborErrorIO The `open` call failed for some reason, + * see the POSIX standard `errno` variable + * for why. + * + * \retval CborNoError CBOR encoder initialised successfully. + */ +CborError filewriter_open( + CborEncoder * const encoder, + struct filewriter * const context, + const char* path, + int flags, + mode_t mode +) +{ + CborError error = CborNoError; + + context->fd = open(path, O_WRONLY | flags, mode); + if (context->fd < 0) { + /* Open fails */ + error = CborErrorIO; + } else { + /* Initialise structure */ + context->pos = 0; + + /* Initialise the CBOR encoder */ + cbor_encoder_init_writer(encoder, filewriter_writer_impl, context); + } + + return error; +} + +/** + * Explicitly flush the content of the buffer. This is called automatically + * when the file is closed or when we run out of buffer space. Is a no-op if + * there is nothing to flush. + * + * \param[inout] context File writer context to flush. + * + * \retval CborErrorIO The `write` call failed for some reason, + * see the POSIX standard `errno` variable + * for why. + * + * \retval CborNoError Buffer flushed, position reset. + */ +CborError filewriter_flush(struct filewriter * const context) +{ + CborError error = CborNoError; + + if (context->pos > 0) { + if (write(context->fd, context->buffer, context->pos) < context->pos) { + error = CborErrorIO; + } else { + /* Success */ + context->pos = 0; + } + } + + return error; +} + +/** + * Close the file writer, flushing any remaining data and finishing the write. + * It is assumed that relevant CBOR containers have been closed first. + * + * \param[inout] context File writer context to flush. + * + * \retval CborErrorIO The `write` or `close` call failed for + * some reason, see the POSIX standard + * `errno` variable for why. (CBOR file + * should be considered invalid in this + * case.) *The file may still be open!* + * + * \retval CborNoError File closed, `fd` should be set to -1. + */ +CborError filewriter_close(struct filewriter * const context) +{ + CborError error = filewriter_flush(context); + + if (error == CborNoError) { + int res = close(context->fd); + if (res < 0) { + /* Close failed */ + error = CborErrorIO; + } else { + /* Mark context as closed */ + context->fd = -1; + } + } + + return error; +} + +/** + * CBOR Writer implementation. This function implements the necessary + * interface expected by `tinycbor` to perform synchronous writes to a + * file arbitrarily. Flushing is automatically handled. + */ +static CborError filewriter_writer_impl( + void* token, const void* data, size_t len, CborEncoderAppendType append +) +{ + struct filewriter* context = (struct filewriter*)token; + const uint8_t* rptr = (const uint8_t*)data; + CborError error = CborNoError; + + (void)append; /* We don't use the `append` argument */ + + while ((len > 0) && (error == CborNoError)) { + /* How much space is left? */ + uint8_t rem = FILEWRITER_BUFFER_SZ - context->pos; + + /* Is there any space? */ + if (rem > 0) { + /* Where is our write pointer at? */ + uint8_t* wptr = &(context->buffer[context->pos]); + + /* How much can we write? */ + uint8_t sz = rem; + if (sz > len) { + /* Clamp to amount of data available */ + sz = len; + } + + /* Copy that into the buffer */ + memcpy(wptr, rptr, sz); + context->pos += sz; + rptr += sz; + len -= sz; + rem -= sz; + } + + /* Are we full yet? */ + if (rem == 0) { + error = filewriter_flush(context); + } + } + + return error; +} + +/* --- Example usage of the above writer --- */ + +/** + * Print the error encountered. If the error is `CborErrorIO`, also check + * the global `errno` variable and print the resultant error seen. + * + * \param[in] error CBORError constant + */ +void print_err(CborError error) +{ + if (error == CborErrorIO) { + printf("IO: %s\n", strerror(errno)); + } else { + printf("%s\n", cbor_error_string(error)); + } +} + +/* Forward declarations */ +int exec_arg_array(CborEncoder * const encoder, size_t len, int argc, char **argv); +int exec_arg_map(CborEncoder * const encoder, size_t len, int argc, char **argv); + +/** + * Interpret the arguments given and execute one of the `tinycbor` routines. + * + * \param[inout] encoder CBOREncoder instance + * \param[in] argc Number of command line arguments remaining + * \param[in] argv Command line arguments' values + * + * \retval ≤0 CBOR error occurred, stop here. + * \retval >0 Number of arguments consumed. + */ +int exec_arg(CborEncoder * const encoder, int argc, char **argv) +{ + if (argc > 1) { + CborError error; + int consumed; + int len = strlen(argv[0]); + + printf("Command: %s (%d bytes)\n", argv[0], len); + if (len == 1) { + /* Single-character commands */ + switch (argv[0][0]) { + case '{': /* Begin unknown-length map */ + consumed = exec_arg_map( + encoder, CborIndefiniteLength, + argc - 1, argv + 1 + ); + if (consumed > 0) { + consumed++; + } + return consumed; + case '[': /* Begin unknown-length array */ + consumed = exec_arg_array( + encoder, CborIndefiniteLength, + argc - 1, argv + 1 + ); + if (consumed > 0) { + consumed++; + } + return consumed; + + case 'N': /* Null */ + case 'n': + error = cbor_encode_null(encoder); + if (error != CborNoError) { + printf("Failed at null: "); + print_err(error); + return -1; + } + return 1; + case 'U': /* Undefined */ + case 'u': + error = cbor_encode_undefined(encoder); + if (error != CborNoError) { + printf("Failed at undefined: "); + print_err(error); + return -1; + } + return 1; + case 'F': /* False */ + case 'f': + error = cbor_encode_boolean(encoder, false); + if (error != CborNoError) { + printf("Failed at false: "); + print_err(error); + return -1; + } + return 1; + case 'T': /* True */ + case 't': + error = cbor_encode_boolean(encoder, true); + if (error != CborNoError) { + printf("Failed at true: "); + print_err(error); + return -1; + } + return 1; + default: + printf("Unknown single-character command: %s", argv[0]); + return -1; + } + } else if (strncmp(argv[0], "map(", 4) == 0) { + /* Fixed-size map */ + char *endptr = NULL; + unsigned long maplen = strtoul(&(argv[0][4]), &endptr, 0); + + if (!endptr || (*endptr != ')')) { + /* Not a valid length */ + printf("Invalid length for map: %s\n", argv[0]); + return -1; + } else { + consumed = exec_arg_map(encoder, maplen, argc - 1, argv + 1); + if (consumed > 0) { + consumed++; + } + return consumed; + } + } else if (strncmp(argv[0], "array(", 6) == 0) { + /* Fixed-size array */ + char *endptr = NULL; + unsigned long arraylen = strtoul(&(argv[0][4]), &endptr, 0); + + if (!endptr || (*endptr != ')')) { + /* Not a valid length */ + printf("Invalid length for array: %s\n", argv[0]); + return -1; + } else { + consumed = exec_arg_array( + encoder, arraylen, argc - 1, argv + 1 + ); + if (consumed > 0) { + consumed++; + } + return consumed; + } + } else if (argv[0][0] == 's') { + /* Text string */ + error = cbor_encode_text_string(encoder, &(argv[0][1]), len - 1); + if (error != CborNoError) { + printf( + "Failed at text string (%s): ", + argv[0] + ); + print_err(error); + return -1; + } else { + return 1; + } + } else if (argv[0][0] == 'x') { + /* Byte string, total length must be odd with 'x' prefix */ + if ((len % 2) == 0) { + printf("Byte string must be an even number of hex digits.\n"); + return -1; + } + + uint8_t bytes[len / 2]; + int i; + for (i = 1; i < len; i++) { + char nybble = argv[0][i]; + if ((nybble >= '0') && (nybble <= '9')) { + nybble -= '0'; + } else if ((nybble >= 'A') && (nybble <= 'F')) { + nybble -= 'A'; + nybble += 10; + } else if ((nybble >= 'a') && (nybble <= 'f')) { + nybble -= 'a'; + nybble += 10; + } else { + printf("Unsupported character '%c' in byte string at %d\n", + nybble, i); + return -1; + } + + if ((i % 2) == 0) { + /* Even numbered: lower nybble */ + bytes[(i - 1)/2] |= nybble; + } else { + /* Odd numbered: upper nybble */ + bytes[i/2] = nybble << 4; + } + } + + error = cbor_encode_byte_string(encoder, bytes, len / 2); + if (error != CborNoError) { + printf( + "Failed at byte string (%s): ", + argv[0] + ); + print_err(error); + return -1; + } else { + return 1; + } + } else if (argv[0][0] == 'd') { + /* 32-bit float number */ + char *endptr = NULL; + double d = strtod(&(argv[0][1]), &endptr); + + if (!endptr || (*endptr)) { + /* Invalid number */ + printf("Invalid double %s\n", argv[0]); + return -1; + } + + error = cbor_encode_double(encoder, d); + if (error != CborNoError) { + printf( + "Failed at double (%s): ", + argv[0] + ); + print_err(error); + return -1; + } else { + return 1; + } + } else if (argv[0][0] == 'f') { + /* 32-bit float number */ + char *endptr = NULL; + float f = strtof(&(argv[0][1]), &endptr); + + if (!endptr || (*endptr)) { + /* Invalid number */ + printf("Invalid float %s\n", argv[0]); + return -1; + } + + error = cbor_encode_float(encoder, f); + if (error != CborNoError) { + printf( + "Failed at float (%s): ", + argv[0] + ); + print_err(error); + return -1; + } else { + return 1; + } + } else if (argv[0][0] == 'u') { + /* Unsigned integer (positive) number */ + char *endptr = NULL; + unsigned long long ull = strtoull(&(argv[0][1]), &endptr, 0); + + if (!endptr || (*endptr)) { + /* Invalid number */ + printf("Invalid unsigned integer %s\n", argv[0]); + return -1; + } + + error = cbor_encode_uint(encoder, ull); + if (error != CborNoError) { + printf( + "Failed at unsigned integer (%s): ", + argv[0] + ); + print_err(error); + return -1; + } else { + return 1; + } + } else if (argv[0][0] == '-') { + /* Unsigned integer (negative) number */ + char *endptr = NULL; + unsigned long long ull = strtoull(&(argv[0][1]), &endptr, 0); + + if (!endptr || (*endptr)) { + /* Invalid number */ + printf("Invalid negative unsigned integer %s\n", argv[0]); + return -1; + } + + error = cbor_encode_negative_int(encoder, ull); + if (error != CborNoError) { + printf( + "Failed at negative unsigned integer (%s): ", + argv[0] + ); + print_err(error); + return -1; + } else { + return 1; + } + } else { + printf("Unknown command: %s", argv[0]); + return -1; + } + } else { + /* No arguments to consume. */ + printf("End of arguments.\n"); + return 0; + } +} + +/** + * Interpret the arguments given and execute one of the `tinycbor` routines, + * in an array context. + * + * \param[inout] encoder CBOREncoder instance + * \param[in] len Length of the array. + * \param[in] argc Number of command line arguments remaining + * \param[in] argv Command line arguments' values + * + * \retval ≤0 CBOR error occurred, stop here. + * \retval >0 Number of arguments consumed. + */ +int exec_arg_array(CborEncoder * const encoder, size_t len, int argc, char **argv) { + int consumed = 0; + CborEncoder container; + CborError error; + + error = cbor_encoder_create_array(encoder, &container, len); + if (error != CborNoError) { + printf("Failed to create array (length=%lu): ", len); + print_err(error); + consumed = -1; + } else { + while ((consumed >= 0) && (argc > 0) && (argv[0][0] != ']')) { + int arg_consumed = exec_arg(&container, argc, argv); + + if (arg_consumed > 0) { + consumed += arg_consumed; + argc -= arg_consumed; + argv += arg_consumed; + } else { + /* Error condition */ + printf( + "Failed inside array context (after %d arguments).\n", + consumed + ); + consumed = -1; + } + } + + if (consumed >= 0) { + printf("Close array after %d arguments\n", consumed); + + /* Count end-of-array */ + consumed++; + + error = cbor_encoder_close_container(encoder, &container); + if (error != CborNoError) { + printf("Failed to finish array (length=%lu): ", len); + print_err(error); + consumed = -1; + } + } + } + + return consumed; +} + +/** + * Interpret the arguments given and execute one of the `tinycbor` routines, + * in a map context. + * + * \param[inout] encoder CBOREncoder instance + * \param[in] len Length of the map. + * \param[in] argc Number of command line arguments remaining + * \param[in] argv Command line arguments' values + * + * \retval ≤0 CBOR error occurred, stop here. + * \retval >0 Number of arguments consumed. + */ +int exec_arg_map(CborEncoder * const encoder, size_t len, int argc, char **argv) { + int consumed = 0; + CborEncoder container; + CborError error; + + error = cbor_encoder_create_map(encoder, &container, len); + if (error != CborNoError) { + printf("Failed to create map (length=%lu): ", len); + print_err(error); + consumed = -1; + } else { + while ((consumed >= 0) && (argc > 0) && (argv[0][0] != '}')) { + int arg_consumed = exec_arg(&container, argc, argv); + + if (arg_consumed > 0) { + consumed += arg_consumed; + argc -= arg_consumed; + argv += arg_consumed; + } else { + /* Error condition */ + printf( + "Failed inside map context (after %d arguments).\n", + consumed + ); + consumed = -1; + } + } + + if (consumed >= 0) { + printf("Close map after %d arguments\n", consumed); + + /* Count end-of-map */ + consumed++; + + error = cbor_encoder_close_container(encoder, &container); + if (error != CborNoError) { + printf("Failed to finish map (length=%lu): ", len); + print_err(error); + consumed = -1; + } + } + } + + return consumed; +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf( + "Usage: %s ...\n" + "Valid commands:\n" + "\t{\tStart an unknown-length map\n" + "\t[\tStart an unknown-length array\n" + "\tmap() {\tStart a map of length \n" + "\tarray() [\tStart an array of length \n" + "\ts\tInsert a text string\n" + "\tx\tInsert a byte string\n" + "\tu\tInsert an unsigned positive integer\n" + "\t-\tInsert an unsigned negative integer\n" + "\td\tInsert a 64-bit float\n" + "\tf\tInsert a 32-bit float\n" + "\tf, t\tInsert FALSE or TRUE (case insensitive)\n" + "\tn, u\tInsert NULL or UNDEFINED (case insensitive)\n" + "\nInside maps:\n" + "\t}\tEnd the current map\n" + "\nInside arrays:\n" + "\t]\tEnd the current array\n", + argv[0] + ); + return 1; + } else { + struct filewriter context; + CborEncoder encoder; + CborError error; + + /* Open the file for writing, create if needed */ + error = filewriter_open( + &encoder, /* CBOR context */ + &context, /* Writer context */ + argv[1], /* File name */ + O_CREAT, /* Open flags */ + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH /* File permissions */ + ); + + if (error != CborNoError) { + printf("Failed to open %s for writing: ", argv[1]); + print_err(error); + } else { + argv += 2; + argc -= 2; + + while (argc > 0) { + int consumed = exec_arg(&encoder, argc, argv); + + if (consumed > 0) { + argc -= consumed; + argv += consumed; + } else { + break; + } + } + + error = filewriter_close(&context); + if (error != CborNoError) { + printf("Failed to close file: "); + print_err(error); + } + } + + if (error != CborNoError) { + return 2; + } else { + return 0; + } + } +} diff --git a/examples/simplereader.c b/examples/simplereader.c index 4752c08d..0612ba44 100644 --- a/examples/simplereader.c +++ b/examples/simplereader.c @@ -15,6 +15,8 @@ static uint8_t *readfile(const char *fname, size_t *size) if (fstat(fileno(f), &st) == -1) return NULL; uint8_t *buf = malloc(st.st_size); + if (buf == NULL) + return NULL; *size = fread(buf, st.st_size, 1, f) == 1 ? st.st_size : 0; fclose(f); return buf; @@ -23,13 +25,15 @@ static uint8_t *readfile(const char *fname, size_t *size) static void indent(int nestingLevel) { while (nestingLevel--) - puts(" "); + printf(" "); } static void dumpbytes(const uint8_t *buf, size_t len) { + printf("\""); while (len--) - printf("%02X ", *buf++); + printf("\\x%02X", *buf++); + printf("\""); } static CborError dumprecursive(CborValue *it, int nestingLevel) @@ -85,7 +89,7 @@ static CborError dumprecursive(CborValue *it, int nestingLevel) err = cbor_value_dup_text_string(it, &buf, &n, it); if (err) return err; // parse error - puts(buf); + printf("\"%s\"\n", buf); free(buf); continue; } @@ -153,9 +157,9 @@ static CborError dumprecursive(CborValue *it, int nestingLevel) int main(int argc, char **argv) { - if (argc == 1) { + if (argc != 2) { puts("simplereader "); - return 0; + return 1; } size_t length; 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 fb4970be..33e428f0 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,43 @@ typedef enum CborError { CBOR_API const char *cbor_error_string(CborError error); /* Encoder API */ + +typedef enum CborEncoderAppendType +{ + CborEncoderAppendCborData = 0, + CborEncoderAppendStringData = 1, + CborEncoderApendRawData = 2 +} CborEncoderAppendType; + +/** + * Writer interface call-back function. When there is data to be written to + * the CBOR document, this routine will be called. The \a token parameter is + * taken from the \a token argument provided to \ref cbor_encoder_init_writer + * and may be used in any way the writer function sees fit. + * + * The \a data parameter contains a pointer to the raw bytes to be copied to + * the output buffer, with \a len specifying how long the payload is, which + * can be as small as a single byte or an entire (byte or text) string. + * + * The \a append parameter informs the writer function whether it is writing + * a string or general CBOR data. + */ +typedef CborError (*CborEncoderWriteFunction)(void *token, const void *data, size_t len, CborEncoderAppendType append); + +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; }; @@ -218,6 +250,7 @@ static const size_t CborIndefiniteLength = SIZE_MAX; #ifndef CBOR_NO_ENCODER_API CBOR_API void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags); +CBOR_API void cbor_encoder_init_writer(CborEncoder *encoder, CborEncoderWriteFunction writer, void *); CBOR_API CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value); CBOR_API CborError cbor_encode_int(CborEncoder *encoder, int64_t value); CBOR_API CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value); @@ -228,6 +261,7 @@ CBOR_INLINE_API CborError cbor_encode_text_stringz(CborEncoder *encoder, const c { return cbor_encode_text_string(encoder, string, strlen(string)); } CBOR_API CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length); CBOR_API CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value); +CBOR_API CborError cbor_encode_raw(CborEncoder *encoder, const uint8_t *raw, size_t length); CBOR_INLINE_API CborError cbor_encode_boolean(CborEncoder *encoder, bool value) { return cbor_encode_simple_value(encoder, (int)value - 1 + (CborBooleanType & 0x1f)); } @@ -244,10 +278,10 @@ CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value) CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value) { return cbor_encode_floating_point(encoder, CborDoubleType, &value); } -CBOR_API CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length); -CBOR_API CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length); -CBOR_API CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder); -CBOR_API CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder); +CBOR_API CborError cbor_encoder_create_array(CborEncoder *parentEncoder, CborEncoder *arrayEncoder, size_t length); +CBOR_API CborError cbor_encoder_create_map(CborEncoder *parentEncoder, CborEncoder *mapEncoder, size_t length); +CBOR_API CborError cbor_encoder_close_container(CborEncoder *parentEncoder, const CborEncoder *containerEncoder); +CBOR_API CborError cbor_encoder_close_container_checked(CborEncoder *parentEncoder, const CborEncoder *containerEncoder); CBOR_INLINE_API uint8_t *_cbor_encoder_get_buffer_pointer(const CborEncoder *encoder) { @@ -267,27 +301,122 @@ CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *en /* 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; + +/** + * Defines an interface for abstract document readers. This structure is used + * in conjunction with \ref cbor_parser_init_reader to define how the various + * required operations are to be implemented. + */ +struct CborParserOperations +{ + /** + * Determines whether \a len bytes may be read from the reader. This is + * called before \ref read_bytes and \ref transfer_bytes to ensure it is safe + * to read the requested number of bytes from the reader. + * + * \param value The CBOR value being parsed. + * + * \param len The number of bytes sought. + * + * \retval true \a len bytes may be read from the reader. + * \retval false Insufficient data is available to be read at this time. + */ + bool (*can_read_bytes)(const struct CborValue *value, size_t len); + + /** + * Reads \a len bytes from the reader starting at \a offset bytes from + * the current read position and copies them to \a dst. The read pointer + * is *NOT* modified by this operation. + * + * \param value The CBOR value being parsed. + * + * \param dst The buffer the read bytes will be copied to. + * + * \param offset The starting position for the read relative to the + * current read position. + * + * \param len The number of bytes sought. + */ + void *(*read_bytes)(const struct CborValue *value, void *dst, size_t offset, size_t len); + + /** + * Skips past \a len bytes from the reader without reading them. The read + * pointer is advanced in the process. + * + * \param value The CBOR value being parsed. + * + * \param len The number of bytes skipped. + */ + void (*advance_bytes)(struct CborValue *value, size_t len); + + /** + * Overwrite the user-supplied pointer \a userptr with the address where the + * data indicated by \a offset is located, then advance the read pointer + * \a len bytes beyond that point. + * + * This routine is used for accessing strings embedded in CBOR documents + * (both text and binary strings). + * + * \param value The CBOR value being parsed. + * + * \param userptr The pointer that will be updated to reference the location + * of the data in the buffer. + * + * \param offset The starting position for the read relative to the + * current read position. + * + * \param len The number of bytes sought. + */ + CborError (*transfer_string)(struct CborValue *value, const void **userptr, size_t offset, size_t len); +}; + struct CborParser { - const uint8_t *end; - uint32_t flags; + union { + const uint8_t *end; + void *ctx; + } data; + const struct CborParserOperations *ops; + 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; @@ -297,13 +426,15 @@ 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) @@ -458,6 +589,34 @@ CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uin return _cbor_value_dup_string(value, (void **)buffer, buflen, next); } +CBOR_PRIVATE_API CborError _cbor_value_get_string_chunk_size(const CborValue *value, size_t *len); +CBOR_INLINE_API CborError cbor_value_get_string_chunk_size(const CborValue *value, size_t *len) +{ + assert(value->flags & CborIteratorFlag_IteratingStringChunks); + return _cbor_value_get_string_chunk_size(value, len); +} + +CBOR_INLINE_API bool cbor_value_string_iteration_at_end(const CborValue *value) +{ + size_t dummy; + return cbor_value_get_string_chunk_size(value, &dummy) == CborErrorNoMoreStringChunks; +} + +CBOR_PRIVATE_API CborError _cbor_value_begin_string_iteration(CborValue *value); +CBOR_INLINE_API CborError cbor_value_begin_string_iteration(CborValue *value) +{ + assert(cbor_value_is_text_string(value) || cbor_value_is_byte_string(value)); + assert(!(value->flags & CborIteratorFlag_IteratingStringChunks)); + return _cbor_value_begin_string_iteration(value); +} + +CBOR_PRIVATE_API CborError _cbor_value_finish_string_iteration(CborValue *value); +CBOR_INLINE_API CborError cbor_value_finish_string_iteration(CborValue *value) +{ + assert(cbor_value_string_iteration_at_end(value)); + return _cbor_value_finish_string_iteration(value); +} + CBOR_PRIVATE_API CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, size_t *len, CborValue *next); CBOR_INLINE_API CborError cbor_value_get_text_string_chunk(const CborValue *value, const char **bufferptr, @@ -512,9 +671,16 @@ CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *s /* Floating point */ CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) { return value->type == CborHalfFloatType; } -CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value); -CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result); CBOR_API CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result); +CBOR_INLINE_API CborError cbor_value_get_half_float(const CborValue *value, void *result) +{ + assert(cbor_value_is_half_float(value)); + assert((value->flags & CborIteratorFlag_IntegerValueTooLarge) == 0); + + /* size has already been computed */ + memcpy(result, &value->extra, sizeof(value->extra)); + return CborNoError; +} CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) { return value->type == CborFloatType; } diff --git a/src/cborencoder.c b/src/cborencoder.c index fb0d0401..1abd3ba6 100644 --- a/src/cborencoder.c +++ b/src/cborencoder.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation +** Copyright (C) 2021 Intel Corporation ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal @@ -208,6 +208,18 @@ void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int f encoder->flags = flags; } +void cbor_encoder_init_writer(CborEncoder *encoder, CborEncoderWriteFunction writer, void *token) +{ +#ifdef CBOR_ENCODER_WRITE_FUNCTION + (void) writer; +#else + encoder->data.writer = writer; +#endif + encoder->end = (uint8_t *)token; + encoder->remaining = 2; + encoder->flags = CborIteratorFlag_WriterFunction; +} + static inline void put16(void *where, uint16_t v) { uint16_t v_be = cbor_htons(v); @@ -221,8 +233,12 @@ static inline void put16(void *where, uint16_t v) * being created in the tinycbor output */ static inline bool isOomError(CborError err) { - (void) err; - return true; + if (CBOR_ENCODER_WRITER_CONTROL < 0) + return true; + + /* CborErrorOutOfMemory is the only negative error code, intentionally + * so we can write the test like this */ + return (int)err < 0; } static inline void put32(void *where, uint32_t v) @@ -253,8 +269,20 @@ static inline void advance_ptr(CborEncoder *encoder, size_t n) encoder->data.bytes_needed += n; } -static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len) +static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len, + CborEncoderAppendType appendType) { + if (CBOR_ENCODER_WRITER_CONTROL >= 0) { + if (encoder->flags & CborIteratorFlag_WriterFunction || CBOR_ENCODER_WRITER_CONTROL != 0) { +# ifdef CBOR_ENCODER_WRITE_FUNCTION + return CBOR_ENCODER_WRITE_FUNCTION(encoder->end, data, len, appendType); +# else + return encoder->data.writer(encoder->end, data, len, appendType); +# endif + } + } + +#if CBOR_ENCODER_WRITER_CONTROL <= 0 if (would_overflow(encoder, len)) { if (encoder->end != NULL) { len -= encoder->end - encoder->data.ptr; @@ -268,12 +296,13 @@ static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, memcpy(encoder->data.ptr, data, len); encoder->data.ptr += len; +#endif return CborNoError; } static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte) { - return append_to_buffer(encoder, &byte, 1); + return append_to_buffer(encoder, &byte, 1, CborEncoderAppendCborData); } static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) @@ -302,7 +331,7 @@ static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t u *bufstart = shiftedMajorType + Value8Bit + more; } - return append_to_buffer(encoder, bufstart, bufend - bufstart); + return append_to_buffer(encoder, bufstart, bufend - bufstart, CborEncoderAppendCborData); } static inline void saturated_decrement(CborEncoder *encoder) @@ -399,7 +428,7 @@ CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, cons else put16(buf + 1, *(const uint16_t*)value); saturated_decrement(encoder); - return append_to_buffer(encoder, buf, size + 1); + return append_to_buffer(encoder, buf, size + 1, CborEncoderAppendCborData); } /** @@ -418,7 +447,7 @@ static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shif CborError err = encode_number(encoder, length, shiftedMajorType); if (err && !isOomError(err)) return err; - return append_to_buffer(encoder, string, length); + return append_to_buffer(encoder, string, length, CborEncoderAppendStringData); } /** @@ -443,6 +472,20 @@ CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, s return encode_string(encoder, length, ByteStringType << MajorTypeShift, string); } +/** + * Puts the data of length \a length in \a raw into to the encoding buffer of + * \a encoder. This function can be used if you have stored CBOR encoded data + * and want to push it to your current encoding buffer. Be aware, you are + * responsible for the data in \a raw is valid and that the validity of the + * resulting stream after this operation remains valid. + * + * \sa CborError cbor_encode_byte_string + */ +CborError cbor_encode_raw(CborEncoder *encoder, const uint8_t *raw, size_t length) +{ + return append_to_buffer(encoder, raw, length, CborEncoderApendRawData); +} + /** * Appends the text string \a string of length \a length to the CBOR stream * provided by \a encoder. CBOR requires that \a string be valid UTF-8, but @@ -466,9 +509,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; @@ -482,7 +528,7 @@ static CborError create_container(CborEncoder *encoder, CborEncoder *container, } /** - * Creates a CBOR array in the CBOR stream provided by \a encoder and + * Creates a CBOR array in the CBOR stream provided by \a parentEncoder and * initializes \a arrayEncoder so that items can be added to the array using * the CborEncoder functions. The array must be terminated by calling either * cbor_encoder_close_container() or cbor_encoder_close_container_checked() @@ -491,17 +537,17 @@ static CborError create_container(CborEncoder *encoder, CborEncoder *container, * The number of items inserted into the array must be exactly \a length items, * otherwise the stream is invalid. If the number of items is not known when * creating the array, the constant \ref CborIndefiniteLength may be passed as - * length instead. + * length instead, and an indefinite length array is created. * * \sa cbor_encoder_create_map */ -CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length) +CborError cbor_encoder_create_array(CborEncoder *parentEncoder, CborEncoder *arrayEncoder, size_t length) { - return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift); + return create_container(parentEncoder, arrayEncoder, length, ArrayType << MajorTypeShift); } /** - * Creates a CBOR map in the CBOR stream provided by \a encoder and + * Creates a CBOR map in the CBOR stream provided by \a parentEncoder and * initializes \a mapEncoder so that items can be added to the map using * the CborEncoder functions. The map must be terminated by calling either * cbor_encoder_close_container() or cbor_encoder_close_container_checked() @@ -510,7 +556,7 @@ CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEnco * The number of pair of items inserted into the map must be exactly \a length * items, otherwise the stream is invalid. If the number is not known * when creating the map, the constant \ref CborIndefiniteLength may be passed as - * length instead. + * length instead, and an indefinite length map is created. * * \b{Implementation limitation:} TinyCBOR cannot encode more than SIZE_MAX/2 * key-value pairs in the stream. If the length \a length is larger than this @@ -519,11 +565,11 @@ CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEnco * * \sa cbor_encoder_create_array */ -CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length) +CborError cbor_encoder_create_map(CborEncoder *parentEncoder, CborEncoder *mapEncoder, size_t length) { if (length != CborIndefiniteLength && length > SIZE_MAX / 2) return CborErrorDataTooLarge; - return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift); + return create_container(parentEncoder, mapEncoder, length, MapType << MajorTypeShift); } /** @@ -538,19 +584,19 @@ CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, * * \sa cbor_encoder_create_array(), cbor_encoder_create_map() */ -CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder) +CborError cbor_encoder_close_container(CborEncoder *parentEncoder, const CborEncoder *containerEncoder) { // synchronise buffer state with that of the container - encoder->end = containerEncoder->end; - encoder->data = containerEncoder->data; + parentEncoder->end = containerEncoder->end; + parentEncoder->data = containerEncoder->data; if (containerEncoder->flags & CborIteratorFlag_UnknownLength) - return append_byte_to_buffer(encoder, BreakByte); + return append_byte_to_buffer(parentEncoder, BreakByte); if (containerEncoder->remaining != 1) return containerEncoder->remaining == 0 ? CborErrorTooManyItems : CborErrorTooFewItems; - if (!encoder->end) + if (!parentEncoder->end) return CborErrorOutOfMemory; /* keep the state */ return CborNoError; 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 b9623002..dd6d283b 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,7 +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); +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, n); +#else + return it->parser->ops->can_read_bytes(it, 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->data.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, n); +#else + it->parser->ops->advance_bytes(it, 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, ptr, offset, len); +#else + return it->parser->ops->transfer_string(it, 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, dst, offset, n); +#else + return it->parser->ops->read_bytes(it, 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 840eb18c..efbf3d4a 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); -} - -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; + /* 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); - 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,18 @@ 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); + return read_uint32(value, 1); +} - cbor_assert((*value->ptr & SmallValueMask) == Value64Bit); - return get64(value->ptr + 1); +static void cbor_parser_init_common(CborParser *parser, CborValue *it) +{ + memset(parser, 0, sizeof(*parser)); + it->parser = parser; + it->remaining = 1; /* there's one type altogether, usually an array or map */ + it->flags = 0; } /** @@ -391,13 +353,39 @@ 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; - it->parser = parser; - it->ptr = buffer; - it->remaining = 1; /* there's one type altogether, usually an array or map */ - it->flags = 0; + cbor_parser_init_common(parser, it); + parser->data.end = buffer + size; + parser->flags = (enum CborParserGlobalFlags)flags; + it->source.ptr = buffer; + return preparse_value(it); +} + +/** + * Initializes the CBOR parser for parsing a document that is read by an + * abstract reader interface defined by \a ops. The iterator to the first + * element is returned in \a it. + * + * The \a parser structure needs to remain valid throughout the decoding + * process. It is not thread-safe to share one CborParser among multiple + * threads iterating at the same time, but the object can be copied so multiple + * threads can iterate. + * + * The \a ops structure defines functions that implement the read process from + * the buffer given, see \ref CborParserOperations for further details. + * + * The \a ctx is stored in the \ref CborParser object as `data.ctx` and may be + * used however the reader implementation sees fit. For cursor-specific + * context information, the \ref CborValue `source.token` union member is + * initialised to `NULL` and may be used however the reader implementation + * sees fit. + */ +CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborParser *parser, CborValue *it, void *ctx) +{ + cbor_parser_init_common(parser, it); + parser->ops = ops; + parser->flags = CborParserFlag_ExternalSource; + parser->data.ctx = ctx; + it->source.token = NULL; return preparse_value(it); } @@ -431,9 +419,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 +609,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 +654,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,73 +992,99 @@ CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len return _cbor_value_copy_string(value, NULL, len, NULL); } -static inline void prepare_string_iteration(CborValue *it) +CborError _cbor_value_begin_string_iteration(CborValue *it) { + it->flags |= CborIteratorFlag_IteratingStringChunks | + CborIteratorFlag_BeforeFirstStringChunk; if (!cbor_value_is_length_known(it)) { /* chunked string: we're before the first chunk; * advance to the first chunk */ - ++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; } @@ -1143,7 +1170,7 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t */ CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, - size_t *len, CborValue *next) + size_t *len, CborValue *next) { CborValue tmp; if (!next) @@ -1190,14 +1217,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; @@ -1216,7 +1247,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); } /** @@ -1503,6 +1534,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 @@ -1515,30 +1548,5 @@ CborError cbor_value_map_find_value(const CborValue *map, const char *string, Cb * * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float() */ -CborError cbor_value_get_half_float(const CborValue *value, void *result) -{ - uint16_t v; - - v = _cbor_value_get_half_float_helper(value); - memcpy(result, &v, sizeof(v)); - - return CborNoError; -} - -/** \internal - * - * Retrieves the CBOR half-precision floating point value binary - * representation as 16-bit unsigned integer. - * The result can be used as-is, e.g. to copy bitwise into the - * system-dependent half-precision floating point type, or it can be - * converted to the C language standard floating point type - * (float or double). - */ -CBOR_PRIVATE_API uint16_t _cbor_value_get_half_float_helper(const CborValue *value) -{ - cbor_assert(cbor_value_is_half_float(value)); - /* size has been computed already */ - return get16(value->ptr + 1); -} /** @} */ diff --git a/src/cborparser_float.c b/src/cborparser_float.c index 739d3488..426cd7d4 100644 --- a/src/cborparser_float.c +++ b/src/cborparser_float.c @@ -44,8 +44,8 @@ CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result) { uint16_t v; - - v = _cbor_value_get_half_float_helper(value); + CborError err = cbor_value_get_half_float(value, &v); + cbor_assert(err == CborNoError); *result = (float)decode_half((unsigned short)v); diff --git a/src/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 a0f32625..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 @@ -179,6 +180,10 @@ static CborError dump_bytestring_base16(char **result, CborValue *it) /* a Base16 (hex) output is twice as big as our buffer */ buffer = (uint8_t *)malloc(n * 2 + 1); + if (buffer == NULL) + /* out of memory */ + return CborErrorOutOfMemory; + *result = (char *)buffer; /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */ @@ -204,7 +209,12 @@ static CborError generic_dump_base64(char **result, CborValue *it, const char al /* a Base64 output (untruncated) has 4 bytes for every 3 in the input */ size_t len = (n + 5) / 3 * 4; - out = buffer = (uint8_t *)malloc(len + 1); + buffer = (uint8_t *)malloc(len + 1); + if (buffer == NULL) + /* out of memory */ + return CborErrorOutOfMemory; + + out = buffer; *result = (char *)buffer; /* we read our byte string at the tail end of the buffer @@ -498,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) @@ -508,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 93784a4f..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,9 +471,11 @@ static CborError validate_container(CborValue *it, int containerType, uint32_t f continue; if (flags & CborValidateMapIsSorted) { + if (it->parser->flags & CborParserFlag_ExternalSource) + return CborErrorUnimplementedValidation; if (previous) { size_t bytelen1 = (size_t)(previous_end - previous); - size_t bytelen2 = (size_t)(it->ptr - current); + size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - current); int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2); if (r == 0 && bytelen1 != bytelen2) @@ -487,7 +487,7 @@ static CborError validate_container(CborValue *it, int containerType, uint32_t f } previous = current; - previous_end = it->ptr; + previous_end = cbor_value_get_next_byte(it); } /* map: that was the key, so get the value */ @@ -521,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); @@ -543,24 +543,24 @@ static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft size_t n = 0; const void *ptr; - err = _cbor_value_prepare_string_iteration(it); + err = cbor_value_begin_string_iteration(it); if (err) return err; while (1) { CborValue next; err = _cbor_value_get_string_chunk(it, &ptr, &n, &next); - if (err) - return err; - if (ptr) { + if (!err) { err = validate_number(it, type, flags); if (err) return err; } *it = next; - if (!ptr) - break; + if (err == CborErrorNoMoreStringChunks) + return cbor_value_finish_string_iteration(it); + if (err) + return err; if (type == CborTextStringType && flags & CborValidateUtf8) { err = validate_utf8_string(ptr, n); @@ -647,7 +647,7 @@ CborError cbor_value_validate(const CborValue *it, uint32_t flags) CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS); if (err) return err; - if (flags & CborValidateCompleteData && it->ptr != it->parser->end) + if (flags & CborValidateCompleteData && can_read_bytes(it, 1)) return CborErrorGarbageAtEnd; return CborNoError; } 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.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/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 7955e11a..9e683f45 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 @@ -61,6 +61,10 @@ private slots: void maps_data() { tags_data(); } void maps(); + void writerApi_data() { tags_data(); } + void writerApi(); + void writerApiFail_data() { tags_data(); } + void writerApiFail(); void shortBuffer_data() { tags_data(); } void shortBuffer(); void tooShortArrays_data() { tags_data(); } @@ -73,108 +77,13 @@ private slots: void tooBigMaps(); void illegalSimpleType_data(); void illegalSimpleType(); + + void encodeRaw_data() { tags_data(); } + void encodeRaw(); }; #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) { @@ -315,227 +224,6 @@ void compare(const QVariant &input, const QByteArray &output) compare(input, encodeVariant, output); } -void addColumns() -{ - QTest::addColumn("output"); - QTest::addColumn("input"); -} - -void addFixedData() -{ - // unsigned integers - QTest::newRow("0U") << raw("\x00") << QVariant(0U); - QTest::newRow("1U") << raw("\x01") << QVariant(1U); - QTest::newRow("10U") << raw("\x0a") << QVariant(10U); - QTest::newRow("23U") << raw("\x17") << QVariant(23U); - QTest::newRow("24U") << raw("\x18\x18") << QVariant(24U); - QTest::newRow("255U") << raw("\x18\xff") << QVariant(255U); - QTest::newRow("256U") << raw("\x19\x01\x00") << QVariant(256U); - QTest::newRow("65535U") << raw("\x19\xff\xff") << QVariant(65535U); - QTest::newRow("65536U") << raw("\x1a\0\1\x00\x00") << QVariant(65536U); - QTest::newRow("4294967295U") << raw("\x1a\xff\xff\xff\xff") << QVariant(4294967295U); - QTest::newRow("4294967296U") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_UINT64_C(4294967296)); - QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") - << QVariant(std::numeric_limits::max()); - - // signed integers containing non-negative numbers - QTest::newRow("0") << raw("\x00") << QVariant(0); - QTest::newRow("1") << raw("\x01") << QVariant(1); - QTest::newRow("10") << raw("\x0a") << QVariant(10); - QTest::newRow("23") << raw("\x17") << QVariant(23); - QTest::newRow("24") << raw("\x18\x18") << QVariant(24); - QTest::newRow("255") << raw("\x18\xff") << QVariant(255); - QTest::newRow("256") << raw("\x19\x01\x00") << QVariant(256); - QTest::newRow("65535") << raw("\x19\xff\xff") << QVariant(65535); - QTest::newRow("65536") << raw("\x1a\0\1\x00\x00") << QVariant(65536); - QTest::newRow("4294967295") << raw("\x1a\xff\xff\xff\xff") << QVariant(Q_INT64_C(4294967295)); - QTest::newRow("4294967296") << raw("\x1b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(4294967296)); - - // signed integers containing negative numbers - QTest::newRow("-1") << raw("\x20") << QVariant(-1); - QTest::newRow("-2") << raw("\x21") << QVariant(-2); - QTest::newRow("-24") << raw("\x37") << QVariant(-24); - QTest::newRow("-25") << raw("\x38\x18") << QVariant(-25); - QTest::newRow("-UINT8_MAX") << raw("\x38\xff") << QVariant(-256); - QTest::newRow("-UINT8_MAX-1") << raw("\x39\x01\x00") << QVariant(-257); - QTest::newRow("-UINT16_MAX") << raw("\x39\xff\xff") << QVariant(-65536); - QTest::newRow("-UINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << QVariant(-65537); - QTest::newRow("-UINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << QVariant(Q_INT64_C(-4294967296)); - QTest::newRow("-UINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << QVariant(Q_INT64_C(-4294967297)); - - // negative integers - auto neg = [](quint64 v) { return QVariant::fromValue({v}); }; - QTest::newRow("negative1") << raw("\x20") << neg(1); - QTest::newRow("negative2") << raw("\x21") << neg(2); - QTest::newRow("negative24") << raw("\x37") << neg(24); - QTest::newRow("negative25") << raw("\x38\x18") << neg(25); - QTest::newRow("negativeUINT8_MAX") << raw("\x38\xff") << neg(256); - QTest::newRow("negativeUINT8_MAX-1") << raw("\x39\x01\x00") << neg(257); - QTest::newRow("negativeUINT16_MAX") << raw("\x39\xff\xff") << neg(65536); - QTest::newRow("negativeUINT16_MAX-1") << raw("\x3a\0\1\x00\x00") << neg(65537); - QTest::newRow("negativeUINT32_MAX") << raw("\x3a\xff\xff\xff\xff") << neg(Q_UINT64_C(4294967296)); - QTest::newRow("negativeUINT32_MAX-1") << raw("\x3b\0\0\0\1\0\0\0\0") << neg(Q_UINT64_C(4294967297)); - QTest::newRow("negativeUINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe") - << neg(std::numeric_limits::max()); - QTest::newRow("negativeUINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff") << neg(0); - - QTest::newRow("simple0") << raw("\xe0") << QVariant::fromValue(SimpleType{0}); - QTest::newRow("simple19") << raw("\xf3") << QVariant::fromValue(SimpleType{19}); - QTest::newRow("false") << raw("\xf4") << QVariant(false); - QTest::newRow("true") << raw("\xf5") << QVariant(true); - QTest::newRow("null") << raw("\xf6") << QVariant::fromValue(nullptr); - QTest::newRow("undefined") << raw("\xf7") << QVariant(); - QTest::newRow("simple32") << raw("\xf8\x20") << QVariant::fromValue(SimpleType{32}); - QTest::newRow("simple255") << raw("\xf8\xff") << QVariant::fromValue(SimpleType{255}); - - // floating point -#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) - QTest::newRow("0.f16") << raw("\xf9\0\0") << QVariant::fromValue(Float16Standin{0x0000}); -#else - QTest::newRow("0.f16") << raw("\xf9\0\0") << QVariant::fromValue(qfloat16(0)); - QTest::newRow("-1.f16") << raw("\xf9\xbc\0") << QVariant::fromValue(qfloat16(-1)); - QTest::newRow("1.5f16") << raw("\xf9\x3e\0") << QVariant::fromValue(qfloat16(1.5)); - QTest::newRow("nan_f16") << raw("\xf9\x7e\0") << QVariant::fromValue(myNaNf()); - QTest::newRow("-inf_f16") << raw("\xf9\xfc\0") << QVariant::fromValue(myNInff()); - QTest::newRow("+inf_f16") << raw("\xf9\x7c\0") << QVariant::fromValue(myInff()); -#endif - - QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << QVariant::fromValue(0.f); - QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << QVariant(0.); - QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << QVariant::fromValue(-1.f); - QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << QVariant(-1.); - QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << QVariant::fromValue(16777215.f); - QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << QVariant::fromValue(16777215.); - QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << QVariant(-16777215.f); - QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << QVariant::fromValue(-16777215.); - - QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << QVariant::fromValue(myNaNf()); - QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << QVariant(myNaN()); - QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << QVariant::fromValue(myNInff()); - QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << QVariant(myNInf()); - QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << QVariant::fromValue(myInff()); - QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << QVariant(myInf()); -} - -void addStringsData() -{ - // byte strings - QTest::newRow("emptybytestring") << raw("\x40") << QVariant(QByteArray("")); - QTest::newRow("bytestring1") << raw("\x41 ") << QVariant(QByteArray(" ")); - QTest::newRow("bytestring1-nul") << raw("\x41\0") << QVariant(QByteArray("", 1)); - QTest::newRow("bytestring5") << raw("\x45Hello") << QVariant(QByteArray("Hello")); - QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234") - << QVariant(QByteArray("123456789012345678901234")); - QTest::newRow("bytestring256") << raw("\x59\1\0") + QByteArray(256, '3') - << QVariant(QByteArray(256, '3')); - - // text strings - QTest::newRow("emptytextstring") << raw("\x60") << QVariant(""); - QTest::newRow("textstring1") << raw("\x61 ") << QVariant(" "); - QTest::newRow("textstring1-nul") << raw("\x61\0") << QVariant(QString::fromLatin1("", 1)); - QTest::newRow("textstring5") << raw("\x65Hello") << QVariant("Hello"); - QTest::newRow("textstring24") << raw("\x78\x18""123456789012345678901234") - << QVariant("123456789012345678901234"); - QTest::newRow("textstring256") << raw("\x79\1\0") + QByteArray(256, '3') - << QVariant(QString(256, '3')); -} - -void addArraysAndMaps() -{ - QTest::newRow("emptyarray") << raw("\x80") << make_list(); - QTest::newRow("emptymap") << raw("\xa0") << make_map({}); - - QTest::newRow("array-0") << raw("\x81\0") << make_list(0); - QTest::newRow("array-{0-0}") << raw("\x82\0\0") << make_list(0, 0); - QTest::newRow("array-Hello") << raw("\x81\x65Hello") << make_list("Hello"); - QTest::newRow("array-array-0") << raw("\x81\x81\0") << make_list(make_list(0)); - QTest::newRow("array-array-{0-0}") << raw("\x81\x82\0\0") << make_list(make_list(0, 0)); - QTest::newRow("array-array-0-0") << raw("\x82\x81\0\0") << make_list(make_list(0),0); - QTest::newRow("array-array-Hello") << raw("\x81\x81\x65Hello") << make_list(make_list("Hello")); - - QTest::newRow("map-0:0") << raw("\xa1\0\0") << make_map({{0,0}}); - QTest::newRow("map-0:0-1:1") << raw("\xa2\0\0\1\1") << make_map({{0,0}, {1,1}}); - QTest::newRow("map-0:{map-0:0-1:1}") << raw("\xa1\0\xa2\0\0\1\1") << make_map({{0, make_map({{0,0}, {1,1}})}}); - - QTest::newRow("array-map1") << raw("\x81\xa1\0\0") << make_list(make_map({{0,0}})); - QTest::newRow("array-map2") << raw("\x82\xa1\0\0\xa1\1\1") << make_list(make_map({{0,0}}), make_map({{1,1}})); - - QTest::newRow("map-array1") << raw("\xa1\x62oc\x81\0") << make_map({{"oc", make_list(0)}}); - QTest::newRow("map-array2") << raw("\xa1\x62oc\x84\0\1\2\3") << make_map({{"oc", make_list(0, 1, 2, 3)}}); - QTest::newRow("map-array3") << raw("\xa2\x62oc\x82\0\1\2\3") << make_map({{"oc", make_list(0, 1)}, {2, 3}}); - - // indeterminate length - QTest::newRow("_emptyarray") << raw("\x9f\xff") << QVariant::fromValue(IndeterminateLengthArray{}); - QTest::newRow("_emptymap") << raw("\xbf\xff") << make_ilmap({}); - - QTest::newRow("_array-0") << raw("\x9f\0\xff") << make_ilarray({0}); - QTest::newRow("_array-{0-0}") << raw("\x9f\0\0\xff") << make_ilarray({0, 0}); - QTest::newRow("_array-Hello") << raw("\x9f\x65Hello\xff") << make_ilarray({"Hello"}); - QTest::newRow("_array-array-0") << raw("\x9f\x81\0\xff") << make_ilarray({make_list(0)}); - QTest::newRow("_array-_array-0") << raw("\x9f\x9f\0\xff\xff") << make_ilarray({make_ilarray({0})}); - QTest::newRow("_array-_array-{0-0}") << raw("\x9f\x9f\0\0\xff\xff") << make_ilarray({make_ilarray({0, 0})}); - QTest::newRow("_array-_array-0-0") << raw("\x9f\x9f\0\xff\0\xff") << make_ilarray({make_ilarray({0}),0}); - QTest::newRow("_array-_array-Hello") << raw("\x9f\x9f\x65Hello\xff\xff") << make_ilarray({make_ilarray({"Hello"})}); - - QTest::newRow("_map-0:0") << raw("\xbf\0\0\xff") << make_ilmap({{0,0}}); - QTest::newRow("_map-0:0-1:1") << raw("\xbf\0\0\1\1\xff") << make_ilmap({{0,0}, {1,1}}); - QTest::newRow("_map-0:{map-0:0-1:1}") << raw("\xbf\0\xa2\0\0\1\1\xff") << make_ilmap({{0, make_map({{0,0}, {1,1}})}}); - QTest::newRow("_map-0:{_map-0:0-1:1}") << raw("\xbf\0\xbf\0\0\1\1\xff\xff") << make_ilmap({{0, make_ilmap({{0,0}, {1,1}})}}); - - QTest::newRow("_array-map1") << raw("\x9f\xa1\0\0\xff") << make_ilarray({make_map({{0,0}})}); - QTest::newRow("_array-_map1") << raw("\x9f\xbf\0\0\xff\xff") << make_ilarray({make_ilmap({{0,0}})}); - QTest::newRow("_array-map2") << raw("\x9f\xa1\0\0\xa1\1\1\xff") << make_ilarray({make_map({{0,0}}), make_map({{1,1}})}); - QTest::newRow("_array-_map2") << raw("\x9f\xbf\0\0\xff\xbf\1\1\xff\xff") << make_ilarray({make_ilmap({{0,0}}), make_ilmap({{1,1}})}); - - QTest::newRow("_map-array1") << raw("\xbf\x62oc\x81\0\xff") << make_ilmap({{"oc", make_list(0)}}); - QTest::newRow("_map-_array1") << raw("\xbf\x62oc\x9f\0\xff\xff") << make_ilmap({{"oc", make_ilarray({0})}}); - QTest::newRow("_map-array2") << raw("\xbf\x62oc\x84\0\1\2\3\xff") << make_ilmap({{"oc", make_list(0, 1, 2, 3)}}); - QTest::newRow("_map-_array2") << raw("\xbf\x62oc\x9f\0\1\2\3\xff\xff") << make_ilmap({{"oc", make_ilarray({0, 1, 2, 3})}}); - QTest::newRow("_map-array3") << raw("\xbf\x62oc\x82\0\1\2\3\xff") << make_ilmap({{"oc", make_list(0, 1)}, {2, 3}}); - QTest::newRow("_map-_array3") << raw("\xbf\x62oc\x9f\0\1\xff\2\3\xff") << make_ilmap({{"oc", make_ilarray({0, 1})}, {2, 3}}); - - // tagged - QTest::newRow("array-1(0)") << raw("\x81\xc1\0") << make_list(QVariant::fromValue(Tag{1, 0})); - QTest::newRow("array-1(map)") << raw("\x81\xc1\xa0") << make_list(QVariant::fromValue(Tag{1, make_map({})})); - QTest::newRow("map-1(2):3(4)") << raw("\xa1\xc1\2\xc3\4") << make_map({{QVariant::fromValue(Tag{1, 2}), QVariant::fromValue(Tag{3, 4})}}); -} - -static void addHalfFloat() -{ - QTest::addColumn("output"); - QTest::addColumn("rawInput"); - QTest::addColumn("floatInput"); - - QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0; - QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0; - - QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10); - QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10); - - QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10)); - QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10)); - - QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14); - QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14); - - QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0; - QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0; - - QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5; - QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5; - - QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10)); - QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10)); - - QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf(); - QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf(); - - QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN(); - QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN(); - QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN(); - QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN(); -} - void tst_Encoder::floatAsHalfFloat_data() { addHalfFloat(); @@ -784,6 +472,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); @@ -880,4 +609,22 @@ void tst_Encoder::illegalSimpleType() QCOMPARE(cbor_encode_simple_value(&encoder, type), CborErrorIllegalSimpleType); } +void tst_Encoder::encodeRaw() +{ + QFETCH(QVariant, input); + QFETCH(QByteArray, output); + + // just confirm it copies the data + QByteArray buffer(output.length(), Qt::Uninitialized); + + uint8_t *bufptr = reinterpret_cast(buffer.data()); + CborEncoder encoder; + cbor_encoder_init(&encoder, bufptr, buffer.length(), 0); + + CborError error = cbor_encode_raw(&encoder, reinterpret_cast(output.data()), + output.size()); + QCOMPARE(error, CborNoError); + QCOMPARE(buffer, output); +} + QTEST_MAIN(tst_Encoder) 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 891264f0..0843bff4 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) @@ -85,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(); @@ -106,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(); @@ -257,11 +261,6 @@ CborError parseOneChunk(CborValue *it, QString *parsed) return err; } -template QByteArray raw(const char (&data)[N]) -{ - return QByteArray::fromRawData(data, N - 1); -} - void tst_Parser::initParserEmpty() { CborParser parser; @@ -270,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) { @@ -336,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() { @@ -615,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(); @@ -739,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(); @@ -959,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(); @@ -1038,38 +757,99 @@ void tst_Parser::mapsAndArrays() "{_ 1: [_ " + expected + "], \"Hello\": {_ " + expected + ": (_ )}}"); } +static const CborParserOperations byteArrayOps = { + /* can_read_bytes = */ [](const CborValue *value, size_t len) { + auto data = static_cast(value->parser->data.ctx); + auto consumed = uintptr_t(value->source.token); + return uintptr_t(data->size()) - consumed >= uintptr_t(len); + }, + /* read_bytes = */ [](const CborValue *value, void *dst, size_t offset, size_t len) { + auto data = static_cast(value->parser->data.ctx); + auto consumed = uintptr_t(value->source.token); + return memcpy(dst, data->constData() + consumed + offset, len); + }, + /* advance_bytes = */ [](CborValue *value, size_t len) { + auto consumed = uintptr_t(value->source.token); + consumed += int(len); + value->source.token = (void*)consumed; + }, + /* transfer_string = */ [](CborValue *value, const void **userptr, size_t offset, size_t len) { + // ### + auto data = static_cast(value->parser->data.ctx); + auto consumed = uintptr_t(value->source.token); + if (uintptr_t(data->size()) - consumed < uintptr_t(len + offset)) + return CborErrorUnexpectedEOF; + consumed += int(offset); + *userptr = data->constData() + consumed; + consumed += int(len); + value->source.token = (void*)consumed; + return CborNoError; + } +}; + +void tst_Parser::readerApi() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + + CborParser parser; + CborValue first; + CborError err = cbor_parser_init_reader(&byteArrayOps, &parser, &first, &data); + QCOMPARE(err, CborNoError); + + QString decoded; + err = parseOne(&first, &decoded); + QCOMPARE(err, CborNoError); + QCOMPARE(decoded, expected); + + // check we consumed everything + QCOMPARE(uintptr_t(first.source.token), uintptr_t(data.size())); +} + +void tst_Parser::reparse_data() +{ + // only one-item rows + addColumns(); + addFixedData(); +} + +void tst_Parser::reparse() +{ + QFETCH(QByteArray, data); + QFETCH(QString, expected); + + QByteArray buffer; + CborParser parser; + CborValue first; + CborError err = cbor_parser_init_reader(&byteArrayOps, &parser, &first, &buffer); + QCOMPARE(err, CborErrorUnexpectedEOF); + + for (int i = 0; i < data.size(); ++i) { + buffer = data.left(i); + err = cbor_value_reparse(&first); + if (err != CborErrorUnexpectedEOF) + qDebug() << "At" << i; + QCOMPARE(err, CborErrorUnexpectedEOF); + QCOMPARE(uintptr_t(first.source.token), 0U); + } + + // now it should work + buffer = 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(uintptr_t(first.source.token), uintptr_t(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, @@ -1087,18 +867,23 @@ static void chunkedStringTest(const QByteArray &data, const QString &concatenate CborValue copy = value; + err = cbor_value_begin_string_iteration(&value); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); forever { QString decoded; err = parseOneChunk(&value, &decoded); - QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); - - if (decoded.isEmpty()) + if (err == CborErrorNoMoreStringChunks) break; // last chunk + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + QVERIFY2(!chunks.isEmpty(), "Too many chunks"); QString expected = chunks.takeFirst(); QCOMPARE(decoded, expected); } + + err = cbor_value_finish_string_iteration(&value); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(chunks.isEmpty(), "Too few chunks"); // compare to the concatenated data @@ -1550,248 +1335,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(); @@ -2213,7 +1756,7 @@ void tst_Parser::strictValidation() QCOMPARE(err, expectedError); } -void tst_Parser::resumeParsing_data() +void tst_Parser::incompleteData_data() { addColumns(); addFixedData(); @@ -2222,7 +1765,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/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);