Merge branch 'main' into dev
diff --git a/.appveyor.yml b/.appveyor.yml index f673552..46876a8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml
@@ -1,4 +1,4 @@ -version: 0.6-build-{build} +version: build-{build} pull_requests: do_not_increment_build_number: true image: @@ -13,16 +13,18 @@ set path=%PATH%;%QTDIR%\bin build_script: - cmd: >- - nmake -f Makefile.nmake -nologo CFLAGS="-W3 -Os -MDd" + cmake -S. -Bbuild -GNinja -DWITH_CBOR2JSON=OFF -DBUILD_TESTING=ON -DCMAKE_C_FLAGS="-W3 -Os -MDd" -DCMAKE_CXX_FLAGS="-W3 -O2 -MDd" - cd tests + ninja -C build - qmake CONFIG-=release CONFIG+=debug - - nmake -nologo -s test_script: - cmd: >- - nmake -s -nologo TESTARGS=-silent check + ctest --test-dir build --output-on-failure --output-junit ctest.junitxml + +after_test: +- cmd: >- + curl -F file=@build/ctest.junitxml https://ci.appveyor.com/api/testresults/junit/%APPVEYOR_JOB_ID% + artifacts: -- path: lib\tinycbor.lib +#- path: build\tinycbor.lib deploy: off
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 998935a..1951296 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml
@@ -27,59 +27,68 @@ fail-fast: false matrix: os: [ ubuntu-latest ] - build_cfg: [ - { "name": "gcc-no-math", - "flags": - '{ "QMAKESPEC": "linux-gcc-no-math", - "EVAL": "export CXX=false && touch math.h float.h", - "CFLAGS": "-ffreestanding -DCBOR_NO_FLOATING_POINT -Os", - "LDFLAGS": "-Wl,--no-undefined", - "LDLIBS": "" - }', - }, - { "name": "gcc-freestanding", - "flags": - '{ "QMAKESPEC": "linux-gcc-freestanding", - "EVAL": "export CXX=false", - "CFLAGS": "-ffreestanding -Os", - "LDFLAGS": "-Wl,--no-undefined -lm" - }', - }, - { "name": "clang", - "flags": - '{ "QMAKESPEC": "linux-clang", - "EVAL": "export CC=clang && export CXX=clang++", - "CFLAGS": "-Oz", - "LDFLAGS": "-Wl,--no-undefined -lm", - "QMAKEFLAGS": "-config release", - "MAKEFLAGS": "-s", - "TESTARGS": "-silent" - }', - }, - { "name": "linux-g++", - "flags": - '{ "QMAKESPEC": "linux-g++", - "EVAL": "export CC=gcc && export CXX=g++", - "CFLAGS": "-Os", - "LDFLAGS": "-Wl,--no-undefined -lm", - "QMAKEFLAGS": "-config release", - "QT_NO_CPU_FEATURE": "rdrnd" - }' - } - ] + build_cfg: + - name: gcc-no-math + cmakeflags: >- + -DCMAKE_C_FLAGS="-Os -Werror" + -DWITH_FLOATING_POINT=OFF + -DWITH_FREESTANDING=ON + - name: gcc-freestanding + cmakeflags: >- + -DCMAKE_C_FLAGS="-Os -Werror" + -DWITH_FREESTANDING=ON + - name: gcc-small + cmakeflags: >- + -DBUILD_TESTING=OFF + -DCMAKE_C_FLAGS="-Os -Werror" + - name: clang-small + cmakeflags: >- + -DBUILD_TESTING=OFF + -DCMAKE_C_COMPILER=clang + -DCMAKE_C_FLAGS="-Oz -g -Werror" + - name: clang + cmakeflags: >- + -DBUILD_EXAMPLES=ON + -DBUILD_SHARED_LIBS=ON + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_C_COMPILER=clang + -DCMAKE_C_FLAGS_DEBUG="-Werror" + -DCMAKE_CXX_COMPILER=clang++ + -DCMAKE_CXX_FLAGS_DEBUG="-Werror" + - name: linux-g++ + cmakeflags: >- + -DBUILD_EXAMPLES=ON + -DBUILD_SHARED_LIBS=ON + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_C_COMPILER=gcc + -DCMAKE_C_FLAGS_DEBUG="-Werror" + -DCMAKE_CXX_COMPILER=g++ + -DCMAKE_CXX_FLAGS_DEBUG="-Werror" include: - - os: macos-13 - build_cfg: { "name": "clang", - "flags": - '{ "QMAKESPEC": "macx-clang", - "EVAL": "export CC=clang && export CXX=clang++", - "CFLAGS": "-Oz", - "QMAKEFLAGS": "-config debug", - "MAKEFLAGS": "-s", - "TESTARGS": "-silent", - "PATH": "/usr/local/opt/qt/bin:$PATH" - }' - } + - os: macos-latest + build_cfg: + name: clang-small + cmakeflags: >- + -DBUILD_TESTING=OFF + -DBUILD_TOOLS=OFF + -DCMAKE_C_COMPILER=clang + -DCMAKE_C_FLAGS="-Oz -g -Werror" + -DCMAKE_CXX_COMPILER=clang++ + -DCMAKE_CXX_FLAGS="-O2 -g -Werror" + - os: macos-15-intel + build_cfg: + name: clang + cmakeflags: >- + -DBUILD_EXAMPLES=ON + -DBUILD_SHARED_LIBS=ON + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_C_COMPILER=clang + -DCMAKE_C_FLAGS_DEBUG="-Werror -fsanitize=address" + -DCMAKE_CXX_COMPILER=clang++ + -DCMAKE_CXX_FLAGS_DEBUG="-Werror -fsanitize=address" + -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" + -DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address" + # Default job name is too long to be visible in the "Checks" tab. name: ${{ matrix.os }}/${{ matrix.build_cfg.name }} @@ -87,7 +96,7 @@ runs-on: ${{ matrix.os }} steps: - name: Clone tinycbor - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: install Linux software if: matrix.os == 'ubuntu-latest' @@ -97,44 +106,36 @@ sudo apt-get update sudo apt-get install -y --no-install-recommends \ doxygen \ - jq \ + cmake \ libc6-dbg \ libcjson-dev \ libfuntools-dev \ - qtbase5-dev + ninja-build \ + qt6-base-dev - name: install macOS software if: runner.os == 'macOS' run: | - # Doxygen 1.9.7 is broken with ifdefs again, install 1.9.4 which works. - wget https://raw.githubusercontent.com/Homebrew/homebrew-core/41828ee36b96e35b63b2a4c8cfc2df2c3728944a/Formula/doxygen.rb - brew install doxygen.rb - rm doxygen.rb - brew install qt cjson + brew install -q \ + cjson \ + cmake \ + ninja \ + qt + + - name: Compile + run: | + set -x + cmake -S. -Bbuild -GNinja -DBUILD_TESTING=ON \ + ${{ matrix.build_cfg.cmakeflags }} + ninja -C build -v + if [[ -f build/libtinycbor.a ]]; then + size build/libtinycbor.a | tee sizes + fi - name: Execute tests run: | - set -x - PATH=`echo /opt/qt*/bin`:$PATH - eval $(echo '${{ matrix.build_cfg.flags }}' | jq -r 'to_entries[] | "\(.key)=\"\(.value)\""') - eval "$EVAL" - # FIXME: remove -Wno-error-line below. - export CFLAGS="$CFLAGS -Wno-error=implicit-function-declaration" - make OUT=.config V=1 -s -f Makefile.configure configure && cat .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 + ctest --output-on-failure --test-dir build + + - name: Build docs + if: matrix.build_cfg.docs + run: ./scripts/update-docs.sh
diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4df21cb..0000000 --- a/.travis.yml +++ /dev/null
@@ -1,92 +0,0 @@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e185474 --- /dev/null +++ b/CMakeLists.txt
@@ -0,0 +1,190 @@ +# /**************************************************************************** +# ** +# ** Copyright (C) 2015 Intel Corporation +# ** +# ** Permission is hereby granted, free of charge, to any person obtaining a copy +# ** of this software and associated documentation files (the "Software"), to deal +# ** in the Software without restriction, including without limitation the rights +# ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# ** copies of the Software, and to permit persons to whom the Software is +# ** furnished to do so, subject to the following conditions: +# ** +# ** The above copyright notice and this permission notice shall be included in +# ** all copies or substantial portions of the Software. +# ** +# ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# ** THE SOFTWARE. +# ** +# ****************************************************************************/ + +cmake_minimum_required(VERSION 3.10) + +project(tinycbor LANGUAGES C VERSION 7.0) + +# Set path to additional cmake scripts +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) + +set(TARGETS_EXPORT_NAME "TinyCBOR-targets") + +option(WITH_FLOATING_POINT "Use floating point code in TinyCBOR" ON) +option(WITH_FREESTANDING "Compile TinyCBOR in C freestanding mode" OFF) +if(WITH_FLOATING_POINT AND NOT WITH_FREESTANDING) + option(WITH_CBOR2JSON "Compile code to convert from CBOR to JSON" ON) + option(BUILD_EXAMPLES "Compile the TinyCBOR examples" OFF) + option(BUILD_TOOLS "Compile the TinyCBOR tools" ON) +endif() + +# Include additional modules that are used unconditionally +include(GNUInstallDirs) +include(GenerateExportHeader) +include(CheckLinkerFlag) +include(CheckSymbolExists) + +add_library(tinycbor + src/cborencoder.c + src/cborencoder_close_container_checked.c + src/cborerrorstrings.c + src/cborparser.c + src/cborpretty.c + src/cborvalidation.c + src/cbor.h +) +if(WITH_FREESTANDING) + target_compile_options(tinycbor PUBLIC + $<$<NOT:$<C_COMPILER_ID:MSVC>>:-ffreestanding> + ) +else() + target_sources(tinycbor PRIVATE + src/cborparser_dup_string.c + src/cborpretty_stdio.c + ) + if(WITH_CBOR2JSON) + target_sources(tinycbor PRIVATE + src/cbortojson.c + ) + endif() +endif() +if(WITH_FLOATING_POINT) + target_sources(tinycbor PRIVATE + src/cborencoder_float.c + src/cborparser_float.c + ) + if(NOT WIN32) + target_link_libraries(tinycbor m) + endif() +else() + target_compile_definitions(tinycbor PUBLIC CBOR_NO_FLOATING_POINT) +endif() + +set_target_properties(tinycbor PROPERTIES + # Force this library to link as C and compile as C99, to ensure we + # don't use something of a newer language level. + LINKER_LANGUAGE C + C_EXTENSIONS OFF + C_STANDARD 99 + + # Set version and output name + VERSION "0.${PROJECT_VERSION}" + SOVERSION "0" +) +if(BUILD_SHARED_LIBS) + set_target_properties(tinycbor PROPERTIES C_VISIBILITY_PRESET hidden) + + # Check if the linker supports "-z defs" (a.k.a "--no-undefined") + check_linker_flag(C "-Wl,-z,defs" HAVE_NO_UNDEFINED) + if(HAVE_NO_UNDEFINED) + target_link_options(tinycbor PRIVATE "-Wl,-z,defs") + endif() +else() + target_compile_definitions(tinycbor PUBLIC CBOR_STATIC_DEFINE) +endif() + +# Enable warnings +target_compile_options(tinycbor PRIVATE + $<$<C_COMPILER_ID:MSVC>:-W3> + $<$<NOT:$<C_COMPILER_ID:MSVC>>: + -Wall -Wextra + -Werror=format-security + -Werror=incompatible-pointer-types + -Werror=implicit-function-declaration + -Werror=int-conversion + > +) + +# Generate export macros +generate_export_header(tinycbor + BASE_NAME "cbor" + EXPORT_MACRO_NAME "CBOR_API" + EXPORT_FILE_NAME "tinycbor-export.h" +) + +# Generate version header +configure_file(src/tinycbor-version.h.in tinycbor-version.h) + +# Generate pkgconfig file +configure_file(tinycbor.pc.in tinycbor.pc @ONLY) + +# Check for open_memstream and store the result in HAVE_OPEN_MEMSTREAM +check_symbol_exists(open_memstream stdio.h HAVE_OPEN_MEMSTREAM) +check_symbol_exists(funopen stdio.h HAVE_OPEN_FUNOPEN) +check_symbol_exists(fopencookie stdio.h HAVE_OPEN_FOPENCOOKIE) + +if(NOT HAVE_OPEN_MEMSTREAM) + if (HAVE_OPEN_FUNOPEN) + message(STATUS "implementing open_memstream using funopen()") + target_compile_definitions(tinycbor PRIVATE HAVE_OPEN_FUNOPEN) + target_sources(tinycbor PRIVATE src/open_memstream.c) + elseif (HAVE_OPEN_FOPENCOOKIE) + message(STATUS "implementing open_memstream using fopencookie()") + target_compile_definitions(tinycbor PRIVATE HAVE_OPEN_FOPENCOOKIE) + target_sources(tinycbor PRIVATE src/open_memstream.c) + else() + target_compile_definitions(tinycbor PRIVATE WITHOUT_OPEN_MEMSTREAM) + message(WARNING "funopen and fopencookie unavailable, open_memstream can not be implemented and conversion to JSON will not work properly!") + endif() +endif() + +target_include_directories(tinycbor + PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>" + PUBLIC "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>" + PUBLIC "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" +) + +install(FILES + ${PROJECT_SOURCE_DIR}/src/cbor.h + ${PROJECT_BINARY_DIR}/tinycbor-version.h + ${PROJECT_BINARY_DIR}/tinycbor-export.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tinycbor +) +install(FILES + ${CMAKE_BINARY_DIR}/tinycbor.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig +) +install( + TARGETS tinycbor + EXPORT "${TARGETS_EXPORT_NAME}" + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # import library + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # .so files are libraries + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # .dll files are binaries + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # this does not actually install anything (but used by downstream projects) +) + +set(PROJECT_LIBRARIES TinyCBOR) +include(PackageConfig) + +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif() +if(BUILD_TESTING) + enable_language(CXX) + enable_testing() + add_subdirectory(tests) +endif() +if(BUILD_TOOLS) + add_subdirectory(tools) +endif()
diff --git a/Makefile b/Makefile deleted file mode 100644 index 6df1655..0000000 --- a/Makefile +++ /dev/null
@@ -1,242 +0,0 @@ -# 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) - --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='$@' configure - -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) - -ifneq ($(DISABLE_WERROR),1) -cflags += \ - $(shell $(CC) -S -Werror -Wdiscarded-qualifiers -o - -xc /dev/null >/dev/null 2>&1 && echo -Werror=discarded-qualifiers) \ - -Werror=incompatible-pointer-types \ - -Werror=implicit-function-declaration \ - -Werror=int-conversion -endif - -%.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
diff --git a/Makefile.nmake b/Makefile.nmake index 6afe517..fa79fa2 100644 --- a/Makefile.nmake +++ b/Makefile.nmake
@@ -1,6 +1,6 @@ CFLAGS = -W3 -TINYCBOR_HEADERS = src\cbor.h src\cborjson.h +TINYCBOR_HEADERS = src\cbor.h src\cborjson.h src\tinycbor-export.h TINYCBOR_SOURCES = \ src\cborerrorstrings.c \ src\cborencoder.c \ @@ -24,7 +24,7 @@ src\cborpretty_stdio.obj \ src\cborvalidation.obj -all: lib\tinycbor.lib +all: src/tinycbor-export.h lib\tinycbor.lib check: tests\Makefile lib\tinycbor.lib cd tests & $(MAKE) check silentcheck: @@ -32,12 +32,16 @@ tests\Makefile: tests\tests.pro qmake -o $@ $** +src\tinycbor-export.h: src\tinycbor-export.h.in + copy $** $@ + lib\tinycbor.lib: $(TINYCBOR_OBJS) -if not exist lib\NUL md lib lib -nologo /out:$@ $** mostlyclean: -del $(TINYCBOR_OBJS) + -del src\tinycbor-export.h clean: mostlyclean -del lib\tinycbor.lib if exist tests\Makefile (cd tests & $(MAKE) clean)
diff --git a/VERSION b/VERSION deleted file mode 100644 index a918a2a..0000000 --- a/VERSION +++ /dev/null
@@ -1 +0,0 @@ -0.6.0
diff --git a/cmake/PackageConfig.cmake b/cmake/PackageConfig.cmake new file mode 100644 index 0000000..73cc913 --- /dev/null +++ b/cmake/PackageConfig.cmake
@@ -0,0 +1,35 @@ +# This cmake code creates the configuration that is found and used by +# find_package() of another cmake project + +# get lower and upper case project name for the configuration files + +# configure and install the configuration files +include(CMakePackageConfigHelpers) + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/project-config.cmake.in" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + #PATH_VARS CMAKE_INSTALL_DIR +) + +write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMinorVersion +) + +install(FILES + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake" + COMPONENT devel + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +if (PROJECT_LIBRARIES OR PROJECT_STATIC_LIBRARIES) + install( + EXPORT "${TARGETS_EXPORT_NAME}" + FILE ${TARGETS_EXPORT_NAME}.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) +endif ()
diff --git a/cmake/TinyCBORHelpers.cmake b/cmake/TinyCBORHelpers.cmake new file mode 100644 index 0000000..bb08117 --- /dev/null +++ b/cmake/TinyCBORHelpers.cmake
@@ -0,0 +1,38 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +function(tinycbor_add_executable target) + add_executable(${target} ${ARGN}) + target_link_libraries(${target} tinycbor) + target_compile_options(${target} PRIVATE + $<$<CXX_COMPILER_ID:MSVC>:-W3> + $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra> + ) +endfunction() + +function(tinycbor_add_test target) + tinycbor_add_executable(${target} ${ARGN}) + set(memcheck_command ${CMAKE_MEMORYCHECK_COMMAND} ${CMAKE_MEMORYCHECK_COMMAND_OPTIONS}) + separate_arguments(memcheck_command) + add_test(NAME ${target} COMMAND ${memcheck_command} $<TARGET_FILE:${target}>) +endfunction() + +function(tinycbor_add_qtest target) + tinycbor_add_test(${target} ${ARGN}) + target_link_libraries(${target} Qt::Core Qt::Test) +endfunction() + +option(WITH_VALGRIND "Use Valgrind (if found) to run tests" ON) +if(WITH_VALGRIND AND NOT DEFINED CMAKE_MEMORYCHECK_COMMAND) + find_program(VALGRIND "valgrind") + if(VALGRIND) + set(CMAKE_MEMORYCHECK_COMMAND ${VALGRIND} --tool=memcheck) + set(CMAKE_MEMORYCHECK_COMMAND_OPTIONS} + --error-exitcode=255 + --errors-for-leak-kinds=definite + --leak-check=yes + --num-callers=20 + ) + endif() +else() + set(VALGRIND OFF) +endif()
diff --git a/cmake/project-config.cmake.in b/cmake/project-config.cmake.in new file mode 100644 index 0000000..6729c5c --- /dev/null +++ b/cmake/project-config.cmake.in
@@ -0,0 +1,16 @@ +# Config file for @PROJECT_NAME_LOWER@ +# +# It defines the following variables: +# +# @PROJECT_NAME_UPPER@_INCLUDE_DIRS - include directory +# @PROJECT_NAME_UPPER@_LIBRARIES - all dynamic libraries +# @PROJECT_NAME_UPPER@_STATIC_LIBRARIES - all static libraries + +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# Add optional dependencies here + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@")
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..3f567e1 --- /dev/null +++ b/examples/CMakeLists.txt
@@ -0,0 +1,5 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +include(TinyCBORHelpers) + +tinycbor_add_executable(simplereader simplereader.c)
diff --git a/examples/examples.pro b/examples/examples.pro deleted file mode 100644 index 22071ac..0000000 --- a/examples/examples.pro +++ /dev/null
@@ -1,2 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = simplereader.pro
diff --git a/examples/simplereader.c b/examples/simplereader.c index a815a36..13473ef 100644 --- a/examples/simplereader.c +++ b/examples/simplereader.c
@@ -174,12 +174,13 @@ 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", cbor_value_get_next_byte(&it) - buf, cbor_error_string(err)); + free(buf); return 1; } + free(buf); return 0; }
diff --git a/examples/simplereader.pro b/examples/simplereader.pro deleted file mode 100644 index 07fdc6a..0000000 --- a/examples/simplereader.pro +++ /dev/null
@@ -1,3 +0,0 @@ -CONFIG -= qt -SOURCES = simplereader.c -include(../src/src.pri)
diff --git a/src/cbor.h b/src/cbor.h index 86909d5..d557026 100644 --- a/src/cbor.h +++ b/src/cbor.h
@@ -38,6 +38,7 @@ #include "cbor_cfg.h" #endif +#include "tinycbor-export.h" #include "tinycbor-version.h" #define TINYCBOR_VERSION ((TINYCBOR_VERSION_MAJOR << 16) | (TINYCBOR_VERSION_MINOR << 8) | TINYCBOR_VERSION_PATCH) @@ -59,11 +60,8 @@ # define SIZE_MAX ((size_t)-1) #endif -#ifndef CBOR_API -# define CBOR_API -#endif #ifndef CBOR_PRIVATE_API -# define CBOR_PRIVATE_API +# define CBOR_PRIVATE_API CBOR_API #endif #ifndef CBOR_INLINE_API # if defined(__cplusplus) @@ -213,7 +211,7 @@ { CborEncoderAppendCborData = 0, CborEncoderAppendStringData = 1, - CborEncoderApendRawData = 2 + CborEncoderAppendRawData = 2 } CborEncoderAppendType; typedef CborError (*CborEncoderWriteFunction)(void *, const void *, size_t, CborEncoderAppendType); @@ -727,4 +725,3 @@ #endif #endif /* CBOR_H */ -
diff --git a/src/cborencoder.c b/src/cborencoder.c index 69aebf4..b21c1da 100644 --- a/src/cborencoder.c +++ b/src/cborencoder.c
@@ -22,16 +22,7 @@ ** ****************************************************************************/ -#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 -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h" #include "cborinternal_p.h" @@ -213,6 +204,7 @@ { #ifdef CBOR_ENCODER_WRITE_FUNCTION (void) writer; + encoder->data.writer = CBOR_NULLPTR; #else encoder->data.writer = writer; #endif @@ -484,7 +476,7 @@ */ CborError cbor_encode_raw(CborEncoder *encoder, const uint8_t *raw, size_t length) { - return append_to_buffer(encoder, raw, length, CborEncoderApendRawData); + return append_to_buffer(encoder, raw, length, CborEncoderAppendRawData); } /**
diff --git a/src/cborencoder_close_container_checked.c b/src/cborencoder_close_container_checked.c index b88f06c..fd203a8 100644 --- a/src/cborencoder_close_container_checked.c +++ b/src/cborencoder_close_container_checked.c
@@ -22,12 +22,7 @@ ** ****************************************************************************/ -#define _BSD_SOURCE 1 -#define _DEFAULT_SOURCE 1 -#ifndef __STDC_LIMIT_MACROS -# define __STDC_LIMIT_MACROS 1 -#endif -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h"
diff --git a/src/cborencoder_float.c b/src/cborencoder_float.c index d87919e..ff87942 100644 --- a/src/cborencoder_float.c +++ b/src/cborencoder_float.c
@@ -22,12 +22,7 @@ ** ****************************************************************************/ -#define _BSD_SOURCE 1 -#define _DEFAULT_SOURCE 1 -#ifndef __STDC_LIMIT_MACROS -# define __STDC_LIMIT_MACROS 1 -#endif -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h"
diff --git a/src/cborinternal_p.h b/src/cborinternal_p.h index 19273ac..ee9c117 100644 --- a/src/cborinternal_p.h +++ b/src/cborinternal_p.h
@@ -48,8 +48,12 @@ /* Check for FLT16_MANT_DIG using integer comparison. Clang headers incorrectly * define this macro unconditionally when __STDC_WANT_IEC_60559_TYPES_EXT__ * is defined (regardless of actual support for _Float16). + * + * GCC defines these macros but doesn't support arithmetic including + * conversions on x86 without SSE2. */ -# if FLT16_MANT_DIG > 0 || __FLT16_MANT_DIG__ > 0 +# if (FLT16_MANT_DIG > 0 || __FLT16_MANT_DIG__ > 0) && \ + !(defined(__i386__) && !defined(__SSE2__)) static inline unsigned short encode_half(float x) { unsigned short h;
diff --git a/src/cborinternalmacros_p.h b/src/cborinternalmacros_p.h new file mode 100644 index 0000000..8450a3b --- /dev/null +++ b/src/cborinternalmacros_p.h
@@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2025 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 +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ +# define __STDC_WANT_IEC_60559_TYPES_EXT__ +#endif
diff --git a/src/cborparser.c b/src/cborparser.c index 75a4088..31c8d8b 100644 --- a/src/cborparser.c +++ b/src/cborparser.c
@@ -22,16 +22,7 @@ ** ****************************************************************************/ -#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 -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h" #include "cborinternal_p.h" @@ -231,7 +222,7 @@ case SinglePrecisionFloat: case DoublePrecisionFloat: it->flags |= CborIteratorFlag_IntegerValueTooLarge; - /* fall through */ + CBOR_FALLTHROUGH; case TrueValue: case NullValue: case UndefinedValue:
diff --git a/src/cborparser_dup_string.c b/src/cborparser_dup_string.c index b634617..21d88c2 100644 --- a/src/cborparser_dup_string.c +++ b/src/cborparser_dup_string.c
@@ -22,16 +22,7 @@ ** ****************************************************************************/ -#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 -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h" #include "compilersupport_p.h" @@ -49,16 +40,19 @@ * 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 + * 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. * + * If \c malloc returns a NULL pointer, this function will return error + * condition \ref CborErrorOutOfMemory. In this case, \c *buflen should contain + * the number of bytes necessary to copy this string and \a value will be + * updated to point to the next element. On all other failure cases, the values + * contained in \c *buffer, \c *buflen and \a next are undefined and mustn't be + * used (for example, calling \c{free()}). + * * 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. @@ -80,16 +74,19 @@ * 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 + * 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. * + * If \c malloc returns a NULL pointer, this function will return error + * condition \ref CborErrorOutOfMemory. In this case, \c *buflen should contain + * the number of bytes necessary to copy this string and \a value will be + * updated to point to the next element. On all other failure cases, the values + * contained in \c *buffer, \c *buflen and \a next are undefined and mustn't be + * used (for example, calling \c{free()}). + * * 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. @@ -98,24 +95,28 @@ */ CborError _cbor_value_dup_string(const CborValue *value, void **buffer, size_t *buflen, CborValue *next) { + const CborValue it = *value; // often value == next CborError err; + void *tmpbuf; cbor_assert(buffer); cbor_assert(buflen); *buflen = SIZE_MAX; - err = _cbor_value_copy_string(value, NULL, buflen, NULL); + err = _cbor_value_copy_string(&it, NULL, buflen, next); if (err) return err; ++*buflen; - *buffer = cbor_malloc(*buflen); - if (!*buffer) { + tmpbuf = cbor_malloc(*buflen); + if (!tmpbuf) { /* out of memory */ return CborErrorOutOfMemory; } - err = _cbor_value_copy_string(value, *buffer, buflen, next); + err = _cbor_value_copy_string(&it, tmpbuf, buflen, next); if (err) { - cbor_free(*buffer); + /* This shouldn't have happened! We've iterated once. */ + cbor_free(tmpbuf); return err; } + *buffer = tmpbuf; return CborNoError; }
diff --git a/src/cborparser_float.c b/src/cborparser_float.c index 268239e..f7d0189 100644 --- a/src/cborparser_float.c +++ b/src/cborparser_float.c
@@ -22,12 +22,7 @@ ** ****************************************************************************/ -#define _BSD_SOURCE 1 -#define _DEFAULT_SOURCE 1 -#ifndef __STDC_LIMIT_MACROS -# define __STDC_LIMIT_MACROS 1 -#endif -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h"
diff --git a/src/cborpretty.c b/src/cborpretty.c index a07741f..02b5706 100644 --- a/src/cborpretty.c +++ b/src/cborpretty.c
@@ -22,12 +22,7 @@ ** ****************************************************************************/ -#define _BSD_SOURCE 1 -#define _DEFAULT_SOURCE 1 -#ifndef __STDC_LIMIT_MACROS -# define __STDC_LIMIT_MACROS 1 -#endif -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h" #include "cborinternal_p.h" @@ -169,7 +164,7 @@ * value, chosen in an implementation-defined manner. */ supremum = -2.0 * INT64_MIN; /* -2 * (- 2^63) == 2^64 */ - if (v >= supremum) + if (!(v < supremum)) /* out of range or NaN */ return false; /* Now we can convert, these two conversions cannot be UB */ @@ -313,7 +308,7 @@ const char *comma = ""; CborError err = CborNoError; - if (!recursionsLeft) { + if (recursionsLeft <= 0) { printRecursionLimit(stream, out); while (!cbor_value_at_end(it) && !err) { err = cbor_value_advance(it); @@ -361,6 +356,9 @@ copy_current_position(it, &recursed); return err; /* parse error */ } + /* N.B. recursionsLeft can be zero, in which case container_to_pretty is called with + * recursionsLeft = -1 and reports that nesting is too deep. + */ err = container_to_pretty(stream, out, &recursed, type, flags, recursionsLeft - 1); if (err) { copy_current_position(it, &recursed); @@ -457,7 +455,7 @@ err = stream(out, "%" PRIu64 "%s(", tag, get_indicator(it, flags)); if (!err) err = cbor_value_advance_fixed(it); - if (!err && recursionsLeft) + if (!err && recursionsLeft > 0) err = value_to_pretty(stream, out, it, flags, recursionsLeft - 1); else if (!err) printRecursionLimit(stream, out);
diff --git a/src/cbortojson.c b/src/cbortojson.c index 890769c..9a1cccf 100644 --- a/src/cbortojson.c +++ b/src/cbortojson.c
@@ -22,13 +22,7 @@ ** ****************************************************************************/ -#define _BSD_SOURCE 1 -#define _DEFAULT_SOURCE 1 -#define _GNU_SOURCE 1 -#ifndef __STDC_LIMIT_MACROS -# define __STDC_LIMIT_MACROS 1 -#endif -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h" #include "cborjson.h" @@ -167,11 +161,19 @@ int flags; } ConversionStatus; -static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status); +static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, + int nestingLevel, ConversionStatus *status); + +static void append_hex(void *buffer, uint8_t byte) +{ + static const char characters[] = "0123456789abcdef"; + char *str = buffer; + str[0] = characters[byte >> 4]; + str[1] = characters[byte & 0xf]; +} static CborError dump_bytestring_base16(char **result, CborValue *it) { - static const char characters[] = "0123456789abcdef"; size_t i; size_t n = 0; uint8_t *buffer; @@ -180,7 +182,10 @@ return err; /* a Base16 (hex) output is twice as big as our buffer */ - buffer = (uint8_t *)cbor_malloc(n * 2 + 1); + size_t needed; + if (mul_check_overflow(n, 2, &needed) || add_check_overflow(needed, 1, &needed)) + return CborErrorDataTooLarge; + buffer = (uint8_t *)cbor_malloc(needed); if (buffer == NULL) /* out of memory */ return CborErrorOutOfMemory; @@ -194,8 +199,7 @@ 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]; + append_hex(buffer + 2 * i, byte); } return CborNoError; } @@ -209,8 +213,12 @@ 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 *)cbor_malloc(len + 1); + size_t len, needed; + if (add_check_overflow(n, 5, &len) || mul_check_overflow(len / 3, 4, &len) + || add_check_overflow(len, 1, &needed)) { + return CborErrorDataTooLarge; + } + buffer = (uint8_t *)cbor_malloc(needed); if (buffer == NULL) /* out of memory */ return CborErrorOutOfMemory; @@ -292,6 +300,98 @@ return generic_dump_base64(result, it, alphabet); } +static CborError escape_text_string(char **str, size_t *alloc, size_t *offsetp, const char *input, size_t len) +{ + /* JSON requires escaping some characters in strings, so we iterate and + * escape as necessary + * https://www.rfc-editor.org/rfc/rfc8259#section-7: + * All Unicode characters may be placed within the + * quotation marks, except for the characters that MUST be escaped: + * quotation mark, reverse solidus, and the control characters (U+0000 + * through U+001F). + * We additionally choose to escape BS, HT, CR, LF and FF. + */ + char *buf = *str; + + /* Ensure we have enough space for this chunk. In the worst case, we + * have 6 escaped characters per input character. + * + * The overflow checking here is only practically useful for 32-bit + * machines, as SIZE_MAX/6 for a 64-bit machine is 2.6667 exabytes. + * That is much more than any current architecture can even address and + * cbor_value_get_text_string_chunk() only works for data already + * loaded into memory. + */ + size_t needed; + size_t offset = offsetp ? *offsetp : 0; + if (mul_check_overflow(len, 6, &needed) || add_check_overflow(needed, offset, &needed) + || add_check_overflow(needed, 1, &needed)) { + return CborErrorDataTooLarge; + } + if (!alloc || needed > *alloc) { + buf = cbor_realloc(buf, needed); + if (!buf) + return CborErrorOutOfMemory; + if (alloc) + *alloc = needed; + } + + for (size_t i = 0; i < len; ++i) { + static const char escapeChars[] = "\b\t\n\r\f\"\\"; + static const char escapedChars[] = "btnrf\"\\"; + unsigned char c = input[i]; + + char *esc = c > 0 ? strchr(escapeChars, c) : NULL; + if (esc) { + buf[offset++] = '\\'; + buf[offset++] = escapedChars[esc - escapeChars]; + } else if (c <= 0x1F) { + buf[offset++] = '\\'; + buf[offset++] = 'u'; + buf[offset++] = '0'; + buf[offset++] = '0'; + append_hex(buf + offset, c); + offset += 2; + } else { + buf[offset++] = c; + } + } + buf[offset] = '\0'; + *str = buf; + if (offsetp) + *offsetp = offset; + return CborNoError; +} + +static CborError text_string_to_escaped(char **str, CborValue *it) +{ + size_t alloc = 0, offset = 0; + CborError err; + + *str = NULL; + err = cbor_value_begin_string_iteration(it); + while (err == CborNoError) { + const char *chunk; + size_t len; + err = cbor_value_get_text_string_chunk(it, &chunk, &len, it); + if (err == CborNoError) + err = escape_text_string(str, &alloc, &offset, chunk, len); + } + + if (likely(err == CborErrorNoMoreStringChunks)) { + /* success */ + if (!*str) + *str = strdup(""); // wasteful, but very atypical + err = cbor_value_finish_string_iteration(it); + if (likely(err == CborNoError)) + return CborNoError; + } + + cbor_free(*str); + *str = NULL; + return err; +} + static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status) { int flags = status->flags; @@ -328,11 +428,13 @@ return CborNoError; } -static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type) +static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type, int nestingLevel) { CborError err = CborNoError; *type = cbor_value_get_type(it); while (*type == CborTagType) { + if (nestingLevel-- == 0) + return CborErrorNestingTooDeep; cbor_value_get_tag(it, tag); /* can't fail */ err = cbor_value_advance_fixed(it); if (err) @@ -343,7 +445,7 @@ return err; } -static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status) { CborTag tag; CborError err; @@ -358,7 +460,7 @@ return CborErrorIO; CborType type = cbor_value_get_type(it); - err = value_to_json(out, it, flags, type, status); + err = value_to_json(out, it, flags, type, nestingLevel, status); if (err) return err; if (flags & CborConvertAddMetadata && status->flags) { @@ -374,7 +476,7 @@ } CborType type; - err = find_tagged_type(it, &status->lastTag, &type); + err = find_tagged_type(it, &status->lastTag, &type, nestingLevel); if (err) return err; tag = status->lastTag; @@ -402,7 +504,7 @@ } /* no special handling */ - err = value_to_json(out, it, flags, type, status); + err = value_to_json(out, it, flags, type, nestingLevel, status); status->flags |= TypeWasTagged | type; return err; } @@ -417,19 +519,25 @@ return CborErrorJsonNotImplemented; #else size_t size; + char *stringified; - FILE *memstream = open_memstream(key, &size); + FILE *memstream = open_memstream(&stringified, &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)) + if (unlikely(fclose(memstream) < 0 || stringified == NULL)) return CborErrorInternalError; + if (err == CborNoError) { + /* escape the stringified CBOR stream */ + err = escape_text_string(key, NULL, NULL, stringified, size); + } + cbor_free(stringified); return err; #endif } -static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +static CborError array_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status) { const char *comma = ""; while (!cbor_value_at_end(it)) { @@ -437,27 +545,26 @@ return CborErrorIO; comma = ","; - CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status); + CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), nestingLevel, status); if (err) return err; } return CborNoError; } -static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +static CborError map_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status) { const char *comma = ""; CborError err; while (!cbor_value_at_end(it)) { - char *key; + char *key = NULL; 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); + err = text_string_to_escaped(&key, it); } else if (flags & CborConvertStringifyMapKeys) { err = stringify_map_key(&key, it, flags, keyType); } else { @@ -474,7 +581,7 @@ /* then, print the value */ CborType valueType = cbor_value_get_type(it); - err = value_to_json(out, it, flags, valueType, status); + err = value_to_json(out, it, flags, valueType, nestingLevel, status); /* finally, print any metadata we may have */ if (flags & CborConvertAddMetadata) { @@ -497,11 +604,15 @@ return CborNoError; } -static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status) +static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, + int nestingLevel, ConversionStatus *status) { CborError err; status->flags = 0; + if (nestingLevel == 0) + return CborErrorNestingTooDeep; + switch (type) { case CborArrayType: case CborMapType: { @@ -516,8 +627,8 @@ return CborErrorIO; err = (type == CborArrayType) ? - array_to_json(out, &recursed, flags, status) : - map_to_json(out, &recursed, flags, status); + array_to_json(out, &recursed, flags, nestingLevel - 1, status) : + map_to_json(out, &recursed, flags, nestingLevel - 1, status); if (err) { copy_current_position(it, &recursed); return err; /* parse error */ @@ -533,28 +644,11 @@ 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 CborIntegerType: + case CborNullType: + case CborBooleanType: + /* just use cborpretty.c */ + return cbor_value_to_pretty_advance(out, it); case CborByteStringType: case CborTextStringType: { @@ -563,8 +657,7 @@ err = dump_bytestring_base64url(&str, it); status->flags = TypeWasNotNative; } else { - size_t n = 0; - err = cbor_value_dup_text_string(it, &str, &n, it); + err = text_string_to_escaped(&str, it); } if (err) return err; @@ -574,7 +667,7 @@ } case CborTagType: - return tagged_value_to_json(out, it, flags, status); + return tagged_value_to_json(out, it, flags, nestingLevel - 1, status); case CborSimpleType: { uint8_t simple_type; @@ -586,25 +679,12 @@ 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; @@ -637,8 +717,10 @@ status->flags |= r == FP_NAN ? NumberWasNaN : NumberWasInfinite | (val < 0 ? NumberWasNegative : 0); } else { - uint64_t ival = (uint64_t)fabs(val); - if ((double)ival == fabs(val)) { + const double limit = (UINT32_MAX + 1.0) * (UINT32_MAX + 1.0); /* 2^64 */ + uint64_t ival = 0; + double aval = fabs(val); + if (aval < limit && (double)(ival = (uint64_t)aval) == aval) { /* 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 */ @@ -704,7 +786,8 @@ 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); + return value_to_json(out, value, flags, cbor_value_get_type(value), CBOR_PARSER_MAX_RECURSIONS, + &status); } /** @} */
diff --git a/src/cborvalidation.c b/src/cborvalidation.c index 6e13138..2ad0c18 100644 --- a/src/cborvalidation.c +++ b/src/cborvalidation.c
@@ -22,12 +22,7 @@ ** ****************************************************************************/ -#define _BSD_SOURCE 1 -#define _DEFAULT_SOURCE 1 -#ifndef __STDC_LIMIT_MACROS -# define __STDC_LIMIT_MACROS 1 -#endif -#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include "cborinternalmacros_p.h" #include "cbor.h" #include "cborinternal_p.h"
diff --git a/src/compilersupport_p.h b/src/compilersupport_p.h index 1b826b8..670a291 100644 --- a/src/compilersupport_p.h +++ b/src/compilersupport_p.h
@@ -51,6 +51,24 @@ #else # define cbor_static_assert(x) ((void)sizeof(char[2*!!(x) - 1])) #endif + +#if defined(__has_cpp_attribute) && defined(__cplusplus) // C++17 +# if __has_cpp_attribute(fallthrough) +# define CBOR_FALLTHROUGH [[fallthrough]] +# endif +#elif defined(__has_c_attribute) && !defined(__cplusplus) // C23 +# if __has_c_attribute(fallthrough) +# define CBOR_FALLTHROUGH [[fallthrough]] +# endif +#endif +#ifndef CBOR_FALLTHROUGH +# ifdef __GNUC__ +# define CBOR_FALLTHROUGH __attribute__((fallthrough)) +# else +# define CBOR_FALLTHROUGH do { } while (0) +# endif +#endif + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* inline is a keyword */ #else @@ -189,22 +207,28 @@ # define CONST_CAST(t, v) (t)(uintptr_t)(v) #endif -#ifdef __GNUC__ -#ifndef likely +#if defined(__cplusplus) || __STDC_VERSION__ >= 202311 +# define CBOR_NULLPTR nullptr +#else +# define CBOR_NULLPTR NULL +#endif + +#ifdef likely +/* something has already defined likely(), accept it */ +#elif defined(__GNUC__) # 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 + +#ifdef unreachable +/* C23 has unreachable() */ +#elif defined(__GNUC__) +# define unreachable() __builtin_unreachable() +#elif defined(_MSC_VER) +# define unreachable() __assume(0) #endif static inline bool add_check_overflow(size_t v1, size_t v2, size_t *r) @@ -218,4 +242,15 @@ #endif } +static inline bool mul_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_mul_overflow(v1, v2, r); +#else + /* unsigned multiplications are well-defined */ + *r = v1 * v2; + return *r > v1 && *r > v2; +#endif +} + #endif /* COMPILERSUPPORT_H */
diff --git a/src/memory.h b/src/memory.h index 0032b93..6686eb2 100644 --- a/src/memory.h +++ b/src/memory.h
@@ -26,6 +26,7 @@ # include CBOR_CUSTOM_ALLOC_INCLUDE #else # include <stdlib.h> -# define cbor_malloc malloc -# define cbor_free free +# define cbor_malloc malloc +# define cbor_realloc realloc +# define cbor_free free #endif
diff --git a/src/open_memstream.c b/src/open_memstream.c index 3365378..ed94e89 100644 --- a/src/open_memstream.c +++ b/src/open_memstream.c
@@ -35,10 +35,10 @@ #if defined(__unix__) || defined(__APPLE__) # include <unistd.h> #endif -#ifdef __APPLE__ +#if defined(HAVE_OPEN_FUNOPEN) typedef int RetType; typedef int LenType; -#elif __linux__ +#elif defined(HAVE_OPEN_FOPENCOOKIE) typedef ssize_t RetType; typedef size_t LenType; #else @@ -99,9 +99,9 @@ *bufptr = NULL; *lenptr = 0; -#ifdef __APPLE__ +#if defined(HAVE_OPEN_FUNOPEN) return funopen(b, NULL, write_to_buffer, NULL, close_buffer); -#elif __linux__ +#elif defined(HAVE_OPEN_FOPENCOOKIE) static const cookie_io_functions_t vtable = { NULL, write_to_buffer,
diff --git a/src/tinycbor-export.h.in b/src/tinycbor-export.h.in new file mode 100644 index 0000000..a181cce --- /dev/null +++ b/src/tinycbor-export.h.in
@@ -0,0 +1,3 @@ +#ifndef CBOR_API +#define CBOR_API +#endif
diff --git a/src/tinycbor-version.h b/src/tinycbor-version.h deleted file mode 100644 index c26560c..0000000 --- a/src/tinycbor-version.h +++ /dev/null
@@ -1,3 +0,0 @@ -#define TINYCBOR_VERSION_MAJOR 0 -#define TINYCBOR_VERSION_MINOR 6 -#define TINYCBOR_VERSION_PATCH 0
diff --git a/src/tinycbor-version.h.in b/src/tinycbor-version.h.in new file mode 100644 index 0000000..5ea04e6 --- /dev/null +++ b/src/tinycbor-version.h.in
@@ -0,0 +1,7 @@ +/* Copyright (C) 2025 Intel Corporation + * SPDX-License-Identifier: MIT + */ +/* This is a generated file */ +#define TINYCBOR_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define TINYCBOR_VERSION_MINOR @PROJECT_VERSION_MINOR@ +#define TINYCBOR_VERSION_PATCH 0
diff --git a/src/tinycbor.pro b/src/tinycbor.pro deleted file mode 100644 index 2ba508a..0000000 --- a/src/tinycbor.pro +++ /dev/null
@@ -1,10 +0,0 @@ -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)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..1747927 --- /dev/null +++ b/tests/CMakeLists.txt
@@ -0,0 +1,20 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +include(TinyCBORHelpers) +add_subdirectory(c90) + +if(WITH_FREESTANDING OR NOT WITH_FLOATING_POINT) + return() +endif() + +find_package(Qt6 COMPONENTS Core Test) +if(Qt6_FOUND) + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_AUTOMOC ON) + add_subdirectory(cpp) + add_subdirectory(encoder) + add_subdirectory(parser) + if(WITH_CBOR2JSON) + add_subdirectory(tojson) + endif() +endif()
diff --git a/tests/c90/CMakeLists.txt b/tests/c90/CMakeLists.txt new file mode 100644 index 0000000..17f8cfa --- /dev/null +++ b/tests/c90/CMakeLists.txt
@@ -0,0 +1,8 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +tinycbor_add_test(tst_c90 tst_c90.c) +set_target_properties(tst_c90 PROPERTIES + LINKER_LANGUAGE C + C_STANDARD 90 + C_STANDARD_REQUIRED ON +)
diff --git a/tests/c90/c90.pro b/tests/c90/c90.pro deleted file mode 100644 index 59166b4..0000000 --- a/tests/c90/c90.pro +++ /dev/null
@@ -1,7 +0,0 @@ -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
diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt new file mode 100644 index 0000000..9d69849 --- /dev/null +++ b/tests/cpp/CMakeLists.txt
@@ -0,0 +1,4 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT + +tinycbor_add_qtest(tst_cpp tst_cpp.cpp)
diff --git a/tests/cpp/cpp.pro b/tests/cpp/cpp.pro deleted file mode 100644 index 5e9e608..0000000 --- a/tests/cpp/cpp.pro +++ /dev/null
@@ -1,5 +0,0 @@ -CONFIG += testcase parallel_test c++11 -QT = core testlib - -SOURCES = tst_cpp.cpp -INCLUDEPATH += ../../src
diff --git a/tests/encoder/CMakeLists.txt b/tests/encoder/CMakeLists.txt new file mode 100644 index 0000000..834fd4d --- /dev/null +++ b/tests/encoder/CMakeLists.txt
@@ -0,0 +1,3 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +tinycbor_add_qtest(tst_encoder tst_encoder.cpp)
diff --git a/tests/encoder/encoder.pro b/tests/encoder/encoder.pro deleted file mode 100644 index 180f0d7..0000000 --- a/tests/encoder/encoder.pro +++ /dev/null
@@ -1,11 +0,0 @@ -SOURCES += tst_encoder.cpp - -CONFIG += testcase parallel_test c++11 -QT = core testlib - -DEFINES += QT_NO_FOREACH QT_NO_AS_CONST - -INCLUDEPATH += ../../src -msvc: POST_TARGETDEPS = ../../lib/tinycbor.lib -else: POST_TARGETDEPS += ../../lib/libtinycbor.a -LIBS += $$POST_TARGETDEPS
diff --git a/tests/encoder/tst_encoder.cpp b/tests/encoder/tst_encoder.cpp index 1efc6e9..0708064 100644 --- a/tests/encoder/tst_encoder.cpp +++ b/tests/encoder/tst_encoder.cpp
@@ -107,35 +107,35 @@ { int type = v.userType(); switch (type) { - case QVariant::Int: - case QVariant::LongLong: + case QMetaType::Int: + case QMetaType::LongLong: return cbor_encode_int(encoder, v.toLongLong()); - case QVariant::UInt: - case QVariant::ULongLong: + case QMetaType::UInt: + case QMetaType::ULongLong: return cbor_encode_uint(encoder, v.toULongLong()); - case QVariant::Bool: + case QMetaType::Bool: return cbor_encode_boolean(encoder, v.toBool()); - case QVariant::Invalid: + case QMetaType::UnknownType: return cbor_encode_undefined(encoder); case QMetaType::VoidStar: return cbor_encode_null(encoder); - case QVariant::Double: + case QMetaType::Double: return cbor_encode_double(encoder, v.toDouble()); case QMetaType::Float: return cbor_encode_float(encoder, v.toFloat()); - case QVariant::String: { + case QMetaType::QString: { QByteArray string = v.toString().toUtf8(); return cbor_encode_text_string(encoder, string.constData(), string.length()); } - case QVariant::ByteArray: { + case QMetaType::QByteArray: { QByteArray string = v.toByteArray(); return cbor_encode_byte_string(encoder, reinterpret_cast<const quint8 *>(string.constData()), string.length()); } @@ -158,7 +158,7 @@ return err; return static_cast<CborError>(err | encodeVariant(encoder, v.value<Tag>().tagged)); } - if (type == QVariant::List || type == qMetaTypeId<IndeterminateLengthArray>()) { + if (type == QMetaType::QVariantList || type == qMetaTypeId<IndeterminateLengthArray>()) { CborEncoder sub; QVariantList list = v.toList(); size_t len = list.length();
diff --git a/tests/parser/CMakeLists.txt b/tests/parser/CMakeLists.txt new file mode 100644 index 0000000..af59aa9 --- /dev/null +++ b/tests/parser/CMakeLists.txt
@@ -0,0 +1,3 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +tinycbor_add_qtest(tst_parser tst_parser.cpp)
diff --git a/tests/parser/parser.pro b/tests/parser/parser.pro deleted file mode 100644 index a61291a..0000000 --- a/tests/parser/parser.pro +++ /dev/null
@@ -1,10 +0,0 @@ -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
diff --git a/tests/parser/tst_parser.cpp b/tests/parser/tst_parser.cpp index e38a208..f937458 100644 --- a/tests/parser/tst_parser.cpp +++ b/tests/parser/tst_parser.cpp
@@ -38,6 +38,10 @@ # include <windows.h> #endif +#ifndef CBOR_PARSER_MAX_RECURSIONS +# define CBOR_PARSER_MAX_RECURSIONS 1024 +#endif + #ifndef QCOMPARE_EQ // added for Qt 6.4 # define QCOMPARE_EQ QCOMPARE @@ -264,7 +268,7 @@ if (text) *parsed = '"' + QString::fromUtf8(text, len) + '"'; } else { - Q_ASSERT(false); + Q_UNREACHABLE(); } return err; }
diff --git a/tests/tests.pro b/tests/tests.pro deleted file mode 100644 index 627ffbc..0000000 --- a/tests/tests.pro +++ /dev/null
@@ -1,3 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = parser encoder cpp tojson -msvc: SUBDIRS -= tojson
diff --git a/tests/tojson/CMakeLists.txt b/tests/tojson/CMakeLists.txt new file mode 100644 index 0000000..ade1a8b --- /dev/null +++ b/tests/tojson/CMakeLists.txt
@@ -0,0 +1,3 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +tinycbor_add_qtest(tst_tojson tst_tojson.cpp)
diff --git a/tests/tojson/tojson.pro b/tests/tojson/tojson.pro deleted file mode 100644 index b422652..0000000 --- a/tests/tojson/tojson.pro +++ /dev/null
@@ -1,8 +0,0 @@ -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
diff --git a/tests/tojson/tst_tojson.cpp b/tests/tojson/tst_tojson.cpp index d91aee1..d0eb90b 100644 --- a/tests/tojson/tst_tojson.cpp +++ b/tests/tojson/tst_tojson.cpp
@@ -22,8 +22,10 @@ ** ****************************************************************************/ +#define __STDC_WANT_IEC_60559_TYPES_EXT__ #include <QtTest> #include "cbor.h" +#include "cborinternal_p.h" #include "cborjson.h" #include <locale.h> @@ -75,6 +77,9 @@ void metaDataAndTagsToObjects(); void metaDataForKeys_data(); void metaDataForKeys(); + + void recursionLimit_data(); + void recursionLimit(); }; #include "tst_tojson.moc" @@ -95,13 +100,20 @@ 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"; + QTest::newRow("2^53+1") << raw("\x1b\0\x20\0\0""\0\0\0\1") << "9007199254740993"; + QTest::newRow("2^63-1") << raw("\x1b\x7f\xff\xff\xff""\xff\xff\xff\xff") << "9223372036854775807"; + QTest::newRow("2^64-1") << raw("\x1b\xff\xff\xff\xff""\xff\xff\xff\xff") << "18446744073709551615"; // 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("-2^53-1") << raw("\x3b\0\x20\0\0""\0\0\0\0") << "-9007199254740993"; + QTest::newRow("-2^63+1") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xfe") << "-9223372036854775807"; + QTest::newRow("-2^63") << raw("\x3b\x7f\xff\xff\xff""\xff\xff\xff\xff") << "-9223372036854775808"; + QTest::newRow("-2^63-1") << raw("\x3b\x80\0\0\0""\0\0\0\0") << "-9223372036854775809"; + QTest::newRow("-2^64+1") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xff\xfe") << "-18446744073709551615"; + QTest::newRow("-2^64") << raw("\x3b\xff\xff\xff\xff""\xff\xff\xff\xff") << "-18446744073709551616"; QTest::newRow("false") << raw("\xf4") << "false"; QTest::newRow("true") << raw("\xf5") << "true"; @@ -157,6 +169,18 @@ 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\""; + + // strings containing characters that are escaped in JSON + QTest::newRow("null") << raw("\x61\0") << R"("\u0000")"; + QTest::newRow("bell") << raw("\x61\7") << R"("\u0007")"; // not \\a + QTest::newRow("backspace") << raw("\x61\b") << R"("\b")"; + QTest::newRow("tab") << raw("\x61\t") << R"("\t")"; + QTest::newRow("carriage-return") << raw("\x61\r") << R"("\r")"; + QTest::newRow("line-feed") << raw("\x61\n") << R"("\n")"; + QTest::newRow("form-feed") << raw("\x61\f") << R"("\f")"; + QTest::newRow("esc") << raw("\x61\x1f") << R"("\u001f")"; + QTest::newRow("quote") << raw("\x61\"") << R"("\"")"; + QTest::newRow("backslash") << raw("\x61\\") << R"("\\")"; } void addNonJsonData() @@ -409,6 +433,15 @@ 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}"; + + // nested strings ought to be escaped + QTest::newRow("array-emptystring") << raw("\x81\x60") << R"([\"\"])"; + QTest::newRow("array-string1") << raw("\x81\x61 ") << R"([\" \"])"; + + // and escaped chracters in strings end up doubly escaped + QTest::newRow("array-string-null") << raw("\x81\x61\0") << R"([\"\\u0000\"])"; + QTest::newRow("array-string-quote") << raw("\x81\x61\"") << R"([\"\\\"\"])"; + QTest::newRow("array-string-backslash") << raw("\x81\x61\\") << R"([\"\\\\\"])"; } void tst_ToJson::nonStringKeyMaps() @@ -592,16 +625,6 @@ 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"; @@ -728,4 +751,33 @@ CborConvertAddMetadata | CborConvertStringifyMapKeys); } +void tst_ToJson::recursionLimit_data() +{ + static const int recursions = CBOR_PARSER_MAX_RECURSIONS + 2; + QTest::addColumn<QByteArray>("data"); + + QTest::newRow("arrays") << QByteArray(recursions, '\x81'); + QTest::newRow("tags") << QByteArray(recursions, '\xc1'); + + QByteArray mapData; + mapData.reserve(2 * recursions); + for (int i = 0; i < recursions; ++i) + mapData.append("\xa1\x60", 2); + QTest::newRow("maps") << mapData; +} + +void tst_ToJson::recursionLimit() +{ + QFETCH(QByteArray, data); + CborParser parser; + CborValue first; + CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first); + QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); + + QString parsed; + err = parseOne(&first, &parsed, 0); + + QCOMPARE(err, CborErrorNestingTooDeep); +} + QTEST_MAIN(tst_ToJson)
diff --git a/tinycbor.pc.in b/tinycbor.pc.in index 382779a..ef16fc8 100644 --- a/tinycbor.pc.in +++ b/tinycbor.pc.in
@@ -1,11 +1,11 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_INCLUDEDIR@ Name: TinyCBOR Description: A tiny CBOR encoder and decoder library -Version: @version@ +Version: @PROJECT_VERSION@ Libs: -L${libdir} -ltinycbor Libs.private: -lm Cflags: -I${includedir}/tinycbor
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..8ad12e2 --- /dev/null +++ b/tools/CMakeLists.txt
@@ -0,0 +1,5 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +include(TinyCBORHelpers) +add_subdirectory(cbordump) +add_subdirectory(json2cbor)
diff --git a/tools/Makefile b/tools/Makefile deleted file mode 100644 index fed6108..0000000 --- a/tools/Makefile +++ /dev/null
@@ -1,12 +0,0 @@ -CFLAGS = -O2 -g -CPPFLAGS = -I../src -VPATH = cbordump:../src - -all: ../bin ../bin/cbordump -../bin: - @-mkdir ../bin - -../bin/cbordump: cbordump.o cborparser.o cborparser_dup_string.o cbortojson.o cborerrorstrings.o cborpretty.o - $(CC) -o $@ $^ - $(RM) $^ -
diff --git a/tools/cbordump/CMakeLists.txt b/tools/cbordump/CMakeLists.txt new file mode 100644 index 0000000..c03fc5e --- /dev/null +++ b/tools/cbordump/CMakeLists.txt
@@ -0,0 +1,6 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +if(NOT WIN32) + tinycbor_add_executable(cbordump cbordump.c) + install(TARGETS cbordump) +endif()
diff --git a/tools/cbordump/cbordump.pro b/tools/cbordump/cbordump.pro deleted file mode 100644 index 71ae6f7..0000000 --- a/tools/cbordump/cbordump.pro +++ /dev/null
@@ -1,10 +0,0 @@ -TEMPLATE = app -CONFIG += console -CONFIG -= app_bundle -CONFIG -= qt -DESTDIR = ../../bin - -CBORDIR = $$PWD/../../src -INCLUDEPATH += $$CBORDIR -SOURCES += cbordump.c -LIBS += ../../lib/libtinycbor.a
diff --git a/tools/json2cbor/CMakeLists.txt b/tools/json2cbor/CMakeLists.txt new file mode 100644 index 0000000..a873114 --- /dev/null +++ b/tools/json2cbor/CMakeLists.txt
@@ -0,0 +1,10 @@ +# Copyright (C) 2025 Intel Corporation +# SPDX-License-Identifier: MIT +include(FindPkgConfig) +pkg_check_modules(LIBCJSON libcjson) +if(LIBCJSON_FOUND) + tinycbor_add_executable(json2cbor json2cbor.c) + target_include_directories(json2cbor SYSTEM PUBLIC ${LIBCJSON_INCLUDE_DIRS}) + target_link_libraries(json2cbor ${LIBCJSON_LIBRARIES}) + install(TARGETS json2cbor) +endif()
diff --git a/tools/json2cbor/json2cbor.c b/tools/json2cbor/json2cbor.c index 090ee15..bb35108 100644 --- a/tools/json2cbor/json2cbor.c +++ b/tools/json2cbor/json2cbor.c
@@ -328,15 +328,17 @@ err = cbor_encode_double(encoder, json->valuedouble); if (err == CborErrorOutOfMemory) { - buffersize += 1024; - uint8_t *newbuffer = realloc(buffer, buffersize); + ptrdiff_t offset = cbor_encoder_get_buffer_size(&container, buffer); + size_t newbuffersize = buffersize + 1024; + uint8_t *newbuffer = realloc(buffer, newbuffersize); if (newbuffer == NULL) return err; *encoder = container; // restore state - encoder->data.ptr = newbuffer + (container.data.ptr - buffer); - encoder->end = newbuffer + buffersize; + encoder->data.ptr = newbuffer + offset; + encoder->end = newbuffer + newbuffersize; buffer = newbuffer; + buffersize = newbuffersize; goto encode_double; } return err;
diff --git a/tools/json2cbor/json2cbor.pro b/tools/json2cbor/json2cbor.pro deleted file mode 100644 index fd6bcd0..0000000 --- a/tools/json2cbor/json2cbor.pro +++ /dev/null
@@ -1,20 +0,0 @@ -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 -}