Vendor tinycbor, since v5 no longer includes it

custom
jacqueline 2 years ago
parent bb50cf52cd
commit 393b268e15
  1. 22
      lib/cbor/CMakeLists.txt
  2. 21
      lib/cbor/LICENSE
  3. 13
      lib/cbor/README.md
  4. 5
      lib/cbor/idf_component.yml
  5. 2
      lib/cbor/port/include/cbor.h
  6. 4
      lib/cbor/test/CMakeLists.txt
  7. 230
      lib/cbor/test/test.c
  8. 28
      lib/cbor/tinycbor/.appveyor.yml
  9. 4
      lib/cbor/tinycbor/.gitattributes
  10. 81
      lib/cbor/tinycbor/.gitignore
  11. 1
      lib/cbor/tinycbor/.tag
  12. 92
      lib/cbor/tinycbor/.travis.yml
  13. 49
      lib/cbor/tinycbor/Doxyfile
  14. 21
      lib/cbor/tinycbor/LICENSE
  15. 250
      lib/cbor/tinycbor/Makefile
  16. 13
      lib/cbor/tinycbor/README
  17. 25
      lib/cbor/tinycbor/TODO
  18. 1
      lib/cbor/tinycbor/VERSION
  19. 2
      lib/cbor/tinycbor/examples/examples.pro
  20. 185
      lib/cbor/tinycbor/examples/simplereader.c
  21. 3
      lib/cbor/tinycbor/examples/simplereader.pro
  22. 91
      lib/cbor/tinycbor/scripts/maketag.pl
  23. 52
      lib/cbor/tinycbor/scripts/update-docs.sh
  24. 123
      lib/cbor/tinycbor/src/cbor.dox
  25. 724
      lib/cbor/tinycbor/src/cbor.h
  26. 689
      lib/cbor/tinycbor/src/cborencoder.c
  27. 57
      lib/cbor/tinycbor/src/cborencoder_close_container_checked.c
  28. 42
      lib/cbor/tinycbor/src/cborencoder_float.c
  29. 188
      lib/cbor/tinycbor/src/cborerrorstrings.c
  30. 316
      lib/cbor/tinycbor/src/cborinternal_p.h
  31. 62
      lib/cbor/tinycbor/src/cborjson.h
  32. 1529
      lib/cbor/tinycbor/src/cborparser.c
  33. 119
      lib/cbor/tinycbor/src/cborparser_dup_string.c
  34. 54
      lib/cbor/tinycbor/src/cborparser_float.c
  35. 579
      lib/cbor/tinycbor/src/cborpretty.c
  36. 87
      lib/cbor/tinycbor/src/cborpretty_stdio.c
  37. 709
      lib/cbor/tinycbor/src/cbortojson.c
  38. 657
      lib/cbor/tinycbor/src/cborvalidation.c
  39. 205
      lib/cbor/tinycbor/src/compilersupport_p.h
  40. 114
      lib/cbor/tinycbor/src/open_memstream.c
  41. 116
      lib/cbor/tinycbor/src/parsetags.pl
  42. 26
      lib/cbor/tinycbor/src/src.pri
  43. 23
      lib/cbor/tinycbor/src/tags.txt
  44. 3
      lib/cbor/tinycbor/src/tinycbor-version.h
  45. 10
      lib/cbor/tinycbor/src/tinycbor.pro
  46. 104
      lib/cbor/tinycbor/src/utf8_p.h
  47. 15
      lib/cbor/tinycbor/tests/.gitignore
  48. 7
      lib/cbor/tinycbor/tests/c90/c90.pro
  49. 30
      lib/cbor/tinycbor/tests/c90/tst_c90.c
  50. 5
      lib/cbor/tinycbor/tests/cpp/cpp.pro
  51. 44
      lib/cbor/tinycbor/tests/cpp/tst_cpp.cpp
  52. 346
      lib/cbor/tinycbor/tests/encoder/data.cpp
  53. 9
      lib/cbor/tinycbor/tests/encoder/encoder.pro
  54. 609
      lib/cbor/tinycbor/tests/encoder/tst_encoder.cpp
  55. 607
      lib/cbor/tinycbor/tests/parser/data.cpp
  56. 10
      lib/cbor/tinycbor/tests/parser/parser.pro
  57. 1888
      lib/cbor/tinycbor/tests/parser/tst_parser.cpp
  58. 3
      lib/cbor/tinycbor/tests/tests.pro
  59. 8
      lib/cbor/tinycbor/tests/tojson/tojson.pro
  60. 721
      lib/cbor/tinycbor/tests/tojson/tst_tojson.cpp
  61. 11
      lib/cbor/tinycbor/tinycbor.pc.in
  62. 164
      lib/cbor/tinycbor/tools/cbordump/cbordump.c
  63. 10
      lib/cbor/tinycbor/tools/cbordump/cbordump.pro
  64. 493
      lib/cbor/tinycbor/tools/json2cbor/json2cbor.c
  65. 20
      lib/cbor/tinycbor/tools/json2cbor/json2cbor.pro
  66. 1
      tools/cmake/common.cmake

@ -0,0 +1,22 @@
idf_component_register(SRCS "tinycbor/src/cborencoder_close_container_checked.c"
"tinycbor/src/cborencoder.c"
"tinycbor/src/cborencoder_float.c"
"tinycbor/src/cborerrorstrings.c"
"tinycbor/src/cborparser_dup_string.c"
"tinycbor/src/cborparser.c"
"tinycbor/src/cborparser_float.c"
"tinycbor/src/cborpretty_stdio.c"
"tinycbor/src/cborpretty.c"
"tinycbor/src/cbortojson.c"
"tinycbor/src/cborvalidation.c"
"tinycbor/src/open_memstream.c"
INCLUDE_DIRS "port/include"
PRIV_INCLUDE_DIRS "tinycbor/src")
# for open_memstream.c
set_source_files_properties(tinycbor/src/open_memstream.c PROPERTIES COMPILE_DEFINITIONS "__linux__")
# workaround for the fact that we are passing -ffreestanding to Clang
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options(${COMPONENT_LIB} PRIVATE "-U __STDC_HOSTED__")
endif()

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Intel Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,13 @@
Concise Binary Object Representation (CBOR) Library
---------------------------------------------------
To build TinyCBOR:
make
If you want to change the compiler or pass extra compiler flags:
make CC=clang CFLAGS="-m32 -Oz" LDFLAGS="-m32"
Documentation: https://intel.github.io/tinycbor/current/

@ -0,0 +1,5 @@
dependencies:
idf: '>=4.3'
description: 'CBOR: Concise Binary Object Representation Library'
url: https://github.com/espressif/idf-extra-components/tree/master/cbor
version: 0.6.0

@ -0,0 +1,2 @@
#include "../../tinycbor/src/cbor.h"
#include "../../tinycbor/src/cborjson.h"

@ -0,0 +1,4 @@
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
REQUIRES unity
PRIV_REQUIRES cmock cbor)

@ -0,0 +1,230 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <freertos/FreeRTOS.h>
#include "unity.h"
#if __has_include("esp_random.h")
#include "esp_random.h"
#else
#include "esp_system.h"
#endif
#include "cbor.h"
#define CBOR_CHECK(a, str, goto_tag, ret_value, ...) \
do \
{ \
if ((a) != CborNoError) \
{ \
printf("%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret = ret_value; \
goto goto_tag; \
} \
} while (0)
static void indent(int nestingLevel)
{
while (nestingLevel--) {
printf(" ");
}
}
static void dumpbytes(const uint8_t *buf, size_t len)
{
while (len--) {
printf("%02X ", *buf++);
}
}
/**
* Decode CBOR data manuallly
*/
static CborError example_dump_cbor_buffer(CborValue *it, int nestingLevel)
{
CborError ret = CborNoError;
while (!cbor_value_at_end(it)) {
CborType type = cbor_value_get_type(it);
indent(nestingLevel);
switch (type) {
case CborArrayType: {
CborValue recursed;
assert(cbor_value_is_container(it));
puts("Array[");
ret = cbor_value_enter_container(it, &recursed);
CBOR_CHECK(ret, "enter container failed", err, ret);
ret = example_dump_cbor_buffer(&recursed, nestingLevel + 1);
CBOR_CHECK(ret, "recursive dump failed", err, ret);
ret = cbor_value_leave_container(it, &recursed);
CBOR_CHECK(ret, "leave container failed", err, ret);
indent(nestingLevel);
puts("]");
continue;
}
case CborMapType: {
CborValue recursed;
assert(cbor_value_is_container(it));
puts("Map{");
ret = cbor_value_enter_container(it, &recursed);
CBOR_CHECK(ret, "enter container failed", err, ret);
ret = example_dump_cbor_buffer(&recursed, nestingLevel + 1);
CBOR_CHECK(ret, "recursive dump failed", err, ret);
ret = cbor_value_leave_container(it, &recursed);
CBOR_CHECK(ret, "leave container failed", err, ret);
indent(nestingLevel);
puts("}");
continue;
}
case CborIntegerType: {
int64_t val;
ret = cbor_value_get_int64(it, &val);
CBOR_CHECK(ret, "parse int64 failed", err, ret);
printf("%lld\n", (long long)val);
break;
}
case CborByteStringType: {
uint8_t *buf;
size_t n;
ret = cbor_value_dup_byte_string(it, &buf, &n, it);
CBOR_CHECK(ret, "parse byte string failed", err, ret);
dumpbytes(buf, n);
puts("");
free(buf);
continue;
}
case CborTextStringType: {
char *buf;
size_t n;
ret = cbor_value_dup_text_string(it, &buf, &n, it);
CBOR_CHECK(ret, "parse text string failed", err, ret);
puts(buf);
free(buf);
continue;
}
case CborTagType: {
CborTag tag;
ret = cbor_value_get_tag(it, &tag);
CBOR_CHECK(ret, "parse tag failed", err, ret);
printf("Tag(%lld)\n", (long long)tag);
break;
}
case CborSimpleType: {
uint8_t type;
ret = cbor_value_get_simple_type(it, &type);
CBOR_CHECK(ret, "parse simple type failed", err, ret);
printf("simple(%u)\n", type);
break;
}
case CborNullType:
puts("null");
break;
case CborUndefinedType:
puts("undefined");
break;
case CborBooleanType: {
bool val;
ret = cbor_value_get_boolean(it, &val);
CBOR_CHECK(ret, "parse boolean type failed", err, ret);
puts(val ? "true" : "false");
break;
}
case CborHalfFloatType: {
uint16_t val;
ret = cbor_value_get_half_float(it, &val);
CBOR_CHECK(ret, "parse half float type failed", err, ret);
printf("__f16(%04x)\n", val);
break;
}
case CborFloatType: {
float val;
ret = cbor_value_get_float(it, &val);
CBOR_CHECK(ret, "parse float type failed", err, ret);
printf("%g\n", val);
break;
}
case CborDoubleType: {
double val;
ret = cbor_value_get_double(it, &val);
CBOR_CHECK(ret, "parse double float type failed", err, ret);
printf("%g\n", val);
break;
}
case CborInvalidType: {
ret = CborErrorUnknownType;
CBOR_CHECK(ret, "unknown cbor type", err, ret);
break;
}
}
ret = cbor_value_advance_fixed(it);
CBOR_CHECK(ret, "fix value failed", err, ret);
}
return CborNoError;
err:
return ret;
}
TEST_CASE("CBOR example", "[cbor]")
{
CborEncoder root_encoder;
CborParser root_parser;
CborValue it;
uint8_t buf[100];
// Initialize the outermost cbor encoder
cbor_encoder_init(&root_encoder, buf, sizeof(buf), 0);
// Create an array containing several items
CborEncoder array_encoder;
CborEncoder map_encoder;
cbor_encoder_create_array(&root_encoder, &array_encoder, 5); // [
// 1. Create a map containing several pairs
cbor_encoder_create_map(&array_encoder, &map_encoder, 3); // {
// chip:esp32
cbor_encode_text_stringz(&map_encoder, "chip");
cbor_encode_text_stringz(&map_encoder, "esp32");
// unicore:false
cbor_encode_text_stringz(&map_encoder, "unicore");
cbor_encode_boolean(&map_encoder, false);
// ip:[192,168,1,100]
cbor_encode_text_stringz(&map_encoder, "ip");
CborEncoder array2;
cbor_encoder_create_array(&map_encoder, &array2, 4); // [
// Encode several numbers
cbor_encode_uint(&array2, 192);
cbor_encode_uint(&array2, 168);
cbor_encode_uint(&array2, 1);
cbor_encode_uint(&array2, 100);
cbor_encoder_close_container(&map_encoder, &array2); // ]
cbor_encoder_close_container(&array_encoder, &map_encoder); // }
// 2. Encode float number
cbor_encode_float(&array_encoder, 3.14);
// 3. Encode simple value
cbor_encode_simple_value(&array_encoder, 99);
// 4. Encode a string
cbor_encode_text_stringz(&array_encoder, "2019-07-10 09:00:00+0000");
// 5. Encode a undefined value
cbor_encode_undefined(&array_encoder);
cbor_encoder_close_container(&root_encoder, &array_encoder); // ]
// If error happend when encoding, then this value should be meaningless
printf("encoded buffer size %d", cbor_encoder_get_buffer_size(&root_encoder, buf));
// Initialize the cbor parser and the value iterator
cbor_parser_init(buf, sizeof(buf), 0, &root_parser, &it);
printf("convert CBOR to JSON");
// Dump the values in JSON format
cbor_value_to_json(stdout, &it, 0);
puts("");
printf("decode CBOR manually: ");
// Decode CBOR data manully
TEST_ESP_OK(example_dump_cbor_buffer(&it, 0));
}

@ -0,0 +1,28 @@
version: 0.6-build-{build}
pull_requests:
do_not_increment_build_number: true
image:
- Visual Studio 2017
- Visual Studio 2019
install:
- cmd: >-
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 2019" (call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" x64) & (set QTDIR=C:\Qt\6.1\msvc2019_64)
set path=%PATH%;%QTDIR%\bin
build_script:
- cmd: >-
nmake -f Makefile.nmake -nologo CFLAGS="-W3 -Os -MDd"
cd tests
qmake CONFIG-=release CONFIG+=debug
nmake -nologo -s
test_script:
- cmd: >-
nmake -s -nologo TESTARGS=-silent check
artifacts:
- path: lib\tinycbor.lib
deploy: off

@ -0,0 +1,4 @@
.tag export-subst
.gitignore export-ignore
.gitattributes export-ignore
.appveyor.yml text

@ -0,0 +1,81 @@
# Frequent generated files
callgrind.out.*
pcviewer.cfg
*~
*.a
*.la
*.core
*.d
*.dylib
*.moc
*.o
*.obj
*.orig
*.swp
*.rej
*.so
*.so.*
*.pbxuser
*.mode1
*.mode1v3
*_pch.h.cpp
*_resource.rc
.#*
*.*#
core
.qmake.cache
.qmake.stash
.qmake.vars
.device.vars
tags
.DS_Store
*.debug
Makefile*
*.prl
*.app
*.pro.user*
*.qmlproject.user*
*.gcov
*.gcda
*.gcno
*.flc
.*.swp
tinycbor.pc
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.exe.embed.manifest
*.exe_manifest.rc
*.exe_manifest.res
# MinGW generated files
*.Debug
*.Release
# INTEGRITY generated files
*.gpj
*.int
*.ael
*.dla
*.dnm
*.dep
*.map
bin
doc
lib
src/cjson
src/doxygen.log
!/Makefile
.config

@ -0,0 +1 @@
$Format:%H$

@ -0,0 +1,92 @@
env:
- BUILD_DOCS=false
jobs:
include:
- # only build docs on main
if: branch = main
env: BUILD_DOCS=true
language: cpp
matrix:
include:
- os: linux
dist: xenial
addons:
apt:
sources:
- sourceline: 'ppa:beineri/opt-qt-5.12.1-xenial'
packages:
- qt512base valgrind
- doxygen
env:
- QMAKESPEC=linux-g++
- EVAL="CC=gcc && CXX=g++"
- CFLAGS="-Os"
- LDFLAGS="-Wl,--no-undefined -lm"
- QMAKEFLAGS="-config release"
- QT_NO_CPU_FEATURE=rdrnd
- os: linux
dist: xenial
addons:
apt:
sources:
- sourceline: 'ppa:beineri/opt-qt-5.12.1-xenial'
packages:
- qt512base
env:
- QMAKESPEC=linux-clang
- EVAL="CC=clang && CXX=clang++"
- CFLAGS="-Oz"
- LDFLAGS="-Wl,--no-undefined -lm"
- QMAKEFLAGS="-config release"
- MAKEFLAGS=-s
- TESTARGS=-silent
- os: linux
dist: xenial
env:
- QMAKESPEC=linux-gcc-freestanding
- EVAL="CXX=false"
- CFLAGS="-ffreestanding -Os"
- LDFLAGS="-Wl,--no-undefined -lm"
- os: linux
dist: xenial
env:
- QMAKESPEC=linux-gcc-no-math
- EVAL="CXX=false && touch src/math.h src/float.h"
- CFLAGS="-ffreestanding -DCBOR_NO_FLOATING_POINT -Os"
- LDFLAGS="-Wl,--no-undefined"
- LDLIBS=""
- os: osx
env:
- QMAKESPEC=macx-clang
- CFLAGS="-Oz"
- QMAKEFLAGS="-config debug"
- MAKEFLAGS=-s
- TESTARGS=-silent
- PATH=/usr/local/opt/qt5/bin:$PATH
install:
- if [ "${TRAVIS_OS_NAME}" != "linux" ]; then
brew update;
brew install qt5;
fi
script:
- PATH=`echo /opt/qt*/bin`:$PATH
- eval "$EVAL"
- make -s -f Makefile.configure configure | tee .config
- make -k
CFLAGS="$CFLAGS -march=native -g1 -Wall -Wextra -Werror"
CPPFLAGS="-DNDEBUG -DCBOR_ENCODER_WRITER_CONTROL=-1 -DCBOR_PARSER_READER_CONTROL=-1"
lib/libtinycbor.a
- size lib/libtinycbor.a | tee sizes
- make -s clean
- make -k
CFLAGS="$CFLAGS -O0 -g"
LDFLAGS="$LDFLAGS" ${LDLIBS+LDLIBS="$LDLIBS"}
- grep -q freestanding-pass .config || make
QMAKEFLAGS="$QMAKEFLAGS QMAKE_CXX=$CXX"
tests/Makefile
- grep -q freestanding-pass .config ||
(cd tests && make TESTARGS=-silent check -k
TESTRUNNER=`which valgrind 2>/dev/null`)
- make -s clean
- ! [ $BUILD_DOCS ] || ./scripts/update-docs.sh

@ -0,0 +1,49 @@
PROJECT_NAME = "TinyCBOR $(VERSION) API"
OUTPUT_DIRECTORY = ../doc
ABBREVIATE_BRIEF =
SHORT_NAMES = YES
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = YES
TAB_SIZE = 8
ALIASES = "value=\arg \c"
OPTIMIZE_OUTPUT_FOR_C = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = NO
HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
SHOW_USED_FILES = NO
WARN_IF_UNDOCUMENTED = NO
WARN_LOGFILE = doxygen.log
INPUT = .
FILE_PATTERNS = *.h \
*.c \
*.dox
EXCLUDE_PATTERNS = *_p.h
STRIP_CODE_COMMENTS = NO
REFERENCED_BY_RELATION = YES
IGNORE_PREFIX = cbor_ \
Cbor
HTML_TIMESTAMP = NO
GENERATE_HTMLHELP = YES
GENERATE_CHI = YES
BINARY_TOC = YES
TOC_EXPAND = YES
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
SEARCHENGINE = NO
GENERATE_LATEX = NO
COMPACT_LATEX = YES
MACRO_EXPANSION = YES
PREDEFINED = DOXYGEN \
CBOR_INLINE_API=
CLASS_DIAGRAMS = NO
CLASS_GRAPH = NO
COLLABORATION_GRAPH = NO
GROUP_GRAPHS = NO
INCLUDE_GRAPH = NO
INCLUDED_BY_GRAPH = NO
GRAPHICAL_HIERARCHY = NO
DIRECTORY_GRAPH = NO

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Intel Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,250 @@
# Variables:
prefix = /usr/local
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
libdir = $(exec_prefix)/lib
includedir = $(prefix)/include
pkgconfigdir = $(libdir)/pkgconfig
CFLAGS = -Wall -Wextra
LDFLAGS_GCSECTIONS = -Wl,--gc-sections
LDFLAGS += $(if $(gc_sections-pass),$(LDFLAGS_GCSECTIONS))
LDLIBS = -lm
GIT_ARCHIVE = git archive --prefix="$(PACKAGE)/" -9
INSTALL = install
INSTALL_DATA = $(INSTALL) -m 644
INSTALL_PROGRAM = $(INSTALL) -m 755
QMAKE = qmake
MKDIR = mkdir -p
RMDIR = rmdir
SED = sed
# Our sources
TINYCBOR_HEADERS = src/cbor.h src/cborjson.h src/tinycbor-version.h
TINYCBOR_FREESTANDING_SOURCES = \
src/cborerrorstrings.c \
src/cborencoder.c \
src/cborencoder_close_container_checked.c \
src/cborencoder_float.c \
src/cborparser.c \
src/cborparser_float.c \
src/cborpretty.c \
#
CBORDUMP_SOURCES = tools/cbordump/cbordump.c
BUILD_SHARED = $(shell file -L /bin/sh 2>/dev/null | grep -q ELF && echo 1)
BUILD_STATIC = 1
ifneq ($(BUILD_STATIC),1)
ifneq ($(BUILD_SHARED),1)
$(error error: BUILD_STATIC and BUILD_SHARED can not be both disabled)
endif
endif
INSTALL_TARGETS += $(bindir)/cbordump
ifeq ($(BUILD_SHARED),1)
BINLIBRARY=lib/libtinycbor.so
INSTALL_TARGETS += $(libdir)/libtinycbor.so.$(VERSION)
endif
ifeq ($(BUILD_STATIC),1)
BINLIBRARY=lib/libtinycbor.a
INSTALL_TARGETS += $(libdir)/libtinycbor.a
endif
INSTALL_TARGETS += $(pkgconfigdir)/tinycbor.pc
INSTALL_TARGETS += $(TINYCBOR_HEADERS:src/%=$(includedir)/tinycbor/%)
# setup VPATH
MAKEFILE := $(lastword $(MAKEFILE_LIST))
SRCDIR := $(dir $(MAKEFILE))
VPATH = $(SRCDIR):$(SRCDIR)/src
# Our version
GIT_DIR := $(strip $(shell git -C $(SRCDIR). rev-parse --git-dir 2> /dev/null))
VERSION = $(shell cat $(SRCDIR)VERSION)
SOVERSION = $(shell cut -f1-2 -d. $(SRCDIR)VERSION)
PACKAGE = tinycbor-$(VERSION)
# Check that QMAKE is Qt 5
ifeq ($(origin QMAKE),file)
check_qmake = $(strip $(shell $(1) -query QT_VERSION 2>/dev/null | cut -b1))
ifneq ($(call check_qmake,$(QMAKE)),5)
QMAKE := qmake -qt5
ifneq ($(call check_qmake,$(QMAKE)),5)
QMAKE := qmake-qt5
ifneq ($(call check_qmake,$(QMAKE)),5)
QMAKE := @echo >&2 $(MAKEFILE): Cannot find a Qt 5 qmake; false
endif
endif
endif
endif
-include .config
ifeq ($(wildcard .config),)
$(info .config file not yet created)
endif
ifeq ($(freestanding-pass),1)
TINYCBOR_SOURCES = $(TINYCBOR_FREESTANDING_SOURCES)
else
TINYCBOR_SOURCES = \
$(TINYCBOR_FREESTANDING_SOURCES) \
src/cborparser_dup_string.c \
src/cborpretty_stdio.c \
src/cbortojson.c \
src/cborvalidation.c \
#
# if open_memstream is unavailable on the system, try to implement our own
# version using funopen or fopencookie
ifeq ($(open_memstream-pass),)
ifeq ($(funopen-pass)$(fopencookie-pass),)
CFLAGS += -DWITHOUT_OPEN_MEMSTREAM
ifeq ($(wildcard .config),.config)
$(warning warning: funopen and fopencookie unavailable, open_memstream can not be implemented and conversion to JSON will not work properly!)
endif
else
TINYCBOR_SOURCES += src/open_memstream.c
endif
endif
endif
# json2cbor depends on an external library (cjson)
ifneq ($(cjson-pass)$(system-cjson-pass),)
JSON2CBOR_SOURCES = tools/json2cbor/json2cbor.c
INSTALL_TARGETS += $(bindir)/json2cbor
ifeq ($(system-cjson-pass),1)
LDFLAGS_CJSON = -lcjson
else
JSON2CBOR_SOURCES += src/cjson/cJSON.c
json2cbor_CCFLAGS = -I$(SRCDIR)src/cjson
endif
endif
# Rules
all: .config \
$(if $(subst 0,,$(BUILD_STATIC)),lib/libtinycbor.a) \
$(if $(subst 0,,$(BUILD_SHARED)),lib/libtinycbor.so) \
$(if $(freestanding-pass),,bin/cbordump) \
tinycbor.pc
all: $(if $(JSON2CBOR_SOURCES),bin/json2cbor)
check: tests/Makefile | $(BINLIBRARY)
$(MAKE) -C tests check
silentcheck: | $(BINLIBRARY)
TESTARGS=-silent $(MAKE) -f $(MAKEFILE) -s check
configure: .config
.config: Makefile.configure
$(MAKE) -f $(SRCDIR)Makefile.configure OUT='>&9' configure 9> $@
lib/libtinycbor-freestanding.a: $(TINYCBOR_FREESTANDING_SOURCES:.c=.o)
@$(MKDIR) -p lib
$(AR) cqs $@ $^
lib/libtinycbor.a: $(TINYCBOR_SOURCES:.c=.o)
@$(MKDIR) -p lib
$(AR) cqs $@ $^
lib/libtinycbor.so: $(TINYCBOR_SOURCES:.c=.pic.o)
@$(MKDIR) -p lib
$(CC) -shared -Wl,-soname,libtinycbor.so.$(SOVERSION) -o lib/libtinycbor.so.$(VERSION) $(LDFLAGS) $^ $(LDLIBS)
cd lib ; ln -sf libtinycbor.so.$(VERSION) libtinycbor.so ; ln -sf libtinycbor.so.$(VERSION) libtinycbor.so.$(SOVERSION)
bin/cbordump: $(CBORDUMP_SOURCES:.c=.o) $(BINLIBRARY)
@$(MKDIR) -p bin
$(CC) -o $@ $(LDFLAGS) $^ $(LDLIBS)
bin/json2cbor: $(JSON2CBOR_SOURCES:.c=.o) $(BINLIBRARY)
@$(MKDIR) -p bin
$(CC) -o $@ $(LDFLAGS) $^ $(LDFLAGS_CJSON) $(LDLIBS)
tinycbor.pc: tinycbor.pc.in
$(SED) > $@ < $< \
-e 's,@prefix@,$(prefix),' \
-e 's,@exec_prefix@,$(exec_prefix),' \
-e 's,@libdir@,$(libdir),' \
-e 's,@includedir@,$(includedir),' \
-e 's,@version@,$(VERSION),'
tests/Makefile: tests/tests.pro
$(QMAKE) $(QMAKEFLAGS) -o $@ $<
$(PACKAGE).tar.gz: | .git
GIT_DIR=$(SRCDIR).git $(GIT_ARCHIVE) --format=tar.gz -o "$(PACKAGE).tar.gz" HEAD
$(PACKAGE).zip: | .git
GIT_DIR=$(SRCDIR).git $(GIT_ARCHIVE) --format=zip -o "$(PACKAGE).zip" HEAD
$(DESTDIR)$(libdir)/%: lib/%
$(INSTALL) -d $(@D)
$(INSTALL_DATA) $< $@
$(DESTDIR)$(bindir)/%: bin/%
$(INSTALL) -d $(@D)
$(INSTALL_PROGRAM) $< $@
$(DESTDIR)$(pkgconfigdir)/%: %
$(INSTALL) -d $(@D)
$(INSTALL_DATA) $< $@
$(DESTDIR)$(includedir)/tinycbor/%: src/%
$(INSTALL) -d $(@D)
$(INSTALL_DATA) $< $@
install-strip:
$(MAKE) -f $(MAKEFILE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
install: $(INSTALL_TARGETS:%=$(DESTDIR)%)
ifeq ($(BUILD_SHARED),1)
ln -sf libtinycbor.so.$(VERSION) $(DESTDIR)$(libdir)/libtinycbor.so
ln -sf libtinycbor.so.$(VERSION) $(DESTDIR)$(libdir)/libtinycbor.so.$(SOVERSION)
endif
uninstall:
$(RM) $(INSTALL_TARGETS:%=$(DESTDIR)%)
$(RM) $(DESTDIR)$(libdir)/libtinycbor.so
$(RM) $(DESTDIR)$(libdir)/libtinycbor.so.$(SOVERSION)
mostlyclean:
$(RM) $(TINYCBOR_SOURCES:.c=.o)
$(RM) $(TINYCBOR_SOURCES:.c=.pic.o)
$(RM) $(CBORDUMP_SOURCES:.c=.o)
clean: mostlyclean
$(RM) bin/cbordump
$(RM) bin/json2cbor
$(RM) lib/libtinycbor.a
$(RM) lib/libtinycbor-freestanding.a
$(RM) tinycbor.pc
$(RM) lib/libtinycbor.so*
test -e tests/Makefile && $(MAKE) -C tests clean || :
distclean: clean
test -e tests/Makefile && $(MAKE) -C tests distclean || :
docs:
cd $(SRCDIR)src && VERSION=$(VERSION) doxygen $(SRCDIR)/../Doxyfile
dist: $(PACKAGE).tar.gz $(PACKAGE).zip
distcheck: .git
-$(RM) -r $${TMPDIR-/tmp}/tinycbor-distcheck
GIT_DIR=$(SRCDIR).git git archive --prefix=tinycbor-distcheck/ --format=tar HEAD | tar -xf - -C $${TMPDIR-/tmp}
cd $${TMPDIR-/tmp}/tinycbor-distcheck && $(MAKE) silentcheck
$(RM) -r $${TMPDIR-/tmp}/tinycbor-distcheck
tag: distcheck
@cd $(SRCDIR). && perl scripts/maketag.pl
.PHONY: all check silentcheck configure install uninstall
.PHONY: mostlyclean clean distclean
.PHONY: docs dist distcheck release
.SECONDARY:
cflags := $(CPPFLAGS) -I$(SRCDIR)src
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 $@ $<
%.pic.o: %.c
@test -d $(@D) || $(MKDIR) $(@D)
$(CC) $(cflags) -fPIC $($(basename $(notdir $@))_CCFLAGS) -c -o $@ $<
-include src/*.d

@ -0,0 +1,13 @@
Concise Binary Object Representation (CBOR) Library
---------------------------------------------------
To build TinyCBOR:
make
If you want to change the compiler or pass extra compiler flags:
make CC=clang CFLAGS="-m32 -Oz" LDFLAGS="-m32"
Documentation: https://intel.github.io/tinycbor/current/

@ -0,0 +1,25 @@
==== To Do list for libcbor ====
=== General ===
* API review
* Benchmark
* Write examples
** Simple decoder
** Decoder to JSON
** Windowed encoding/decoding (limited memory)
=== Encoder ===
* Write API docs
* Add API for creating indeterminate-length arrays and maps
* Add API for creating indeterminate-length strings
* Add API for relaxing doubles to floats and to integers
* Add length-checking of the sub-containers (#ifndef CBOR_ENCODER_NO_USER_CHECK)
* Decide how to indicate number of bytes needed
** Suggestion: return negative number from the functions
=== Decoder ===
* Write functions not yet implemented
* Add API for stream-decoding strings
* Add API for checking known tags and simple types
* (unlikely) Add API for checking the pairing of a tag and the tagged type
* Write tests for error conditions
* Fuzzy-test the decoder

@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS = simplereader.pro

@ -0,0 +1,185 @@
#include "../src/cbor.h"
#include <sys/stat.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static uint8_t *readfile(const char *fname, size_t *size)
{
struct stat st;
FILE *f = fopen(fname, "rb");
if (!f)
return NULL;
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;
}
static void indent(int nestingLevel)
{
while (nestingLevel--)
printf(" ");
}
static void dumpbytes(const uint8_t *buf, size_t len)
{
printf("\"");
while (len--)
printf("\\x%02X", *buf++);
printf("\"");
}
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);
puts("");
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
printf("\"%s\"\n", 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;
}
int main(int argc, char **argv)
{
if (argc != 2) {
puts("simplereader <filename>");
return 1;
}
size_t length;
uint8_t *buf = readfile(argv[1], &length);
if (!buf) {
perror("readfile");
return 1;
}
CborParser parser;
CborValue it;
CborError err = cbor_parser_init(buf, length, 0, &parser, &it);
if (!err)
err = dumprecursive(&it, 0);
free(buf);
if (err) {
fprintf(stderr, "CBOR parsing failure at offset %ld: %s\n",
it.ptr - buf, cbor_error_string(err));
return 1;
}
return 0;
}

@ -0,0 +1,3 @@
CONFIG -= qt
SOURCES = simplereader.c
include(../src/src.pri)

@ -0,0 +1,91 @@
#!perl
use strict;
sub run(@) {
open PROC, "-|", @_ or die("Cannot run $_[0]: $!");
my @out;
while (<PROC>) {
chomp;
push @out, $_;
}
close PROC;
return @out;
}
my @tags = run("git", "tag");
my @v = run("git", "show", "HEAD:VERSION");
my $v = $v[0];
my $tagfile = ".git/TAG_EDITMSG";
open TAGFILE, ">", $tagfile
or die("Cannot create file for editing tag message: $!");
select TAGFILE;
print "TinyCBOR release $v\n";
print "\n";
print "# Write something nice about this release here\n";
# Do we have a commit template?
my @result = run("git", "config", "--get", "commit.template");
if (scalar @result) {
open TEMPLATE, "<", $result[0];
map { print $_; } <TEMPLATE>;
close TEMPLATE;
}
print "\n";
print "# Commit log\n";
open LOG, "-|", "git", "shortlog", "-e", "--no-merges", "--not", @tags;
map { print "# $_"; } <LOG>;
close LOG;
print "# Header diff:\n";
open DIFF, "-|", "git", "diff", "HEAD", "--not", @tags, "--", 'src/*.h', ':!*_p.h';
map { print "# $_"; } <DIFF>;
close DIFF;
select STDOUT;
close TAGFILE;
# Run the editor.
# We use system so that stdin, stdout and stderr are forwarded.
@result = run("git", "var", "GIT_EDITOR");
@result = ($result[0], $tagfile);
system @result;
exit ($? >> 8) if $?;
# Create the tag
# Also using system so that hte user can see the output.
system("git", "tag", "-a", "-F", $tagfile, split(' ', $ENV{GITTAGFLAGS}), "v$v");
exit ($? >> 8) if $?;
# Update the version files for the next patch release
@v = split(/\./, $v);
if (scalar @v < 3) {
push @v, '1';
} else {
++$v[-1];
}
$v = join('.', @v);
open VERSION, ">", "VERSION" or die("Cannot open VERSION file: $!");
print VERSION "$v\n";
close VERSION;
open VERSION, ">", "src/tinycbor-version.h" or die("Cannot open src/tinycbor-version.h: $!");
print VERSION "#define TINYCBOR_VERSION_MAJOR ", $v[0], "\n";
print VERSION "#define TINYCBOR_VERSION_MINOR ", $v[1], "\n";
print VERSION "#define TINYCBOR_VERSION_PATCH ", $v[2], "\n";
close VERSION;
if (open APPVEYORYML, "<", ".appveyor.yml") {
my @contents = map {
s/^version:.*/version: $v[0].$v[1].$v[2]-build-{build}/;
$_;
} <APPVEYORYML>;
close APPVEYORYML;
open APPVEYORYML, ">", ".appveyor.yml";
print APPVEYORYML join('', @contents);
close APPVEYORYML;
}
# Print summary
print "Tag created and next versions updated.\n";
print "Don't forget to create the docs.\n" if $v[2] == 1;

@ -0,0 +1,52 @@
#!/bin/sh -ex
tuple="$TRAVIS_BRANCH${TRAVIS_TAG:+tag:$TRAVIS_TAG},$TRAVIS_PULL_REQUEST"
case "$tuple" in
dev,false|main,false|tag:*)
;;
*)
exit 0
;;
esac
V=`cut -f1-2 -d. <VERSION`
git fetch origin gh-pages
# Fail if the library sizes file isn't present
test -r sizes
# Run doxygen (maybe)
if [ -n "${TRAVIS_TAG-$FORCE_DOCS}" ] && make -s docs 2>/dev/null; then
git checkout -b gh-pages FETCH_HEAD
if [ -d "$V" ]; then
mv "$V" "old-$V"
fi
mv doc/html "$V"
git add -A "$V"
else
git checkout -b gh-pages FETCH_HEAD
mkdir -p "$V"
fi
# Update the symlink for the branch name
rm -f "./$TRAVIS_BRANCH"
ln -s "$V" "$TRAVIS_BRANCH"
git add "./$TRAVIS_BRANCH"
# Update the library sizes file
# (will fail if the release build failed)
mkdir -p "library_sizes/$TRAVIS_BRANCH"
mv sizes "library_sizes/$TRAVIS_BRANCH/$QMAKESPEC"
(cd "library_sizes/$TRAVIS_BRANCH/";
for f in *; do echo "$f:"; cat "$f" ; done) > "$V/library_sizes.txt"
git add "library_sizes/$TRAVIS_BRANCH" "$V/library_sizes.txt"
git diff --cached -U0 "$V/library_sizes.txt"
# Commit everything
if git commit -m "Update docs for $V (Travis build $TRAVIS_BUILD_NUMBER)
Matching commit $TRAVIS_COMMIT:
$TRAVIS_COMMIT_MESSAGE"; then
# We've made a commit, push it
set +x
url=`git config --get remote.origin.url | sed -e s,://github,://$GITHUB_AUTH@github,`
git push "$url" @:gh-pages
fi

@ -0,0 +1,123 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
/**
* \mainpage
* The TinyCBOR $(VERSION) library is a small CBOR encoder and decoder library,
* optimized for very fast operation with very small footprint. The main encoder
* and decoder functions do not allocate memory.
*
* TinyCBOR is divided into the following groups of functions and structures:
* - \ref CborGlobals
* - \ref CborEncoding
* - \ref CborParsing
* - \ref CborPretty
* - \ref CborToJson
*/
/**
* \file <cbor.h>
* The <cbor.h> is the main header in TinyCBOR and defines the constants used by most functions
* as well as the structures for encoding (CborEncoder) and decoding (CborValue).
*
* \sa <cborjson.h>
*/
/**
* \file <cborjson.h>
* The <cborjson.h> file contains the routines that are used to convert a CBOR
* data stream into JSON.
*
* \sa <cbor.h>
*/
/**
* \defgroup CborGlobals Global constants
* \brief Constants used by all TinyCBOR function groups.
*/
/**
* \addtogroup CborGlobals
* @{
*/
/**
* \var size_t CborIndefiniteLength
*
* This variable is a constant used to indicate that the length of the map or
* array is not yet determined. It is used in functions
* cbor_encoder_create_map() and cbor_encoder_create_array()
*/
/**
* \enum CborType
* The CborType enum contains the types known to TinyCBOR.
*
* \value CborIntegerType Type is an integer value, positive, negative or zero
* \value CborByteStringType Type is a string of arbitrary raw bytes
* \value CborTextStringType Type is a text string encoded in UTF-8
* \value CborArrayType Type is a CBOR array
* \value CborMapType Type is a CBOR map (an associative container with key and value pairs)
* \value CborTagType Type is a CBOR tag (a 64-bit integer describing the item that follows, see CborKnownTags)
* \value CborSimpleType Type is one of the CBOR Simple Types
* \value CborBooleanType Type is a boolean (true or false)
* \value CborNullType Type encodes a null
* \value CborUndefinedType Type encodes an undefined value
* \value CborHalfFloatType Type is an IEEE 754 half precision (16-bit) floating point type
* \value CborFloatType Type is an IEEE 754 single precision (32-bit) floating point type
* \value CborDoubleType Type is an IEEE 754 double precision (64-bit) floating point type
* \value CborInvalidType Type is not valid (this value is used to indicate error conditions)
*/
/**
* \enum CborKnownTags
* The CborKnownTags enum contains known tags specified in RFC 7049, for use by the application.
* TinyCBOR does not usually interpret the meaning of these tags and does not add them to the
* output stream, unless specifically instructed to do so in functions for that effect.
*
* \value CborDateTimeStringTag Text string contains a date-time encoded in RFC 3339 format, "YYYY-MM-DD hh:mm:ss+zzzz"
* \value CborUnixTime_tTag Number is a Unix time_t quantity, the number of seconds since 1970-01-01 midnight UTC
* \value CborPositiveBignumTag Item is a CBOR byte string encoding a positive integer of arbitrary precision
* \value CborNegativeBignumTag Item is a CBOR byte string encoding a negative integer of arbitrary precision
* \value CborDecimalTag Item is a CBOR array of two integers encoding a fixed-point decimal
* \value CborBigfloatTag Item is a bigfloat
* \value CborExpectedBase64urlTag Item is a CBOR byte string that is expected to be encoded as Base64Url
* \value CborExpectedBase64Tag Item is a CBOR byte string that is expected to be encoded as Base64
* \value CborExpectedBase16Tag Item is a CBOR byte string that is expected to be encoded as Base16 (also known as "hexdump")
* \value CborUriTag Item is a CBOR text string containing a URI (RFC 3986) or IRI (RFC 3987)
* \value CborBase64urlTag Item is a CBOR text string that was encoded as Base64Url
* \value CborBase64Tag Item is a CBOR text string that was encoded as Base64
* \value CborRegularExpressionTag Item is a CBOR text string containing a regular expression
* \value CborMimeMessageTag Item is a CBOR text string containing a MIME message (RFC 2045, 2046, 2047, 2822)
* \value CborSignatureTag Item contains CBOR-encoded data.
* This tag is also used as "file magic," marking a file as containing CBOR
*/
/**
* \typedef CborTag
* This typedef is an unsigned 64-bit integer. Known CBOR tags can be used from the CborKnownTags enum
* but the user application may use other tag values than the ones specified in RFC 7049.
*/
/** @} */

@ -0,0 +1,724 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#ifndef CBOR_H
#define CBOR_H
#ifndef assert
#include <assert.h>
#endif
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "tinycbor-version.h"
#define TINYCBOR_VERSION ((TINYCBOR_VERSION_MAJOR << 16) | (TINYCBOR_VERSION_MINOR << 8) | TINYCBOR_VERSION_PATCH)
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#ifndef SIZE_MAX
/* Some systems fail to define SIZE_MAX in <stdint.h>, even though C99 requires it...
* Conversion from signed to unsigned is defined in 6.3.1.3 (Signed and unsigned integers) p2,
* which says: "the value is converted by repeatedly adding or subtracting one more than the
* maximum value that can be represented in the new type until the value is in the range of the
* new type."
* So -1 gets converted to size_t by adding SIZE_MAX + 1, which results in SIZE_MAX.
*/
# define SIZE_MAX ((size_t)-1)
#endif
#ifndef CBOR_API
# define CBOR_API
#endif
#ifndef CBOR_PRIVATE_API
# define CBOR_PRIVATE_API
#endif
#ifndef CBOR_INLINE_API
# if defined(__cplusplus)
# define CBOR_INLINE inline
# define CBOR_INLINE_API inline
# else
# define CBOR_INLINE_API static CBOR_INLINE
# if defined(_MSC_VER)
# define CBOR_INLINE __inline
# elif defined(__GNUC__)
# define CBOR_INLINE __inline__
# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
# define CBOR_INLINE inline
# else
# define CBOR_INLINE
# endif
# endif
#endif
typedef enum CborType {
CborIntegerType = 0x00,
CborByteStringType = 0x40,
CborTextStringType = 0x60,
CborArrayType = 0x80,
CborMapType = 0xa0,
CborTagType = 0xc0,
CborSimpleType = 0xe0,
CborBooleanType = 0xf5,
CborNullType = 0xf6,
CborUndefinedType = 0xf7,
CborHalfFloatType = 0xf9,
CborFloatType = 0xfa,
CborDoubleType = 0xfb,
CborInvalidType = 0xff /* equivalent to the break byte, so it will never be used */
} CborType;
typedef uint64_t CborTag;
typedef enum CborKnownTags {
CborDateTimeStringTag = 0,
CborUnixTime_tTag = 1,
CborPositiveBignumTag = 2,
CborNegativeBignumTag = 3,
CborDecimalTag = 4,
CborBigfloatTag = 5,
CborCOSE_Encrypt0Tag = 16,
CborCOSE_Mac0Tag = 17,
CborCOSE_Sign1Tag = 18,
CborExpectedBase64urlTag = 21,
CborExpectedBase64Tag = 22,
CborExpectedBase16Tag = 23,
CborEncodedCborTag = 24,
CborUrlTag = 32,
CborBase64urlTag = 33,
CborBase64Tag = 34,
CborRegularExpressionTag = 35,
CborMimeMessageTag = 36,
CborCOSE_EncryptTag = 96,
CborCOSE_MacTag = 97,
CborCOSE_SignTag = 98,
CborSignatureTag = 55799
} CborKnownTags;
/* #define the constants so we can check with #ifdef */
#define CborDateTimeStringTag CborDateTimeStringTag
#define CborUnixTime_tTag CborUnixTime_tTag
#define CborPositiveBignumTag CborPositiveBignumTag
#define CborNegativeBignumTag CborNegativeBignumTag
#define CborDecimalTag CborDecimalTag
#define CborBigfloatTag CborBigfloatTag
#define CborCOSE_Encrypt0Tag CborCOSE_Encrypt0Tag
#define CborCOSE_Mac0Tag CborCOSE_Mac0Tag
#define CborCOSE_Sign1Tag CborCOSE_Sign1Tag
#define CborExpectedBase64urlTag CborExpectedBase64urlTag
#define CborExpectedBase64Tag CborExpectedBase64Tag
#define CborExpectedBase16Tag CborExpectedBase16Tag
#define CborEncodedCborTag CborEncodedCborTag
#define CborUrlTag CborUrlTag
#define CborBase64urlTag CborBase64urlTag
#define CborBase64Tag CborBase64Tag
#define CborRegularExpressionTag CborRegularExpressionTag
#define CborMimeMessageTag CborMimeMessageTag
#define CborCOSE_EncryptTag CborCOSE_EncryptTag
#define CborCOSE_MacTag CborCOSE_MacTag
#define CborCOSE_SignTag CborCOSE_SignTag
#define CborSignatureTag CborSignatureTag
/* Error API */
typedef enum CborError {
CborNoError = 0,
/* errors in all modes */
CborUnknownError,
CborErrorUnknownLength, /* request for length in array, map, or string with indeterminate length */
CborErrorAdvancePastEOF,
CborErrorIO,
/* parser errors streaming errors */
CborErrorGarbageAtEnd = 256,
CborErrorUnexpectedEOF,
CborErrorUnexpectedBreak,
CborErrorUnknownType, /* can only happen in major type 7 */
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,
CborErrorUnknownTag,
CborErrorInappropriateTagForType,
CborErrorDuplicateObjectKeys,
CborErrorInvalidUtf8TextString,
CborErrorExcludedType,
CborErrorExcludedValue,
CborErrorImproperValue,
CborErrorOverlongEncoding,
CborErrorMapKeyNotString,
CborErrorMapNotSorted,
CborErrorMapKeysNotUnique,
/* encoder errors */
CborErrorTooManyItems = 768,
CborErrorTooFewItems,
/* internal implementation errors */
CborErrorDataTooLarge = 1024,
CborErrorNestingTooDeep,
CborErrorUnsupportedType,
CborErrorUnimplementedValidation,
/* errors in converting to JSON */
CborErrorJsonObjectKeyIsAggregate = 1280,
CborErrorJsonObjectKeyNotString,
CborErrorJsonNotImplemented,
CborErrorOutOfMemory = (int) (~0U / 2 + 1),
CborErrorInternalError = (int) (~0U / 2) /* INT_MAX on two's complement machines */
} CborError;
CBOR_API const char *cbor_error_string(CborError error);
/* Encoder API */
typedef enum CborEncoderAppendType
{
CborEncoderAppendCborData = 0,
CborEncoderAppendStringData = 1
} CborEncoderAppendType;
typedef CborError (*CborEncoderWriteFunction)(void *, const void *, size_t, CborEncoderAppendType);
enum CborEncoderFlags
{
CborIteratorFlag_WriterFunction = 0x01,
CborIteratorFlag_ContainerIsMap_ = 0x20
};
struct CborEncoder
{
union {
uint8_t *ptr;
ptrdiff_t bytes_needed;
CborEncoderWriteFunction writer;
} data;
uint8_t *end;
size_t remaining;
int flags;
};
typedef struct CborEncoder CborEncoder;
static const size_t CborIndefiniteLength = SIZE_MAX;
#ifndef CBOR_NO_ENCODER_API
CBOR_API void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags);
CBOR_API void cbor_encoder_init_writer(CborEncoder *encoder, CborEncoderWriteFunction writer, void *);
CBOR_API CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value);
CBOR_API CborError cbor_encode_int(CborEncoder *encoder, int64_t value);
CBOR_API CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value);
CBOR_API CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value);
CBOR_API CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag);
CBOR_API CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length);
CBOR_INLINE_API CborError cbor_encode_text_stringz(CborEncoder *encoder, const char *string)
{ return cbor_encode_text_string(encoder, string, strlen(string)); }
CBOR_API CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length);
CBOR_API CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value);
CBOR_INLINE_API CborError cbor_encode_boolean(CborEncoder *encoder, bool value)
{ return cbor_encode_simple_value(encoder, (int)value - 1 + (CborBooleanType & 0x1f)); }
CBOR_INLINE_API CborError cbor_encode_null(CborEncoder *encoder)
{ return cbor_encode_simple_value(encoder, CborNullType & 0x1f); }
CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder)
{ return cbor_encode_simple_value(encoder, CborUndefinedType & 0x1f); }
CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value)
{ return cbor_encode_floating_point(encoder, CborHalfFloatType, value); }
CBOR_API CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value);
CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value)
{ return cbor_encode_floating_point(encoder, CborFloatType, &value); }
CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value)
{ return cbor_encode_floating_point(encoder, CborDoubleType, &value); }
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)
{
return encoder->data.ptr;
}
CBOR_INLINE_API size_t cbor_encoder_get_buffer_size(const CborEncoder *encoder, const uint8_t *buffer)
{
return (size_t)(encoder->data.ptr - buffer);
}
CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *encoder)
{
return encoder->end ? 0 : (size_t)encoder->data.bytes_needed;
}
#endif /* CBOR_NO_ENCODER_API */
/* Parser API */
enum CborParserGlobalFlags
{
CborParserFlag_ExternalSource = 0x01
};
enum CborParserIteratorFlags
{
/* used for all types, but not during string chunk iteration
* (values are static-asserted, don't change) */
CborIteratorFlag_IntegerValueIs64Bit = 0x01,
CborIteratorFlag_IntegerValueTooLarge = 0x02,
/* used only for CborIntegerType */
CborIteratorFlag_NegativeInteger = 0x04,
/* used only during string iteration */
CborIteratorFlag_BeforeFirstStringChunk = 0x04,
CborIteratorFlag_IteratingStringChunks = 0x08,
/* used for arrays, maps and strings, including during chunk iteration */
CborIteratorFlag_UnknownLength = 0x10,
/* used for maps, but must be kept for all types
* (ContainerIsMap value must be CborMapType - CborArrayType) */
CborIteratorFlag_ContainerIsMap = 0x20,
CborIteratorFlag_NextIsMapKey = 0x40
};
struct CborValue;
struct CborParserOperations
{
bool (*can_read_bytes)(void *token, size_t len);
void *(*read_bytes)(void *token, void *dst, size_t offset, size_t len);
void (*advance_bytes)(void *token, size_t len);
CborError (*transfer_string)(void *token, const void **userptr, size_t offset, size_t len);
};
struct CborParser
{
union {
const uint8_t *end;
const struct CborParserOperations *ops;
} source;
enum CborParserGlobalFlags flags;
};
typedef struct CborParser CborParser;
struct CborValue
{
const CborParser *parser;
union {
const uint8_t *ptr;
void *token;
} source;
uint32_t remaining;
uint16_t extra;
uint8_t type;
uint8_t flags;
};
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->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)
{ return it->type == CborArrayType || it->type == CborMapType; }
CBOR_API CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed);
CBOR_API CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed);
CBOR_PRIVATE_API uint64_t _cbor_value_decode_int64_internal(const CborValue *value);
CBOR_INLINE_API uint64_t _cbor_value_extract_int64_helper(const CborValue *value)
{
return value->flags & CborIteratorFlag_IntegerValueTooLarge ?
_cbor_value_decode_int64_internal(value) : value->extra;
}
CBOR_INLINE_API bool cbor_value_is_valid(const CborValue *value)
{ return value && value->type != CborInvalidType; }
CBOR_INLINE_API CborType cbor_value_get_type(const CborValue *value)
{ return (CborType)value->type; }
/* Null & undefined type */
CBOR_INLINE_API bool cbor_value_is_null(const CborValue *value)
{ return value->type == CborNullType; }
CBOR_INLINE_API bool cbor_value_is_undefined(const CborValue *value)
{ return value->type == CborUndefinedType; }
/* Booleans */
CBOR_INLINE_API bool cbor_value_is_boolean(const CborValue *value)
{ return value->type == CborBooleanType; }
CBOR_INLINE_API CborError cbor_value_get_boolean(const CborValue *value, bool *result)
{
assert(cbor_value_is_boolean(value));
*result = !!value->extra;
return CborNoError;
}
/* Simple types */
CBOR_INLINE_API bool cbor_value_is_simple_type(const CborValue *value)
{ return value->type == CborSimpleType; }
CBOR_INLINE_API CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result)
{
assert(cbor_value_is_simple_type(value));
*result = (uint8_t)value->extra;
return CborNoError;
}
/* Integers */
CBOR_INLINE_API bool cbor_value_is_integer(const CborValue *value)
{ return value->type == CborIntegerType; }
CBOR_INLINE_API bool cbor_value_is_unsigned_integer(const CborValue *value)
{ return cbor_value_is_integer(value) && (value->flags & CborIteratorFlag_NegativeInteger) == 0; }
CBOR_INLINE_API bool cbor_value_is_negative_integer(const CborValue *value)
{ return cbor_value_is_integer(value) && (value->flags & CborIteratorFlag_NegativeInteger); }
CBOR_INLINE_API CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result)
{
assert(cbor_value_is_integer(value));
*result = _cbor_value_extract_int64_helper(value);
return CborNoError;
}
CBOR_INLINE_API CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result)
{
assert(cbor_value_is_unsigned_integer(value));
*result = _cbor_value_extract_int64_helper(value);
return CborNoError;
}
CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t *result)
{
assert(cbor_value_is_integer(value));
*result = (int64_t) _cbor_value_extract_int64_helper(value);
if (value->flags & CborIteratorFlag_NegativeInteger)
*result = -*result - 1;
return CborNoError;
}
CBOR_INLINE_API CborError cbor_value_get_int(const CborValue *value, int *result)
{
assert(cbor_value_is_integer(value));
*result = (int) _cbor_value_extract_int64_helper(value);
if (value->flags & CborIteratorFlag_NegativeInteger)
*result = -*result - 1;
return CborNoError;
}
CBOR_API CborError cbor_value_get_int64_checked(const CborValue *value, int64_t *result);
CBOR_API CborError cbor_value_get_int_checked(const CborValue *value, int *result);
CBOR_INLINE_API bool cbor_value_is_length_known(const CborValue *value)
{ return (value->flags & CborIteratorFlag_UnknownLength) == 0; }
/* Tags */
CBOR_INLINE_API bool cbor_value_is_tag(const CborValue *value)
{ return value->type == CborTagType; }
CBOR_INLINE_API CborError cbor_value_get_tag(const CborValue *value, CborTag *result)
{
assert(cbor_value_is_tag(value));
*result = _cbor_value_extract_int64_helper(value);
return CborNoError;
}
CBOR_API CborError cbor_value_skip_tag(CborValue *it);
/* Strings */
CBOR_INLINE_API bool cbor_value_is_byte_string(const CborValue *value)
{ return value->type == CborByteStringType; }
CBOR_INLINE_API bool cbor_value_is_text_string(const CborValue *value)
{ return value->type == CborTextStringType; }
CBOR_INLINE_API CborError cbor_value_get_string_length(const CborValue *value, size_t *length)
{
uint64_t v;
assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value));
if (!cbor_value_is_length_known(value))
return CborErrorUnknownLength;
v = _cbor_value_extract_int64_helper(value);
*length = (size_t)v;
if (*length != v)
return CborErrorDataTooLarge;
return CborNoError;
}
CBOR_PRIVATE_API CborError _cbor_value_copy_string(const CborValue *value, void *buffer,
size_t *buflen, CborValue *next);
CBOR_PRIVATE_API CborError _cbor_value_dup_string(const CborValue *value, void **buffer,
size_t *buflen, CborValue *next);
CBOR_API CborError cbor_value_calculate_string_length(const CborValue *value, size_t *length);
CBOR_INLINE_API CborError cbor_value_copy_text_string(const CborValue *value, char *buffer,
size_t *buflen, CborValue *next)
{
assert(cbor_value_is_text_string(value));
return _cbor_value_copy_string(value, buffer, buflen, next);
}
CBOR_INLINE_API CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer,
size_t *buflen, CborValue *next)
{
assert(cbor_value_is_byte_string(value));
return _cbor_value_copy_string(value, buffer, buflen, next);
}
CBOR_INLINE_API CborError cbor_value_dup_text_string(const CborValue *value, char **buffer,
size_t *buflen, CborValue *next)
{
assert(cbor_value_is_text_string(value));
return _cbor_value_dup_string(value, (void **)buffer, buflen, next);
}
CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer,
size_t *buflen, CborValue *next)
{
assert(cbor_value_is_byte_string(value));
return _cbor_value_dup_string(value, (void **)buffer, buflen, next);
}
CBOR_PRIVATE_API CborError _cbor_value_get_string_chunk_size(const CborValue *value, size_t *len);
CBOR_INLINE_API CborError cbor_value_get_string_chunk_size(const CborValue *value, size_t *len)
{
assert(value->flags & CborIteratorFlag_IteratingStringChunks);
return _cbor_value_get_string_chunk_size(value, len);
}
CBOR_INLINE_API bool cbor_value_string_iteration_at_end(const CborValue *value)
{
size_t dummy;
return cbor_value_get_string_chunk_size(value, &dummy) == CborErrorNoMoreStringChunks;
}
CBOR_PRIVATE_API CborError _cbor_value_begin_string_iteration(CborValue *value);
CBOR_INLINE_API CborError cbor_value_begin_string_iteration(CborValue *value)
{
assert(cbor_value_is_text_string(value) || cbor_value_is_byte_string(value));
assert(!(value->flags & CborIteratorFlag_IteratingStringChunks));
return _cbor_value_begin_string_iteration(value);
}
CBOR_PRIVATE_API CborError _cbor_value_finish_string_iteration(CborValue *value);
CBOR_INLINE_API CborError cbor_value_finish_string_iteration(CborValue *value)
{
assert(cbor_value_string_iteration_at_end(value));
return _cbor_value_finish_string_iteration(value);
}
CBOR_PRIVATE_API CborError _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr,
size_t *len, CborValue *next);
CBOR_INLINE_API CborError cbor_value_get_text_string_chunk(const CborValue *value, const char **bufferptr,
size_t *len, CborValue *next)
{
assert(cbor_value_is_text_string(value));
return _cbor_value_get_string_chunk(value, (const void **)bufferptr, len, next);
}
CBOR_INLINE_API CborError cbor_value_get_byte_string_chunk(const CborValue *value, const uint8_t **bufferptr,
size_t *len, CborValue *next)
{
assert(cbor_value_is_byte_string(value));
return _cbor_value_get_string_chunk(value, (const void **)bufferptr, len, next);
}
CBOR_API CborError cbor_value_text_string_equals(const CborValue *value, const char *string, bool *result);
/* Maps and arrays */
CBOR_INLINE_API bool cbor_value_is_array(const CborValue *value)
{ return value->type == CborArrayType; }
CBOR_INLINE_API bool cbor_value_is_map(const CborValue *value)
{ return value->type == CborMapType; }
CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, size_t *length)
{
uint64_t v;
assert(cbor_value_is_array(value));
if (!cbor_value_is_length_known(value))
return CborErrorUnknownLength;
v = _cbor_value_extract_int64_helper(value);
*length = (size_t)v;
if (*length != v)
return CborErrorDataTooLarge;
return CborNoError;
}
CBOR_INLINE_API CborError cbor_value_get_map_length(const CborValue *value, size_t *length)
{
uint64_t v;
assert(cbor_value_is_map(value));
if (!cbor_value_is_length_known(value))
return CborErrorUnknownLength;
v = _cbor_value_extract_int64_helper(value);
*length = (size_t)v;
if (*length != v)
return CborErrorDataTooLarge;
return CborNoError;
}
CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element);
/* Floating point */
CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value)
{ return value->type == CborHalfFloatType; }
CBOR_API CborError cbor_value_get_half_float_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; }
CBOR_INLINE_API CborError cbor_value_get_float(const CborValue *value, float *result)
{
uint32_t data;
assert(cbor_value_is_float(value));
assert(value->flags & CborIteratorFlag_IntegerValueTooLarge);
data = (uint32_t)_cbor_value_decode_int64_internal(value);
memcpy(result, &data, sizeof(*result));
return CborNoError;
}
CBOR_INLINE_API bool cbor_value_is_double(const CborValue *value)
{ return value->type == CborDoubleType; }
CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double *result)
{
uint64_t data;
assert(cbor_value_is_double(value));
assert(value->flags & CborIteratorFlag_IntegerValueTooLarge);
data = _cbor_value_decode_int64_internal(value);
memcpy(result, &data, sizeof(*result));
return CborNoError;
}
/* Validation API */
#ifndef CBOR_NO_VALIDATION_API
enum CborValidationFlags {
/* Bit mapping:
* bits 0-7 (8 bits): canonical format
* bits 8-11 (4 bits): canonical format & strict mode
* bits 12-20 (8 bits): strict mode
* bits 21-31 (10 bits): other
*/
CborValidateShortestIntegrals = 0x0001,
CborValidateShortestFloatingPoint = 0x0002,
CborValidateShortestNumbers = CborValidateShortestIntegrals | CborValidateShortestFloatingPoint,
CborValidateNoIndeterminateLength = 0x0100,
CborValidateMapIsSorted = 0x0200 | CborValidateNoIndeterminateLength,
CborValidateCanonicalFormat = 0x0fff,
CborValidateMapKeysAreUnique = 0x1000 | CborValidateMapIsSorted,
CborValidateTagUse = 0x2000,
CborValidateUtf8 = 0x4000,
CborValidateStrictMode = 0xfff00,
CborValidateMapKeysAreString = 0x100000,
CborValidateNoUndefined = 0x200000,
CborValidateNoTags = 0x400000,
CborValidateFiniteFloatingPoint = 0x800000,
/* unused = 0x1000000, */
/* unused = 0x2000000, */
CborValidateNoUnknownSimpleTypesSA = 0x4000000,
CborValidateNoUnknownSimpleTypes = 0x8000000 | CborValidateNoUnknownSimpleTypesSA,
CborValidateNoUnknownTagsSA = 0x10000000,
CborValidateNoUnknownTagsSR = 0x20000000 | CborValidateNoUnknownTagsSA,
CborValidateNoUnknownTags = 0x40000000 | CborValidateNoUnknownTagsSR,
CborValidateCompleteData = (int)0x80000000,
CborValidateStrictest = (int)~0U,
CborValidateBasic = 0
};
CBOR_API CborError cbor_value_validate(const CborValue *it, uint32_t flags);
#endif /* CBOR_NO_VALIDATION_API */
/* Human-readable (dump) API */
#ifndef CBOR_NO_PRETTY_API
enum CborPrettyFlags {
CborPrettyNumericEncodingIndicators = 0x01,
CborPrettyTextualEncodingIndicators = 0,
CborPrettyIndicateIndeterminateLength = 0x02,
CborPrettyIndicateIndetermineLength = CborPrettyIndicateIndeterminateLength, /* deprecated */
CborPrettyIndicateOverlongNumbers = 0x04,
CborPrettyShowStringFragments = 0x100,
CborPrettyMergeStringFragments = 0,
CborPrettyDefaultFlags = CborPrettyIndicateIndeterminateLength
};
typedef CborError (*CborStreamFunction)(void *token, const char *fmt, ...)
#ifdef __GNUC__
__attribute__((__format__(printf, 2, 3)))
#endif
;
CBOR_API CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags);
/* The following API requires a hosted C implementation (uses FILE*) */
#if !defined(__STDC_HOSTED__) || __STDC_HOSTED__-0 == 1
CBOR_API CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags);
CBOR_API CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value);
CBOR_INLINE_API CborError cbor_value_to_pretty(FILE *out, const CborValue *value)
{
CborValue copy = *value;
return cbor_value_to_pretty_advance_flags(out, &copy, CborPrettyDefaultFlags);
}
#endif /* __STDC_HOSTED__ check */
#endif /* CBOR_NO_PRETTY_API */
#endif /* CBOR_NO_PARSER_API */
#ifdef __cplusplus
}
#endif
#endif /* CBOR_H */

@ -0,0 +1,689 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#ifndef _BSD_SOURCE
#define _BSD_SOURCE 1
#endif
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE 1
#endif
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "cborinternal_p.h"
#include "compilersupport_p.h"
#include <stdlib.h>
#include <string.h>
/**
* \defgroup CborEncoding Encoding to CBOR
* \brief Group of functions used to encode data to CBOR.
*
* CborEncoder is used to encode data into a CBOR stream. The outermost
* CborEncoder is initialized by calling cbor_encoder_init(), with the buffer
* where the CBOR stream will be stored. The outermost CborEncoder is usually
* used to encode exactly one item, most often an array or map. It is possible
* to encode more than one item, but care must then be taken on the decoder
* side to ensure the state is reset after each item was decoded.
*
* Nested CborEncoder objects are created using cbor_encoder_create_array() and
* cbor_encoder_create_map(), later closed with cbor_encoder_close_container()
* or cbor_encoder_close_container_checked(). The pairs of creation and closing
* must be exactly matched and their parameters are always the same.
*
* CborEncoder writes directly to the user-supplied buffer, without extra
* buffering. CborEncoder does not allocate memory and CborEncoder objects are
* usually created on the stack of the encoding functions.
*
* The example below initializes a CborEncoder object with a buffer and encodes
* a single integer.
*
* \code
* uint8_t buf[16];
* CborEncoder encoder;
* cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
* cbor_encode_int(&encoder, some_value);
* \endcode
*
* As explained before, usually the outermost CborEncoder object is used to add
* one array or map, which in turn contains multiple elements. The example
* below creates a CBOR map with one element: a key "foo" and a boolean value.
*
* \code
* uint8_t buf[16];
* CborEncoder encoder, mapEncoder;
* cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
* cbor_encoder_create_map(&encoder, &mapEncoder, 1);
* cbor_encode_text_stringz(&mapEncoder, "foo");
* cbor_encode_boolean(&mapEncoder, some_value);
* cbor_encoder_close_container(&encoder, &mapEncoder);
* \endcode
*
* <h3 class="groupheader">Error checking and buffer size</h3>
*
* All functions operating on CborEncoder return a condition of type CborError.
* If the encoding was successful, they return CborNoError. Some functions do
* extra checking on the input provided and may return some other error
* conditions (for example, cbor_encode_simple_value() checks that the type is
* of the correct type).
*
* In addition, all functions check whether the buffer has enough bytes to
* encode the item being appended. If that is not possible, they return
* CborErrorOutOfMemory.
*
* It is possible to continue with the encoding of data past the first function
* that returns CborErrorOutOfMemory. CborEncoder functions will not overrun
* the buffer, but will instead count how many more bytes are needed to
* complete the encoding. At the end, you can obtain that count by calling
* cbor_encoder_get_extra_bytes_needed().
*
* \section1 Finalizing the encoding
*
* Once all items have been appended and the containers have all been properly
* closed, the user-supplied buffer will contain the CBOR stream and may be
* immediately used. To obtain the size of the buffer, call
* cbor_encoder_get_buffer_size() with the original buffer pointer.
*
* The example below illustrates how one can encode an item with error checking
* and then pass on the buffer for network sending.
*
* \code
* uint8_t buf[16];
* CborError err;
* CborEncoder encoder, mapEncoder;
* cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
* err = cbor_encoder_create_map(&encoder, &mapEncoder, 1);
* if (err)
* return err;
* err = cbor_encode_text_stringz(&mapEncoder, "foo");
* if (err)
* return err;
* err = cbor_encode_boolean(&mapEncoder, some_value);
* if (err)
* return err;
* err = cbor_encoder_close_container_checked(&encoder, &mapEncoder);
* if (err)
* return err;
*
* size_t len = cbor_encoder_get_buffer_size(&encoder, buf);
* send_payload(buf, len);
* return CborNoError;
* \endcode
*
* Finally, the example below expands on the one above and also
* deals with dynamically growing the buffer if the initial allocation wasn't
* big enough. Note the two places where the error checking was replaced with
* an cbor_assertion, showing where the author assumes no error can occur.
*
* \code
* uint8_t *encode_string_array(const char **strings, int n, size_t *bufsize)
* {
* CborError err;
* CborEncoder encoder, arrayEncoder;
* size_t size = 256;
* uint8_t *buf = NULL;
*
* while (1) {
* int i;
* size_t more_bytes;
* uint8_t *nbuf = realloc(buf, size);
* if (nbuf == NULL)
* goto error;
* buf = nbuf;
*
* cbor_encoder_init(&encoder, buf, size, 0);
* err = cbor_encoder_create_array(&encoder, &arrayEncoder, n);
* cbor_assert(!err); // can't fail, the buffer is always big enough
*
* for (i = 0; i < n; ++i) {
* err = cbor_encode_text_stringz(&arrayEncoder, strings[i]);
* if (err && err != CborErrorOutOfMemory)
* goto error;
* }
*
* err = cbor_encoder_close_container_checked(&encoder, &arrayEncoder);
* cbor_assert(!err); // shouldn't fail!
*
* more_bytes = cbor_encoder_get_extra_bytes_needed(encoder);
* if (more_size) {
* // buffer wasn't big enough, try again
* size += more_bytes;
* continue;
* }
*
* *bufsize = cbor_encoder_get_buffer_size(encoder, buf);
* return buf;
* }
* error:
* free(buf);
* return NULL;
* }
* \endcode
*/
/**
* \addtogroup CborEncoding
* @{
*/
/**
* \struct CborEncoder
* Structure used to encode to CBOR.
*/
/**
* Initializes a CborEncoder structure \a encoder by pointing it to buffer \a
* buffer of size \a size. The \a flags field is currently unused and must be
* zero.
*/
void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags)
{
encoder->data.ptr = buffer;
encoder->end = buffer + size;
encoder->remaining = 2;
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);
memcpy(where, &v_be, sizeof(v_be));
}
/* Note: Since this is currently only used in situations where OOM is the only
* valid error, we KNOW this to be true. Thus, this function now returns just 'true',
* but if in the future, any function starts returning a non-OOM error, this will need
* to be changed to the test. At the moment, this is done to prevent more branches
* being created in the tinycbor output */
static inline bool isOomError(CborError err)
{
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)
{
uint32_t v_be = cbor_htonl(v);
memcpy(where, &v_be, sizeof(v_be));
}
static inline void put64(void *where, uint64_t v)
{
uint64_t v_be = cbor_htonll(v);
memcpy(where, &v_be, sizeof(v_be));
}
static inline bool would_overflow(CborEncoder *encoder, size_t len)
{
ptrdiff_t remaining = (ptrdiff_t)encoder->end;
remaining -= remaining ? (ptrdiff_t)encoder->data.ptr : encoder->data.bytes_needed;
remaining -= (ptrdiff_t)len;
return unlikely(remaining < 0);
}
static inline void advance_ptr(CborEncoder *encoder, size_t n)
{
if (encoder->end)
encoder->data.ptr += n;
else
encoder->data.bytes_needed += n;
}
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;
encoder->end = NULL;
encoder->data.bytes_needed = 0;
}
advance_ptr(encoder, len);
return CborErrorOutOfMemory;
}
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, CborEncoderAppendCborData);
}
static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
{
/* Little-endian would have been so much more convenient here:
* We could just write at the beginning of buf but append_to_buffer
* only the necessary bytes.
* Since it has to be big endian, do it the other way around:
* write from the end. */
uint64_t buf[2];
uint8_t *const bufend = (uint8_t *)buf + sizeof(buf);
uint8_t *bufstart = bufend - 1;
put64(buf + 1, ui); /* we probably have a bunch of zeros in the beginning */
if (ui < Value8Bit) {
*bufstart += shiftedMajorType;
} else {
uint8_t more = 0;
if (ui > 0xffU)
++more;
if (ui > 0xffffU)
++more;
if (ui > 0xffffffffU)
++more;
bufstart -= (size_t)1 << more;
*bufstart = shiftedMajorType + Value8Bit + more;
}
return append_to_buffer(encoder, bufstart, bufend - bufstart, CborEncoderAppendCborData);
}
static inline void saturated_decrement(CborEncoder *encoder)
{
if (encoder->remaining)
--encoder->remaining;
}
static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType)
{
saturated_decrement(encoder);
return encode_number_no_update(encoder, ui, shiftedMajorType);
}
/**
* Appends the unsigned 64-bit integer \a value to the CBOR stream provided by
* \a encoder.
*
* \sa cbor_encode_negative_int, cbor_encode_int
*/
CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value)
{
return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift);
}
/**
* Appends the negative 64-bit integer whose absolute value is \a
* absolute_value to the CBOR stream provided by \a encoder.
*
* If the value \a absolute_value is zero, this function encodes -2^64.
*
* \sa cbor_encode_uint, cbor_encode_int
*/
CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value)
{
return encode_number(encoder, absolute_value - 1, NegativeIntegerType << MajorTypeShift);
}
/**
* Appends the signed 64-bit integer \a value to the CBOR stream provided by
* \a encoder.
*
* \sa cbor_encode_negative_int, cbor_encode_uint
*/
CborError cbor_encode_int(CborEncoder *encoder, int64_t value)
{
/* adapted from code in RFC 7049 appendix C (pseudocode) */
uint64_t ui = value >> 63; /* extend sign to whole length */
uint8_t majorType = ui & 0x20; /* extract major type */
ui ^= value; /* complement negatives */
return encode_number(encoder, ui, majorType);
}
/**
* Appends the CBOR Simple Type of value \a value to the CBOR stream provided by
* \a encoder.
*
* This function may return error CborErrorIllegalSimpleType if the \a value
* variable contains a number that is not a valid simple type.
*/
CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
{
#ifndef CBOR_ENCODER_NO_CHECK_USER
/* check if this is a valid simple type */
if (value >= HalfPrecisionFloat && value <= Break)
return CborErrorIllegalSimpleType;
#endif
return encode_number(encoder, value, SimpleTypesType << MajorTypeShift);
}
/**
* Appends the floating-point value of type \a fpType and pointed to by \a
* value to the CBOR stream provided by \a encoder. The value of \a fpType must
* be one of CborHalfFloatType, CborFloatType or CborDoubleType, otherwise the
* behavior of this function is undefined.
*
* This function is useful for code that needs to pass through floating point
* values but does not wish to have the actual floating-point code.
*
* \sa cbor_encode_half_float, cbor_encode_float_as_half_float, cbor_encode_float, cbor_encode_double
*/
CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
{
unsigned size;
uint8_t buf[1 + sizeof(uint64_t)];
cbor_assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType);
buf[0] = fpType;
size = 2U << (fpType - CborHalfFloatType);
if (size == 8)
put64(buf + 1, *(const uint64_t*)value);
else if (size == 4)
put32(buf + 1, *(const uint32_t*)value);
else
put16(buf + 1, *(const uint16_t*)value);
saturated_decrement(encoder);
return append_to_buffer(encoder, buf, size + 1, CborEncoderAppendCborData);
}
/**
* Appends the CBOR tag \a tag to the CBOR stream provided by \a encoder.
*
* \sa CborTag
*/
CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag)
{
/* tags don't count towards the number of elements in an array or map */
return encode_number_no_update(encoder, tag, TagType << MajorTypeShift);
}
static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string)
{
CborError err = encode_number(encoder, length, shiftedMajorType);
if (err && !isOomError(err))
return err;
return append_to_buffer(encoder, string, length, CborEncoderAppendStringData);
}
/**
* \fn CborError cbor_encode_text_stringz(CborEncoder *encoder, const char *string)
*
* Appends the null-terminated text string \a string to the CBOR stream
* provided by \a encoder. CBOR requires that \a string be valid UTF-8, but
* TinyCBOR makes no verification of correctness. The terminating null is not
* included in the stream.
*
* \sa cbor_encode_text_string, cbor_encode_byte_string
*/
/**
* Appends the byte string \a string of length \a length to the CBOR stream
* provided by \a encoder. CBOR byte strings are arbitrary raw data.
*
* \sa cbor_encode_text_stringz, cbor_encode_text_string
*/
CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length)
{
return encode_string(encoder, length, ByteStringType << MajorTypeShift, string);
}
/**
* Appends the text string \a string of length \a length to the CBOR stream
* provided by \a encoder. CBOR requires that \a string be valid UTF-8, but
* TinyCBOR makes no verification of correctness.
*
* \sa CborError cbor_encode_text_stringz, cbor_encode_byte_string
*/
CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length)
{
return encode_string(encoder, length, TextStringType << MajorTypeShift, string);
}
#ifdef __GNUC__
__attribute__((noinline))
#endif
static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType)
{
CborError err;
container->data.ptr = encoder->data.ptr;
container->end = encoder->end;
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;
err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength);
} else {
if (shiftedMajorType & CborIteratorFlag_ContainerIsMap)
container->remaining += length;
err = encode_number_no_update(container, length, shiftedMajorType);
}
return err;
}
/**
* Creates a CBOR array in the CBOR stream provided by \a 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()
* with the same \a encoder and \a arrayEncoder parameters.
*
* The number of items inserted into the array must be exactly \a length items,
* otherwise the stream is invalid. If the number of items is not known when
* creating the array, the constant \ref CborIndefiniteLength may be passed as
* length instead, and an indefinite length array is created.
*
* \sa cbor_encoder_create_map
*/
CborError cbor_encoder_create_array(CborEncoder *parentEncoder, CborEncoder *arrayEncoder, size_t length)
{
return create_container(parentEncoder, arrayEncoder, length, ArrayType << MajorTypeShift);
}
/**
* 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()
* with the same \a encoder and \a mapEncoder parameters.
*
* The number of pair of items inserted into the map must be exactly \a length
* items, otherwise the stream is invalid. If the number is not known
* when creating the map, the constant \ref CborIndefiniteLength may be passed as
* 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
* value (and is not \ref CborIndefiniteLength), this function returns error
* CborErrorDataTooLarge.
*
* \sa cbor_encoder_create_array
*/
CborError cbor_encoder_create_map(CborEncoder *parentEncoder, CborEncoder *mapEncoder, size_t length)
{
if (length != CborIndefiniteLength && length > SIZE_MAX / 2)
return CborErrorDataTooLarge;
return create_container(parentEncoder, mapEncoder, length, MapType << MajorTypeShift);
}
/**
* Closes the CBOR container (array or map) provided by \a containerEncoder and
* updates the CBOR stream provided by \a encoder. Both parameters must be the
* same as were passed to cbor_encoder_create_array() or
* cbor_encoder_create_map().
*
* Since version 0.5, this function verifies that the number of items (or pairs
* of items, in the case of a map) was correct. It is no longer necessary to call
* cbor_encoder_close_container_checked() instead.
*
* \sa cbor_encoder_create_array(), cbor_encoder_create_map()
*/
CborError cbor_encoder_close_container(CborEncoder *parentEncoder, const CborEncoder *containerEncoder)
{
// synchronise buffer state with that of the container
parentEncoder->end = containerEncoder->end;
parentEncoder->data = containerEncoder->data;
if (containerEncoder->flags & CborIteratorFlag_UnknownLength)
return append_byte_to_buffer(parentEncoder, BreakByte);
if (containerEncoder->remaining != 1)
return containerEncoder->remaining == 0 ? CborErrorTooManyItems : CborErrorTooFewItems;
if (!parentEncoder->end)
return CborErrorOutOfMemory; /* keep the state */
return CborNoError;
}
/**
* \fn CborError cbor_encode_boolean(CborEncoder *encoder, bool value)
*
* Appends the boolean value \a value to the CBOR stream provided by \a encoder.
*/
/**
* \fn CborError cbor_encode_null(CborEncoder *encoder)
*
* Appends the CBOR type representing a null value to the CBOR stream provided
* by \a encoder.
*
* \sa cbor_encode_undefined()
*/
/**
* \fn CborError cbor_encode_undefined(CborEncoder *encoder)
*
* Appends the CBOR type representing an undefined value to the CBOR stream
* provided by \a encoder.
*
* \sa cbor_encode_null()
*/
/**
* \fn CborError cbor_encode_half_float(CborEncoder *encoder, const void *value)
*
* Appends the IEEE 754 half-precision (16-bit) floating point value pointed to
* by \a value to the CBOR stream provided by \a encoder.
*
* \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double()
*/
/**
* \fn CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value)
*
* Convert the IEEE 754 single-precision (32-bit) floating point value \a value
* to the IEEE 754 half-precision (16-bit) floating point value and append it
* to the CBOR stream provided by \a encoder.
* The \a value should be in the range of the IEEE 754 half-precision floating point type,
* INFINITY, -INFINITY, or NAN, otherwise the behavior of this function is undefined.
*
* \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double()
*/
/**
* \fn CborError cbor_encode_float(CborEncoder *encoder, float value)
*
* Appends the IEEE 754 single-precision (32-bit) floating point value \a value
* to the CBOR stream provided by \a encoder.
*
* \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_double()
*/
/**
* \fn CborError cbor_encode_double(CborEncoder *encoder, double value)
*
* Appends the IEEE 754 double-precision (64-bit) floating point value \a value
* to the CBOR stream provided by \a encoder.
*
* \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_float()
*/
/**
* \fn size_t cbor_encoder_get_buffer_size(const CborEncoder *encoder, const uint8_t *buffer)
*
* Returns the total size of the buffer starting at \a buffer after the
* encoding finished without errors. The \a encoder and \a buffer arguments
* must be the same as supplied to cbor_encoder_init().
*
* If the encoding process had errors, the return value of this function is
* meaningless. If the only errors were CborErrorOutOfMemory, instead use
* cbor_encoder_get_extra_bytes_needed() to find out by how much to grow the
* buffer before encoding again.
*
* See \ref CborEncoding for an example of using this function.
*
* \sa cbor_encoder_init(), cbor_encoder_get_extra_bytes_needed(), CborEncoding
*/
/**
* \fn size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *encoder)
*
* Returns how many more bytes the original buffer supplied to
* cbor_encoder_init() needs to be extended by so that no CborErrorOutOfMemory
* condition will happen for the encoding. If the buffer was big enough, this
* function returns 0. The \a encoder must be the original argument as passed
* to cbor_encoder_init().
*
* This function is usually called after an encoding sequence ended with one or
* more CborErrorOutOfMemory errors, but no other error. If any other error
* happened, the return value of this function is meaningless.
*
* See \ref CborEncoding for an example of using this function.
*
* \sa cbor_encoder_init(), cbor_encoder_get_buffer_size(), CborEncoding
*/
/** @} */

@ -0,0 +1,57 @@
/****************************************************************************
**
** Copyright (C) 2015 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
/**
* \addtogroup CborEncoding
* @{
*/
/**
* @deprecated
*
* Closes the CBOR container (array or map) provided by \a containerEncoder and
* updates the CBOR stream provided by \a encoder. Both parameters must be the
* same as were passed to cbor_encoder_create_array() or
* cbor_encoder_create_map().
*
* Prior to version 0.5, cbor_encoder_close_container() did not check the
* number of items added. Since that version, it does and now
* cbor_encoder_close_container_checked() is no longer needed.
*
* \sa cbor_encoder_create_array(), cbor_encoder_create_map()
*/
CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder)
{
return cbor_encoder_close_container(encoder, containerEncoder);
}
/** @} */

@ -0,0 +1,42 @@
/****************************************************************************
**
** Copyright (C) 2019 S.Phirsov
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "cborinternal_p.h"
#ifndef CBOR_NO_HALF_FLOAT_TYPE
CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value)
{
uint16_t v = (uint16_t)encode_half(value);
return cbor_encode_floating_point(encoder, CborHalfFloatType, &v);
}
#endif

@ -0,0 +1,188 @@
/****************************************************************************
**
** 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 "cbor.h"
#ifndef _
# define _(msg) msg
#endif
/**
* \enum CborError
* \ingroup CborGlobals
* The CborError enum contains the possible error values used by the CBOR encoder and decoder.
*
* TinyCBOR functions report success by returning CborNoError, or one error
* condition by returning one of the values below. One exception is the
* out-of-memory condition (CborErrorOutOfMemory), which the functions for \ref
* CborEncoding may report in bit-wise OR with other conditions.
*
* This technique allows code to determine whether the only error condition was
* a lack of buffer space, which may not be a fatal condition if the buffer can
* be resized. Additionally, the functions for \ref CborEncoding may continue
* to be used even after CborErrorOutOfMemory is returned, and instead they
* will simply calculate the extra space needed.
*
* \value CborNoError No error occurred
* \omitvalue CborUnknownError
* \value CborErrorUnknownLength Request for the length of an array, map or string whose length is not provided in the CBOR stream
* \value CborErrorAdvancePastEOF Not enough data in the stream to decode item (decoding would advance past end of stream)
* \value CborErrorIO An I/O error occurred, probably due to an out-of-memory situation
* \value CborErrorGarbageAtEnd Bytes exist past the end of the CBOR stream
* \value CborErrorUnexpectedEOF End of stream reached unexpectedly
* \value CborErrorUnexpectedBreak A CBOR break byte was found where not expected
* \value CborErrorUnknownType An unknown type (future extension to CBOR) was found in the stream
* \value CborErrorIllegalType An invalid type was found while parsing a chunked CBOR string
* \value CborErrorIllegalNumber An illegal initial byte (encoding unspecified additional information) was found
* \value CborErrorIllegalSimpleType An illegal encoding of a CBOR Simple Type of value less than 32 was found
* \omitvalue CborErrorUnknownSimpleType
* \omitvalue CborErrorUnknownTag
* \omitvalue CborErrorInappropriateTagForType
* \omitvalue CborErrorDuplicateObjectKeys
* \value CborErrorInvalidUtf8TextString Illegal UTF-8 encoding found while parsing CBOR Text String
* \value CborErrorTooManyItems Too many items were added to CBOR map or array of pre-determined length
* \value CborErrorTooFewItems Too few items were added to CBOR map or array of pre-determined length
* \value CborErrorDataTooLarge Data item size exceeds TinyCBOR's implementation limits
* \value CborErrorNestingTooDeep Data item nesting exceeds TinyCBOR's implementation limits
* \omitvalue CborErrorUnsupportedType
* \value CborErrorJsonObjectKeyIsAggregate Conversion to JSON failed because the key in a map is a CBOR map or array
* \value CborErrorJsonObjectKeyNotString Conversion to JSON failed because the key in a map is not a text string
* \value CborErrorOutOfMemory During CBOR encoding, the buffer provided is insufficient for encoding the data item;
* in other situations, TinyCBOR failed to allocate memory
* \value CborErrorInternalError An internal error occurred in TinyCBOR
*/
/**
* \ingroup CborGlobals
* Returns the error string corresponding to the CBOR error condition \a error.
*/
const char *cbor_error_string(CborError error)
{
switch (error) {
case CborNoError:
return "";
case CborUnknownError:
return _("unknown error");
case CborErrorOutOfMemory:
return _("out of memory/need more memory");
case CborErrorUnknownLength:
return _("unknown length (attempted to get the length of a map/array/string of indeterminate length");
case CborErrorAdvancePastEOF:
return _("attempted to advance past EOF");
case CborErrorIO:
return _("I/O error");
case CborErrorGarbageAtEnd:
return _("garbage after the end of the content");
case CborErrorUnexpectedEOF:
return _("unexpected end of data");
case CborErrorUnexpectedBreak:
return _("unexpected 'break' byte");
case CborErrorUnknownType:
return _("illegal byte (encodes future extension type)");
case CborErrorIllegalType:
return _("mismatched string type in chunked string");
case CborErrorIllegalNumber:
return _("illegal initial byte (encodes unspecified additional information)");
case CborErrorIllegalSimpleType:
return _("illegal encoding of simple type smaller than 32");
case CborErrorNoMoreStringChunks:
return _("no more byte or text strings available");
case CborErrorUnknownSimpleType:
return _("unknown simple type");
case CborErrorUnknownTag:
return _("unknown tag");
case CborErrorInappropriateTagForType:
return _("inappropriate tag for type");
case CborErrorDuplicateObjectKeys:
return _("duplicate keys in object");
case CborErrorInvalidUtf8TextString:
return _("invalid UTF-8 content in string");
case CborErrorExcludedType:
return _("excluded type found");
case CborErrorExcludedValue:
return _("excluded value found");
case CborErrorImproperValue:
case CborErrorOverlongEncoding:
return _("value encoded in non-canonical form");
case CborErrorMapKeyNotString:
case CborErrorJsonObjectKeyNotString:
return _("key in map is not a string");
case CborErrorMapNotSorted:
return _("map is not sorted");
case CborErrorMapKeysNotUnique:
return _("map keys are not unique");
case CborErrorTooManyItems:
return _("too many items added to encoder");
case CborErrorTooFewItems:
return _("too few items added to encoder");
case CborErrorDataTooLarge:
return _("internal error: data too large");
case CborErrorNestingTooDeep:
return _("internal error: too many nested containers found in recursive function");
case CborErrorUnsupportedType:
return _("unsupported type");
case 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");
case CborErrorJsonNotImplemented:
return _("conversion to JSON failed: open_memstream unavailable");
case CborErrorInternalError:
return _("internal error");
}
return cbor_error_string(CborUnknownError);
}

@ -0,0 +1,316 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#ifndef CBORINTERNAL_P_H
#define CBORINTERNAL_P_H
#include "compilersupport_p.h"
#ifndef CBOR_NO_FLOATING_POINT
# include <float.h>
# include <math.h>
#else
# ifndef CBOR_NO_HALF_FLOAT_TYPE
# define CBOR_NO_HALF_FLOAT_TYPE 1
# endif
#endif
#ifndef CBOR_NO_HALF_FLOAT_TYPE
# if defined(__F16C__) || defined(__AVX2__)
# include <immintrin.h>
static inline unsigned short encode_half(float val)
{
__m128i m = _mm_cvtps_ph(_mm_set_ss(val), _MM_FROUND_CUR_DIRECTION);
return _mm_extract_epi16(m, 0);
}
static inline float decode_half(unsigned short half)
{
__m128i m = _mm_cvtsi32_si128(half);
return _mm_cvtss_f32(_mm_cvtph_ps(m));
}
# else
/* software implementation of float-to-fp16 conversions */
static inline unsigned short encode_half(double val)
{
uint64_t v;
int sign, exp, mant;
memcpy(&v, &val, sizeof(v));
sign = v >> 63 << 15;
exp = (v >> 52) & 0x7ff;
mant = v << 12 >> 12 >> (53-11); /* keep only the 11 most significant bits of the mantissa */
exp -= 1023;
if (exp == 1024) {
/* infinity or NaN */
exp = 16;
mant >>= 1;
} else if (exp >= 16) {
/* overflow, as largest number */
exp = 15;
mant = 1023;
} else if (exp >= -14) {
/* regular normal */
} else if (exp >= -24) {
/* subnormal */
mant |= 1024;
mant >>= -(exp + 14);
exp = -15;
} else {
/* underflow, make zero */
return 0;
}
/* safe cast here as bit operations above guarantee not to overflow */
return (unsigned short)(sign | ((exp + 15) << 10) | mant);
}
/* this function was copied & adapted from RFC 7049 Appendix D */
static inline double decode_half(unsigned short half)
{
int exp = (half >> 10) & 0x1f;
int mant = half & 0x3ff;
double val;
if (exp == 0) val = ldexp(mant, -24);
else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
else val = mant == 0 ? INFINITY : NAN;
return half & 0x8000 ? -val : val;
}
# endif
#endif /* CBOR_NO_HALF_FLOAT_TYPE */
#ifndef CBOR_INTERNAL_API
# define CBOR_INTERNAL_API
#endif
#ifndef CBOR_PARSER_MAX_RECURSIONS
# 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
* See http://tools.ietf.org/html/rfc7049#section-2.1
*/
typedef enum CborMajorTypes {
UnsignedIntegerType = 0U,
NegativeIntegerType = 1U,
ByteStringType = 2U,
TextStringType = 3U,
ArrayType = 4U,
MapType = 5U, /* a.k.a. object */
TagType = 6U,
SimpleTypesType = 7U
} CborMajorTypes;
/*
* CBOR simple and floating point types
* Encoded in the low 8 bits of the descriptor byte when the
* Major Type is 7.
*/
typedef enum CborSimpleTypes {
FalseValue = 20,
TrueValue = 21,
NullValue = 22,
UndefinedValue = 23,
SimpleTypeInNextByte = 24, /* not really a simple type */
HalfPrecisionFloat = 25, /* ditto */
SinglePrecisionFloat = 26, /* ditto */
DoublePrecisionFloat = 27, /* ditto */
Break = 31
} CborSimpleTypes;
enum {
SmallValueBitLength = 5U,
SmallValueMask = (1U << SmallValueBitLength) - 1, /* 31 */
Value8Bit = 24U,
Value16Bit = 25U,
Value32Bit = 26U,
Value64Bit = 27U,
IndefiniteLength = 31U,
MajorTypeShift = SmallValueBitLength,
MajorTypeMask = (int) (~0U << MajorTypeShift),
BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift)
};
static inline void copy_current_position(CborValue *dst, const CborValue *src)
{
/* This "if" is here for pedantry only: the two branches should perform
* the same memory operation. */
if (src->parser->flags & CborParserFlag_ExternalSource)
dst->source.token = src->source.token;
else
dst->source.ptr = src->source.ptr;
}
static inline bool can_read_bytes(const CborValue *it, size_t n)
{
if (CBOR_PARSER_READER_CONTROL >= 0) {
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
#ifdef CBOR_PARSER_CAN_READ_BYTES_FUNCTION
return CBOR_PARSER_CAN_READ_BYTES_FUNCTION(it->source.token, n);
#else
return it->parser->source.ops->can_read_bytes(it->source.token, n);
#endif
}
}
/* Convert the pointer subtraction to size_t since end >= ptr
* (this prevents issues with (ptrdiff_t)n becoming negative).
*/
return (size_t)(it->parser->source.end - it->source.ptr) >= n;
}
static inline void advance_bytes(CborValue *it, size_t n)
{
if (CBOR_PARSER_READER_CONTROL >= 0) {
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
#ifdef CBOR_PARSER_ADVANCE_BYTES_FUNCTION
CBOR_PARSER_ADVANCE_BYTES_FUNCTION(it->source.token, n);
#else
it->parser->source.ops->advance_bytes(it->source.token, n);
#endif
return;
}
}
it->source.ptr += n;
}
static inline CborError transfer_string(CborValue *it, const void **ptr, size_t offset, size_t len)
{
if (CBOR_PARSER_READER_CONTROL >= 0) {
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
#ifdef CBOR_PARSER_TRANSFER_STRING_FUNCTION
return CBOR_PARSER_TRANSFER_STRING_FUNCTION(it->source.token, ptr, offset, len);
#else
return it->parser->source.ops->transfer_string(it->source.token, ptr, offset, len);
#endif
}
}
it->source.ptr += offset;
if (can_read_bytes(it, len)) {
*CONST_CAST(const void **, ptr) = it->source.ptr;
it->source.ptr += len;
return CborNoError;
}
return CborErrorUnexpectedEOF;
}
static inline void *read_bytes_unchecked(const CborValue *it, void *dst, size_t offset, size_t n)
{
if (CBOR_PARSER_READER_CONTROL >= 0) {
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
#ifdef CBOR_PARSER_READ_BYTES_FUNCTION
return CBOR_PARSER_READ_BYTES_FUNCTION(it->source.token, dst, offset, n);
#else
return it->parser->source.ops->read_bytes(it->source.token, dst, offset, n);
#endif
}
}
return memcpy(dst, it->source.ptr + offset, n);
}
#ifdef __GNUC__
__attribute__((warn_unused_result))
#endif
static inline void *read_bytes(const CborValue *it, void *dst, size_t offset, size_t n)
{
if (can_read_bytes(it, offset + n))
return read_bytes_unchecked(it, dst, offset, n);
return NULL;
}
static inline uint16_t read_uint8(const CborValue *it, size_t offset)
{
uint8_t result;
read_bytes_unchecked(it, &result, offset, sizeof(result));
return result;
}
static inline uint16_t read_uint16(const CborValue *it, size_t offset)
{
uint16_t result;
read_bytes_unchecked(it, &result, offset, sizeof(result));
return cbor_ntohs(result);
}
static inline uint32_t read_uint32(const CborValue *it, size_t offset)
{
uint32_t result;
read_bytes_unchecked(it, &result, offset, sizeof(result));
return cbor_ntohl(result);
}
static inline uint64_t read_uint64(const CborValue *it, size_t offset)
{
uint64_t result;
read_bytes_unchecked(it, &result, offset, sizeof(result));
return cbor_ntohll(result);
}
static inline CborError extract_number_checked(const CborValue *it, uint64_t *value, size_t *bytesUsed)
{
uint8_t descriptor;
size_t bytesNeeded = 0;
/* We've already verified that there's at least one byte to be read */
read_bytes_unchecked(it, &descriptor, 0, 1);
descriptor &= SmallValueMask;
if (descriptor < Value8Bit) {
*value = descriptor;
} else if (unlikely(descriptor > Value64Bit)) {
return CborErrorIllegalNumber;
} else {
bytesNeeded = (size_t)(1 << (descriptor - Value8Bit));
if (!can_read_bytes(it, 1 + bytesNeeded))
return CborErrorUnexpectedEOF;
if (descriptor <= Value16Bit) {
if (descriptor == Value16Bit)
*value = read_uint16(it, 1);
else
*value = read_uint8(it, 1);
} else {
if (descriptor == Value32Bit)
*value = read_uint32(it, 1);
else
*value = read_uint64(it, 1);
}
}
if (bytesUsed)
*bytesUsed = bytesNeeded;
return CborNoError;
}
#endif /* CBORINTERNAL_P_H */

@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2015 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#ifndef CBORJSON_H
#define CBORJSON_H
#include "cbor.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Conversion to JSON */
enum CborToJsonFlags
{
CborConvertAddMetadata = 1,
CborConvertTagsToObjects = 2,
CborConvertIgnoreTags = 0,
CborConvertObeyByteStringTags = 0,
CborConvertByteStringsToBase64Url = 4,
CborConvertRequireMapStringKeys = 0,
CborConvertStringifyMapKeys = 8,
CborConvertDefaultFlags = 0
};
CBOR_API CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags);
CBOR_INLINE_API CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags)
{
CborValue copy = *value;
return cbor_value_to_json_advance(out, &copy, flags);
}
#ifdef __cplusplus
}
#endif
#endif /* CBORJSON_H */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,119 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#ifndef _BSD_SOURCE
#define _BSD_SOURCE 1
#endif
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE 1
#endif
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "compilersupport_p.h"
#include <stdlib.h>
/**
* \fn CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next)
*
* Allocates memory for the string pointed by \a value and copies it into this
* buffer. The pointer to the buffer is stored in \a buffer and the number of
* bytes copied is stored in \a buflen (those variables must not be NULL).
*
* If the iterator \a value does not point to a text string, the behaviour is
* undefined, so checking with \ref cbor_value_get_type or \ref
* cbor_value_is_text_string is recommended.
*
* If \c malloc returns a NULL pointer, this function will return error
* condition \ref CborErrorOutOfMemory.
*
* On success, \c{*buffer} will contain a valid pointer that must be freed by
* calling \c{free()}. This is the case even for zero-length strings.
*
* The \a next pointer, if not null, will be updated to point to the next item
* after this string. If \a value points to the last item, then \a next will be
* invalid.
*
* This function may not run in constant time (it will run in O(n) time on the
* number of chunks). It requires constant memory (O(1)) in addition to the
* malloc'ed block.
*
* \note This function does not perform UTF-8 validation on the incoming text
* string.
*
* \sa cbor_value_get_text_string_chunk(), cbor_value_copy_text_string(), cbor_value_dup_byte_string()
*/
/**
* \fn CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, size_t *buflen, CborValue *next)
*
* Allocates memory for the string pointed by \a value and copies it into this
* buffer. The pointer to the buffer is stored in \a buffer and the number of
* bytes copied is stored in \a buflen (those variables must not be NULL).
*
* If the iterator \a value does not point to a byte string, the behaviour is
* undefined, so checking with \ref cbor_value_get_type or \ref
* cbor_value_is_byte_string is recommended.
*
* If \c malloc returns a NULL pointer, this function will return error
* condition \ref CborErrorOutOfMemory.
*
* On success, \c{*buffer} will contain a valid pointer that must be freed by
* calling \c{free()}. This is the case even for zero-length strings.
*
* The \a next pointer, if not null, will be updated to point to the next item
* after this string. If \a value points to the last item, then \a next will be
* invalid.
*
* This function may not run in constant time (it will run in O(n) time on the
* number of chunks). It requires constant memory (O(1)) in addition to the
* malloc'ed block.
*
* \sa cbor_value_get_text_string_chunk(), cbor_value_copy_byte_string(), cbor_value_dup_text_string()
*/
CborError _cbor_value_dup_string(const CborValue *value, void **buffer, size_t *buflen, CborValue *next)
{
CborError err;
cbor_assert(buffer);
cbor_assert(buflen);
*buflen = SIZE_MAX;
err = _cbor_value_copy_string(value, NULL, buflen, NULL);
if (err)
return err;
++*buflen;
*buffer = malloc(*buflen);
if (!*buffer) {
/* out of memory */
return CborErrorOutOfMemory;
}
err = _cbor_value_copy_string(value, *buffer, buflen, next);
if (err) {
free(*buffer);
return err;
}
return CborNoError;
}

@ -0,0 +1,54 @@
/****************************************************************************
**
** Copyright (C) 2019 S.Phirsov
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "cborinternal_p.h"
#ifndef CBOR_NO_HALF_FLOAT_TYPE
/**
* Retrieves the CBOR half-precision floating point (16-bit) value that \a
* value points to, converts it to the float and store it in \a result.
* If the iterator \a value does not point to a half-precision floating
* point value, the behavior is undefined, so checking with \ref
* cbor_value_get_type or with \ref cbor_value_is_half_float is recommended.
* \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float(), cbor_value_get_float()
*/
CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result)
{
uint16_t v;
CborError err = cbor_value_get_half_float(value, &v);
cbor_assert(err == CborNoError);
*result = (float)decode_half((unsigned short)v);
return CborNoError;
}
#endif

@ -0,0 +1,579 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "cborinternal_p.h"
#include "compilersupport_p.h"
#include "utf8_p.h"
#include <inttypes.h>
#include <string.h>
/**
* \defgroup CborPretty Converting CBOR to text
* \brief Group of functions used to convert CBOR to text form.
*
* This group contains two functions that can be used to convert a \ref
* CborValue object to a text representation. This module attempts to follow
* the recommendations from RFC 7049 section 6 "Diagnostic Notation", though it
* has a few differences. They are noted below.
*
* TinyCBOR does not provide a way to convert from the text representation back
* to encoded form. To produce a text form meant to be parsed, CborToJson is
* recommended instead.
*
* Either of the functions in this section will attempt to convert exactly one
* CborValue object to text. Those functions may return any error documented
* for the functions for CborParsing. In addition, if the C standard library
* stream functions return with error, the text conversion will return with
* error CborErrorIO.
*
* These functions also perform UTF-8 validation in CBOR text strings. If they
* encounter a sequence of bytes that is not permitted in UTF-8, they will return
* CborErrorInvalidUtf8TextString. That includes encoding of surrogate points
* in UTF-8.
*
* \warning The output type produced by these functions is not guaranteed to
* remain stable. A future update of TinyCBOR may produce different output for
* the same input and parsers may be unable to handle it.
*
* \sa CborParsing, CborToJson, cbor_parser_init()
*/
/**
* \addtogroup CborPretty
* @{
* <h2 class="groupheader">Text format</h2>
*
* As described in RFC 7049 section 6 "Diagnostic Notation", the format is
* largely borrowed from JSON, but modified to suit CBOR's different data
* types. TinyCBOR makes further modifications to distinguish different, but
* similar values.
*
* CBOR values are currently encoded as follows:
* \par Integrals (unsigned and negative)
* Base-10 (decimal) text representation of the value
* \par Byte strings:
* <tt>"h'"</tt> followed by the Base16 (hex) representation of the binary data, followed by an ending quote (')
* \par Text strings:
* C-style escaped string in quotes, with C11/C++11 escaping of Unicode codepoints above U+007F.
* \par Tags:
* Tag value, with the tagged value in parentheses. No special encoding of the tagged value is performed.
* \par Simple types:
* <tt>"simple(nn)"</tt> where \c nn is the simple value
* \par Null:
* \c null
* \par Undefined:
* \c undefined
* \par Booleans:
* \c true or \c false
* \par Floating point:
* If NaN or infinite, the actual words \c NaN or \c infinite.
* Otherwise, the decimal representation with as many digits as necessary to ensure no loss of information.
* By default, float values are suffixed by "f" and half-float values suffixed by "f16" (doubles have no suffix).
* If the CborPrettyNumericEncodingIndicators flag is active, the values instead are encoded following the
* Section 6 recommended encoding indicators: float values are suffixed with "_2" and half-float with "_1".
* A decimal point is always present.
* \par Arrays:
* Comma-separated list of elements, enclosed in square brackets ("[" and "]").
* \par Maps:
* Comma-separated list of key-value pairs, with the key and value separated
* by a colon (":"), enclosed in curly braces ("{" and "}").
*
* The CborPrettyFlags enumerator contains flags to control some aspects of the
* encoding:
* \par String fragmentation
* When the CborPrettyShowStringFragments option is active, text and byte
* strings that are transmitted in fragments are shown instead inside
* parentheses ("(" and ")") with no preceding number and each fragment is
* displayed individually. If a tag precedes the string, then the output
* will contain a double set of parentheses. If the option is not active,
* the fragments are merged together and the display will not show any
* difference from a string transmitted with determinate length.
* \par Encoding indicators
* Numbers and lengths in CBOR can be encoded in multiple representations.
* If the CborPrettyIndicateOverlongNumbers option is active, numbers
* and lengths that are transmitted in a longer encoding than necessary
* will be indicated, by appending an underscore ("_") to either the
* number or the opening bracket or brace, followed by a number
* indicating the CBOR additional information: 0 for 1 byte, 1 for 2
* bytes, 2 for 4 bytes and 3 for 8 bytes.
* If the CborPrettyIndicateIndeterminateLength option is active, maps,
* arrays and strings encoded with indeterminate length will be marked by
* an underscore after the opening bracket or brace or the string (if not
* showing fragments), without a number after it.
*/
/**
* \enum CborPrettyFlags
* The CborPrettyFlags enum contains flags that control the conversion of CBOR to text format.
*
* \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float.
* \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16").
* \value CborPrettyIndicateIndeterminateLength (default) Indicate when a map or array has indeterminate length.
* \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed.
* \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually.
* \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry.
* \value CborPrettyDefaultFlags Default conversion flags.
*/
#ifndef CBOR_NO_FLOATING_POINT
static inline bool convertToUint64(double v, uint64_t *absolute)
{
double supremum;
v = fabs(v);
/* C11 standard section 6.3.1.4 "Real floating and integer" says:
*
* 1 When a finite value of real floating type is converted to an integer
* type other than _Bool, the fractional part is discarded (i.e., the
* value is truncated toward zero). If the value of the integral part
* cannot be represented by the integer type, the behavior is undefined.
*
* So we must perform a range check that v <= UINT64_MAX, but we can't use
* UINT64_MAX + 1.0 because the standard continues:
*
* 2 When a value of integer type is converted to a real floating type, if
* the value being converted can be represented exactly in the new type,
* it is unchanged. If the value being converted is in the range of
* values that can be represented but cannot be represented exactly, the
* result is either the nearest higher or nearest lower representable
* value, chosen in an implementation-defined manner.
*/
supremum = -2.0 * INT64_MIN; /* -2 * (- 2^63) == 2^64 */
if (v >= supremum)
return false;
/* Now we can convert, these two conversions cannot be UB */
*absolute = v;
return *absolute == v;
}
#endif
static void printRecursionLimit(CborStreamFunction stream, void *out)
{
stream(out, "<nesting too deep, recursion stopped>");
}
static CborError hexDump(CborStreamFunction stream, void *out, const void *ptr, size_t n)
{
const uint8_t *buffer = (const uint8_t *)ptr;
CborError err = CborNoError;
while (n-- && !err)
err = stream(out, "%02" PRIx8, *buffer++);
return err;
}
/* This function decodes buffer as UTF-8 and prints as escaped UTF-16.
* On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */
static CborError utf8EscapedDump(CborStreamFunction stream, void *out, const void *ptr, size_t n)
{
const uint8_t *buffer = (const uint8_t *)ptr;
const uint8_t * const end = buffer + n;
CborError err = CborNoError;
while (buffer < end && !err) {
uint32_t uc = get_utf8(&buffer, end);
if (uc == ~0U)
return CborErrorInvalidUtf8TextString;
if (uc < 0x80) {
/* single-byte UTF-8 */
unsigned char escaped = (unsigned char)uc;
if (uc < 0x7f && uc >= 0x20 && uc != '\\' && uc != '"') {
err = stream(out, "%c", (char)uc);
continue;
}
/* print as an escape sequence */
switch (uc) {
case '"':
case '\\':
break;
case '\b':
escaped = 'b';
break;
case '\f':
escaped = 'f';
break;
case '\n':
escaped = 'n';
break;
case '\r':
escaped = 'r';
break;
case '\t':
escaped = 't';
break;
default:
goto print_utf16;
}
err = stream(out, "\\%c", escaped);
continue;
}
/* now print the sequence */
if (uc > 0xffffU) {
/* needs surrogate pairs */
err = stream(out, "\\u%04" PRIX32 "\\u%04" PRIX32,
(uc >> 10) + 0xd7c0, /* high surrogate */
(uc % 0x0400) + 0xdc00);
} else {
print_utf16:
/* no surrogate pair needed */
err = stream(out, "\\u%04" PRIX32, uc);
}
}
return err;
}
static const char *resolve_indicator(const CborValue *it, int flags)
{
static const char indicators[8][3] = {
"_0", "_1", "_2", "_3",
"", "", "", /* these are not possible */
"_"
};
const char *no_indicator = indicators[5]; /* empty string */
uint8_t additional_information;
uint8_t expected_information;
uint64_t value;
CborError err;
if (!read_bytes(it, &additional_information, 0, 1))
return NULL; /* CborErrorUnexpectedEOF */
additional_information &= SmallValueMask;
if (additional_information < Value8Bit)
return no_indicator;
/* determine whether to show anything */
if ((flags & CborPrettyIndicateIndeterminateLength) &&
additional_information == IndefiniteLength)
return indicators[IndefiniteLength - Value8Bit];
if ((flags & CborPrettyIndicateOverlongNumbers) == 0)
return no_indicator;
err = extract_number_checked(it, &value, NULL);
if (err)
return NULL; /* CborErrorUnexpectedEOF */
expected_information = Value8Bit - 1;
if (value >= Value8Bit)
++expected_information;
if (value > 0xffU)
++expected_information;
if (value > 0xffffU)
++expected_information;
if (value > 0xffffffffU)
++expected_information;
return expected_information == additional_information ?
no_indicator :
indicators[additional_information - Value8Bit];
}
static const char *get_indicator(const CborValue *it, int flags)
{
return resolve_indicator(it, flags);
}
static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft);
static CborError container_to_pretty(CborStreamFunction stream, void *out, CborValue *it, CborType containerType,
int flags, int recursionsLeft)
{
const char *comma = "";
CborError err = CborNoError;
if (!recursionsLeft) {
printRecursionLimit(stream, out);
return err; /* do allow the dumping to continue */
}
while (!cbor_value_at_end(it) && !err) {
err = stream(out, "%s", comma);
comma = ", ";
if (!err)
err = value_to_pretty(stream, out, it, flags, recursionsLeft);
if (containerType == CborArrayType)
continue;
/* map: that was the key, so get the value */
if (!err)
err = stream(out, ": ");
if (!err)
err = value_to_pretty(stream, out, it, flags, recursionsLeft);
}
return err;
}
static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft)
{
CborError err = CborNoError;
CborType type = cbor_value_get_type(it);
switch (type) {
case CborArrayType:
case CborMapType: {
/* recursive type */
CborValue recursed;
const char *indicator = get_indicator(it, flags);
const char *space = *indicator ? " " : indicator;
err = stream(out, "%c%s%s", type == CborArrayType ? '[' : '{', indicator, space);
if (err)
return err;
err = cbor_value_enter_container(it, &recursed);
if (err) {
copy_current_position(it, &recursed);
return err; /* parse error */
}
err = container_to_pretty(stream, out, &recursed, type, flags, recursionsLeft - 1);
if (err) {
copy_current_position(it, &recursed);
return err; /* parse error */
}
err = cbor_value_leave_container(it, &recursed);
if (err)
return err; /* parse error */
return stream(out, type == CborArrayType ? "]" : "}");
}
case CborIntegerType: {
uint64_t val;
cbor_value_get_raw_integer(it, &val); /* can't fail */
if (cbor_value_is_unsigned_integer(it)) {
err = stream(out, "%" PRIu64, val);
} else {
/* CBOR stores the negative number X as -1 - X
* (that is, -1 is stored as 0, -2 as 1 and so forth) */
if (++val) { /* unsigned overflow may happen */
err = stream(out, "-%" PRIu64, val);
} else {
/* overflown
* 0xffff`ffff`ffff`ffff + 1 =
* 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64) */
err = stream(out, "-18446744073709551616");
}
}
if (!err)
err = stream(out, "%s", get_indicator(it, flags));
break;
}
case CborByteStringType:
case CborTextStringType: {
size_t n = 0;
const void *ptr;
bool showingFragments = (flags & CborPrettyShowStringFragments) && !cbor_value_is_length_known(it);
const char *separator = "";
char close = '\'';
char open[3] = "h'";
const char *indicator = NULL;
if (type == CborTextStringType) {
close = open[0] = '"';
open[1] = '\0';
}
if (showingFragments)
err = stream(out, "(_ ");
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, flags);
}
err = _cbor_value_get_string_chunk(it, &ptr, &n, it);
if (err == CborErrorNoMoreStringChunks) {
err = cbor_value_finish_string_iteration(it);
break;
}
if (!err && showingFragments)
err = stream(out, "%s%s", separator, open);
if (!err)
err = (type == CborByteStringType ?
hexDump(stream, out, ptr, n) :
utf8EscapedDump(stream, out, ptr, n));
if (!err && showingFragments) {
err = stream(out, "%c%s", close, indicator);
separator = ", ";
}
}
if (!err) {
if (showingFragments)
err = stream(out, ")");
else
err = stream(out, "%c%s", close, indicator);
}
return err;
}
case CborTagType: {
CborTag tag;
cbor_value_get_tag(it, &tag); /* can't fail */
err = stream(out, "%" PRIu64 "%s(", tag, get_indicator(it, flags));
if (!err)
err = cbor_value_advance_fixed(it);
if (!err && recursionsLeft)
err = value_to_pretty(stream, out, it, flags, recursionsLeft - 1);
else if (!err)
printRecursionLimit(stream, out);
if (!err)
err = stream(out, ")");
return err;
}
case CborSimpleType: {
/* simple types can't fail and can't have overlong encoding */
uint8_t simple_type;
cbor_value_get_simple_type(it, &simple_type);
err = stream(out, "simple(%" PRIu8 ")", simple_type);
break;
}
case CborNullType:
err = stream(out, "null");
break;
case CborUndefinedType:
err = stream(out, "undefined");
break;
case CborBooleanType: {
bool val;
cbor_value_get_boolean(it, &val); /* can't fail */
err = stream(out, val ? "true" : "false");
break;
}
#ifndef CBOR_NO_FLOATING_POINT
case CborDoubleType: {
const char *suffix;
double val;
int r;
uint64_t ival;
if (false) {
float f;
case CborFloatType:
cbor_value_get_float(it, &f);
val = f;
suffix = flags & CborPrettyNumericEncodingIndicators ? "_2" : "f";
} else if (false) {
uint16_t f16;
case CborHalfFloatType:
#ifndef CBOR_NO_HALF_FLOAT_TYPE
cbor_value_get_half_float(it, &f16);
val = decode_half(f16);
suffix = flags & CborPrettyNumericEncodingIndicators ? "_1" : "f16";
#else
(void)f16;
err = CborErrorUnsupportedType;
break;
#endif
} else {
cbor_value_get_double(it, &val);
suffix = "";
}
if ((flags & CborPrettyNumericEncodingIndicators) == 0) {
r = fpclassify(val);
if (r == FP_NAN || r == FP_INFINITE)
suffix = "";
}
if (convertToUint64(val, &ival)) {
/* this double value fits in a 64-bit integer, so show it as such
* (followed by a floating point suffix, to disambiguate) */
err = stream(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix);
} else {
/* this number is definitely not a 64-bit integer */
err = stream(out, "%." DBL_DECIMAL_DIG_STR "g%s", val, suffix);
}
break;
}
#else
case CborDoubleType:
case CborFloatType:
case CborHalfFloatType:
err = CborErrorUnsupportedType;
break;
#endif /* !CBOR_NO_FLOATING_POINT */
case CborInvalidType:
err = stream(out, "invalid");
if (err)
return err;
return CborErrorUnknownType;
}
if (!err)
err = cbor_value_advance_fixed(it);
return err;
}
/**
* Converts the current CBOR type pointed by \a value to its textual
* representation and writes it to the stream by calling the \a streamFunction.
* If an error occurs, this function returns an error code similar to
* \ref CborParsing.
*
* The textual representation can be controlled by the \a flags parameter (see
* \ref CborPrettyFlags for more information).
*
* If no error ocurred, this function advances \a value to the next element.
* Often, concatenating the text representation of multiple elements can be
* done by appending a comma to the output stream in between calls to this
* function.
*
* The \a streamFunction function will be called with the \a token value as the
* first parameter and a printf-style format string as the second, with a variable
* number of further parameters.
*
* \sa cbor_value_to_pretty(), cbor_value_to_json_advance()
*/
CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags)
{
return value_to_pretty(streamFunction, token, value, flags, CBOR_PARSER_MAX_RECURSIONS);
}
/** @} */

@ -0,0 +1,87 @@
/****************************************************************************
**
** Copyright (C) 2017 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#include "cbor.h"
#include <stdarg.h>
#include <stdio.h>
static CborError cbor_fprintf(void *out, const char *fmt, ...)
{
int n;
va_list list;
va_start(list, fmt);
n = vfprintf((FILE *)out, fmt, list);
va_end(list);
return n < 0 ? CborErrorIO : CborNoError;
}
/**
* \fn CborError cbor_value_to_pretty(FILE *out, const CborValue *value)
*
* Converts the current CBOR type pointed to by \a value to its textual
* representation and writes it to the \a out stream. If an error occurs, this
* function returns an error code similar to CborParsing.
*
* \sa cbor_value_to_pretty_advance(), cbor_value_to_json_advance()
*/
/**
* Converts the current CBOR type pointed to by \a value to its textual
* representation and writes it to the \a out stream. If an error occurs, this
* function returns an error code similar to CborParsing.
*
* If no error ocurred, this function advances \a value to the next element.
* Often, concatenating the text representation of multiple elements can be
* done by appending a comma to the output stream in between calls to this
* function.
*
* \sa cbor_value_to_pretty(), cbor_value_to_pretty_stream(), cbor_value_to_json_advance()
*/
CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value)
{
return cbor_value_to_pretty_stream(cbor_fprintf, out, value, CborPrettyDefaultFlags);
}
/**
* Converts the current CBOR type pointed to by \a value to its textual
* representation and writes it to the \a out stream. If an error occurs, this
* function returns an error code similar to CborParsing.
*
* The textual representation can be controlled by the \a flags parameter (see
* CborPrettyFlags for more information).
*
* If no error ocurred, this function advances \a value to the next element.
* Often, concatenating the text representation of multiple elements can be
* done by appending a comma to the output stream in between calls to this
* function.
*
* \sa cbor_value_to_pretty_stream(), cbor_value_to_pretty(), cbor_value_to_json_advance()
*/
CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags)
{
return cbor_value_to_pretty_stream(cbor_fprintf, out, value, flags);
}

@ -0,0 +1,709 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#define _GNU_SOURCE 1
#define _POSIX_C_SOURCE 200809L
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "cborjson.h"
#include "cborinternal_p.h"
#include "compilersupport_p.h"
#include "cborinternal_p.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
* \defgroup CborToJson Converting CBOR to JSON
* \brief Group of functions used to convert CBOR to JSON.
*
* This group contains two functions that can be used to convert a \ref
* CborValue object to an equivalent JSON representation. This module attempts
* to follow the recommendations from RFC 7049 section 4.1 "Converting from
* CBOR to JSON", though it has a few differences. They are noted below.
*
* These functions produce a "minified" JSON output, with no spacing,
* indentation or line breaks. If those are necessary, they need to be applied
* in a post-processing phase.
*
* Note that JSON cannot support all CBOR types with fidelity, so the
* conversion is usually lossy. For that reason, TinyCBOR supports adding a set
* of metadata JSON values that can be used by a JSON-to-CBOR converter to
* restore the original data types.
*
* The TinyCBOR library does not provide a way to convert from JSON
* representation back to encoded form. However, it provides a tool called
* \c json2cbor which can be used for that purpose. That tool supports the
* metadata format that these functions may produce.
*
* Either of the functions in this section will attempt to convert exactly one
* CborValue object to JSON. Those functions may return any error documented
* for the functions for CborParsing. In addition, if the C standard library
* stream functions return with error, the text conversion will return with
* error CborErrorIO.
*
* These functions also perform UTF-8 validation in CBOR text strings. If they
* encounter a sequence of bytes that is not permitted in UTF-8, they will return
* CborErrorInvalidUtf8TextString. That includes encoding of surrogate points
* in UTF-8.
*
* \warning The metadata produced by these functions is not guaranteed to
* remain stable. A future update of TinyCBOR may produce different output for
* the same input and parsers may be unable to handle it.
*
* \sa CborParsing, CborPretty, cbor_parser_init()
*/
/**
* \addtogroup CborToJson
* @{
* <h2 class="groupheader">Conversion limitations</h2>
*
* When converting from CBOR to JSON, there may be information loss. This
* section lists the possible scenarios.
*
* \par Number precision:
* ALL JSON numbers, due to its JavaScript heritage, are IEEE 754
* double-precision floating point. This means JSON is not capable of
* representing all integers numbers outside the range [-(2<sup>53</sup>)+1,
* 2<sup>53</sup>-1] and is not capable of representing NaN or infinite. If the
* CBOR data contains a number outside the valid range, the conversion will
* lose precision. If the input was NaN or infinite, the result of the
* conversion will be the JSON null value. In addition, the distinction between
* half-, single- and double-precision is lost.
*
* \par
* If enabled, the original value and original type are stored in the metadata.
*
* \par Non-native types:
* CBOR's type system is richer than JSON's, which means some data values
* cannot be represented when converted to JSON. The conversion silently turns
* them into strings: CBOR simple types become "simple(nn)" where \c nn is the
* simple type's value, with the exception of CBOR undefined, which becomes
* "undefined", while CBOR byte strings are converted to an Base16, Base64, or
* Base64url encoding
*
* \par
* If enabled, the original type is stored in the metadata.
*
* \par Presence of tags:
* JSON has no support for tagged values, so by default tags are dropped when
* converting to JSON. However, if the CborConvertObeyByteStringTags option is
* active (default), then certain known tags are honored and are used to format
* the conversion of the tagged byte string to JSON.
*
* \par
* If the CborConvertTagsToObjects option is active, then the tag and the
* tagged value are converted to a JSON object. Otherwise, if enabled, the
* last (innermost) tag is stored in the metadata.
*
* \par Non-string keys in maps:
* JSON requires all Object keys to be strings, while CBOR does not. By
* default, if a non-string key is found, the conversion fails with error
* CborErrorJsonObjectKeyNotString. If the CborConvertStringifyMapKeys option
* is active, then the conversion attempts to create a string representation
* using CborPretty. Note that the \c json2cbor tool is not able to parse this
* back to the original form.
*
* \par Duplicate keys in maps:
* Neither JSON nor CBOR allow duplicated keys, but current TinyCBOR does not
* validate that this is the case. If there are duplicated keys in the input,
* they will be repeated in the output, which many JSON tools may flag as
* invalid. In addition to that, if the CborConvertStringifyMapKeys option is
* active, it is possible that a non-string key in a CBOR map will be converted
* to a string form that is identical to another key.
*
* \par
* When metadata support is active, the conversion will add extra key-value
* pairs to the JSON output so it can store the metadata. It is possible that
* the keys for the metadata clash with existing keys in the JSON map.
*/
extern FILE *open_memstream(char **bufptr, size_t *sizeptr);
enum ConversionStatusFlags {
TypeWasNotNative = 0x100, /* anything but strings, boolean, null, arrays and maps */
TypeWasTagged = 0x200,
NumberPrecisionWasLost = 0x400,
NumberWasNaN = 0x800,
NumberWasInfinite = 0x1000,
NumberWasNegative = 0x2000, /* only used with NumberWasInifite or NumberWasTooBig */
FinalTypeMask = 0xff
};
typedef struct ConversionStatus {
CborTag lastTag;
uint64_t originalNumber;
int flags;
} ConversionStatus;
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status);
static CborError dump_bytestring_base16(char **result, CborValue *it)
{
static const char characters[] = "0123456789abcdef";
size_t i;
size_t n = 0;
uint8_t *buffer;
CborError err = cbor_value_calculate_string_length(it, &n);
if (err)
return err;
/* a Base16 (hex) output is twice as big as our buffer */
buffer = (uint8_t *)malloc(n * 2 + 1);
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 */
++n;
err = cbor_value_copy_byte_string(it, buffer + n - 1, &n, it);
cbor_assert(err == CborNoError);
for (i = 0; i < n; ++i) {
uint8_t byte = buffer[n + i];
buffer[2*i] = characters[byte >> 4];
buffer[2*i + 1] = characters[byte & 0xf];
}
return CborNoError;
}
static CborError generic_dump_base64(char **result, CborValue *it, const char alphabet[65])
{
size_t n = 0, i;
uint8_t *buffer, *out, *in;
CborError err = cbor_value_calculate_string_length(it, &n);
if (err)
return err;
/* a Base64 output (untruncated) has 4 bytes for every 3 in the input */
size_t len = (n + 5) / 3 * 4;
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
* so we can do an in-place conversion while iterating forwards */
in = buffer + len - n;
/* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */
++n;
err = cbor_value_copy_byte_string(it, in, &n, it);
cbor_assert(err == CborNoError);
uint_least32_t val = 0;
for (i = 0; n - i >= 3; i += 3) {
/* read 3 bytes x 8 bits = 24 bits */
if (false) {
#ifdef __GNUC__
} else if (i) {
__builtin_memcpy(&val, in + i - 1, sizeof(val));
val = cbor_ntohl(val);
#endif
} else {
val = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2];
}
/* write 4 chars x 6 bits = 24 bits */
*out++ = alphabet[(val >> 18) & 0x3f];
*out++ = alphabet[(val >> 12) & 0x3f];
*out++ = alphabet[(val >> 6) & 0x3f];
*out++ = alphabet[val & 0x3f];
}
/* maybe 1 or 2 bytes left */
if (n - i) {
/* we can read in[i + 1] even if it's past the end of the string because
* we know (by construction) that it's a NUL byte */
#ifdef __GNUC__
uint16_t val16;
__builtin_memcpy(&val16, in + i, sizeof(val16));
val = cbor_ntohs(val16);
#else
val = (in[i] << 8) | in[i + 1];
#endif
val <<= 8;
/* the 65th character in the alphabet is our filler: either '=' or '\0' */
out[4] = '\0';
out[3] = alphabet[64];
if (n - i == 2) {
/* write the third char in 3 chars x 6 bits = 18 bits */
out[2] = alphabet[(val >> 6) & 0x3f];
} else {
out[2] = alphabet[64]; /* filler */
}
out[1] = alphabet[(val >> 12) & 0x3f];
out[0] = alphabet[(val >> 18) & 0x3f];
} else {
out[0] = '\0';
}
return CborNoError;
}
static CborError dump_bytestring_base64(char **result, CborValue *it)
{
static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
"ghijklmn" "opqrstuv" "wxyz0123" "456789+/" "=";
return generic_dump_base64(result, it, alphabet);
}
static CborError dump_bytestring_base64url(char **result, CborValue *it)
{
static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
"ghijklmn" "opqrstuv" "wxyz0123" "456789-_";
return generic_dump_base64(result, it, alphabet);
}
static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status)
{
int flags = status->flags;
if (flags & TypeWasTagged) {
/* extract the tagged type, which may be JSON native */
type = flags & FinalTypeMask;
flags &= ~(FinalTypeMask | TypeWasTagged);
if (fprintf(out, "\"tag\":\"%" PRIu64 "\"%s", status->lastTag,
flags & ~TypeWasTagged ? "," : "") < 0)
return CborErrorIO;
}
if (!flags)
return CborNoError;
/* print at least the type */
if (fprintf(out, "\"t\":%d", type) < 0)
return CborErrorIO;
if (flags & NumberWasNaN)
if (fprintf(out, ",\"v\":\"nan\"") < 0)
return CborErrorIO;
if (flags & NumberWasInfinite)
if (fprintf(out, ",\"v\":\"%sinf\"", flags & NumberWasNegative ? "-" : "") < 0)
return CborErrorIO;
if (flags & NumberPrecisionWasLost)
if (fprintf(out, ",\"v\":\"%c%" PRIx64 "\"", flags & NumberWasNegative ? '-' : '+',
status->originalNumber) < 0)
return CborErrorIO;
if (type == CborSimpleType)
if (fprintf(out, ",\"v\":%d", (int)status->originalNumber) < 0)
return CborErrorIO;
return CborNoError;
}
static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
{
CborError err = CborNoError;
*type = cbor_value_get_type(it);
while (*type == CborTagType) {
cbor_value_get_tag(it, tag); /* can't fail */
err = cbor_value_advance_fixed(it);
if (err)
return err;
*type = cbor_value_get_type(it);
}
return err;
}
static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
{
CborTag tag;
CborError err;
if (flags & CborConvertTagsToObjects) {
cbor_value_get_tag(it, &tag); /* can't fail */
err = cbor_value_advance_fixed(it);
if (err)
return err;
if (fprintf(out, "{\"tag%" PRIu64 "\":", tag) < 0)
return CborErrorIO;
CborType type = cbor_value_get_type(it);
err = value_to_json(out, it, flags, type, status);
if (err)
return err;
if (flags & CborConvertAddMetadata && status->flags) {
if (fprintf(out, ",\"tag%" PRIu64 "$cbor\":{", tag) < 0 ||
add_value_metadata(out, type, status) != CborNoError ||
fputc('}', out) < 0)
return CborErrorIO;
}
if (fputc('}', out) < 0)
return CborErrorIO;
status->flags = TypeWasNotNative | CborTagType;
return CborNoError;
}
CborType type;
err = find_tagged_type(it, &status->lastTag, &type);
if (err)
return err;
tag = status->lastTag;
/* special handling of byte strings? */
if (type == CborByteStringType && (flags & CborConvertByteStringsToBase64Url) == 0 &&
(tag == CborNegativeBignumTag || tag == CborExpectedBase16Tag || tag == CborExpectedBase64Tag)) {
char *str;
const char *pre = "";
if (tag == CborNegativeBignumTag) {
pre = "~";
err = dump_bytestring_base64url(&str, it);
} else if (tag == CborExpectedBase64Tag) {
err = dump_bytestring_base64(&str, it);
} else { /* tag == CborExpectedBase16Tag */
err = dump_bytestring_base16(&str, it);
}
if (err)
return err;
err = fprintf(out, "\"%s%s\"", pre, str) < 0 ? CborErrorIO : CborNoError;
free(str);
status->flags = TypeWasNotNative | TypeWasTagged | CborByteStringType;
return err;
}
/* no special handling */
err = value_to_json(out, it, flags, type, status);
status->flags |= TypeWasTagged | type;
return err;
}
static CborError stringify_map_key(char **key, CborValue *it, int flags, CborType type)
{
(void)flags; /* unused */
(void)type; /* unused */
#ifdef WITHOUT_OPEN_MEMSTREAM
(void)key; /* unused */
(void)it; /* unused */
return CborErrorJsonNotImplemented;
#else
size_t size;
FILE *memstream = open_memstream(key, &size);
if (memstream == NULL)
return CborErrorOutOfMemory; /* could also be EMFILE, but it's unlikely */
CborError err = cbor_value_to_pretty_advance(memstream, it);
if (unlikely(fclose(memstream) < 0 || *key == NULL))
return CborErrorInternalError;
return err;
#endif
}
static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
{
const char *comma = "";
while (!cbor_value_at_end(it)) {
if (fprintf(out, "%s", comma) < 0)
return CborErrorIO;
comma = ",";
CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status);
if (err)
return err;
}
return CborNoError;
}
static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
{
const char *comma = "";
CborError err;
while (!cbor_value_at_end(it)) {
char *key;
if (fprintf(out, "%s", comma) < 0)
return CborErrorIO;
comma = ",";
CborType keyType = cbor_value_get_type(it);
if (likely(keyType == CborTextStringType)) {
size_t n = 0;
err = cbor_value_dup_text_string(it, &key, &n, it);
} else if (flags & CborConvertStringifyMapKeys) {
err = stringify_map_key(&key, it, flags, keyType);
} else {
return CborErrorJsonObjectKeyNotString;
}
if (err)
return err;
/* first, print the key */
if (fprintf(out, "\"%s\":", key) < 0) {
free(key);
return CborErrorIO;
}
/* then, print the value */
CborType valueType = cbor_value_get_type(it);
err = value_to_json(out, it, flags, valueType, status);
/* finally, print any metadata we may have */
if (flags & CborConvertAddMetadata) {
if (!err && keyType != CborTextStringType) {
if (fprintf(out, ",\"%s$keycbordump\":true", key) < 0)
err = CborErrorIO;
}
if (!err && status->flags) {
if (fprintf(out, ",\"%s$cbor\":{", key) < 0 ||
add_value_metadata(out, valueType, status) != CborNoError ||
fputc('}', out) < 0)
err = CborErrorIO;
}
}
free(key);
if (err)
return err;
}
return CborNoError;
}
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status)
{
CborError err;
status->flags = 0;
switch (type) {
case CborArrayType:
case CborMapType: {
/* recursive type */
CborValue recursed;
err = cbor_value_enter_container(it, &recursed);
if (err) {
copy_current_position(it, &recursed);
return err; /* parse error */
}
if (fputc(type == CborArrayType ? '[' : '{', out) < 0)
return CborErrorIO;
err = (type == CborArrayType) ?
array_to_json(out, &recursed, flags, status) :
map_to_json(out, &recursed, flags, status);
if (err) {
copy_current_position(it, &recursed);
return err; /* parse error */
}
if (fputc(type == CborArrayType ? ']' : '}', out) < 0)
return CborErrorIO;
err = cbor_value_leave_container(it, &recursed);
if (err)
return err; /* parse error */
status->flags = 0; /* reset, there are never conversion errors for us */
return CborNoError;
}
case CborIntegerType: {
double num; /* JS numbers are IEEE double precision */
uint64_t val;
cbor_value_get_raw_integer(it, &val); /* can't fail */
num = (double)val;
if (cbor_value_is_negative_integer(it)) {
num = -num - 1; /* convert to negative */
if ((uint64_t)(-num - 1) != val) {
status->flags = NumberPrecisionWasLost | NumberWasNegative;
status->originalNumber = val;
}
} else {
if ((uint64_t)num != val) {
status->flags = NumberPrecisionWasLost;
status->originalNumber = val;
}
}
if (fprintf(out, "%.0f", num) < 0) /* this number has no fraction, so no decimal points please */
return CborErrorIO;
break;
}
case CborByteStringType:
case CborTextStringType: {
char *str;
if (type == CborByteStringType) {
err = dump_bytestring_base64url(&str, it);
status->flags = TypeWasNotNative;
} else {
size_t n = 0;
err = cbor_value_dup_text_string(it, &str, &n, it);
}
if (err)
return err;
err = (fprintf(out, "\"%s\"", str) < 0) ? CborErrorIO : CborNoError;
free(str);
return err;
}
case CborTagType:
return tagged_value_to_json(out, it, flags, status);
case CborSimpleType: {
uint8_t simple_type;
cbor_value_get_simple_type(it, &simple_type); /* can't fail */
status->flags = TypeWasNotNative;
status->originalNumber = simple_type;
if (fprintf(out, "\"simple(%" PRIu8 ")\"", simple_type) < 0)
return CborErrorIO;
break;
}
case CborNullType:
if (fprintf(out, "null") < 0)
return CborErrorIO;
break;
case CborUndefinedType:
status->flags = TypeWasNotNative;
if (fprintf(out, "\"undefined\"") < 0)
return CborErrorIO;
break;
case CborBooleanType: {
bool val;
cbor_value_get_boolean(it, &val); /* can't fail */
if (fprintf(out, val ? "true" : "false") < 0)
return CborErrorIO;
break;
}
#ifndef CBOR_NO_FLOATING_POINT
case CborDoubleType: {
double val;
if (false) {
float f;
case CborFloatType:
status->flags = TypeWasNotNative;
cbor_value_get_float(it, &f);
val = f;
} else if (false) {
uint16_t f16;
case CborHalfFloatType:
# ifndef CBOR_NO_HALF_FLOAT_TYPE
status->flags = TypeWasNotNative;
cbor_value_get_half_float(it, &f16);
val = decode_half(f16);
# else
(void)f16;
err = CborErrorUnsupportedType;
break;
# endif
} else {
cbor_value_get_double(it, &val);
}
int r = fpclassify(val);
if (r == FP_NAN || r == FP_INFINITE) {
if (fprintf(out, "null") < 0)
return CborErrorIO;
status->flags |= r == FP_NAN ? NumberWasNaN :
NumberWasInfinite | (val < 0 ? NumberWasNegative : 0);
} else {
uint64_t ival = (uint64_t)fabs(val);
if ((double)ival == fabs(val)) {
/* print as integer so we get the full precision */
r = fprintf(out, "%s%" PRIu64, val < 0 ? "-" : "", ival);
status->flags |= TypeWasNotNative; /* mark this integer number as a double */
} else {
/* this number is definitely not a 64-bit integer */
r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g", val);
}
if (r < 0)
return CborErrorIO;
}
break;
}
#else
case CborDoubleType:
case CborFloatType:
case CborHalfFloatType:
err = CborErrorUnsupportedType;
break;
#endif /* !CBOR_NO_FLOATING_POINT */
case CborInvalidType:
return CborErrorUnknownType;
}
return cbor_value_advance_fixed(it);
}
/**
* \enum CborToJsonFlags
* The CborToJsonFlags enum contains flags that control the conversion of CBOR to JSON.
*
* \value CborConvertAddMetadata Adds metadata to facilitate restoration of the original CBOR data.
* \value CborConvertTagsToObjects Converts CBOR tags to JSON objects
* \value CborConvertIgnoreTags (default) Ignore CBOR tags, except for byte strings
* \value CborConvertObeyByteStringTags (default) Honor formatting of CBOR byte strings if so tagged
* \value CborConvertByteStringsToBase64Url Force the conversion of all CBOR byte strings to Base64url encoding, despite any tags
* \value CborConvertRequireMapStringKeys (default) Require CBOR map keys to be strings, failing the conversion if they are not
* \value CborConvertStringifyMapKeys Convert non-string keys in CBOR maps to a string form
* \value CborConvertDefaultFlags Default conversion flags.
*/
/**
* \fn CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags)
*
* Converts the current CBOR type pointed to by \a value to JSON and writes that
* to the \a out stream. If an error occurs, this function returns an error
* code similar to CborParsing. The \a flags parameter indicates one or more of
* the flags from CborToJsonFlags that control the conversion.
*
* \sa cbor_value_to_json_advance(), cbor_value_to_pretty()
*/
/**
* Converts the current CBOR type pointed to by \a value to JSON and writes that
* to the \a out stream. If an error occurs, this function returns an error
* code similar to CborParsing. The \a flags parameter indicates one or more of
* the flags from CborToJsonFlags that control the conversion.
*
* If no error ocurred, this function advances \a value to the next element.
*
* \sa cbor_value_to_json(), cbor_value_to_pretty_advance()
*/
CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags)
{
ConversionStatus status;
return value_to_json(out, value, flags, cbor_value_get_type(value), &status);
}
/** @} */

@ -0,0 +1,657 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#ifndef __STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS 1
#endif
#include "cbor.h"
#include "cborinternal_p.h"
#include "compilersupport_p.h"
#include "utf8_p.h"
#include <string.h>
#ifndef CBOR_NO_FLOATING_POINT
# include <float.h>
# include <math.h>
#endif
#ifndef CBOR_PARSER_MAX_RECURSIONS
# define CBOR_PARSER_MAX_RECURSIONS 1024
#endif
/**
* \addtogroup CborParsing
* @{
*/
/**
* \enum CborValidationFlags
* The CborValidationFlags enum contains flags that control the validation of a
* CBOR stream.
*
* \value CborValidateBasic Validates only the syntactic correctedness of the stream.
* \value CborValidateCanonical Validates that the stream is in canonical format, according to
* RFC 7049 section 3.9.
* \value CborValidateStrictMode Performs strict validation, according to RFC 7049 section 3.10.
* \value CborValidateStrictest Attempt to perform the strictest validation we know of.
*
* \value CborValidateShortestIntegrals (Canonical) Validate that integral numbers and lengths are
* enconded in their shortest form possible.
* \value CborValidateShortestFloatingPoint (Canonical) Validate that floating-point numbers are encoded
* in their shortest form possible.
* \value CborValidateShortestNumbers (Canonical) Validate both integral and floating-point numbers
* are in their shortest form possible.
* \value CborValidateNoIndeterminateLength (Canonical) Validate that no string, array or map uses
* indeterminate length encoding.
* \value CborValidateMapIsSorted (Canonical & Strict mode) Validate that map keys appear in
* sorted order.
* \value CborValidateMapKeysAreUnique (Strict mode) Validate that map keys are unique.
* \value CborValidateTagUse (Strict mode) Validate that known tags are used with the
* correct types. This does not validate that the content of
* those types is syntactically correct. For example, this
* option validates that tag 1 (DateTimeString) is used with
* a Text String, but it does not validate that the string is
* a valid date/time representation.
* \value CborValidateUtf8 (Strict mode) Validate that text strings are appropriately
* encoded in UTF-8.
* \value CborValidateMapKeysAreString Validate that all map keys are text strings.
* \value CborValidateNoUndefined Validate that no elements of type "undefined" are present.
* \value CborValidateNoTags Validate that no tags are used.
* \value CborValidateFiniteFloatingPoint Validate that all floating point numbers are finite (no NaN or
* infinities are allowed).
* \value CborValidateCompleteData Validate that the stream is complete and there is no more data
* in the buffer.
* \value CborValidateNoUnknownSimpleTypesSA Validate that all Standards Action simple types are registered
* with IANA.
* \value CborValidateNoUnknownSimpleTypes Validate that all simple types used are registered with IANA.
* \value CborValidateNoUnknownTagsSA Validate that all Standard Actions tags are registered with IANA.
* \value CborValidateNoUnknownTagsSR Validate that all Standard Actions and Specification Required tags
* are registered with IANA (see below for limitations).
* \value CborValidateNoUnkonwnTags Validate that all tags are registered with IANA
* (see below for limitations).
*
* \par Simple type registry
* The CBOR specification requires that registration for use of the first 19
* simple types must be done by way of Standards Action. The rest of the simple
* types only require a specification. The official list can be obtained from
* https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml.
*
* \par
* There are no registered simple types recognized by this release of TinyCBOR
* (beyond those defined by RFC 7049).
*
* \par Tag registry
* The CBOR specification requires that registration for use of the first 23
* tags must be done by way of Standards Action. The next up to tag 255 only
* require a specification. Finally, all other tags can be registered on a
* first-come-first-serve basis. The official list can be ontained from
* https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml.
*
* \par
* Given the variability of this list, TinyCBOR cannot recognize all tags
* registered with IANA. Instead, the implementation only recognizes tags
* that are backed by an RFC.
*
* \par
* These are the tags known to the current TinyCBOR release:
<table>
<tr>
<th>Tag</th>
<th>Data Item</th>
<th>Semantics</th>
</tr>
<tr>
<td>0</td>
<td>UTF-8 text string</td>
<td>Standard date/time string</td>
</tr>
<tr>
<td>1</td>
<td>integer</td>
<td>Epoch-based date/time</td>
</tr>
<tr>
<td>2</td>
<td>byte string</td>
<td>Positive bignum</td>
</tr>
<tr>
<td>3</td>
<td>byte string</td>
<td>Negative bignum</td>
</tr>
<tr>
<td>4</td>
<td>array</td>
<td>Decimal fraction</td>
</tr>
<tr>
<td>5</td>
<td>array</td>
<td>Bigfloat</td>
</tr>
<tr>
<td>16</td>
<td>array</td>
<td>COSE Single Recipient Encrypted Data Object (RFC 8152)</td>
</tr>
<tr>
<td>17</td>
<td>array</td>
<td>COSE Mac w/o Recipients Object (RFC 8152)</td>
</tr>
<tr>
<td>18</td>
<td>array</td>
<td>COSE Single Signer Data Object (RFC 8162)</td>
</tr>
<tr>
<td>21</td>
<td>byte string, array, map</td>
<td>Expected conversion to base64url encoding</td>
</tr>
<tr>
<td>22</td>
<td>byte string, array, map</td>
<td>Expected conversion to base64 encoding</td>
</tr>
<tr>
<td>23</td>
<td>byte string, array, map</td>
<td>Expected conversion to base16 encoding</td>
</tr>
<tr>
<td>24</td>
<td>byte string</td>
<td>Encoded CBOR data item</td>
</tr>
<tr>
<td>32</td>
<td>UTF-8 text string</td>
<td>URI</td>
</tr>
<tr>
<td>33</td>
<td>UTF-8 text string</td>
<td>base64url</td>
</tr>
<tr>
<td>34</td>
<td>UTF-8 text string</td>
<td>base64</td>
</tr>
<tr>
<td>35</td>
<td>UTF-8 text string</td>
<td>Regular expression</td>
</tr>
<tr>
<td>36</td>
<td>UTF-8 text string</td>
<td>MIME message</td>
</tr>
<tr>
<td>96</td>
<td>array</td>
<td>COSE Encrypted Data Object (RFC 8152)</td>
</tr>
<tr>
<td>97</td>
<td>array</td>
<td>COSE MACed Data Object (RFC 8152)</td>
</tr>
<tr>
<td>98</td>
<td>array</td>
<td>COSE Signed Data Object (RFC 8152)</td>
</tr>
<tr>
<td>55799</td>
<td>any</td>
<td>Self-describe CBOR</td>
</tr>
</table>
*/
struct KnownTagData { uint32_t tag; uint32_t types; };
static const struct KnownTagData knownTagData[] = {
{ 0, (uint32_t)CborTextStringType },
{ 1, (uint32_t)(CborIntegerType+1) },
{ 2, (uint32_t)CborByteStringType },
{ 3, (uint32_t)CborByteStringType },
{ 4, (uint32_t)CborArrayType },
{ 5, (uint32_t)CborArrayType },
{ 16, (uint32_t)CborArrayType },
{ 17, (uint32_t)CborArrayType },
{ 18, (uint32_t)CborArrayType },
{ 21, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) },
{ 22, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) },
{ 23, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) },
{ 24, (uint32_t)CborByteStringType },
{ 32, (uint32_t)CborTextStringType },
{ 33, (uint32_t)CborTextStringType },
{ 34, (uint32_t)CborTextStringType },
{ 35, (uint32_t)CborTextStringType },
{ 36, (uint32_t)CborTextStringType },
{ 96, (uint32_t)CborArrayType },
{ 97, (uint32_t)CborArrayType },
{ 98, (uint32_t)CborArrayType },
{ 55799, 0U }
};
static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft);
static inline CborError validate_utf8_string(const void *ptr, size_t n)
{
const uint8_t *buffer = (const uint8_t *)ptr;
const uint8_t * const end = buffer + n;
while (buffer < end) {
uint32_t uc = get_utf8(&buffer, end);
if (uc == ~0U)
return CborErrorInvalidUtf8TextString;
}
return CborNoError;
}
static inline CborError validate_simple_type(uint8_t simple_type, uint32_t flags)
{
/* At current time, all known simple types are those from RFC 7049,
* which are parsed by the parser into different CBOR types.
* That means that if we've got here, the type is unknown */
if (simple_type < 32)
return (flags & CborValidateNoUnknownSimpleTypesSA) ? CborErrorUnknownSimpleType : CborNoError;
return (flags & CborValidateNoUnknownSimpleTypes) == CborValidateNoUnknownSimpleTypes ?
CborErrorUnknownSimpleType : CborNoError;
}
static inline CborError validate_number(const CborValue *it, CborType type, uint32_t flags)
{
CborError err = CborNoError;
size_t bytesUsed, bytesNeeded;
uint64_t value;
if ((flags & CborValidateShortestIntegrals) == 0)
return err;
if (type >= CborHalfFloatType && type <= CborDoubleType)
return err; /* checked elsewhere */
err = extract_number_checked(it, &value, &bytesUsed);
if (err)
return err;
bytesNeeded = 0;
if (value >= Value8Bit)
++bytesNeeded;
if (value > 0xffU)
++bytesNeeded;
if (value > 0xffffU)
bytesNeeded += 2;
if (value > 0xffffffffU)
bytesNeeded += 4;
if (bytesNeeded < bytesUsed)
return CborErrorOverlongEncoding;
return CborNoError;
}
static inline CborError validate_tag(CborValue *it, CborTag tag, uint32_t flags, int recursionLeft)
{
CborType type = cbor_value_get_type(it);
const size_t knownTagCount = sizeof(knownTagData) / sizeof(knownTagData[0]);
const struct KnownTagData *tagData = knownTagData;
const struct KnownTagData * const knownTagDataEnd = knownTagData + knownTagCount;
if (!recursionLeft)
return CborErrorNestingTooDeep;
if (flags & CborValidateNoTags)
return CborErrorExcludedType;
/* find the tag data, if any */
for ( ; tagData != knownTagDataEnd; ++tagData) {
if (tagData->tag < tag)
continue;
if (tagData->tag > tag)
tagData = NULL;
break;
}
if (tagData == knownTagDataEnd)
tagData = NULL;
if (flags & CborValidateNoUnknownTags && !tagData) {
/* tag not found */
if (flags & CborValidateNoUnknownTagsSA && tag < 24)
return CborErrorUnknownTag;
if ((flags & CborValidateNoUnknownTagsSR) == CborValidateNoUnknownTagsSR && tag < 256)
return CborErrorUnknownTag;
if ((flags & CborValidateNoUnknownTags) == CborValidateNoUnknownTags)
return CborErrorUnknownTag;
}
if (flags & CborValidateTagUse && tagData && tagData->types) {
uint32_t allowedTypes = tagData->types;
/* correct Integer so it's not zero */
if (type == CborIntegerType)
type = (CborType)(type + 1);
while (allowedTypes) {
if ((uint8_t)(allowedTypes & 0xff) == type)
break;
allowedTypes >>= 8;
}
if (!allowedTypes)
return CborErrorInappropriateTagForType;
}
return validate_value(it, flags, recursionLeft);
}
#ifndef CBOR_NO_FLOATING_POINT
static inline CborError validate_floating_point(CborValue *it, CborType type, uint32_t flags)
{
CborError err;
int r;
double val;
float valf;
uint16_t valf16 = 0x7c01; /* dummy value, an infinity */
if (type != CborDoubleType) {
if (type == CborFloatType) {
err = cbor_value_get_float(it, &valf);
val = valf;
} else {
# ifdef CBOR_NO_HALF_FLOAT_TYPE
(void)valf16;
return CborErrorUnsupportedType;
# else
err = cbor_value_get_half_float(it, &valf16);
val = decode_half(valf16);
# endif
}
} else {
err = cbor_value_get_double(it, &val);
}
cbor_assert(err == CborNoError); /* can't fail */
r = fpclassify(val);
if (r == FP_NAN || r == FP_INFINITE) {
if (flags & CborValidateFiniteFloatingPoint)
return CborErrorExcludedValue;
if (flags & CborValidateShortestFloatingPoint) {
if (type == CborDoubleType)
return CborErrorOverlongEncoding;
# ifndef CBOR_NO_HALF_FLOAT_TYPE
if (type == CborFloatType)
return CborErrorOverlongEncoding;
if (r == FP_NAN && valf16 != 0x7e00)
return CborErrorImproperValue;
if (r == FP_INFINITE && valf16 != 0x7c00 && valf16 != 0xfc00)
return CborErrorImproperValue;
# endif
}
}
if (flags & CborValidateShortestFloatingPoint && type > CborHalfFloatType) {
if (type == CborDoubleType) {
valf = (float)val;
if ((double)valf == val)
return CborErrorOverlongEncoding;
}
# ifndef CBOR_NO_HALF_FLOAT_TYPE
if (type == CborFloatType) {
valf16 = encode_half(valf);
if (valf == decode_half(valf16))
return CborErrorOverlongEncoding;
}
# endif
}
return CborNoError;
}
#endif
static CborError validate_container(CborValue *it, int containerType, uint32_t flags, int recursionLeft)
{
CborError err;
const uint8_t *previous = NULL;
const uint8_t *previous_end = NULL;
if (!recursionLeft)
return CborErrorNestingTooDeep;
while (!cbor_value_at_end(it)) {
const uint8_t *current = cbor_value_get_next_byte(it);
if (containerType == CborMapType) {
if (flags & CborValidateMapKeysAreString) {
CborType type = cbor_value_get_type(it);
if (type == CborTagType) {
/* skip the tags */
CborValue copy = *it;
err = cbor_value_skip_tag(&copy);
if (err)
return err;
type = cbor_value_get_type(&copy);
}
if (type != CborTextStringType)
return CborErrorMapKeyNotString;
}
}
err = validate_value(it, flags, recursionLeft);
if (err)
return err;
if (containerType != CborMapType)
continue;
if (flags & CborValidateMapIsSorted) {
if (it->parser->flags & CborParserFlag_ExternalSource)
return CborErrorUnimplementedValidation;
if (previous) {
size_t bytelen1 = (size_t)(previous_end - previous);
size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - current);
int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2);
if (r == 0 && bytelen1 != bytelen2)
r = bytelen1 < bytelen2 ? -1 : +1;
if (r > 0)
return CborErrorMapNotSorted;
if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique)
return CborErrorMapKeysNotUnique;
}
previous = current;
previous_end = cbor_value_get_next_byte(it);
}
/* map: that was the key, so get the value */
err = validate_value(it, flags, recursionLeft);
if (err)
return err;
}
return CborNoError;
}
static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft)
{
CborError err;
CborType type = cbor_value_get_type(it);
if (cbor_value_is_length_known(it)) {
err = validate_number(it, type, flags);
if (err)
return err;
} else {
if (flags & CborValidateNoIndeterminateLength)
return CborErrorUnknownLength;
}
switch (type) {
case CborArrayType:
case CborMapType: {
/* recursive type */
CborValue recursed;
err = cbor_value_enter_container(it, &recursed);
if (!err)
err = validate_container(&recursed, type, flags, recursionLeft - 1);
if (err) {
copy_current_position(it, &recursed);
return err;
}
err = cbor_value_leave_container(it, &recursed);
if (err)
return err;
return CborNoError;
}
case CborIntegerType: {
uint64_t val;
err = cbor_value_get_raw_integer(it, &val);
cbor_assert(err == CborNoError); /* can't fail */
break;
}
case CborByteStringType:
case CborTextStringType: {
size_t n = 0;
const void *ptr;
err = cbor_value_begin_string_iteration(it);
if (err)
return err;
while (1) {
CborValue next;
err = _cbor_value_get_string_chunk(it, &ptr, &n, &next);
if (!err) {
err = validate_number(it, type, flags);
if (err)
return err;
}
*it = next;
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);
if (err)
return err;
}
}
return CborNoError;
}
case CborTagType: {
CborTag tag;
err = cbor_value_get_tag(it, &tag);
cbor_assert(err == CborNoError); /* can't fail */
err = cbor_value_advance_fixed(it);
if (err)
return err;
err = validate_tag(it, tag, flags, recursionLeft - 1);
if (err)
return err;
return CborNoError;
}
case CborSimpleType: {
uint8_t simple_type;
err = cbor_value_get_simple_type(it, &simple_type);
cbor_assert(err == CborNoError); /* can't fail */
err = validate_simple_type(simple_type, flags);
if (err)
return err;
break;
}
case CborNullType:
case CborBooleanType:
break;
case CborUndefinedType:
if (flags & CborValidateNoUndefined)
return CborErrorExcludedType;
break;
case CborHalfFloatType:
case CborFloatType:
case CborDoubleType: {
#ifdef CBOR_NO_FLOATING_POINT
return CborErrorUnsupportedType;
#else
err = validate_floating_point(it, type, flags);
if (err)
return err;
break;
#endif /* !CBOR_NO_FLOATING_POINT */
}
case CborInvalidType:
return CborErrorUnknownType;
}
err = cbor_value_advance_fixed(it);
return err;
}
/**
* Performs a full validation, controlled by the \a flags options, of the CBOR
* stream pointed by \a it and returns the error it found. If no error was
* found, it returns CborNoError and the application can iterate over the items
* with certainty that no errors will appear during parsing.
*
* If \a flags is CborValidateBasic, the result should be the same as
* cbor_value_validate_basic().
*
* This function has the same timing and memory requirements as
* cbor_value_advance() and cbor_value_validate_basic().
*
* \sa CborValidationFlags, cbor_value_validate_basic(), cbor_value_advance()
*/
CborError cbor_value_validate(const CborValue *it, uint32_t flags)
{
CborValue value = *it;
CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS);
if (err)
return err;
if (flags & CborValidateCompleteData && can_read_bytes(it, 1))
return CborErrorGarbageAtEnd;
return CborNoError;
}
/**
* @}
*/

@ -0,0 +1,205 @@
/****************************************************************************
**
** Copyright (C) 2017 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#ifndef COMPILERSUPPORT_H
#define COMPILERSUPPORT_H
#include "cbor.h"
#ifndef _BSD_SOURCE
# define _BSD_SOURCE
#endif
#ifndef _DEFAULT_SOURCE
# define _DEFAULT_SOURCE
#endif
#ifndef assert
# include <assert.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifndef __cplusplus
# include <stdbool.h>
#endif
#if __STDC_VERSION__ >= 201112L || (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(__cpp_static_assert) && __cpp_static_assert >= 200410)
# define cbor_static_assert(x) static_assert(x, #x)
#elif !defined(__cplusplus) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) && (__STDC_VERSION__ > 199901L)
# define cbor_static_assert(x) _Static_assert(x, #x)
#else
# define cbor_static_assert(x) ((void)sizeof(char[2*!!(x) - 1]))
#endif
#if __STDC_VERSION__ >= 199901L || defined(__cplusplus)
/* inline is a keyword */
#else
/* use the definition from cbor.h */
# define inline CBOR_INLINE
#endif
#ifdef NDEBUG
# define cbor_assert(cond) do { if (!(cond)) unreachable(); } while (0)
#else
# define cbor_assert(cond) assert(cond)
#endif
#ifndef STRINGIFY
#define STRINGIFY(x) STRINGIFY2(x)
#endif
#define STRINGIFY2(x) #x
#if !defined(UINT32_MAX) || !defined(INT64_MAX)
/* C89? We can define UINT32_MAX portably, but not INT64_MAX */
# error "Your system has stdint.h but that doesn't define UINT32_MAX or INT64_MAX"
#endif
#ifndef DBL_DECIMAL_DIG
/* DBL_DECIMAL_DIG is C11 */
# define DBL_DECIMAL_DIG 17
#endif
#define DBL_DECIMAL_DIG_STR STRINGIFY(DBL_DECIMAL_DIG)
#if defined(__GNUC__) && defined(__i386__) && !defined(__iamcu__)
# define CBOR_INTERNAL_API_CC __attribute__((regparm(3)))
#elif defined(_MSC_VER) && defined(_M_IX86)
# define CBOR_INTERNAL_API_CC __fastcall
#else
# define CBOR_INTERNAL_API_CC
#endif
#ifndef __has_builtin
# define __has_builtin(x) 0
#endif
#if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) || \
(__has_builtin(__builtin_bswap64) && __has_builtin(__builtin_bswap32))
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define cbor_ntohll __builtin_bswap64
# define cbor_htonll __builtin_bswap64
# define cbor_ntohl __builtin_bswap32
# define cbor_htonl __builtin_bswap32
# ifdef __INTEL_COMPILER
# define cbor_ntohs _bswap16
# define cbor_htons _bswap16
# elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 608) || __has_builtin(__builtin_bswap16)
# define cbor_ntohs __builtin_bswap16
# define cbor_htons __builtin_bswap16
# else
# define cbor_ntohs(x) (((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8))
# define cbor_htons cbor_ntohs
# endif
# else
# define cbor_ntohll
# define cbor_htonll
# define cbor_ntohl
# define cbor_htonl
# define cbor_ntohs
# define cbor_htons
# endif
#elif defined(__sun)
# include <sys/byteorder.h>
#elif defined(_MSC_VER)
/* MSVC, which implies Windows, which implies little-endian and sizeof(long) == 4 */
# include <stdlib.h>
# define cbor_ntohll _byteswap_uint64
# define cbor_htonll _byteswap_uint64
# define cbor_ntohl _byteswap_ulong
# define cbor_htonl _byteswap_ulong
# define cbor_ntohs _byteswap_ushort
# define cbor_htons _byteswap_ushort
#endif
#ifndef cbor_ntohs
# include <arpa/inet.h>
# define cbor_ntohs ntohs
# define cbor_htons htons
#endif
#ifndef cbor_ntohl
# include <arpa/inet.h>
# define cbor_ntohl ntohl
# define cbor_htonl htonl
#endif
#ifndef cbor_ntohll
# define cbor_ntohll ntohll
# define cbor_htonll htonll
/* ntohll isn't usually defined */
# ifndef ntohll
# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
(defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || \
(defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \
(defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) || \
defined(__ARMEB__) || defined(__MIPSEB__) || defined(__s390__) || defined(__sparc__)
# define ntohll
# define htonll
# elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \
(defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || \
(defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN) || \
defined(_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64)
# define ntohll(x) ((ntohl((uint32_t)(x)) * UINT64_C(0x100000000)) + (ntohl((x) >> 32)))
# define htonll ntohll
# else
# error "Unable to determine byte order!"
# endif
# endif
#endif
#ifdef __cplusplus
# define CONST_CAST(t, v) const_cast<t>(v)
#else
/* C-style const_cast without triggering a warning with -Wcast-qual */
# define CONST_CAST(t, v) (t)(uintptr_t)(v)
#endif
#ifdef __GNUC__
#ifndef likely
# define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif
# define unreachable() __builtin_unreachable()
#elif defined(_MSC_VER)
# define likely(x) (x)
# define unlikely(x) (x)
# define unreachable() __assume(0)
#else
# define likely(x) (x)
# define unlikely(x) (x)
# define unreachable() do {} while (0)
#endif
static inline bool add_check_overflow(size_t v1, size_t v2, size_t *r)
{
#if ((defined(__GNUC__) && (__GNUC__ >= 5)) && !defined(__INTEL_COMPILER)) || __has_builtin(__builtin_add_overflow)
return __builtin_add_overflow(v1, v2, r);
#else
/* unsigned additions are well-defined */
*r = v1 + v2;
return v1 > v1 + v2;
#endif
}
#endif /* COMPILERSUPPORT_H */

@ -0,0 +1,114 @@
/****************************************************************************
**
** Copyright (C) 2015 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#define _BSD_SOURCE 1
#define _DEFAULT_SOURCE 1
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__unix__) || defined(__APPLE__)
# include <unistd.h>
#endif
#ifdef __APPLE__
typedef int RetType;
typedef int LenType;
#elif __linux__
typedef ssize_t RetType;
typedef size_t LenType;
#else
# error "Cannot implement open_memstream!"
#endif
#include "compilersupport_p.h"
struct Buffer
{
char **ptr;
size_t *len;
size_t alloc;
};
static RetType write_to_buffer(void *cookie, const char *data, LenType len)
{
struct Buffer *b = (struct Buffer *)cookie;
char *ptr = *b->ptr;
size_t newsize;
errno = EFBIG;
if (unlikely(add_check_overflow(*b->len, len, &newsize)))
return -1;
if (newsize >= b->alloc) { // NB! one extra byte is needed to avoid buffer overflow at close_buffer
// make room
size_t newalloc = newsize + newsize / 2 + 1; // give 50% more room
ptr = realloc(ptr, newalloc);
if (ptr == NULL)
return -1;
b->alloc = newalloc;
*b->ptr = ptr;
}
memcpy(ptr + *b->len, data, len);
*b->len = newsize;
return len;
}
static int close_buffer(void *cookie)
{
struct Buffer *b = (struct Buffer *)cookie;
if (*b->ptr)
(*b->ptr)[*b->len] = '\0';
free(b);
return 0;
}
FILE *open_memstream(char **bufptr, size_t *lenptr)
{
struct Buffer *b = (struct Buffer *)malloc(sizeof(struct Buffer));
if (b == NULL)
return NULL;
b->alloc = 0;
b->len = lenptr;
b->ptr = bufptr;
*bufptr = NULL;
*lenptr = 0;
#ifdef __APPLE__
return funopen(b, NULL, write_to_buffer, NULL, close_buffer);
#elif __linux__
static const cookie_io_functions_t vtable = {
NULL,
write_to_buffer,
NULL,
close_buffer
};
return fopencookie(b, "w", vtable);
#endif
}

@ -0,0 +1,116 @@
#!/usr/bin/perl -l
## Copyright (C) 2017 Intel Corporation
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in
## all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
## THE SOFTWARE.
##
use strict;
my $fname = shift @ARGV
or die("Usage: parsetags.pl tags.txt");
open TAGS, "<", $fname
or die("Cannot open $fname: $!");
my %typedescriptions = (
"Integer" => "integer",
"ByteString" => "byte string",
"TextString" => "UTF-8 text string",
"Array" => "array",
"Map" => "map",
"Tag" => "tag", # shouldn't happen
"Simple" => "any simple type",
"Boolean" => "boolean",
"Null" => "null",
"Undefined" => "undefined",
"HalfFloat" => "IEEE 754 half-precision floating point",
"Float" => "IEEE 754 single-precision floating point",
"Double" => "IEEE 754 double-precision floating point"
);
my %tags;
while (<TAGS>) {
s/\s*#.*$//;
next if /^$/;
chomp;
die("Could not parse line \"$_\"")
unless /^(\d+);(\w+);([\w,]*);(.*)$/;
$tags{$1}{id} = $2;
$tags{$1}{semantic} = $4;
my @types = split(',', $3);
$tags{$1}{types} = \@types;
}
close TAGS or die;
my @tagnumbers = sort { $a <=> $b } keys %tags;
print "==== HTML listing ====";
print "<table>\n <tr>\n <th>Tag</th>\n <th>Data Item</th>\n <th>Semantics</th>\n </tr>";
for my $n (@tagnumbers) {
print " <tr>";
print " <td>$n</td>";
my @types = @{$tags{$n}{types}};
@types = map { $typedescriptions{$_}; } @types;
unshift @types, "any"
if (scalar @types == 0);
printf " <td>%s</td>\n", join(', ', @types);
printf " <td>%s</td>\n", $tags{$n}{semantic};
print " </td>";
}
print "</table>";
print "\n==== enum listing for cbor.h ====\n";
printf "typedef enum CborKnownTags {";
my $comma = "";
for my $n (@tagnumbers) {
printf "%s\n Cbor%sTag%s = %d", $comma,
$tags{$n}{id},
' ' x (23 - length($tags{$n}{id})),
$n;
$comma = ",";
}
print "\n} CborKnownTags;";
print "\n/* #define the constants so we can check with #ifdef */";
for my $n (@tagnumbers) {
printf "#define Cbor%sTag Cbor%sTag\n", $tags{$n}{id}, $tags{$n}{id};
}
print "\n==== search table ====\n";
print "struct KnownTagData { uint32_t tag; uint32_t types; };";
printf "static const struct KnownTagData knownTagData[] = {";
$comma = "";
for my $n (@tagnumbers) {
my @types = @{$tags{$n}{types}};
my $typemask;
my $shift = 0;
for my $type (@types) {
die("Too many match types for tag $n") if $shift == 32;
my $actualtype = "Cbor${type}Type";
$actualtype = "($actualtype+1)" if $type eq "Integer";
$typemask .= " | " if $typemask ne "";
$typemask .= "((uint32_t)$actualtype << $shift)" if $shift;
$typemask .= "(uint32_t)$actualtype" unless $shift;
$shift += 8;
}
$typemask = "0U" if $typemask eq "";
printf "%s\n { %d, %s }", $comma, $n, $typemask;
$comma = ",";
}
print "\n};";

@ -0,0 +1,26 @@
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/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
INCLUDEPATH += $$PWD
CONFIG(release, debug|release): DEFINES += NDEBUG

@ -0,0 +1,23 @@
# Tag number; Tag ID; Applicable types (comma-separated); Semantics
0;DateTimeString;TextString;Standard date/time string
1;UnixTime_t;Integer;Epoch-based date/time
2;PositiveBignum;ByteString;Positive bignum
3;NegativeBignum;ByteString;Negative bignum
4;Decimal;Array;Decimal fraction
5;Bigfloat;Array;Bigfloat
16;COSE_Encrypt0;Array;COSE Single Recipient Encrypted Data Object (RFC 8152)
17;COSE_Mac0;Array;COSE Mac w/o Recipients Object (RFC 8152)
18;COSE_Sign1;Array;COSE Single Signer Data Object (RFC 8162)
21;ExpectedBase64url;ByteString,Array,Map;Expected conversion to base64url encoding
22;ExpectedBase64;ByteString,Array,Map;Expected conversion to base64 encoding
23;ExpectedBase16;ByteString,Array,Map;Expected conversion to base16 encoding
24;EncodedCbor;ByteString;Encoded CBOR data item
32;Url;TextString;URI
33;Base64url;TextString;base64url
34;Base64;TextString;base64
35;RegularExpression;TextString;Regular expression
36;MimeMessage;TextString;MIME message
96;COSE_Encrypt;Array;COSE Encrypted Data Object (RFC 8152)
97;COSE_Mac;Array;COSE MACed Data Object (RFC 8152)
98;COSE_Sign;Array;COSE Signed Data Object (RFC 8152)
55799;Signature;;Self-describe CBOR

@ -0,0 +1,3 @@
#define TINYCBOR_VERSION_MAJOR 0
#define TINYCBOR_VERSION_MINOR 6
#define TINYCBOR_VERSION_PATCH 0

@ -0,0 +1,10 @@
TEMPLATE = lib
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)

@ -0,0 +1,104 @@
/****************************************************************************
**
** Copyright (C) 2017 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#ifndef CBOR_UTF8_H
#define CBOR_UTF8_H
#include "compilersupport_p.h"
#include <stdint.h>
static inline uint32_t get_utf8(const uint8_t **buffer, const uint8_t *end)
{
int charsNeeded;
uint32_t uc, min_uc;
uint8_t b;
ptrdiff_t n = end - *buffer;
if (n == 0)
return ~0U;
uc = *(*buffer)++;
if (uc < 0x80) {
/* single-byte UTF-8 */
return uc;
}
/* multi-byte UTF-8, decode it */
if (unlikely(uc <= 0xC1))
return ~0U;
if (uc < 0xE0) {
/* two-byte UTF-8 */
charsNeeded = 2;
min_uc = 0x80;
uc &= 0x1f;
} else if (uc < 0xF0) {
/* three-byte UTF-8 */
charsNeeded = 3;
min_uc = 0x800;
uc &= 0x0f;
} else if (uc < 0xF5) {
/* four-byte UTF-8 */
charsNeeded = 4;
min_uc = 0x10000;
uc &= 0x07;
} else {
return ~0U;
}
if (n < charsNeeded)
return ~0U;
/* first continuation character */
b = *(*buffer)++;
if ((b & 0xc0) != 0x80)
return ~0U;
uc <<= 6;
uc |= b & 0x3f;
if (charsNeeded > 2) {
/* second continuation character */
b = *(*buffer)++;
if ((b & 0xc0) != 0x80)
return ~0U;
uc <<= 6;
uc |= b & 0x3f;
if (charsNeeded > 3) {
/* third continuation character */
b = *(*buffer)++;
if ((b & 0xc0) != 0x80)
return ~0U;
uc <<= 6;
uc |= b & 0x3f;
}
}
/* overlong sequence? surrogate pair? out or range? */
if (uc < min_uc || uc - 0xd800U < 2048U || uc > 0x10ffff)
return ~0U;
return uc;
}
#endif /* CBOR_UTF8_H */

@ -0,0 +1,15 @@
Makefile
debug
moc_predefs.h
release
target_wrapper.*
# The executables
cpp/cpp
cpp/cpp.exe
encoder/encoder
encoder/encoder.exe
parser/parser
parser/parser.exe
tojson/tojson
tojson/tojson.exe

@ -0,0 +1,7 @@
CONFIG += testcase parallel_test console
CONFIG -= qt app_bundle
gcc: QMAKE_CFLAGS += -std=c90 -pedantic-errors -Wall -Wextra -Werror
darwin: QMAKE_CFLAGS += -Wno-long-long
SOURCES += tst_c90.c
INCLUDEPATH += ../../src

@ -0,0 +1,30 @@
/****************************************************************************
**
** Copyright (C) 2018 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#include "cbor.h"
int main()
{
return 0;
}

@ -0,0 +1,5 @@
CONFIG += testcase parallel_test c++11
QT = core testlib
SOURCES = tst_cpp.cpp
INCLUDEPATH += ../../src

@ -0,0 +1,44 @@
/****************************************************************************
**
** Copyright (C) 2017 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#include "../../src/cborencoder.c"
#include "../../src/cborencoder_float.c"
#include "../../src/cborerrorstrings.c"
#include "../../src/cborparser.c"
#include "../../src/cborparser_dup_string.c"
#include "../../src/cborparser_float.c"
#include "../../src/cborvalidation.c"
#include <QtTest>
// This is a compilation-only test.
// All it does is verify that the four source files above
// compile as C++ without errors.
class tst_Cpp : public QObject
{
Q_OBJECT
};
QTEST_MAIN(tst_Cpp)
#include "tst_cpp.moc"

@ -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 <QtTest>
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 <size_t N> 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 <typename... Args>
QVariant make_list(const Args &... args)
{
return QVariantList{args...};
}
typedef QVector<QPair<QVariant, QVariant>> Map;
Q_DECLARE_METATYPE(Map)
QVariant make_map(const std::initializer_list<QPair<QVariant, QVariant>> &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<QVariant> &list)
{
return QVariant::fromValue(IndeterminateLengthArray(list));
}
QVariant make_ilmap(const std::initializer_list<QPair<QVariant, QVariant>> &list)
{
return QVariant::fromValue(IndeterminateLengthMap(list));
}
void addHalfFloat()
{
QTest::addColumn<QByteArray>("output");
QTest::addColumn<unsigned>("rawInput");
QTest::addColumn<double>("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<QByteArray>("output");
QTest::addColumn<QVariant>("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<quint64>::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<NegativeInteger>({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<quint64>::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<void *>(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<qfloat16>(myNaNf());
QTest::newRow("-inf_f16") << raw("\xf9\xfc\0") << QVariant::fromValue<qfloat16>(myNInff());
QTest::newRow("+inf_f16") << raw("\xf9\x7c\0") << QVariant::fromValue<qfloat16>(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<float>(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<float>(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<float>(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})}});
}

@ -0,0 +1,9 @@
SOURCES += tst_encoder.cpp
CONFIG += testcase parallel_test c++11
QT = core testlib
INCLUDEPATH += ../../src
msvc: POST_TARGETDEPS = ../../lib/tinycbor.lib
else: POST_TARGETDEPS += ../../lib/libtinycbor.a
LIBS += $$POST_TARGETDEPS

@ -0,0 +1,609 @@
/****************************************************************************
**
** 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 <QtTest>
#include "cbor.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
#include <qfloat16.h>
#endif
Q_DECLARE_METATYPE(CborError)
namespace QTest {
template<> char *toString<CborError>(const CborError &err)
{
return qstrdup(cbor_error_string(err));
}
}
class tst_Encoder : public QObject
{
Q_OBJECT
private slots:
void floatAsHalfFloat_data();
void floatAsHalfFloat();
void halfFloat_data();
void halfFloat();
void floatAsHalfFloatCloseToZero_data();
void floatAsHalfFloatCloseToZero();
void floatAsHalfFloatNaN();
void fixed_data();
void fixed();
void strings_data();
void strings() { fixed(); }
void arraysAndMaps_data();
void arraysAndMaps() { fixed(); }
void tags_data();
void tags();
void arrays_data() { tags_data(); }
void arrays();
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(); }
void tooShortArrays();
void tooShortMaps_data() { tags_data(); }
void tooShortMaps();
void tooBigArrays_data() { tags_data(); }
void tooBigArrays();
void tooBigMaps_data() { tags_data(); }
void tooBigMaps();
void illegalSimpleType_data();
void illegalSimpleType();
};
#include "tst_encoder.moc"
#include "data.cpp"
static inline bool isOomError(CborError err)
{
return err == CborErrorOutOfMemory;
}
CborError encodeVariant(CborEncoder *encoder, const QVariant &v)
{
int type = v.userType();
switch (type) {
case QVariant::Int:
case QVariant::LongLong:
return cbor_encode_int(encoder, v.toLongLong());
case QVariant::UInt:
case QVariant::ULongLong:
return cbor_encode_uint(encoder, v.toULongLong());
case QVariant::Bool:
return cbor_encode_boolean(encoder, v.toBool());
case QVariant::Invalid:
return cbor_encode_undefined(encoder);
case QMetaType::VoidStar:
return cbor_encode_null(encoder);
case QVariant::Double:
return cbor_encode_double(encoder, v.toDouble());
case QMetaType::Float:
return cbor_encode_float(encoder, v.toFloat());
case QVariant::String: {
QByteArray string = v.toString().toUtf8();
return cbor_encode_text_string(encoder, string.constData(), string.length());
}
case QVariant::ByteArray: {
QByteArray string = v.toByteArray();
return cbor_encode_byte_string(encoder, reinterpret_cast<const quint8 *>(string.constData()), string.length());
}
default:
if (type == qMetaTypeId<NegativeInteger>())
return cbor_encode_negative_int(encoder, v.value<NegativeInteger>().abs);
if (type == qMetaTypeId<SimpleType>())
return cbor_encode_simple_value(encoder, v.value<SimpleType>().type);
#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
if (type == qMetaTypeId<Float16Standin>())
return cbor_encode_half_float(encoder, v.constData());
#else
if (type == qMetaTypeId<qfloat16>())
return cbor_encode_half_float(encoder, v.constData());
#endif
if (type == qMetaTypeId<Tag>()) {
CborError err = cbor_encode_tag(encoder, v.value<Tag>().tag);
if (err && !isOomError(err))
return err;
return static_cast<CborError>(err | encodeVariant(encoder, v.value<Tag>().tagged));
}
if (type == QVariant::List || type == qMetaTypeId<IndeterminateLengthArray>()) {
CborEncoder sub;
QVariantList list = v.toList();
size_t len = list.length();
if (type == qMetaTypeId<IndeterminateLengthArray>()) {
len = CborIndefiniteLength;
list = v.value<IndeterminateLengthArray>();
}
CborError err = cbor_encoder_create_array(encoder, &sub, len);
if (err && !isOomError(err))
return err;
foreach (const QVariant &v2, list) {
err = static_cast<CborError>(err | encodeVariant(&sub, v2));
if (err && !isOomError(err))
return err;
}
return cbor_encoder_close_container_checked(encoder, &sub);
}
if (type == qMetaTypeId<Map>() || type == qMetaTypeId<IndeterminateLengthMap>()) {
CborEncoder sub;
Map map = v.value<Map>();
size_t len = map.length();
if (type == qMetaTypeId<IndeterminateLengthMap>()) {
len = CborIndefiniteLength;
map = v.value<IndeterminateLengthMap>();
}
CborError err = cbor_encoder_create_map(encoder, &sub, len);
if (err && !isOomError(err))
return err;
for (auto pair : map) {
err = static_cast<CborError>(err | encodeVariant(&sub, pair.first));
if (err && !isOomError(err))
return err;
err = static_cast<CborError>(err | encodeVariant(&sub, pair.second));
if (err && !isOomError(err))
return err;
}
return cbor_encoder_close_container_checked(encoder, &sub);
}
}
return CborErrorUnknownType;
}
template <typename Input, typename FnUnderTest>
void encodeOne(Input input, FnUnderTest fn_under_test, QByteArray &buffer, CborError &error)
{
uint8_t *bufptr = reinterpret_cast<quint8 *>(buffer.data());
CborEncoder encoder;
cbor_encoder_init(&encoder, bufptr, buffer.length(), 0);
error = fn_under_test(&encoder, input);
if (error == CborNoError) {
QCOMPARE(encoder.remaining, size_t(1));
QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0));
buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr)));
}
}
template <typename Input, typename FnUnderTest>
void compare(Input input, FnUnderTest fn_under_test, const QByteArray &output)
{
QByteArray buffer(output.length(), Qt::Uninitialized);
CborError error;
encodeOne(input, fn_under_test, buffer, error);
if (QTest::currentTestFailed())
return;
QCOMPARE(error, CborNoError);
QCOMPARE(buffer, output);
}
void compare(const QVariant &input, const QByteArray &output)
{
compare(input, encodeVariant, output);
}
void tst_Encoder::floatAsHalfFloat_data()
{
addHalfFloat();
}
void tst_Encoder::floatAsHalfFloat()
{
QFETCH(unsigned, rawInput);
QFETCH(double, floatInput);
QFETCH(QByteArray, output);
if (rawInput == 0U || rawInput == 0x8000U)
QSKIP("zero values are out of scope of this test case", QTest::SkipSingle);
if (qIsNaN(floatInput))
QSKIP("NaN values are out of scope of this test case", QTest::SkipSingle);
output.prepend('\xf9');
compare((float)floatInput, cbor_encode_float_as_half_float, output);
}
void tst_Encoder::halfFloat_data()
{
addHalfFloat();
}
void tst_Encoder::halfFloat()
{
QFETCH(unsigned, rawInput);
QFETCH(QByteArray, output);
uint16_t v = (uint16_t)rawInput;
output.prepend('\xf9');
compare(&v, cbor_encode_half_float, output);
}
void tst_Encoder::floatAsHalfFloatCloseToZero_data()
{
QTest::addColumn<double>("floatInput");
QTest::newRow("+0") << 0.0;
QTest::newRow("-0") << -0.0;
QTest::newRow("below min.denorm") << ldexp(1.0, -14) * ldexp(1.0, -11);
QTest::newRow("above -min.denorm") << ldexp(-1.0, -14) * ldexp(1.0, -11);
}
void tst_Encoder::floatAsHalfFloatCloseToZero()
{
QFETCH(double, floatInput);
QByteArray buffer(4, Qt::Uninitialized);
CborError error;
encodeOne((float)floatInput, cbor_encode_float_as_half_float, buffer, error);
QCOMPARE(error, CborNoError);
QVERIFY2(
buffer == raw("\xf9\x00\x00") || buffer == raw("\xf9\x80\x00"),
"Got value " + QByteArray::number(floatInput) + " encoded to: " + buffer);
}
void tst_Encoder::floatAsHalfFloatNaN()
{
QByteArray buffer(4, Qt::Uninitialized);
CborError error;
encodeOne(myNaNf(), cbor_encode_float_as_half_float, buffer, error);
QCOMPARE(error, CborNoError);
QCOMPARE(buffer.size(), 3);
uint8_t ini_byte = (uint8_t)buffer[0],
exp = (uint8_t)buffer[1] & 0x7cU,
manth = (uint8_t)buffer[1] & 0x03U,
mantl = (uint8_t)buffer[2];
QCOMPARE((unsigned)ini_byte, 0xf9U);
QCOMPARE((unsigned)exp, 0x7cU);
QVERIFY((manth | mantl) != 0);
}
void tst_Encoder::fixed_data()
{
addColumns();
addFixedData();
}
void tst_Encoder::fixed()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(input, output);
}
void tst_Encoder::strings_data()
{
addColumns();
addStringsData();
}
void tst_Encoder::arraysAndMaps_data()
{
addColumns();
addArraysAndMaps();
}
void tst_Encoder::tags_data()
{
addColumns();
addFixedData();
addStringsData();
addArraysAndMaps();
}
void tst_Encoder::tags()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(QVariant::fromValue(Tag{1, input}), "\xc1" + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{24, input}), "\xd8\x18" + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{255, input}), "\xd8\xff" + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{256, input}), raw("\xd9\1\0") + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{CborSignatureTag, input}), raw("\xd9\xd9\xf7") + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{65535, input}), raw("\xd9\xff\xff") + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{65536, input}), raw("\xda\0\1\0\0") + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{UINT32_MAX, input}), raw("\xda\xff\xff\xff\xff") + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{UINT32_MAX + Q_UINT64_C(1), input}), raw("\xdb\0\0\0\1\0\0\0\0") + output);
if (QTest::currentTestFailed()) return;
compare(QVariant::fromValue(Tag{UINT64_MAX, input}), raw("\xdb\xff\xff\xff\xff\xff\xff\xff\xff") + output);
if (QTest::currentTestFailed()) return;
// nested tags
compare(QVariant::fromValue(Tag{1, QVariant::fromValue(Tag{1, input})}), "\xc1\xc1" + output);
}
void tst_Encoder::arrays()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(make_list(input), "\x81" + output);
if (QTest::currentTestFailed()) return;
compare(make_list(input, input), "\x82" + output + output);
if (QTest::currentTestFailed()) return;
{
QVariantList list{input};
QByteArray longoutput = output;
// make a list with 32 elements (1 << 5)
for (int i = 0; i < 5; ++i) {
list += list;
longoutput += longoutput;
}
compare(list, "\x98\x20" + longoutput);
if (QTest::currentTestFailed()) return;
// now 256 elements (32 << 3)
for (int i = 0; i < 3; ++i) {
list += list;
longoutput += longoutput;
}
compare(list, raw("\x99\1\0") + longoutput);
if (QTest::currentTestFailed()) return;
}
// nested lists
compare(make_list(make_list(input)), "\x81\x81" + output);
if (QTest::currentTestFailed()) return;
compare(make_list(make_list(input, input)), "\x81\x82" + output + output);
if (QTest::currentTestFailed()) return;
compare(make_list(make_list(input), input), "\x82\x81" + output + output);
if (QTest::currentTestFailed()) return;
compare(make_list(make_list(input), make_list(input)), "\x82\x81" + output + "\x81" + output);
}
void tst_Encoder::maps()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(make_map({{1, input}}), "\xa1\1" + output);
if (QTest::currentTestFailed()) return;
compare(make_map({{1, input}, {input, 24}}), "\xa2\1" + output + output + "\x18\x18");
if (QTest::currentTestFailed()) return;
compare(make_map({{input, input}}), "\xa1" + output + output);
if (QTest::currentTestFailed()) return;
{
Map map{{1, input}};
QByteArray longoutput = "\1" + output;
// make a map with 32 elements (1 << 5)
for (int i = 0; i < 5; ++i) {
map += map;
longoutput += longoutput;
}
compare(QVariant::fromValue(map), "\xb8\x20" + longoutput);
if (QTest::currentTestFailed()) return;
// now 256 elements (32 << 3)
for (int i = 0; i < 3; ++i) {
map += map;
longoutput += longoutput;
}
compare(QVariant::fromValue(map), raw("\xb9\1\0") + longoutput);
if (QTest::currentTestFailed()) return;
}
// nested maps
compare(make_map({{1, make_map({{2, input}})}}), "\xa1\1\xa1\2" + output);
if (QTest::currentTestFailed()) return;
compare(make_map({{1, make_map({{2, input}, {input, false}})}}), "\xa1\1\xa2\2" + output + output + "\xf4");
if (QTest::currentTestFailed()) return;
compare(make_map({{1, make_map({{2, input}})}, {input, false}}), "\xa2\1\xa1\2" + output + output + "\xf4");
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<QBuffer *>(token);
buffer->write(static_cast<const char *>(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<int *>(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);
QFETCH(QByteArray, output);
QByteArray buffer(output.length(), Qt::Uninitialized);
for (int len = 0; len < output.length(); ++len) {
CborEncoder encoder;
cbor_encoder_init(&encoder, reinterpret_cast<quint8 *>(buffer.data()), len, 0);
QCOMPARE(encodeVariant(&encoder, input), CborErrorOutOfMemory);
QVERIFY(cbor_encoder_get_extra_bytes_needed(&encoder) != 0);
QCOMPARE(len + cbor_encoder_get_extra_bytes_needed(&encoder), size_t(output.length()));
}
}
void tst_Encoder::tooShortArrays()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
QByteArray buffer(output.length() + 1, Qt::Uninitialized);
CborEncoder encoder, container;
cbor_encoder_init(&encoder, reinterpret_cast<quint8 *>(buffer.data()), buffer.length(), 0);
QCOMPARE(cbor_encoder_create_array(&encoder, &container, 2), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(container.remaining, size_t(2));
QCOMPARE(cbor_encoder_close_container_checked(&encoder, &container), CborErrorTooFewItems);
}
void tst_Encoder::tooShortMaps()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
QByteArray buffer(output.length() + 1, Qt::Uninitialized);
CborEncoder encoder, container;
cbor_encoder_init(&encoder, reinterpret_cast<quint8 *>(buffer.data()), buffer.length(), 0);
QCOMPARE(cbor_encoder_create_map(&encoder, &container, 2), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(container.remaining, size_t(4));
QCOMPARE(cbor_encoder_close_container_checked(&encoder, &container), CborErrorTooFewItems);
}
void tst_Encoder::tooBigArrays()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
QByteArray buffer(output.length() * 2 + 1, Qt::Uninitialized);
CborEncoder encoder, container;
cbor_encoder_init(&encoder, reinterpret_cast<quint8 *>(buffer.data()), buffer.length(), 0);
QCOMPARE(cbor_encoder_create_array(&encoder, &container, 1), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(container.remaining, size_t(0));
QCOMPARE(cbor_encoder_close_container_checked(&encoder, &container), CborErrorTooManyItems);
}
void tst_Encoder::tooBigMaps()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
QByteArray buffer(output.length() * 3 + 1, Qt::Uninitialized);
CborEncoder encoder, container;
cbor_encoder_init(&encoder, reinterpret_cast<quint8 *>(buffer.data()), buffer.length(), 0);
QCOMPARE(cbor_encoder_create_map(&encoder, &container, 1), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(encodeVariant(&container, input), CborNoError);
QCOMPARE(container.remaining, size_t(0));
QCOMPARE(cbor_encoder_close_container_checked(&encoder, &container), CborErrorTooManyItems);
}
void tst_Encoder::illegalSimpleType_data()
{
QTest::addColumn<int>("type");
QTest::newRow("half-float") << 25;
QTest::newRow("float") << 26;
QTest::newRow("double") << 27;
QTest::newRow("28") << 28;
QTest::newRow("29") << 29;
QTest::newRow("30") << 30;
QTest::newRow("31") << 31;
}
void tst_Encoder::illegalSimpleType()
{
QFETCH(int, type);
quint8 buf[2];
CborEncoder encoder;
cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
QCOMPARE(cbor_encode_simple_value(&encoder, type), CborErrorIllegalSimpleType);
}
QTEST_MAIN(tst_Encoder)

@ -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 <QtTest>
#include <limits>
#include <cbor.h>
Q_DECLARE_METATYPE(CborError)
template <size_t N> QByteArray raw(const char (&data)[N])
{
return QByteArray::fromRawData(data, N - 1);
}
void addIntegers()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<quint64>("expectedRaw");
QTest::addColumn<qint64>("expectedValue");
QTest::addColumn<bool>("isNegative");
QTest::addColumn<bool>("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<qint64>::max())
<< std::numeric_limits<qint64>::max() << false << true;
QTest::newRow("UINT64_MAX") << raw("\x1b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
<< std::numeric_limits<quint64>::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<qint64>::max() - 1)
<< (std::numeric_limits<qint64>::min() + 1)
<< true << true;
QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff")
<< quint64(std::numeric_limits<qint64>::max())
<< std::numeric_limits<qint64>::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<quint64>::max() - 1) << qint64(-123456) << true << false;
QTest::newRow("-UINT64_MAX+1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
<< std::numeric_limits<quint64>::max() << qint64(-123456) << true << false;
}
void addColumns()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QString>("expected");
QTest::addColumn<int>("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<uint64_t>::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<int64_t>::min() + 1);
QTest::newRow("INT64_MIN") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff")
<< QString::number(std::numeric_limits<int64_t>::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<uint64_t>::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<uint64_t>::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<QByteArray>("data");
QTest::addColumn<QString>("concatenated");
QTest::addColumn<QStringList>("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<QByteArray>("data");
QTest::addColumn<int>("flags"); // future
QTest::addColumn<CborError>("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;
}

@ -0,0 +1,10 @@
SOURCES += tst_parser.cpp ../../src/cborparser.c
CONFIG += testcase parallel_test c++11
QT = core testlib
DEFINES += CBOR_PARSER_MAX_RECURSIONS=16
INCLUDEPATH += ../../src
msvc: POST_TARGETDEPS = ../../lib/tinycbor.lib
else: POST_TARGETDEPS += ../../lib/libtinycbor.a
LIBS += $$POST_TARGETDEPS

File diff suppressed because it is too large Load Diff

@ -0,0 +1,3 @@
TEMPLATE = subdirs
SUBDIRS = parser encoder c90 cpp tojson
msvc: SUBDIRS -= tojson

@ -0,0 +1,8 @@
CONFIG += testcase parallel_test c++11
QT = core testlib
SOURCES += tst_tojson.cpp
INCLUDEPATH += ../../src
msvc: POST_TARGETDEPS = ../../lib/tinycbor.lib
else: POST_TARGETDEPS += ../../lib/libtinycbor.a
LIBS += $$POST_TARGETDEPS

@ -0,0 +1,721 @@
/****************************************************************************
**
** 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 <QtTest>
#include "cbor.h"
#include "cborjson.h"
#include <locale.h>
extern "C" FILE *open_memstream(char **bufptr, size_t *sizeptr);
class tst_ToJson : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void fixed_data();
void fixed();
void textstrings_data();
void textstrings() { fixed(); }
void nonjson_data();
void nonjson() { fixed(); }
void bytestrings_data();
void bytestrings() { fixed(); }
void emptyContainers_data();
void emptyContainers() { fixed(); }
void arrays_data();
void arrays();
void nestedArrays_data() { arrays_data(); }
void nestedArrays();
void maps_data() { arrays_data(); }
void maps();
void nestedMaps_data() { maps_data(); }
void nestedMaps();
void nonStringKeyMaps_data();
void nonStringKeyMaps();
void tagsToObjects_data();
void tagsToObjects();
void taggedByteStringsToBase16_data();
void taggedByteStringsToBase16();
void taggedByteStringsToBase64_data() { taggedByteStringsToBase16_data(); }
void taggedByteStringsToBase64();
void taggedByteStringsToBigNum_data() { taggedByteStringsToBase16_data(); }
void taggedByteStringsToBigNum();
void otherTags_data();
void otherTags();
void metaData_data();
void metaData();
void metaDataAndTagsToObjects_data() { tagsToObjects_data(); }
void metaDataAndTagsToObjects();
void metaDataForKeys_data();
void metaDataForKeys();
};
#include "tst_tojson.moc"
template <size_t N> QByteArray raw(const char (&data)[N])
{
return QByteArray::fromRawData(data, N - 1);
}
void addColumns()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QString>("expected");
}
void addFixedData()
{
// unsigned integers
QTest::newRow("0") << raw("\x00") << "0";
QTest::newRow("1") << raw("\x01") << "1";
QTest::newRow("2^53-1") << raw("\x1b\0\x1f\xff\xff""\xff\xff\xff\xff") << "9007199254740991";
QTest::newRow("2^64-epsilon") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xf8\x00") << "18446744073709549568";
// negative integers
QTest::newRow("-1") << raw("\x20") << "-1";
QTest::newRow("-2") << raw("\x21") << "-2";
QTest::newRow("-2^53+1") << raw("\x3b\0\x1f\xff\xff""\xff\xff\xff\xfe") << "-9007199254740991";
QTest::newRow("-2^64+epsilon") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xf8\x00") << "-18446744073709549568";
QTest::newRow("false") << raw("\xf4") << "false";
QTest::newRow("true") << raw("\xf5") << "true";
QTest::newRow("null") << raw("\xf6") << "null";
QTest::newRow("0.f16") << raw("\xf9\0\0") << "0";
QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "0";
QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "0";
QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "-1";
QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "-1";
QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "-1";
QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "16777215";
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";
QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "-16777215";
QTest::newRow("0.5f16") << raw("\xf9\x38\0") << "0.5";
QTest::newRow("0.5f") << raw("\xfa\x3f\0\0\0") << "0.5";
QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << "0.5";
QTest::newRow("2.f^24-1") << raw("\xfa\x4b\x7f\xff\xff") << "16777215";
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";
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+19";
QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << "1.8446744073709552e+19";
// infinities and NaN are not supported in JSON, they convert to null
QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "null";
QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "null";
QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "null";
QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "null";
QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "null";
QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "null";
QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "null";
QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "null";
QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "null";
}
void addTextStringsData()
{
QTest::newRow("emptytextstring") << raw("\x60") << "\"\"";
QTest::newRow("textstring1") << raw("\x61 ") << "\" \"";
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') + '"';
// strings with undefined length
QTest::newRow("_emptytextstring") << raw("\x7f\xff") << "\"\"";
QTest::newRow("_emptytextstring2") << raw("\x7f\x60\xff") << "\"\"";
QTest::newRow("_emptytextstring3") << raw("\x7f\x60\x60\xff") << "\"\"";
QTest::newRow("_textstring5*2") << raw("\x7f\x63Hel\x62lo\xff") << "\"Hello\"";
QTest::newRow("_textstring5*5") << raw("\x7f\x61H\x61""e\x61l\x61l\x61o\xff") << "\"Hello\"";
QTest::newRow("_textstring5*6") << raw("\x7f\x61H\x61""e\x61l\x60\x61l\x61o\xff") << "\"Hello\"";
}
void addNonJsonData()
{
QTest::newRow("undefined") << raw("\xf7") << "\"undefined\"";
QTest::newRow("simple0") << raw("\xe0") << "\"simple(0)\"";
QTest::newRow("simple19") << raw("\xf3") << "\"simple(19)\"";
QTest::newRow("simple32") << raw("\xf8\x20") << "\"simple(32)\"";
QTest::newRow("simple255") << raw("\xf8\xff") << "\"simple(255)\"";
}
void addByteStringsData()
{
QTest::newRow("emptybytestring") << raw("\x40") << "\"\"";
QTest::newRow("bytestring1") << raw("\x41 ") << "\"IA\"";
QTest::newRow("bytestring1-nul") << raw("\x41\0") << "\"AA\"";
QTest::newRow("bytestring2") << raw("\x42Hi") << "\"SGk\"";
QTest::newRow("bytestring3") << raw("\x43Hey") << "\"SGV5\"";
QTest::newRow("bytestring4") << raw("\x44Hola") << "\"SG9sYQ\"";
QTest::newRow("bytestring5") << raw("\x45Hello") << "\"SGVsbG8\"";
QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234")
<< "\"MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0\"";
// strings with undefined length
QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "\"\"";
QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "\"\"";
QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "\"\"";
QTest::newRow("_bytestring5*2") << raw("\x5f\x43Hel\x42lo\xff") << "\"SGVsbG8\"";
QTest::newRow("_bytestring5*5") << raw("\x5f\x41H\x41""e\x41l\x41l\x41o\xff") << "\"SGVsbG8\"";
QTest::newRow("_bytestring5*6") << raw("\x5f\x41H\x41""e\x40\x41l\x41l\x41o\xff") << "\"SGVsbG8\"";
}
void addEmptyContainersData()
{
QTest::newRow("emptyarray") << raw("\x80") << "[]";
QTest::newRow("emptymap") << raw("\xa0") << "{}";
QTest::newRow("_emptyarray") << raw("\x9f\xff") << "[]";
QTest::newRow("_emptymap") << raw("\xbf\xff") << "{}";
}
CborError parseOne(CborValue *it, QString *parsed, int flags)
{
char *buffer;
size_t size;
FILE *f = open_memstream(&buffer, &size);
CborError err = cbor_value_to_json_advance(f, it, flags);
fclose(f);
*parsed = QString::fromLatin1(buffer);
free(buffer);
return err;
}
bool compareFailed = true;
void compareOne_real(const QByteArray &data, const QString &expected, int flags, int line)
{
compareFailed = true;
CborParser parser;
CborValue first;
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\"");
QString decoded;
err = parseOne(&first, &decoded, flags);
QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) +
"\"; decoded stream:\n" + decoded.toLatin1());
QCOMPARE(decoded, expected);
// check that we consumed everything
QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd());
compareFailed = false;
}
#define compareOne(data, expected, flags) \
compareOne_real(data, expected, flags, __LINE__); \
if (compareFailed) return
void tst_ToJson::initTestCase()
{
setlocale(LC_ALL, "C");
}
void tst_ToJson::fixed_data()
{
addColumns();
addFixedData();
}
void tst_ToJson::fixed()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareOne(data, expected, 0);
}
void tst_ToJson::textstrings_data()
{
addColumns();
addTextStringsData();
}
void tst_ToJson::nonjson_data()
{
addColumns();
addNonJsonData();
}
void tst_ToJson::bytestrings_data()
{
addColumns();
addByteStringsData();
}
void tst_ToJson::emptyContainers_data()
{
addColumns();
addEmptyContainersData();
}
void tst_ToJson::arrays_data()
{
addColumns();
addFixedData();
addTextStringsData();
addNonJsonData();
addByteStringsData();
}
void tst_ToJson::arrays()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareOne("\x81" + data, '[' + expected + ']', 0);
compareOne("\x82" + data + data, '[' + expected + ',' + expected + ']', 0);
}
void tst_ToJson::nestedArrays()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareOne("\x81\x81" + data, "[[" + expected + "]]", 0);
compareOne("\x81\x81\x81" + data, "[[[" + expected + "]]]", 0);
compareOne("\x81\x82" + data + data, "[[" + expected + ',' + expected + "]]", 0);
compareOne("\x82\x81" + data + data, "[[" + expected + "]," + expected + "]", 0);
compareOne("\x82\x81" + data + '\x81' + data, "[[" + expected + "],[" + expected + "]]", 0);
}
void tst_ToJson::maps()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareOne("\xa1\x65" "Hello" + data, "{\"Hello\":" + expected + '}', 0);
}
void tst_ToJson::nestedMaps()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareOne("\xa1\x65Hello\xa1\x65World" + data, "{\"Hello\":{\"World\":" + expected + "}}", 0);
// compareOne("\xa1\x63""foo\xa1\63""bar" + data + "\63""baz\xa1\x64quux" + data,
// "{\"foo\":{\"bar\":" + expected + "},\"baz\":{\"quux\":" + expected + "}", 0);
}
void tst_ToJson::nonStringKeyMaps_data()
{
addColumns();
QTest::newRow("0") << raw("\x00") << "0";
QTest::newRow("1") << raw("\x01") << "1";
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<uint64_t>::max());
QTest::newRow("-1") << raw("\x20") << "-1";
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("-UINT64_MAX") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xfe")
<< '-' + QString::number(std::numeric_limits<uint64_t>::max());
QTest::newRow("-UINT64_MAX-1") << raw("\x3b" "\xff\xff\xff\xff" "\xff\xff\xff\xff")
<< "-18446744073709551616";
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)";
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";
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("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("tagUINT64_MAX") << raw("\xdb" "\xff\xff\xff\xff" "\xff\xff\xff\xff" "\x00")
<< QString::number(std::numeric_limits<uint64_t>::max()) + "(0)";
QTest::newRow("emptyarray") << raw("\x80") << "[]";
QTest::newRow("emptymap") << raw("\xa0") << "{}";
QTest::newRow("_emptyarray") << raw("\x9f\xff") << "[_ ]";
QTest::newRow("_emptymap") << raw("\xbf\xff") << "{_ }";
QTest::newRow("map-0-24") << raw("\xa1\0\x18\x18") << "{0: 24}";
QTest::newRow("map-24-0") << raw("\xa1\x18\x18\0") << "{24: 0}";
QTest::newRow("_map-0-24") << raw("\xbf\0\x18\x18\xff") << "{_ 0: 24}";
QTest::newRow("_map-24-0") << raw("\xbf\x18\x18\0\xff") << "{_ 24: 0}";
}
void tst_ToJson::nonStringKeyMaps()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
data = "\xa1" + data + "\1";
compareOne(data, "{\"" + expected + "\":1}", CborConvertStringifyMapKeys);
// and verify that they fail if we use CborConvertRequireMapStringKeys
CborParser parser;
CborValue first;
QString decoded;
cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
CborError err = parseOne(&first, &decoded, CborConvertRequireMapStringKeys);
QCOMPARE(err, CborErrorJsonObjectKeyNotString);
}
void tst_ToJson::tagsToObjects_data()
{
addColumns();
QTest::newRow("0(0)") << raw("\xc0\0") << "{\"tag0\":0}";
QTest::newRow("0(-1)") << raw("\xc0\x20") << "{\"tag0\":-1}";
QTest::newRow("0(\"hello\")") << raw("\xc0\x65hello") << "{\"tag0\":\"hello\"}";
QTest::newRow("22(h'48656c6c6f')") << raw("\xd6\x45Hello") << "{\"tag22\":\"SGVsbG8\"}";
QTest::newRow("0([1,2,3])") << raw("\xc0\x83\1\2\3") << "{\"tag0\":[1,2,3]}";
QTest::newRow("0({\"z\":true,\"y\":1})") << raw("\xc0\xa2\x61z\xf5\x61y\1") << "{\"tag0\":{\"z\":true,\"y\":1}}";
// large tags
QTest::newRow("55799(0)") << raw("\xd9\xd9\xf7\0") << "{\"tag55799\":0}";
QTest::newRow("4294967295") << raw("\xda\xff\xff\xff\xff\0") << "{\"tag4294967295\":0}";
QTest::newRow("18446744073709551615(0)") << raw("\xdb\xff\xff\xff\xff""\xff\xff\xff\xff\0")
<< "{\"tag18446744073709551615\":0}";
// nested tags
QTest::newRow("0(1(2))") << raw("\xc0\xc1\2") << "{\"tag0\":{\"tag1\":2}}";
QTest::newRow("0({\"z\":1(2)})") << raw("\xc0\xa1\x61z\xc1\2") << "{\"tag0\":{\"z\":{\"tag1\":2}}}";
}
void tst_ToJson::tagsToObjects()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareOne(data, expected, CborConvertTagsToObjects);
}
void tst_ToJson::taggedByteStringsToBase16_data()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QString>("base64url");
QTest::addColumn<QString>("base64");
QTest::addColumn<QString>("base16");
QTest::newRow("emptybytestring") << raw("\x40") << "" << "" << "";
QTest::newRow("bytestring1") << raw("\x41 ") << "IA" << "IA==" << "20";
QTest::newRow("bytestring1-nul") << raw("\x41\0") << "AA" << "AA==" << "00";
QTest::newRow("bytestring1-ff") << raw("\x41\xff") << "_w" << "/w==" << "ff";
QTest::newRow("bytestring2") << raw("\x42Hi") << "SGk" << "SGk=" << "4869";
QTest::newRow("bytestring3") << raw("\x43Hey") << "SGV5" << "SGV5" << "486579";
QTest::newRow("bytestring4") << raw("\x44Hola") << "SG9sYQ" << "SG9sYQ==" << "486f6c61";
QTest::newRow("bytestring5") << raw("\x45Hello") << "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
QTest::newRow("bytestring24") << raw("\x58\x18""123456789012345678901234")
<< "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0"
<< "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0"
<< "313233343536373839303132333435363738393031323334";
// strings with undefined length
QTest::newRow("_emptybytestring") << raw("\x5f\xff") << "" << "" << "";
QTest::newRow("_emptybytestring2") << raw("\x5f\x40\xff") << "" << "" << "";
QTest::newRow("_emptybytestring3") << raw("\x5f\x40\x40\xff") << "" << "" << "";
QTest::newRow("_bytestring5*2") << raw("\x5f\x43Hel\x42lo\xff") << "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
QTest::newRow("_bytestring5*5") << raw("\x5f\x41H\x41""e\x41l\x41l\x41o\xff")
<< "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
QTest::newRow("_bytestring5*6") << raw("\x5f\x41H\x41""e\x40\x41l\x41l\x41o\xff")
<< "SGVsbG8" << "SGVsbG8=" << "48656c6c6f";
}
void tst_ToJson::taggedByteStringsToBase16()
{
QFETCH(QByteArray, data);
QFETCH(QString, base16);
compareOne('\xd7' + data, '"' + base16 + '"', 0);
}
void tst_ToJson::taggedByteStringsToBase64()
{
QFETCH(QByteArray, data);
QFETCH(QString, base64);
compareOne('\xd6' + data, '"' + base64 + '"', 0);
}
void tst_ToJson::taggedByteStringsToBigNum()
{
QFETCH(QByteArray, data);
QFETCH(QString, base64url);
compareOne('\xc3' + data, "\"~" + base64url + '"', 0);
}
void tst_ToJson::otherTags_data()
{
addColumns();
addFixedData();
addTextStringsData();
addNonJsonData();
addByteStringsData();
addEmptyContainersData();
}
void tst_ToJson::otherTags()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
// other tags produce no change in output
compareOne("\xc0" + data, expected, 0);
compareOne("\xc1" + data, expected, 0);
compareOne("\xc2" + data, expected, 0);
compareOne("\xc4" + data, expected, 0);
compareOne("\xc5" + data, expected, 0);
compareOne("\xd8\x20" + data, expected, 0);
compareOne("\xd8\x21" + data, expected, 0);
compareOne("\xd8\x22" + data, expected, 0);
compareOne("\xd8\x23" + data, expected, 0);
compareOne("\xd8\x24" + data, expected, 0);
compareOne("\xd9\xd9\xf7" + data, expected, 0);
}
void tst_ToJson::metaData_data()
{
addColumns();
// booleans, null, strings, double precision numbers, regular maps, arrays and integers that
// didn't get rounded don't have metadata
QTest::newRow("0") << raw("\x00") << QString();
QTest::newRow("1") << raw("\x01") << QString();
QTest::newRow("2^53-1") << raw("\x1b\0\x1f\xff\xff""\xff\xff\xff\xff") << QString();
QTest::newRow("2^64-epsilon") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xf8\x00") << QString();
QTest::newRow("-1") << raw("\x20") << QString();
QTest::newRow("-2") << raw("\x21") << QString();
QTest::newRow("-2^53+1") << raw("\x3b\0\x1f\xff\xff""\xff\xff\xff\xfe") << QString();
QTest::newRow("-2^64+epsilon") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xf8\x00") << QString();
QTest::newRow("emptytextstring") << raw("\x60") << QString();
QTest::newRow("textstring1") << raw("\x61 ") << QString();
QTest::newRow("0.5") << raw("\xfb\x3f\xe0\0\0\0\0\0\0") << QString();
QTest::newRow("2.^64") << raw("\xfb\x43\xf0\0\0\0\0\0\0") << QString();
QTest::newRow("false") << raw("\xf4") << QString();
QTest::newRow("true") << raw("\xf5") << QString();
QTest::newRow("null") << raw("\xf6") << QString();
QTest::newRow("emptyarray") << raw("\x80") << QString();
QTest::newRow("emptymap") << raw("\xa0") << QString();
QTest::newRow("array*1") << raw("\x81\xf6") << QString();
QTest::newRow("map*1") << raw("\xa1\x61z\xf4") << QString();
// ---- everything from here on has at least the type ----
QTest::newRow("emptybytestring") << raw("\x40") << "\"t\":64";
QTest::newRow("bytestring1") << raw("\x41 ") << "\"t\":64";
QTest::newRow("undefined") << raw("\xf7") << "\"t\":247";
QTest::newRow("0.f16") << raw("\xf9\0\0") << "\"t\":249";
QTest::newRow("-1.f16") << raw("\xf9\xbc\x00") << "\"t\":249";
QTest::newRow("0.f") << raw("\xfa\0\0\0\0") << "\"t\":250";
QTest::newRow("-1.f") << raw("\xfa\xbf\x80\0\0") << "\"t\":250";
QTest::newRow("16777215.f") << raw("\xfa\x4b\x7f\xff\xff") << "\"t\":250";
QTest::newRow("-16777215.f") << raw("\xfa\xcb\x7f\xff\xff") << "\"t\":250";
QTest::newRow("0.") << raw("\xfb\0\0\0\0\0\0\0\0") << "\"t\":251";
QTest::newRow("-1.") << raw("\xfb\xbf\xf0\0\0\0\0\0\0") << "\"t\":251";
QTest::newRow("16777215.") << raw("\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "\"t\":251";
QTest::newRow("-16777215.") << raw("\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "\"t\":251";
QTest::newRow("2.^53-1") << raw("\xfb\x43\x3f\xff\xff""\xff\xff\xff\xff") << "\"t\":251";
QTest::newRow("2.^64-epsilon") << raw("\xfb\x43\xef\xff\xff""\xff\xff\xff\xff") << "\"t\":251";
// integers that are too precise for double
QTest::newRow("2^53+1") << raw("\x1b\0\x20\0\0""\0\0\0\1")
<< "\"t\":0,\"v\":\"+20000000000001\"";
QTest::newRow("INT64_MAX-1") << raw("\x1b\x7f\xff\xff\xff""\xff\xff\xff\xfe")
<< "\"t\":0,\"v\":\"+7ffffffffffffffe\"";
QTest::newRow("INT64_MAX+1") << raw("\x1b\x80\0\0\0""\0\0\0\1")
<< "\"t\":0,\"v\":\"+8000000000000001\"";
QTest::newRow("-2^53-1") << raw("\x3b\0\x20\0\0""\0\0\0\0")
<< "\"t\":0,\"v\":\"-20000000000000\"";
// simple values
QTest::newRow("simple0") << raw("\xe0") << "\"t\":224,\"v\":0";
QTest::newRow("simple19") << raw("\xf3") << "\"t\":224,\"v\":19";
QTest::newRow("simple32") << raw("\xf8\x20") << "\"t\":224,\"v\":32";
QTest::newRow("simple255") << raw("\xf8\xff") << "\"t\":224,\"v\":255";
// infinities and NaN are not supported in JSON, they convert to null
QTest::newRow("nan_f16") << raw("\xf9\x7e\x00") << "\"t\":249,\"v\":\"nan\"";
QTest::newRow("nan_f") << raw("\xfa\x7f\xc0\0\0") << "\"t\":250,\"v\":\"nan\"";
QTest::newRow("nan") << raw("\xfb\x7f\xf8\0\0\0\0\0\0") << "\"t\":251,\"v\":\"nan\"";
QTest::newRow("-inf_f16") << raw("\xf9\xfc\x00") << "\"t\":249,\"v\":\"-inf\"";
QTest::newRow("-inf_f") << raw("\xfa\xff\x80\0\0") << "\"t\":250,\"v\":\"-inf\"";
QTest::newRow("-inf") << raw("\xfb\xff\xf0\0\0\0\0\0\0") << "\"t\":251,\"v\":\"-inf\"";
QTest::newRow("+inf_f16") << raw("\xf9\x7c\x00") << "\"t\":249,\"v\":\"inf\"";
QTest::newRow("+inf_f") << raw("\xfa\x7f\x80\0\0") << "\"t\":250,\"v\":\"inf\"";
QTest::newRow("+inf") << raw("\xfb\x7f\xf0\0\0\0\0\0\0") << "\"t\":251,\"v\":\"inf\"";
// tags on native types
QTest::newRow("tag+0") << raw("\xc0\x00") << "\"tag\":\"0\"";
QTest::newRow("tag+-2") << raw("\xc0\x21") << "\"tag\":\"0\"";
QTest::newRow("tag+0.5") << raw("\xc0\xfb\x3f\xe0\0\0\0\0\0\0") << "\"tag\":\"0\"";
QTest::newRow("tag+emptytextstring") << raw("\xc0\x60") << "\"tag\":\"0\"";
QTest::newRow("tag+textstring1") << raw("\xc0\x61 ") << "\"tag\":\"0\"";
QTest::newRow("tag+false") << raw("\xc0\xf4") << "\"tag\":\"0\"";
QTest::newRow("tag+true") << raw("\xc0\xf5") << "\"tag\":\"0\"";
QTest::newRow("tag+null") << raw("\xc0\xf6") << "\"tag\":\"0\"";
QTest::newRow("tag+emptyarray") << raw("\xc0\x80") << "\"tag\":\"0\"";
QTest::newRow("tag+emptymap") << raw("\xc0\xa0") << "\"tag\":\"0\"";
QTest::newRow("tag+array*1") << raw("\xc0\x81\xf6") << "\"tag\":\"0\"";
QTest::newRow("tag+map*1") << raw("\xc0\xa1\x61z\xf4") << "\"tag\":\"0\"";
// tags on non-native types
QTest::newRow("tag+emptybytestring") << raw("\xc0\x40") << "\"tag\":\"0\",\"t\":64";
QTest::newRow("tag+bytestring1") << raw("\xc0\x41 ") << "\"tag\":\"0\",\"t\":64";
QTest::newRow("tag+undefined") << raw("\xc0\xf7") << "\"tag\":\"0\",\"t\":247";
QTest::newRow("tag+0.f") << raw("\xc0\xfa\0\0\0\0") << "\"tag\":\"0\",\"t\":250";
QTest::newRow("tag+-1.f") << raw("\xc0\xfa\xbf\x80\0\0") << "\"tag\":\"0\",\"t\":250";
QTest::newRow("tag+16777215.f") << raw("\xc0\xfa\x4b\x7f\xff\xff") << "\"tag\":\"0\",\"t\":250";
QTest::newRow("tag+-16777215.f") << raw("\xc0\xfa\xcb\x7f\xff\xff") << "\"tag\":\"0\",\"t\":250";
QTest::newRow("tag+0.") << raw("\xc0\xfb\0\0\0\0\0\0\0\0") << "\"tag\":\"0\",\"t\":251";
QTest::newRow("tag+-1.") << raw("\xc0\xfb\xbf\xf0\0\0\0\0\0\0") << "\"tag\":\"0\",\"t\":251";
QTest::newRow("tag+16777215.") << raw("\xc0\xfb\x41\x6f\xff\xff\xe0\0\0\0") << "\"tag\":\"0\",\"t\":251";
QTest::newRow("tag+-16777215.") << raw("\xc0\xfb\xc1\x6f\xff\xff\xe0\0\0\0") << "\"tag\":\"0\",\"t\":251";
// big tags (don't fit in JS numbers)
QTest::newRow("bigtag1") << raw("\xdb\0\x20\0\0""\0\0\0\1\x60") << "\"tag\":\"9007199254740993\"";
QTest::newRow("bigtag2") << raw("\xdb\xff\xff\xff\xff""\xff\xff\xff\xfe\x60")
<< "\"tag\":\"18446744073709551614\"";
// specially-handled tags
QTest::newRow("negativebignum") << raw("\xc3\x41 ") << "\"tag\":\"3\",\"t\":64";
QTest::newRow("base64") << raw("\xd6\x41 ") << "\"tag\":\"22\",\"t\":64";
QTest::newRow("base16") << raw("\xd7\x41 ") << "\"tag\":\"23\",\"t\":64";
}
void compareMetaData(QByteArray data, const QString &expected, int otherFlags = 0)
{
QString decoded;
// needs to be in one map, with the entry called "v"
data = "\xa1\x61v" + data;
{
CborParser parser;
CborValue first;
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArrayLiteral(": Got error \"") + cbor_error_string(err) + "\"");
err = parseOne(&first, &decoded, CborConvertAddMetadata | otherFlags);
QVERIFY2(!err, QByteArrayLiteral(": Got error \"") + cbor_error_string(err) +
"\"; decoded stream:\n" + decoded.toLatin1());
// check that we consumed everything
QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd());
}
QVERIFY(decoded.startsWith("{\"v\":"));
QVERIFY(decoded.endsWith('}'));
// qDebug() << "was" << decoded;
// extract just the metadata
static const char needle[] = "\"v$cbor\":{";
int pos = decoded.indexOf(needle);
QCOMPARE(pos == -1, expected.isEmpty());
if (pos != -1) {
decoded.chop(2);
decoded = std::move(decoded).mid(pos + strlen(needle));
QCOMPARE(decoded, expected);
}
}
void tst_ToJson::metaData()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
compareMetaData(data, expected);
}
void tst_ToJson::metaDataAndTagsToObjects()
{
QFETCH(QByteArray, data);
// when a tag is converted to an object, the object gets metadata indicating it was a tag
compareMetaData(data, "\"t\":192", CborConvertTagsToObjects);
}
void tst_ToJson::metaDataForKeys_data()
{
nonStringKeyMaps_data();
// string keys generate no metadata
QTest::newRow("string") << raw("\x60") << QString();
}
void tst_ToJson::metaDataForKeys()
{
QFETCH(QByteArray, data);
QFETCH(QString, expected);
if (expected.isEmpty())
expected = "{\"\":false}";
else
expected = "{\"" + expected + "\":false,\"" + expected + "$keycbordump\":true}";
compareOne('\xa1' + data + '\xf4', expected,
CborConvertAddMetadata | CborConvertStringifyMapKeys);
}
QTEST_MAIN(tst_ToJson)

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: TinyCBOR
Description: A tiny CBOR encoder and decoder library
Version: @version@
Libs: -L${libdir} -ltinycbor
Libs.private: -lm
Cflags: -I${includedir}/tinycbor

@ -0,0 +1,164 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#define _POSIX_C_SOURCE 200809L
#include "cbor.h"
#include "cborjson.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void *xrealloc(void *old, size_t size, const char *fname)
{
old = realloc(old, size);
if (old == NULL) {
fprintf(stderr, "%s: %s\n", fname, strerror(errno));
exit(EXIT_FAILURE);
}
return old;
}
void printerror(CborError err, const char *fname)
{
fprintf(stderr, "%s: %s\n", fname, cbor_error_string(err));
exit(EXIT_FAILURE);
}
void dumpFile(FILE *in, const char *fname, bool printJosn, int flags)
{
static const size_t chunklen = 16 * 1024;
static size_t bufsize = 0;
static uint8_t *buffer = NULL;
size_t buflen = 0;
do {
if (bufsize == buflen)
buffer = xrealloc(buffer, bufsize += chunklen, fname);
size_t n = fread(buffer + buflen, 1, bufsize - buflen, in);
buflen += n;
if (n == 0) {
if (!ferror(in))
continue;
fprintf(stderr, "%s: %s\n", fname, strerror(errno));
exit(EXIT_FAILURE);
}
} while (!feof(in));
CborParser parser;
CborValue value;
CborError err = cbor_parser_init(buffer, buflen, 0, &parser, &value);
if (!err) {
if (printJosn)
err = cbor_value_to_json_advance(stdout, &value, flags);
else
err = cbor_value_to_pretty_advance_flags(stdout, &value, flags);
if (!err)
puts("");
}
if (!err && cbor_value_get_next_byte(&value) != buffer + buflen)
err = CborErrorGarbageAtEnd;
if (err)
printerror(err, fname);
}
int main(int argc, char **argv)
{
bool printJson = false;
int json_flags = CborConvertDefaultFlags;
int cbor_flags = CborPrettyDefaultFlags;
int c;
while ((c = getopt(argc, argv, "MOSUcjhfn")) != -1) {
switch (c) {
case 'c':
printJson = false;
break;
case 'j':
printJson = true;
break;
case 'f':
cbor_flags |= CborPrettyShowStringFragments;
break;
case 'n':
cbor_flags |= CborPrettyIndicateIndeterminateLength | CborPrettyNumericEncodingIndicators;
break;
case 'M':
json_flags |= CborConvertAddMetadata;
break;
case 'O':
json_flags |= CborConvertTagsToObjects;
break;
case 'S':
json_flags |= CborConvertStringifyMapKeys;
break;
case 'U':
json_flags |= CborConvertByteStringsToBase64Url;
break;
case '?':
fprintf(stderr, "Unknown option -%c.\n", optopt);
/* fall through */
case 'h':
puts("Usage: cbordump [OPTION]... [FILE]...\n"
"Interprets FILEs as CBOR binary data and dumps the content to stdout.\n"
"\n"
"Options:\n"
" -c Print a CBOR dump (see RFC 7049) (default)\n"
" -j Print a JSON equivalent version\n"
" -h Print this help output and exit\n"
"When JSON output is active, the following options are recognized:\n"
" -M Add metadata so converting back to CBOR is possible\n"
" -O Convert CBOR tags to JSON objects\n"
" -S Stringify non-text string map keys\n"
" -U Convert all CBOR byte strings to Base64url regardless of tags\n"
"When CBOR dump is active, the following options are recognized:\n"
" -f Show text and byte string fragments\n"
" -n Show overlong encoding of CBOR numbers and length"
"");
return c == '?' ? EXIT_FAILURE : EXIT_SUCCESS;
}
}
char **fname = argv + optind;
if (!*fname) {
dumpFile(stdin, "-", printJson, printJson ? json_flags : cbor_flags);
} else {
for ( ; *fname; ++fname) {
FILE *in = fopen(*fname, "rb");
if (!in) {
perror("open");
return EXIT_FAILURE;
}
dumpFile(in, *fname, printJson, printJson ? json_flags : cbor_flags);
fclose(in);
}
}
return EXIT_SUCCESS;
}

@ -0,0 +1,10 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
DESTDIR = ../../bin
CBORDIR = $$PWD/../../src
INCLUDEPATH += $$CBORDIR
SOURCES += cbordump.c
LIBS += ../../lib/libtinycbor.a

@ -0,0 +1,493 @@
/****************************************************************************
**
** Copyright (C) 2015 Intel Corporation
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
** in the Software without restriction, including without limitation the rights
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the Software is
** furnished to do so, subject to the following conditions:
**
** The above copyright notice and this permission notice shall be included in
** all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
** THE SOFTWARE.
**
****************************************************************************/
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include "cbor.h"
#include "cborinternal_p.h"
#include "compilersupport_p.h"
#include <cjson/cJSON.h>
#include <errno.h>
#include <math.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static const char meta_data_marker[] = "$cbor";
uint8_t *buffer;
size_t buffersize;
bool usingMetaData = false;
struct MetaData {
CborTag tag;
union {
const char *v;
uint8_t simpleType;
};
CborType t;
bool tagged;
};
uint8_t *decode_base64_generic(const char *string, size_t *len, const int8_t reverse_alphabet[256])
{
*len = ((strlen(string) + 3) & ~3) * 3 / 4;
uint8_t *buffer = malloc(*len);
if (buffer == NULL)
return NULL;
uint8_t *out = buffer;
const uint8_t *in = (const uint8_t *)string;
bool done = false;
while (!done) {
if (reverse_alphabet[in[0]] < 0 || reverse_alphabet[in[1]] < 0) {
if (in[0] == '\0')
done = true;
break;
}
uint32_t val = reverse_alphabet[in[0]] << 18;
val |= reverse_alphabet[in[1]] << 12;
if (in[2] == '=' || in[2] == '\0') {
if (in[2] == '=' && (in[3] != '=' || in[4] != '\0'))
break;
val >>= 12;
done = true;
} else if (in[3] == '=' || in[3] == '\0') {
if (in[3] == '=' && in[4] != '\0')
break;
val >>= 6;
val |= reverse_alphabet[in[2]];
done = true;
} else {
val |= reverse_alphabet[in[2]] << 6;
val |= reverse_alphabet[in[3]];
}
*out++ = val >> 16;
*out++ = val >> 8;
*out++ = val;
in += 4;
}
if (!done) {
free(buffer);
return NULL;
}
*len = out - buffer;
return buffer;
}
uint8_t *decode_base64(const char *string, size_t *len)
{
static const int8_t reverse_alphabet[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
return decode_base64_generic(string, len, reverse_alphabet);
}
uint8_t *decode_base64url(const char *string, size_t *len)
{
static const int8_t reverse_alphabet[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
return decode_base64_generic(string, len, reverse_alphabet);
}
uint8_t *decode_base16(const char *string, size_t *len)
{
size_t i;
*len = strlen(string) / 2;
uint8_t *buffer = malloc(*len);
if (buffer == NULL)
return NULL;
for (i = 0; i < *len; ++i) {
char c = string[i * 2];
if (c >= '0' && c <= '9') {
buffer[i] = (c - '0') << 4;
} else if ((c | 0x20) >= 'a' && (c | 0x20) <= 'f') {
buffer[i] = ((c | 0x20) - 'a' + 10) << 4;
} else {
free(buffer);
return NULL;
}
c = string[i * 2 + 1];
if (c >= '0' && c <= '9') {
buffer[i] |= (c - '0');
} else if ((c | 0x20) >= 'a' && (c | 0x20) <= 'f') {
buffer[i] |= ((c | 0x20) - 'a' + 10);
} else {
free(buffer);
return NULL;
}
}
return buffer;
}
size_t get_cjson_size_limited(cJSON *container)
{
// cJSON_GetArraySize is O(n), so don't go too far
unsigned s = 0;
cJSON *item;
for (item = container->child; item; item = item->next) {
if (++s > 255)
return CborIndefiniteLength;
}
return s;
}
cJSON *get_meta_data(cJSON *object, cJSON *item)
{
cJSON *meta;
char *metadatakey;
if (asprintf(&metadatakey, "%s%s", item->string, meta_data_marker) < 0 || metadatakey == NULL)
return NULL;
meta = cJSON_GetObjectItem(object, metadatakey);
free(metadatakey);
return meta;
}
struct MetaData parse_meta_data(cJSON *md)
{
struct MetaData result = { 0, {NULL}, CborInvalidType, false };
if (md == NULL || md->type != cJSON_Object)
return result;
for (md = md->child; md; md = md->next) {
if (strcmp(md->string, "tag") == 0) {
if (md->type != cJSON_String || sscanf(md->valuestring, "%" PRIu64, &result.tag) < 0)
fprintf(stderr, "json2cbor: could not parse tag: %s\n", md->valuestring);
else
result.tagged = true;
} else if (strcmp(md->string, "t") == 0) {
result.t = md->valueint;
} else if (strcmp(md->string, "v") == 0) {
if (md->type == cJSON_Number)
result.simpleType = md->valueint;
else
result.v = md->valuestring;
}
}
return result;
}
CborError decode_json(cJSON *json, CborEncoder *encoder);
CborError decode_json_with_metadata(cJSON *item, CborEncoder *encoder, struct MetaData md)
{
switch (md.t) {
case CborIntegerType: {
// integer that has more than 53 bits of precision
uint64_t v;
bool positive = *md.v++ == '+';
if (sscanf(md.v, "%" PRIx64, &v) < 0) {
fprintf(stderr, "json2cbor: could not parse number: %s\n", md.v);
break;
}
return positive ? cbor_encode_uint(encoder, v) : cbor_encode_negative_int(encoder, v);
}
case CborByteStringType: {
uint8_t *data;
size_t len;
if (md.tag == CborExpectedBase64Tag)
data = decode_base64(item->valuestring, &len);
else if (md.tag == CborExpectedBase16Tag)
data = decode_base16(item->valuestring, &len);
else if (md.tag == CborNegativeBignumTag)
data = decode_base64url(item->valuestring + 1, &len);
else
data = decode_base64url(item->valuestring, &len);
if (data != NULL) {
CborError err = cbor_encode_byte_string(encoder, data, len);
free(data);
return err;
}
fprintf(stderr, "json2cbor: could not decode encoded byte string: %s\n", item->valuestring);
break;
}
case CborSimpleType:
return cbor_encode_simple_value(encoder, md.simpleType);
case CborUndefinedType:
return cbor_encode_undefined(encoder);
case CborHalfFloatType:
case CborFloatType:
case CborDoubleType: {
unsigned short half;
double v;
if (!md.v) {
v = item->valuedouble;
} else if (strcmp(md.v, "nan") == 0) {
v = NAN;
} else if (strcmp(md.v, "-inf") == 0) {
v = -INFINITY;
} else if (strcmp(md.v, "inf") == 0) {
v = INFINITY;
} else {
fprintf(stderr, "json2cbor: invalid floating-point value: %s\n", md.v);
break;
}
// we can't get an OOM here because the metadata makes up for space
// (the smallest metadata is "$cbor":{"t":250} (17 bytes)
return (md.t == CborDoubleType) ? cbor_encode_double(encoder, v) :
(md.t == CborFloatType) ? cbor_encode_float(encoder, v) :
(half = encode_half(v), cbor_encode_half_float(encoder, &half));
}
default:
fprintf(stderr, "json2cbor: invalid CBOR type: %d\n", md.t);
case CborInvalidType:
break;
}
return decode_json(item, encoder);
}
CborError decode_json(cJSON *json, CborEncoder *encoder)
{
CborEncoder container;
CborError err;
cJSON *item;
switch (json->type) {
case cJSON_False:
case cJSON_True:
return cbor_encode_boolean(encoder, json->type == cJSON_True);
case cJSON_NULL:
return cbor_encode_null(encoder);
case cJSON_Number:
if ((double)json->valueint == json->valuedouble)
return cbor_encode_int(encoder, json->valueint);
encode_double:
// the only exception that JSON is larger: floating point numbers
container = *encoder; // save the state
err = cbor_encode_double(encoder, json->valuedouble);
if (err == CborErrorOutOfMemory) {
buffersize += 1024;
uint8_t *newbuffer = realloc(buffer, buffersize);
if (newbuffer == NULL)
return err;
*encoder = container; // restore state
encoder->data.ptr = newbuffer + (container.data.ptr - buffer);
encoder->end = newbuffer + buffersize;
buffer = newbuffer;
goto encode_double;
}
return err;
case cJSON_String:
return cbor_encode_text_stringz(encoder, json->valuestring);
default:
return CborErrorUnknownType;
case cJSON_Array:
err = cbor_encoder_create_array(encoder, &container, get_cjson_size_limited(json));
if (err)
return err;
for (item = json->child; item; item = item->next) {
err = decode_json(item, &container);
if (err)
return err;
}
return cbor_encoder_close_container_checked(encoder, &container);
case cJSON_Object:
err = cbor_encoder_create_map(encoder, &container,
usingMetaData ? CborIndefiniteLength : get_cjson_size_limited(json));
if (err)
return err;
for (item = json->child ; item; item = item->next) {
if (usingMetaData && strlen(item->string) > strlen(meta_data_marker)
&& strcmp(item->string + strlen(item->string) - strlen(meta_data_marker), meta_data_marker) == 0)
continue;
err = cbor_encode_text_stringz(&container, item->string);
if (err)
return err;
if (usingMetaData) {
cJSON *meta = get_meta_data(json, item);
struct MetaData md = parse_meta_data(meta);
if (md.tagged) {
err = cbor_encode_tag(&container, md.tag);
if (err)
return err;
}
err = decode_json_with_metadata(item, &container, md);
} else {
err = decode_json(item, &container);
}
if (err)
return err;
}
return cbor_encoder_close_container_checked(encoder, &container);
}
}
int main(int argc, char **argv)
{
int c;
while ((c = getopt(argc, argv, "M")) != -1) {
switch (c) {
case 'M':
usingMetaData = true;
break;
case '?':
fprintf(stderr, "Unknown option -%c.\n", optopt);
// fall through
case 'h':
puts("Usage: json2cbor [OPTION]... [FILE]...\n"
"Reads JSON content from FILE and converts to CBOR.\n"
"\n"
"Options:\n"
" -M Interpret metadata added by cbordump tool\n"
"");
return c == '?' ? EXIT_FAILURE : EXIT_SUCCESS;
}
}
FILE *in;
const char *fname = argv[optind];
if (fname && strcmp(fname, "-") != 0) {
in = fopen(fname, "r");
if (!in) {
perror("open");
return EXIT_FAILURE;
}
} else {
in = stdin;
fname = "-";
}
/* 1. read the file */
off_t fsize;
if (fseeko(in, 0, SEEK_END) == 0 && (fsize = ftello(in)) >= 0) {
buffersize = fsize + 1;
buffer = malloc(buffersize);
if (buffer == NULL) {
perror("malloc");
return EXIT_FAILURE;
}
rewind(in);
fsize = fread(buffer, 1, fsize, in);
buffer[fsize] = '\0';
} else {
const unsigned chunk = 16384;
buffersize = 0;
buffer = NULL;
do { // it the hard way
buffer = realloc(buffer, buffersize + chunk);
if (buffer == NULL) {
perror("malloc");
return EXIT_FAILURE;
}
buffersize += fread(buffer + buffersize, 1, chunk, in);
} while (!feof(in) && !ferror(in));
buffer[buffersize] = '\0';
}
if (ferror(in)) {
perror("read");
return EXIT_FAILURE;
}
if (in != stdin)
fclose(in);
/* 2. parse as JSON */
cJSON *doc = cJSON_ParseWithOpts((char *)buffer, NULL, true);
if (doc == NULL) {
fprintf(stderr, "json2cbor: %s: could not parse.\n", fname);
return EXIT_FAILURE;
}
/* 3. encode as CBOR */
// We're going to reuse the buffer, as CBOR is usually shorter than the equivalent JSON
CborEncoder encoder;
cbor_encoder_init(&encoder, buffer, buffersize, 0);
CborError err = decode_json(doc, &encoder);
cJSON_Delete(doc);
if (err) {
fprintf(stderr, "json2cbor: %s: error encoding to CBOR: %s\n", fname,
cbor_error_string(err));
return EXIT_FAILURE;
}
fwrite(buffer, 1, encoder.data.ptr - buffer, stdout);
free(buffer);
return EXIT_SUCCESS;
}

@ -0,0 +1,20 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
DESTDIR = ../../bin
CBORDIR = $$PWD/../../src
INCLUDEPATH += $$CBORDIR
SOURCES += json2cbor.c
LIBS += ../../lib/libtinycbor.a
CJSONDIR = .
!exists($$CJSONDIR/cJSON.h): CJSONDIR = $$CBORDIR/cjson
exists($$CJSONDIR/cJSON.h) {
INCLUDEPATH += $$CJSONDIR
SOURCES += $$CJSONDIR/cJSON.c
} else {
message("cJSON not found, not building json2cbor.")
TEMPLATE = aux
}

@ -8,6 +8,7 @@ set(COMPONENTS "")
# External dependencies
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/catch2")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/cbor")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/libmad")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/lvgl")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/result")

Loading…
Cancel
Save