From 5f50d6b344f0d272a12c6f5598dde05bbe025e97 Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Fri, 3 May 2024 18:27:44 -0400 Subject: [PATCH] Vendor import of libcbor 0.11.0 --- .circleci/config.yml | 30 +-- .cirrus.yml | 26 +++ .readthedocs.yaml | 21 ++ CHANGELOG.md | 15 +- CMakeLists.txt | 18 +- CONTRIBUTING.md | 2 +- Doxyfile | 2 +- README.md | 7 +- appveyor.yml | 14 -- doc/source/api/item_types.rst | 2 +- doc/source/conf.py | 18 +- doc/source/development.rst | 6 +- doc/source/index.rst | 8 +- doc/source/internal.rst | 4 +- doc/source/requirements.txt | 52 ++--- ...nformance.rst => standard_conformance.rst} | 6 +- examples/CMakeLists.txt | 4 + .../third_party/libcbor/cbor/configuration.h | 4 +- examples/cbor2cjson.c | 123 +++++++++++ examples/cjson2cbor.c | 4 +- examples/data/all_types.cbor | Bin 0 -> 354 bytes examples/readfile.c | 2 +- oss-fuzz/build.sh | 2 +- src/CMakeLists.txt | 21 +- src/cbor.c | 82 ++++--- src/cbor/common.h | 2 +- src/cbor/internal/builder_callbacks.c | 27 +-- src/cbor/internal/loaders.c | 2 +- src/cbor/internal/memory_utils.h | 2 +- src/cbor/internal/unicode.c | 6 +- src/cbor/internal/unicode.h | 6 +- src/cbor/maps.c | 1 + src/cbor/streaming.c | 43 ++-- src/cbor/strings.c | 10 + src/cbor/strings.h | 19 +- src/libcborConfig.cmake.in | 8 + test/callbacks_test.c | 25 +++ test/cbor_serialize_test.c | 32 +++ test/cbor_stream_decode_test.c | 8 +- test/float_ctrl_test.c | 6 +- test/pretty_printer_test.c | 201 ++++++++++++++++-- test/string_test.c | 67 ++++++ test/tag_test.c | 23 ++ test/test_allocator.c | 2 +- 44 files changed, 748 insertions(+), 215 deletions(-) create mode 100644 .cirrus.yml create mode 100644 .readthedocs.yaml delete mode 100644 appveyor.yml rename doc/source/{rfc_conformance.rst => standard_conformance.rst} (73%) create mode 100644 examples/cbor2cjson.c create mode 100644 examples/data/all_types.cbor create mode 100644 src/libcborConfig.cmake.in diff --git a/.circleci/config.yml b/.circleci/config.yml index 69371b67d624..4391ad8d1e9c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,12 +4,14 @@ commands: linux-setup: steps: - run: sudo apt-get update - - run: sudo apt-get install -y cmake ${TOOLCHAIN_PACKAGES} - - run: sudo apt install libcmocka-dev + # NEEDRESTART_MODE prevents automatic restarts which seem to hang. + - run: sudo NEEDRESTART_MODE=l apt-get install -y cmake ${TOOLCHAIN_PACKAGES} + - run: sudo NEEDRESTART_MODE=l apt-get install -y libcmocka-dev libcjson-dev build: steps: - run: > cmake -DWITH_TESTS=ON \ + -DWITH_EXAMPLES=ON \ -DCMAKE_BUILD_TYPE=Debug \ -DSANITIZE=OFF \ -DCOVERAGE="${CMAKE_COVERAGE:='OFF'}" \ @@ -31,14 +33,14 @@ orbs: jobs: static-test: - machine: - image: ubuntu-2204:2022.10.2 + machine: &default-machine + image: ubuntu-2204:2023.07.2 environment: TOOLCHAIN_PACKAGES: g++ steps: - checkout - linux-setup - - run: sudo apt-get install -y clang-format doxygen cppcheck + - run: sudo NEEDRESTART_MODE=l apt-get install -y clang-format doxygen cppcheck - run: cppcheck --inline-suppr --error-exitcode=1 . - run: bash clang-format.sh --verbose - run: > @@ -60,14 +62,14 @@ jobs: build-and-test: machine: - image: ubuntu-2204:2022.10.2 + <<: *default-machine environment: TOOLCHAIN_PACKAGES: g++ CMAKE_COVERAGE: ON steps: - checkout - linux-setup - - run: sudo apt-get install -y valgrind + - run: sudo NEEDRESTART_MODE=l apt-get install -y valgrind - build - test - run: ctest -T Coverage @@ -81,7 +83,7 @@ jobs: build-and-test-clang: machine: - image: ubuntu-2204:2022.10.2 + <<: *default-machine environment: TOOLCHAIN_PACKAGES: clang CC: clang @@ -94,11 +96,11 @@ jobs: build-and-test-32b: machine: - image: ubuntu-2204:2022.10.2 + <<: *default-machine steps: - checkout - run: sudo apt-get update - - run: sudo apt-get install -y cmake gcc-multilib g++-multilib libc6-dev-i386 + - run: sudo NEEDRESTART_MODE=l apt-get install -y cmake gcc-multilib g++-multilib libc6-dev-i386 # Make cmocka from source w/ 32b setup - run: git clone https://git.cryptomilk.org/projects/cmocka.git ~/cmocka - run: > @@ -117,7 +119,7 @@ jobs: build-and-test-release-clang: machine: - image: ubuntu-2204:2022.10.2 + <<: *default-machine environment: TOOLCHAIN_PACKAGES: clang CC: clang @@ -130,7 +132,7 @@ jobs: llvm-coverage: machine: - image: ubuntu-2204:2022.10.2 + <<: *default-machine environment: TOOLCHAIN_PACKAGES: clang CC: clang @@ -145,7 +147,7 @@ jobs: build-and-test-arm: machine: - image: ubuntu-2204:2022.10.2 + <<: *default-machine environment: TOOLCHAIN_PACKAGES: g++ resource_class: arm.medium @@ -157,7 +159,7 @@ jobs: build-bazel: machine: - image: ubuntu-2204:2022.10.2 + image: ubuntu-2204:2023.07.2 environment: TOOLCHAIN_PACKAGES: g++ steps: diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 000000000000..948ae23b4a98 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,26 @@ +freebsd_task: + install_script: + - ASSUME_ALWAYS_YES=yes pkg bootstrap -f && pkg install -y cmocka cmake ninja + build_script: + - mkdir build + - cd build + - cmake -GNinja -DWITH_TESTS=ON + -DCBOR_CUSTOM_ALLOC=ON + -DCMAKE_BUILD_TYPE=Debug + -DSANITIZE=OFF + .. + - ninja -j $(sysctl -n hw.ncpu) + test_script: + - cd build + - ctest -VV + matrix: + # From gcloud compute images list --project freebsd-org-cloud-dev --no-standard-images + - name: freebsd-13-2 + freebsd_instance: + image_family: freebsd-13-2 + - name: freebsd-14-0 + freebsd_instance: + image_family: freebsd-14-0 + - name: freebsd-15-0-snap + freebsd_instance: + image_family: freebsd-15-0-snap diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000000..f2514ffae113 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,21 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +sphinx: + configuration: doc/source/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: doc/source/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index f2475f308c7a..3c331f9266ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ Template: Next --------------------- +0.11.0 (2024-02-04) +--------------------- +- [Updated documentation to refer to RFC 8949](https://github.com/PJK/libcbor/issues/269) +- Improvements to `cbor_describe` + - [Bytestring data will now be printed as well](https://github.com/PJK/libcbor/pull/281) by [akallabeth](https://github.com/akallabeth) + - [Formatting consistency and clarity improvements](https://github.com/PJK/libcbor/pull/285) +- [Fix `cbor_string_set_handle` not setting the codepoint count](https://github.com/PJK/libcbor/pull/286) +- BREAKING: [`cbor_load` will no longer fail on input strings that are well-formed but not valid UTF-8](https://github.com/PJK/libcbor/pull/286) + - If you were relying on the validation, please check the result using `cbor_string_codepoint_count` instead +- BREAKING: [All decoders like `cbor_load` and `cbor_stream_decode` will accept all well-formed tag values](https://github.com/PJK/libcbor/pull/308) (bug discovered by [dskern-github](https://github.com/dskern-github)) + - Previously, decoding of certain values would fail with `CBOR_ERR_MALFORMATED` or `CBOR_DECODER_ERROR` + - This also makes decoding symmetrical with serialization, which already accepts all values + 0.10.2 (2023-01-31) --------------------- - [Fixed minor test bug causing failures for x86 Linux](https://github.com/PJK/libcbor/pull/266) (discovered by [trofi](https://github.com/PJK/libcbor/issues/263)) @@ -117,7 +130,7 @@ Next Breaks build & header compatibility due to: - Improved build configuration and feature check macros -- Endianess configuration fixes (by Erwin Kroon and David Grigsby) +- Endianness configuration fixes (by Erwin Kroon and David Grigsby) - pkg-config compatibility (by Vincent Bernat) - enable use of versioned SONAME (by Vincent Bernat) - better fuzzer (wasn't random until now, ooops) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0a7846cfa3..16b9f0875d33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,18 @@ cmake_minimum_required(VERSION 3.0) + project(libcbor) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/") include(CTest) +include(GNUInstallDirs) # Provides CMAKE_INSTALL_ variables SET(CBOR_VERSION_MAJOR "0") -SET(CBOR_VERSION_MINOR "10") -SET(CBOR_VERSION_PATCH "2") +SET(CBOR_VERSION_MINOR "11") +SET(CBOR_VERSION_PATCH "0") SET(CBOR_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.${CBOR_VERSION_PATCH}) -set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true) +option(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY "cmake --build --target install does not depend on cmake --build" true) +option(BUILD_SHARED_LIBS "Build as a shared library" false) + include(CheckIncludeFiles) include(TestBigEndian) @@ -19,10 +23,10 @@ endif() option(CBOR_CUSTOM_ALLOC "Custom, dynamically defined allocator support" OFF) if(CBOR_CUSTOM_ALLOC) - message(WARNING + message(WARNING "CBOR_CUSTOM_ALLOC has been deprecated. Custom allocators are now enabled by default." "The flag is a no-op and will be removed in the next version. " - "Please remove CBOR_CUSTOM_ALLOC from your build configuation.") + "Please remove CBOR_CUSTOM_ALLOC from your build configuration.") endif(CBOR_CUSTOM_ALLOC) option(CBOR_PRETTY_PRINTER "Include a pretty-printing routine" ON) @@ -138,12 +142,10 @@ if (COVERAGE) endif() endif (COVERAGE) - # We want to generate configuration.h from the template and make it so that it is accessible using the same # path during both library build and installed header use, without littering the source dir. -# Using cbor/configuration.h in the build dir works b/c headers will be installed to /cbor configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/cbor/configuration.h.in ${PROJECT_BINARY_DIR}/cbor/configuration.h) -install(FILES ${PROJECT_BINARY_DIR}/cbor/configuration.h DESTINATION include/cbor) +install(FILES ${PROJECT_BINARY_DIR}/cbor/configuration.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cbor) # Make the header visible at compile time include_directories(${PROJECT_BINARY_DIR}) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc834d21a46b..9bd42a06b6dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ libcbor is maintained by [@PJK](https://github.com/PJK) in his spare time on a best-effort basis. -Community contributions are welcome as long as they align with the [project priorities](https://github.com/PJK/libcbor#main-features) and [goals](https://libcbor.readthedocs.io/en/latest/development.html#goals) and follow the guidelines described belows. +Community contributions are welcome as long as they align with the [project priorities](https://github.com/PJK/libcbor#main-features) and [goals](https://libcbor.readthedocs.io/en/latest/development.html#goals) and follow the guidelines described below. ## Principles diff --git a/Doxyfile b/Doxyfile index 2571057646d7..67df45cfbb94 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = libcbor # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.10.2 +PROJECT_NUMBER = 0.11.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/README.md b/README.md index 6d34ba9d4dcb..ea54bed9437b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # [libcbor](https://github.com/PJK/libcbor) [![CircleCI](https://circleci.com/gh/PJK/libcbor/tree/master.svg?style=svg)](https://circleci.com/gh/PJK/libcbor/tree/master) -[![Build status](https://ci.appveyor.com/api/projects/status/8kkmvmefelsxp5u2?svg=true)](https://ci.appveyor.com/project/PJK/libcbor) [![Documentation Status](https://readthedocs.org/projects/libcbor/badge/?version=latest)](https://readthedocs.org/projects/libcbor/?badge=latest) [![latest packaged version(s)](https://repology.org/badge/latest-versions/libcbor.svg)](https://repology.org/project/libcbor/versions) [![codecov](https://codecov.io/gh/PJK/libcbor/branch/master/graph/badge.svg)](https://codecov.io/gh/PJK/libcbor) -**libcbor** is a C library for parsing and generating [CBOR](https://tools.ietf.org/html/rfc7049), the general-purpose schema-less binary data format. +**libcbor** is a C library for parsing and generating [CBOR](https://cbor.io/), the general-purpose schema-less binary data format. ## Main features - - Complete RFC conformance - - Robust C99 implementation + - Complete [IETF RFC 8949 (STD 94)](https://www.rfc-editor.org/info/std94) conformance + - Robust platform-independent C99 implementation - Layered architecture offers both control and convenience - Flexible memory management - No shared global state - threading friendly diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 6a0cca219b92..000000000000 --- a/appveyor.yml +++ /dev/null @@ -1,14 +0,0 @@ -image: Visual Studio 2022 -version: '{build}' -platform: x64 - -skip_branch_with_pr: true - -before_build: -- cmake -H. -Bbuild - -build_script: -- if "%APPVEYOR_REPO_TAG%"=="true" (set CONFIGURATION=RelWithDebInfo) else (set CONFIGURATION=Debug) -- cmake --build build --config "%CONFIGURATION%" - -# TODO enable CMocka tests, maybe package the binaries diff --git a/doc/source/api/item_types.rst b/doc/source/api/item_types.rst index 1452b3e5f90e..1051b97ded7b 100644 --- a/doc/source/api/item_types.rst +++ b/doc/source/api/item_types.rst @@ -1,7 +1,7 @@ Types of items =============================================== -Every :type:`cbor_item_t` has a :type:`cbor_type` associated with it - these constants correspond to the types specified by the `CBOR standard `_: +Every :type:`cbor_item_t` has a :type:`cbor_type` associated with it - these constants correspond to the types specified by the `CBOR standard `_: .. doxygenenum:: cbor_type diff --git a/doc/source/conf.py b/doc/source/conf.py index 585efc8a436f..0eee7103bb5a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -33,7 +33,8 @@ 'breathe', 'sphinx.ext.mathjax', 'sphinx.ext.autodoc', - 'sphinx.ext.ifconfig' + 'sphinx.ext.ifconfig', + 'sphinx_rtd_theme' ] import subprocess, os @@ -76,8 +77,8 @@ # built documents. # # The short X.Y version. -version = '0.10' -release = '0.10.2' +version = '0.11' +release = '0.11.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -127,7 +128,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -285,12 +286,3 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False - - -if not on_rtd: # only import and set the theme if we're building docs locally - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# otherwise, readthedocs.org uses their theme by default, so no need to specify it - diff --git a/doc/source/development.rst b/doc/source/development.rst index 13e91ef25762..0b0ac21007ed 100644 --- a/doc/source/development.rst +++ b/doc/source/development.rst @@ -22,15 +22,15 @@ everywhere. Goals ~~~~~~~~~~~~~~~~~~~~~~ -RFC-conformance and full feature support -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Standard conformance and full feature support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Anything the standard allows, libcbor can do. **Why?** Because conformance and interoperability is the point of defining standards. Clients expect the support to be feature-complete and there is no significant complexity reduction that can be achieved by slightly -cutting corners, which means that the incremental cost of full RFC support is +cutting corners, which means that the incremental cost of full [CBOR standard](https://www.rfc-editor.org/info/std94) support is comparatively small over "almost-conformance" seen in many alternatives. diff --git a/doc/source/index.rst b/doc/source/index.rst index 79293e798c3b..d3d62cf75c41 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -9,7 +9,7 @@ Overview Main features - - Complete RFC conformance [#]_ + - Complete `IETF RFC 8949 (STD 94) `_ conformance [#]_ - Robust C99 implementation - Layered architecture offers both control and convenience - Flexible memory management @@ -19,7 +19,7 @@ Main features - Extensive documentation and test suite - No runtime dependencies, small footprint -.. [#] See :doc:`rfc_conformance` +.. [#] See :doc:`standard_conformance` .. [#] With the exception of custom memory allocators (see :doc:`api/item_reference_counting`) @@ -31,9 +31,9 @@ Contents using api tests - rfc_conformance + standard_conformance internal changelog development -.. _CBOR: http://tools.ietf.org/html/rfc7049 +.. _CBOR: https://www.rfc-editor.org/info/std94 diff --git a/doc/source/internal.rst b/doc/source/internal.rst index 2c62a82b2329..e30cb11dffa1 100644 --- a/doc/source/internal.rst +++ b/doc/source/internal.rst @@ -6,7 +6,7 @@ Internal workings of *libcbor* are mostly derived from the specification. The pu Terminology --------------- === ====================== ======================================================================================================================================== -MTB Major Type Byte http://tools.ietf.org/html/rfc7049#section-2.1 +MTB Major Type Byte https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1 --- ---------------------- ---------------------------------------------------------------------------------------------------------------------------------------- DST Dynamically Sized Type Type whose storage requirements cannot be determined @@ -32,7 +32,7 @@ and also borrowing from General notes on the API design -------------------------------- -The API design has two main driving priciples: +The API design has two main driving principles: 1. Let the client manage the memory as much as possible 2. Behave exactly as specified by the standard diff --git a/doc/source/requirements.txt b/doc/source/requirements.txt index a82e34e58ad2..502d79cc62e6 100644 --- a/doc/source/requirements.txt +++ b/doc/source/requirements.txt @@ -1,31 +1,31 @@ -alabaster==0.7.12 -Babel==2.9.1 -breathe==4.33.1 -certifi==2022.12.7 -charset-normalizer==2.0.12 -colorama==0.4.4 -docutils==0.17.1 -idna==3.3 -imagesize==1.3.0 -importlib-metadata==4.11.3 -Jinja2==3.0.3 +alabaster==0.7.13 +Babel==2.13.1 +breathe==4.35.0 +certifi==2023.11.17 +charset-normalizer==3.3.2 +colorama==0.4.6 +docutils==0.18.1 +idna==3.4 +imagesize==1.4.1 +importlib-metadata==6.8.0 +Jinja2==3.1.2 livereload==2.6.3 -MarkupSafe==2.1.1 -packaging==21.3 -Pygments==2.11.2 -pyparsing==3.0.7 +MarkupSafe==2.1.3 +packaging==23.2 +Pygments==2.16.1 +pyparsing==3.1.1 pytz==2021.3 -requests==2.27.1 +requests==2.31.0 snowballstemmer==2.2.0 -Sphinx==4.4.0 +Sphinx==7.2.6 sphinx-autobuild==2021.3.14 -sphinx-rtd-theme==1.0.0 -sphinxcontrib-applehelp==1.0.2 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.0 +sphinx-rtd-theme==1.3.0 +sphinxcontrib-applehelp==1.0.7 +sphinxcontrib-devhelp==1.0.5 +sphinxcontrib-htmlhelp==2.0.4 sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.5 -tornado==6.1 -urllib3==1.26.9 -zipp==3.7.0 +sphinxcontrib-qthelp==1.0.6 +sphinxcontrib-serializinghtml==1.1.9 +tornado==6.3.3 +urllib3==2.1.0 +zipp==3.17.0 diff --git a/doc/source/rfc_conformance.rst b/doc/source/standard_conformance.rst similarity index 73% rename from doc/source/rfc_conformance.rst rename to doc/source/standard_conformance.rst index 6848fbacd464..62965f0c4493 100644 --- a/doc/source/rfc_conformance.rst +++ b/doc/source/standard_conformance.rst @@ -1,13 +1,13 @@ -RFC conformance +IETF standard conformance ========================= -*libcbor* is, generally speaking, very faithful implementation of `RFC 7049 `_. There are, however, some limitations imposed by technical constraints. +*libcbor* is, generally speaking, a very faithful implementation of `IETF RFC 8949 (STD 94) `_. There are, however, some limitations related to the numerical range and precision available in portable C99. Bytestring length ------------------- There is no explicit limitation of indefinite length byte strings. [#]_ *libcbor* will not handle byte strings with more chunks than the maximum value of :type:`size_t`. On any sane platform, such string would not fit in the memory anyway. It is, however, possible to process arbitrarily long strings and byte strings using the streaming decoder. -.. [#] https://tools.ietf.org/html/rfc7049#section-2.2.2 +.. [#] https://www.rfc-editor.org/rfc/rfc8949.html#section-3.2.3 "Half-precision" IEEE 754 floats --------------------------------- diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index eaf985ba32cd..b1f2ec290e7a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -22,6 +22,10 @@ if(CJSON_FOUND) add_executable(cjson2cbor cjson2cbor.c) target_include_directories(cjson2cbor PUBLIC ${CJSON_INCLUDE_DIRS}) target_link_libraries(cjson2cbor cbor ${CJSON_LIBRARY}) + + add_executable(cbor2cjson cbor2cjson.c) + target_include_directories(cbor2cjson PUBLIC ${CJSON_INCLUDE_DIRS}) + target_link_libraries(cbor2cjson cbor ${CJSON_LIBRARY}) endif() file(COPY data DESTINATION .) diff --git a/examples/bazel/third_party/libcbor/cbor/configuration.h b/examples/bazel/third_party/libcbor/cbor/configuration.h index 3472cf49d8fe..ddf6b9dc5f2b 100644 --- a/examples/bazel/third_party/libcbor/cbor/configuration.h +++ b/examples/bazel/third_party/libcbor/cbor/configuration.h @@ -2,8 +2,8 @@ #define LIBCBOR_CONFIGURATION_H #define CBOR_MAJOR_VERSION 0 -#define CBOR_MINOR_VERSION 10 -#define CBOR_PATCH_VERSION 2 +#define CBOR_MINOR_VERSION 11 +#define CBOR_PATCH_VERSION 0 #define CBOR_BUFFER_GROWTH 2 #define CBOR_MAX_STACK_SIZE 2048 diff --git a/examples/cbor2cjson.c b/examples/cbor2cjson.c new file mode 100644 index 000000000000..99ddc9ee09e1 --- /dev/null +++ b/examples/cbor2cjson.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include +#include + +#include "cbor.h" + +void usage(void) { + printf("Usage: cbor2cjson [input file]\n"); + exit(1); +} + +cJSON* cbor_to_cjson(cbor_item_t* item) { + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: + return cJSON_CreateNumber(cbor_get_int(item)); + case CBOR_TYPE_NEGINT: + return cJSON_CreateNumber(-1 - cbor_get_int(item)); + case CBOR_TYPE_BYTESTRING: + // cJSON only handles null-terminated string -- binary data would have to + // be escaped + return cJSON_CreateString("Unsupported CBOR item: Bytestring"); + case CBOR_TYPE_STRING: + if (cbor_string_is_definite(item)) { + // cJSON only handles null-terminated string + char* null_terminated_string = malloc(cbor_string_length(item) + 1); + memcpy(null_terminated_string, cbor_string_handle(item), + cbor_string_length(item)); + null_terminated_string[cbor_string_length(item)] = 0; + cJSON* result = cJSON_CreateString(null_terminated_string); + free(null_terminated_string); + return result; + } + return cJSON_CreateString("Unsupported CBOR item: Chunked string"); + case CBOR_TYPE_ARRAY: { + cJSON* result = cJSON_CreateArray(); + for (size_t i = 0; i < cbor_array_size(item); i++) { + cJSON_AddItemToArray(result, cbor_to_cjson(cbor_array_get(item, i))); + } + return result; + } + case CBOR_TYPE_MAP: { + cJSON* result = cJSON_CreateObject(); + for (size_t i = 0; i < cbor_map_size(item); i++) { + char* key = malloc(128); + snprintf(key, 128, "Surrogate key %zu", i); + // JSON only support string keys + if (cbor_isa_string(cbor_map_handle(item)[i].key) && + cbor_string_is_definite(cbor_map_handle(item)[i].key)) { + size_t key_length = cbor_string_length(cbor_map_handle(item)[i].key); + if (key_length > 127) key_length = 127; + // Null-terminated madness + memcpy(key, cbor_string_handle(cbor_map_handle(item)[i].key), + key_length); + key[key_length] = 0; + } + + cJSON_AddItemToObject(result, key, + cbor_to_cjson(cbor_map_handle(item)[i].value)); + free(key); + } + return result; + } + case CBOR_TYPE_TAG: + return cJSON_CreateString("Unsupported CBOR item: Tag"); + case CBOR_TYPE_FLOAT_CTRL: + if (cbor_float_ctrl_is_ctrl(item)) { + if (cbor_is_bool(item)) return cJSON_CreateBool(cbor_get_bool(item)); + if (cbor_is_null(item)) return cJSON_CreateNull(); + return cJSON_CreateString("Unsupported CBOR item: Control value"); + } + return cJSON_CreateNumber(cbor_float_get_float(item)); + } + + return cJSON_CreateNull(); +} + +/* + * Reads CBOR data from a file and outputs JSON using cJSON + * $ ./examples/cbor2cjson examples/data/nested_array.cbor + */ + +int main(int argc, char* argv[]) { + if (argc != 2) usage(); + FILE* f = fopen(argv[1], "rb"); + if (f == NULL) usage(); + fseek(f, 0, SEEK_END); + size_t length = (size_t)ftell(f); + fseek(f, 0, SEEK_SET); + unsigned char* buffer = malloc(length); + fread(buffer, length, 1, f); + + /* Assuming `buffer` contains `length` bytes of input data */ + struct cbor_load_result result; + cbor_item_t* item = cbor_load(buffer, length, &result); + free(buffer); + + if (result.error.code != CBOR_ERR_NONE) { + printf( + "There was an error while reading the input near byte %zu (read %zu " + "bytes in total): ", + result.error.position, result.read); + exit(1); + } + + cJSON* cjson_item = cbor_to_cjson(item); + char* json_string = cJSON_Print(cjson_item); + printf("%s\n", json_string); + free(json_string); + fflush(stdout); + + /* Deallocate the result */ + cbor_decref(&item); + cJSON_Delete(cjson_item); + + fclose(f); +} diff --git a/examples/cjson2cbor.c b/examples/cjson2cbor.c index eae78e1cf7a6..b67439902442 100644 --- a/examples/cjson2cbor.c +++ b/examples/cjson2cbor.c @@ -7,7 +7,7 @@ /** * This code demonstrates how cJSON (https://github.com/DaveGamble/cJSON) - * callbacks can be used in conjuction with the streaming parser to translate + * callbacks can be used in conjunction with the streaming parser to translate * JSON to CBOR. Please note that cbor_builder_* APIs are internal and thus * subject to change. * @@ -111,7 +111,7 @@ void cjson_cbor_stream_decode(cJSON *source, } void usage(void) { - printf("Usage: cjson [input JSON file]\n"); + printf("Usage: cjson2cbor [input JSON file]\n"); exit(1); } diff --git a/examples/data/all_types.cbor b/examples/data/all_types.cbor new file mode 100644 index 0000000000000000000000000000000000000000..00d66a0e667e734e02797c85b8937212cd9fd791 GIT binary patch literal 354 zcmY*UyG{Z@6rDvITPwgsuOA@zK!vGxygClbM9MQa1tM3pTdn)p7ufnPiS;8P*akh6mq0UEJC&jSuR2{{Q-qG zy6nGxxB6JK(ps9*bTP^tKBUk{G=kEBC*0;{H8LmR19t13T zwsLFhLjxUCxMKcnu=2nVlb10*raS$HlpL8*PhX$C_L%eUUpW+i9RL6T literal 0 HcmV?d00001 diff --git a/examples/readfile.c b/examples/readfile.c index 3f8db6152800..c6f4a3342d08 100644 --- a/examples/readfile.c +++ b/examples/readfile.c @@ -58,7 +58,7 @@ int main(int argc, char* argv[]) { case CBOR_ERR_SYNTAXERROR: { printf( "Syntactically malformed data -- see " - "http://tools.ietf.org/html/rfc7049\n"); + "https://www.rfc-editor.org/info/std94\n"); break; } case CBOR_ERR_NONE: { diff --git a/oss-fuzz/build.sh b/oss-fuzz/build.sh index 10853a398734..e7b1df3ada0a 100755 --- a/oss-fuzz/build.sh +++ b/oss-fuzz/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash -eu +#!/bin/bash -eux # Copyright 2019 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 72a0c901556d..e9312395db25 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,5 @@ set(SOURCES cbor.c allocators.c cbor/streaming.c cbor/internal/encoders.c cbor/internal/builder_callbacks.c cbor/internal/loaders.c cbor/internal/memory_utils.c cbor/internal/stack.c cbor/internal/unicode.c cbor/encoding.c cbor/serialization.c cbor/arrays.c cbor/common.c cbor/floats_ctrls.c cbor/bytestrings.c cbor/callbacks.c cbor/strings.c cbor/maps.c cbor/tags.c cbor/ints.c) -include(GNUInstallDirs) include(JoinPaths) include(CheckFunctionExists) set(CMAKE_SKIP_BUILD_RPATH FALSE) @@ -49,3 +48,23 @@ install(FILES cbor.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libcbor.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +include(CMakePackageConfigHelpers) +configure_package_config_file( + libcborConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/libcborConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libcbor + PATH_VARS CMAKE_INSTALL_INCLUDEDIR +) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/libcborConfigVersion.cmake + VERSION ${CBOR_VERSION} + COMPATIBILITY SameMajorVersion +) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/libcborConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libcborConfigVersion.cmake + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/libcbor + ) diff --git a/src/cbor.c b/src/cbor.c index 626ddddaecfc..a8b4bcd7a080 100644 --- a/src/cbor.c +++ b/src/cbor.c @@ -9,6 +9,7 @@ #include "cbor/internal/builder_callbacks.h" #include "cbor/internal/loaders.h" +#pragma clang diagnostic push cbor_item_t *cbor_load(cbor_data source, size_t source_size, struct cbor_load_result *result) { /* Context stack */ @@ -289,7 +290,6 @@ cbor_item_t *cbor_copy(cbor_item_t *item) { #include #include -#include #include #define __STDC_FORMAT_MACROS @@ -301,89 +301,105 @@ static int _pow(int b, int ex) { return res; } +static void _cbor_type_marquee(FILE *out, char *label, int indent) { + fprintf(out, "%*.*s[%s] ", indent, indent, " ", label); +} + static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) { - setlocale(LC_ALL, ""); + const int indent_offset = 4; switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: { - fprintf(out, "%*s[CBOR_TYPE_UINT] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_UINT", indent); fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item))); fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item)); break; } case CBOR_TYPE_NEGINT: { - fprintf(out, "%*s[CBOR_TYPE_NEGINT] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_NEGINT", indent); fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item))); - fprintf(out, "Value: -%" PRIu64 " -1\n", cbor_get_int(item)); + fprintf(out, "Value: -%" PRIu64 " - 1\n", cbor_get_int(item)); break; } case CBOR_TYPE_BYTESTRING: { - fprintf(out, "%*s[CBOR_TYPE_BYTESTRING] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_BYTESTRING", indent); if (cbor_bytestring_is_indefinite(item)) { - fprintf(out, "Indefinite, with %zu chunks:\n", + fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n", cbor_bytestring_chunk_count(item)); for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) _cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out, - indent + 4); + indent + indent_offset); } else { - fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item)); + const unsigned char *data = cbor_bytestring_handle(item); + fprintf(out, "Definite, Length: %zuB, Data:\n", + cbor_bytestring_length(item)); + fprintf(out, "%*s", indent + indent_offset, " "); + for (size_t i = 0; i < cbor_bytestring_length(item); i++) + fprintf(out, "%02x", (int)(data[i] & 0xff)); + fprintf(out, "\n"); } break; } case CBOR_TYPE_STRING: { - fprintf(out, "%*s[CBOR_TYPE_STRING] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_STRING", indent); if (cbor_string_is_indefinite(item)) { - fprintf(out, "Indefinite, with %zu chunks:\n", + fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n", cbor_string_chunk_count(item)); for (size_t i = 0; i < cbor_string_chunk_count(item); i++) _cbor_nested_describe(cbor_string_chunks_handle(item)[i], out, - indent + 4); + indent + indent_offset); } else { - fprintf(out, "Definite, length %zuB, %zu codepoints\n", + fprintf(out, "Definite, Length: %zuB, Codepoints: %zu, Data:\n", cbor_string_length(item), cbor_string_codepoint_count(item)); - /* Careful - this doesn't support multibyte characters! */ - /* Printing those is out of the scope of this demo :) */ - /* libICU is your friend */ - fprintf(out, "%*s", indent + 4, " "); - /* XXX: no null at the end -> confused vprintf */ - fwrite(cbor_string_handle(item), (int)cbor_string_length(item), 1, out); + fprintf(out, "%*s", indent + indent_offset, " "); + // Note: The string is not escaped, whitespace and control character + // will be printed in verbatim and take effect. + fwrite(cbor_string_handle(item), sizeof(unsigned char), + cbor_string_length(item), out); fprintf(out, "\n"); } break; } case CBOR_TYPE_ARRAY: { - fprintf(out, "%*s[CBOR_TYPE_ARRAY] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_ARRAY", indent); if (cbor_array_is_definite(item)) { - fprintf(out, "Definite, size: %zu\n", cbor_array_size(item)); + fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_array_size(item)); } else { - fprintf(out, "Indefinite, size: %zu\n", cbor_array_size(item)); + fprintf(out, "Indefinite, Size: %zu, Contents:\n", + cbor_array_size(item)); } for (size_t i = 0; i < cbor_array_size(item); i++) - _cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4); + _cbor_nested_describe(cbor_array_handle(item)[i], out, + indent + indent_offset); break; } case CBOR_TYPE_MAP: { - fprintf(out, "%*s[CBOR_TYPE_MAP] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_MAP", indent); if (cbor_map_is_definite(item)) { - fprintf(out, "Definite, size: %zu\n", cbor_map_size(item)); + fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_map_size(item)); } else { - fprintf(out, "Indefinite, size: %zu\n", cbor_map_size(item)); + fprintf(out, "Indefinite, Size: %zu, Contents:\n", cbor_map_size(item)); } + // TODO: Label and group keys and values for (size_t i = 0; i < cbor_map_size(item); i++) { - _cbor_nested_describe(cbor_map_handle(item)[i].key, out, indent + 4); - _cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4); + fprintf(out, "%*sMap entry %zu\n", indent + indent_offset, " ", i); + _cbor_nested_describe(cbor_map_handle(item)[i].key, out, + indent + 2 * indent_offset); + _cbor_nested_describe(cbor_map_handle(item)[i].value, out, + indent + 2 * indent_offset); } break; } case CBOR_TYPE_TAG: { - fprintf(out, "%*s[CBOR_TYPE_TAG] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_TAG", indent); fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item)); - _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4); + _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, + indent + indent_offset); break; } case CBOR_TYPE_FLOAT_CTRL: { - fprintf(out, "%*s[CBOR_TYPE_FLOAT_CTRL] ", indent, " "); + _cbor_type_marquee(out, "CBOR_TYPE_FLOAT_CTRL", indent); if (cbor_float_ctrl_is_ctrl(item)) { if (cbor_is_bool(item)) fprintf(out, "Bool: %s\n", cbor_get_bool(item) ? "true" : "false"); @@ -392,10 +408,10 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) { else if (cbor_is_null(item)) fprintf(out, "Null\n"); else - fprintf(out, "Simple value %d\n", cbor_ctrl_value(item)); + fprintf(out, "Simple value: %d\n", cbor_ctrl_value(item)); } else { fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item))); - fprintf(out, "value: %lf\n", cbor_float_get_float(item)); + fprintf(out, "Value: %lf\n", cbor_float_get_float(item)); } break; } diff --git a/src/cbor/common.h b/src/cbor/common.h index fddaabf3b9cf..1d0b426cff4c 100644 --- a/src/cbor/common.h +++ b/src/cbor/common.h @@ -157,7 +157,7 @@ _CBOR_NODISCARD CBOR_EXPORT cbor_type cbor_typeof( const cbor_item_t *item); /* Will be inlined iff link-time opt is enabled */ -/* Standard item types as described by the RFC */ +/* Standard CBOR Major item types */ /** Does the item have the appropriate major type? * @param item the item diff --git a/src/cbor/internal/builder_callbacks.c b/src/cbor/internal/builder_callbacks.c index 78277f050c2b..257cef3adbd1 100644 --- a/src/cbor/internal/builder_callbacks.c +++ b/src/cbor/internal/builder_callbacks.c @@ -67,14 +67,14 @@ void _cbor_builder_append(cbor_item_t *item, // Note: We use 0 and 1 subitems to distinguish between keys and values in // indefinite items if (ctx->stack->top->subitems % 2) { - /* Odd record, this is a value */ - if (!_cbor_map_add_value(ctx->stack->top->item, item)) { - ctx->creation_failed = true; - cbor_decref(&item); - break; - } + // Odd record, this is a value. + ctx->creation_failed = + !_cbor_map_add_value(ctx->stack->top->item, item); + // Adding a value never fails since the memory is allocated when the + // key is added + CBOR_ASSERT(!ctx->creation_failed); } else { - /* Even record, this is a key */ + // Even record, this is a key. if (!_cbor_map_add_key(ctx->stack->top->item, item)) { ctx->creation_failed = true; cbor_decref(&item); @@ -256,18 +256,8 @@ void cbor_builder_string_callback(void *context, cbor_data data, uint64_t length) { struct _cbor_decoder_context *ctx = context; CHECK_LENGTH(ctx, length); - struct _cbor_unicode_status unicode_status; - uint64_t codepoint_count = - _cbor_unicode_codepoint_count(data, length, &unicode_status); - - if (unicode_status.status != _CBOR_UNICODE_OK) { - ctx->syntax_error = true; - return; - } - CBOR_ASSERT(codepoint_count <= length); unsigned char *new_handle = _cbor_malloc(length); - if (new_handle == NULL) { ctx->creation_failed = true; return; @@ -281,7 +271,6 @@ void cbor_builder_string_callback(void *context, cbor_data data, return; } cbor_string_set_handle(new_chunk, new_handle, length); - new_chunk->metadata.string_metadata.codepoint_count = codepoint_count; // If an indef string is on the stack, extend it (if it were closed, it would // have been popped). Handle any syntax errors upstream. @@ -355,6 +344,8 @@ bool _cbor_is_indefinite(cbor_item_t *item) { case CBOR_TYPE_MAP: return cbor_map_is_indefinite(item); default: + // Should never happen since a non-nested item cannot be on top of the + // stack. return false; } } diff --git a/src/cbor/internal/loaders.c b/src/cbor/internal/loaders.c index c25c63358318..cfa173de7905 100644 --- a/src/cbor/internal/loaders.c +++ b/src/cbor/internal/loaders.c @@ -49,7 +49,7 @@ uint64_t _cbor_load_uint64(const unsigned char *source) { #endif } -/* As per http://tools.ietf.org/html/rfc7049#appendix-D */ +/* As per https://www.rfc-editor.org/rfc/rfc8949.html#name-half-precision */ float _cbor_decode_half(unsigned char *halfp) { int half = (halfp[0] << 8) + halfp[1]; int exp = (half >> 10) & 0x1f; diff --git a/src/cbor/internal/memory_utils.h b/src/cbor/internal/memory_utils.h index 14843c8c56f6..696f67800e29 100644 --- a/src/cbor/internal/memory_utils.h +++ b/src/cbor/internal/memory_utils.h @@ -21,7 +21,7 @@ bool _cbor_safe_to_multiply(size_t a, size_t b); _CBOR_NODISCARD bool _cbor_safe_to_add(size_t a, size_t b); -/** Adds `a` and `b`, propagating zeros and returing 0 on overflow. */ +/** Adds `a` and `b`, propagating zeros and returning 0 on overflow. */ _CBOR_NODISCARD size_t _cbor_safe_signaling_add(size_t a, size_t b); diff --git a/src/cbor/internal/unicode.c b/src/cbor/internal/unicode.c index 1831c8e62057..f87b746a3e19 100644 --- a/src/cbor/internal/unicode.c +++ b/src/cbor/internal/unicode.c @@ -66,12 +66,12 @@ uint32_t _cbor_unicode_decode(uint32_t* state, uint32_t* codep, uint32_t byte) { return *state; } -uint64_t _cbor_unicode_codepoint_count(cbor_data source, uint64_t source_length, - struct _cbor_unicode_status* status) { +size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length, + struct _cbor_unicode_status* status) { *status = (struct _cbor_unicode_status){.location = 0, .status = _CBOR_UNICODE_OK}; uint32_t codepoint, state = UTF8_ACCEPT, res; - uint64_t pos = 0, count = 0; + size_t pos = 0, count = 0; for (; pos < source_length; pos++) { res = _cbor_unicode_decode(&state, &codepoint, source[pos]); diff --git a/src/cbor/internal/unicode.h b/src/cbor/internal/unicode.h index af32cc7ff027..81d03d007782 100644 --- a/src/cbor/internal/unicode.h +++ b/src/cbor/internal/unicode.h @@ -19,12 +19,12 @@ enum _cbor_unicode_status_error { _CBOR_UNICODE_OK, _CBOR_UNICODE_BADCP }; /** Signals unicode validation error and possibly its location */ struct _cbor_unicode_status { enum _cbor_unicode_status_error status; - uint64_t location; + size_t location; }; _CBOR_NODISCARD -uint64_t _cbor_unicode_codepoint_count(cbor_data source, uint64_t source_length, - struct _cbor_unicode_status* status); +size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length, + struct _cbor_unicode_status* status); #ifdef __cplusplus } diff --git a/src/cbor/maps.c b/src/cbor/maps.c index 8711e57acb70..8a3bd6075681 100644 --- a/src/cbor/maps.c +++ b/src/cbor/maps.c @@ -103,6 +103,7 @@ bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) { return true; } +// TODO: Add a more convenient API like add(item, key, val) bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair) { CBOR_ASSERT(cbor_isa_map(item)); if (!_cbor_map_add_key(item, pair.key)) return false; diff --git a/src/cbor/streaming.c b/src/cbor/streaming.c index 922a71d8e6b0..4b37701143ab 100644 --- a/src/cbor/streaming.c +++ b/src/cbor/streaming.c @@ -437,23 +437,21 @@ struct cbor_decoder_result cbor_stream_decode( callbacks->indef_map_start(context); return result; } - case 0xC0: - /* Text date/time - RFC 3339 tag, fallthrough */ - case 0xC1: - /* Epoch date tag, fallthrough */ - case 0xC2: - /* Positive bignum tag, fallthrough */ - case 0xC3: - /* Negative bignum tag, fallthrough */ - case 0xC4: - /* Fraction, fallthrough */ - case 0xC5: - /* Big float */ - { - callbacks->tag(context, (uint64_t)(_cbor_load_uint8(source) - - 0xC0)); /* 0xC0 offset */ - return result; - } + /* See https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml for tag + * assignment. All well-formed tags are processed regardless of validity + * since maintaining the known mapping would be impractical. + * + * Moreover, even tags in the reserved "standard" range are not assigned + * but may get assigned in the future (see e.g. + * https://github.com/PJK/libcbor/issues/307), so processing all tags + * improves forward compatibility. + */ + case 0xC0: /* Fallthrough */ + case 0xC1: /* Fallthrough */ + case 0xC2: /* Fallthrough */ + case 0xC3: /* Fallthrough */ + case 0xC4: /* Fallthrough */ + case 0xC5: /* Fallthrough */ case 0xC6: /* Fallthrough */ case 0xC7: /* Fallthrough */ case 0xC8: /* Fallthrough */ @@ -468,13 +466,10 @@ struct cbor_decoder_result cbor_stream_decode( case 0xD1: /* Fallthrough */ case 0xD2: /* Fallthrough */ case 0xD3: /* Fallthrough */ - case 0xD4: /* Unassigned tag value */ - { - return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; - } - case 0xD5: /* Expected b64url conversion tag - fallthrough */ - case 0xD6: /* Expected b64 conversion tag - fallthrough */ - case 0xD7: /* Expected b16 conversion tag */ + case 0xD4: /* Fallthrough */ + case 0xD5: /* Fallthrough */ + case 0xD6: /* Fallthrough */ + case 0xD7: /* Fallthrough */ { callbacks->tag(context, (uint64_t)(_cbor_load_uint8(source) - 0xC0)); /* 0xC0 offset */ diff --git a/src/cbor/strings.c b/src/cbor/strings.c index de2d1024bb4c..6ae96545cfe3 100644 --- a/src/cbor/strings.c +++ b/src/cbor/strings.c @@ -8,6 +8,7 @@ #include "strings.h" #include #include "internal/memory_utils.h" +#include "internal/unicode.h" cbor_item_t *cbor_new_definite_string(void) { cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t)); @@ -66,6 +67,15 @@ void cbor_string_set_handle(cbor_item_t *item, CBOR_ASSERT(cbor_string_is_definite(item)); item->data = data; item->metadata.string_metadata.length = length; + struct _cbor_unicode_status unicode_status; + size_t codepoint_count = + _cbor_unicode_codepoint_count(data, length, &unicode_status); + CBOR_ASSERT(codepoint_count <= length); + if (unicode_status.status == _CBOR_UNICODE_OK) { + item->metadata.string_metadata.codepoint_count = codepoint_count; + } else { + item->metadata.string_metadata.codepoint_count = 0; + } } cbor_item_t **cbor_string_chunks_handle(const cbor_item_t *item) { diff --git a/src/cbor/strings.h b/src/cbor/strings.h index 7a18f95e29fa..3e03f81385f0 100644 --- a/src/cbor/strings.h +++ b/src/cbor/strings.h @@ -33,7 +33,8 @@ _CBOR_NODISCARD CBOR_EXPORT size_t cbor_string_length(const cbor_item_t *item); /** The number of codepoints in this string * - * Might differ from length if there are multibyte ones + * Might differ from `cbor_string_length` if there are multibyte codepoints. + * If the string data is not valid UTF-8, returns 0. * * @param item A string * @return The number of codepoints in this string @@ -71,6 +72,8 @@ cbor_string_handle(const cbor_item_t *item); /** Set the handle to the underlying string * + * The data is assumed to be a valid UTF-8 string. If the string is non-empty + * and invalid, `cbor_string_codepoint_count` will return 0. * * \rst * .. warning:: Using a pointer to a stack allocated constant is a common @@ -144,7 +147,11 @@ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_indefinite_string(void); /** Creates a new string and initializes it * - * The `val` will be copied to a newly allocated block + * The data from `val` will be copied to a newly allocated memory block. + * + * Note that valid UTF-8 strings do not contain null bytes, so this routine is + * correct for all valid inputs. If the input is not guaranteed to be valid, + * use `cbor_build_stringn` instead. * * @param val A null-terminated UTF-8 string * @return Reference to the new string item. The item's reference count is @@ -155,10 +162,12 @@ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_string(const char *val); /** Creates a new string and initializes it * - * The `handle` will be copied to a newly allocated block + * The data from `handle` will be copied to a newly allocated memory block. * - * @param val A UTF-8 string, at least @p `length` long (excluding the null - * byte) + * All @p `length` bytes will be stored in the string, even if there are null + * bytes or invalid UTF-8 sequences. + * + * @param val A UTF-8 string, at least @p `length` bytes long * @param length Length (in bytes) of the string passed in @p `val`. * @return Reference to the new string item. The item's reference count is * initialized to one. diff --git a/src/libcborConfig.cmake.in b/src/libcborConfig.cmake.in new file mode 100644 index 000000000000..565bed365e24 --- /dev/null +++ b/src/libcborConfig.cmake.in @@ -0,0 +1,8 @@ +set(CBOR_VERSION @CBOR_VERSION@) + +@PACKAGE_INIT@ + +set_and_check(CBOR_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") +set_and_check(CBOR_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") + +check_required_components(libcbor) diff --git a/test/callbacks_test.c b/test/callbacks_test.c index d9e90195c819..65c5d37f4399 100644 --- a/test/callbacks_test.c +++ b/test/callbacks_test.c @@ -371,6 +371,30 @@ static void test_invalid_indef_break(void** _CBOR_UNUSED(_state)) { assert_true(res.error.code == CBOR_ERR_SYNTAXERROR); } +static void test_invalid_state_indef_break(void** _CBOR_UNUSED(_state)) { + struct _cbor_stack stack = _cbor_stack_init(); + assert_non_null(_cbor_stack_push(&stack, cbor_new_int8(), /*subitems=*/0)); + struct _cbor_decoder_context context = { + .creation_failed = false, + .syntax_error = false, + .root = NULL, + .stack = &stack, + }; + + cbor_builder_indef_break_callback(&context); + + assert_false(context.creation_failed); + assert_true(context.syntax_error); + assert_size_equal(context.stack->size, 1); + // The stack remains unchanged + cbor_item_t* small_int = stack.top->item; + assert_size_equal(cbor_refcount(small_int), 1); + assert_true(cbor_isa_uint(small_int)); + + cbor_decref(&small_int); + _cbor_stack_pop(&stack); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_default_callbacks), @@ -388,6 +412,7 @@ int main(void) { cmocka_unit_test(test_append_array_failure), cmocka_unit_test(test_append_map_failure), cmocka_unit_test(test_invalid_indef_break), + cmocka_unit_test(test_invalid_state_indef_break), }; cmocka_run_group_tests(tests, NULL, NULL); diff --git a/test/cbor_serialize_test.c b/test/cbor_serialize_test.c index d549c37217cc..a2907b149345 100644 --- a/test/cbor_serialize_test.c +++ b/test/cbor_serialize_test.c @@ -218,6 +218,34 @@ static void test_serialize_definite_string(void **_CBOR_UNUSED(_state)) { cbor_decref(&item); } +static void test_serialize_definite_string_4b_header( + void **_CBOR_UNUSED(_state)) { +#if SIZE_MAX > UINT16_MAX + cbor_item_t *item = cbor_new_definite_string(); + const size_t size = (size_t)UINT16_MAX + 1; + unsigned char *data = malloc(size); + memset(data, 0, size); + cbor_string_set_handle(item, data, size); + assert_size_equal(cbor_serialized_size(item), 1 + 4 + size); + cbor_decref(&item); +#endif +} + +static void test_serialize_definite_string_8b_header( + void **_CBOR_UNUSED(_state)) { +#if SIZE_MAX > UINT32_MAX + cbor_item_t *item = cbor_new_definite_string(); + const size_t size = (size_t)UINT32_MAX + 1; + unsigned char *data = malloc(1); + data[0] = '\0'; + cbor_string_set_handle(item, data, 1); + // Pretend that we have a big item to avoid the huge malloc + item->metadata.string_metadata.length = size; + assert_size_equal(cbor_serialized_size(item), 1 + 8 + size); + cbor_decref(&item); +#endif +} + static void test_serialize_indefinite_string(void **_CBOR_UNUSED(_state)) { cbor_item_t *item = cbor_new_indefinite_string(); cbor_item_t *chunk = cbor_new_definite_string(); @@ -242,6 +270,7 @@ static void test_serialize_indefinite_string(void **_CBOR_UNUSED(_state)) { static void test_serialize_string_no_space(void **_CBOR_UNUSED(_state)) { cbor_item_t *item = cbor_new_definite_string(); unsigned char *data = malloc(12); + memset(data, 0, 12); cbor_string_set_handle(item, data, 12); assert_size_equal(cbor_serialize(item, buffer, 1), 0); @@ -254,6 +283,7 @@ static void test_serialize_indefinite_string_no_space( cbor_item_t *item = cbor_new_indefinite_string(); cbor_item_t *chunk = cbor_new_definite_string(); unsigned char *data = malloc(256); + memset(data, 0, 256); cbor_string_set_handle(chunk, data, 256); assert_true(cbor_string_add_chunk(item, cbor_move(chunk))); @@ -638,6 +668,8 @@ int main(void) { cmocka_unit_test(test_serialize_bytestring_no_space), cmocka_unit_test(test_serialize_indefinite_bytestring_no_space), cmocka_unit_test(test_serialize_definite_string), + cmocka_unit_test(test_serialize_definite_string_4b_header), + cmocka_unit_test(test_serialize_definite_string_8b_header), cmocka_unit_test(test_serialize_indefinite_string), cmocka_unit_test(test_serialize_string_no_space), cmocka_unit_test(test_serialize_indefinite_string_no_space), diff --git a/test/cbor_stream_decode_test.c b/test/cbor_stream_decode_test.c index 1b8caf47765d..5288016c2fc6 100644 --- a/test/cbor_stream_decode_test.c +++ b/test/cbor_stream_decode_test.c @@ -613,9 +613,9 @@ static void test_int64_tag_decoding(void **_CBOR_UNUSED(_state)) { assert_minimum_input_size(9, int64_tag_data); } -unsigned char bad_tag_data[] = {0xC6}; -static void test_bad_tag_decoding(void **_CBOR_UNUSED(_state)) { - assert_decoder_result(0, CBOR_DECODER_ERROR, decode(bad_tag_data, 1)); +unsigned char reserved_byte_data[] = {0xDC}; +static void test_reserved_byte_decoding(void **_CBOR_UNUSED(_state)) { + assert_decoder_result(0, CBOR_DECODER_ERROR, decode(reserved_byte_data, 1)); } unsigned char float2_data[] = {0xF9, 0x7B, 0xFF}; @@ -729,7 +729,7 @@ int main(void) { stream_test(test_int16_tag_decoding), stream_test(test_int32_tag_decoding), stream_test(test_int64_tag_decoding), - stream_test(test_bad_tag_decoding), + stream_test(test_reserved_byte_decoding), stream_test(test_float2_decoding), stream_test(test_float4_decoding), diff --git a/test/float_ctrl_test.c b/test/float_ctrl_test.c index 1bf8b7b7ba1d..c939486877e7 100644 --- a/test/float_ctrl_test.c +++ b/test/float_ctrl_test.c @@ -30,7 +30,7 @@ static void test_float2(void **_CBOR_UNUSED(_state)) { assert_true(cbor_is_float(float_ctrl)); assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_16); assert_true(cbor_float_get_float2(float_ctrl) == 65504.0F); - assert_true(fabs(cbor_float_get_float(float_ctrl) - 65504.0F) < eps); + assert_float_equal(cbor_float_get_float(float_ctrl), 65504.0F, eps); cbor_decref(&float_ctrl); assert_null(float_ctrl); } @@ -43,7 +43,7 @@ static void test_float4(void **_CBOR_UNUSED(_state)) { assert_true(cbor_is_float(float_ctrl)); assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_32); assert_true(cbor_float_get_float4(float_ctrl) == 100000.0F); - assert_true(fabs(cbor_float_get_float(float_ctrl) - 100000.0F) < eps); + assert_float_equal(cbor_float_get_float(float_ctrl), 100000.0F, eps); cbor_decref(&float_ctrl); assert_null(float_ctrl); } @@ -58,6 +58,8 @@ static void test_float8(void **_CBOR_UNUSED(_state)) { assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_64); // XXX: the cast prevents promotion to 80-bit floats on 32-bit x86 assert_true(cbor_float_get_float8(float_ctrl) == (double)1.0e+300); + // Not using `assert_double_equal` since CI has an old version of cmocka + assert_true(fabs(cbor_float_get_float(float_ctrl) - (double)1.0e+300) < eps); cbor_decref(&float_ctrl); assert_null(float_ctrl); } diff --git a/test/pretty_printer_test.c b/test/pretty_printer_test.c index 4a6249c412d8..6ea40c0e7ce2 100644 --- a/test/pretty_printer_test.c +++ b/test/pretty_printer_test.c @@ -6,33 +6,200 @@ */ #include +#include + #include "assertions.h" #include "cbor.h" -unsigned char data[] = {0x8B, 0x01, 0x20, 0x5F, 0x41, 0x01, 0x41, 0x02, - 0xFF, 0x7F, 0x61, 0x61, 0x61, 0x62, 0xFF, 0x9F, - 0xFF, 0xA1, 0x61, 0x61, 0x61, 0x62, 0xC0, 0xBF, - 0xFF, 0xFB, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, - 0x85, 0x1F, 0xF6, 0xF7, 0xF5}; - -static void test_pretty_printer(void **_CBOR_UNUSED(_state)) { +void assert_describe_result(cbor_item_t *item, char *expected_result) { #if CBOR_PRETTY_PRINTER + // We know the expected size based on `expected_result`, but read everything + // in order to get the full actual output in a useful error message. + const size_t buffer_size = 512; FILE *outfile = tmpfile(); - struct cbor_load_result res; - cbor_item_t *item = cbor_load(data, 37, &res); cbor_describe(item, outfile); - cbor_decref(&item); - - item = cbor_new_ctrl(); - cbor_set_ctrl(item, 1); - cbor_describe(item, outfile); - cbor_decref(&item); - + rewind(outfile); + // Treat string as null-terminated since cmocka doesn't have asserts + // for explicit length strings. + char *output = malloc(buffer_size); + assert_non_null(output); + size_t output_size = fread(output, sizeof(char), buffer_size, outfile); + output[output_size] = '\0'; + assert_string_equal(output, expected_result); + assert_true(feof(outfile)); + free(output); fclose(outfile); #endif } +static void test_uint(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_build_uint8(42); + assert_describe_result(item, "[CBOR_TYPE_UINT] Width: 1B, Value: 42\n"); + cbor_decref(&item); +} + +static void test_negint(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_build_negint16(40); + assert_describe_result(item, + "[CBOR_TYPE_NEGINT] Width: 2B, Value: -40 - 1\n"); + cbor_decref(&item); +} + +static void test_definite_bytestring(void **_CBOR_UNUSED(_state)) { + unsigned char data[] = {0x01, 0x02, 0x03}; + cbor_item_t *item = cbor_build_bytestring(data, 3); + assert_describe_result(item, + "[CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n" + " 010203\n"); + cbor_decref(&item); +} + +static void test_indefinite_bytestring(void **_CBOR_UNUSED(_state)) { + unsigned char data[] = {0x01, 0x02, 0x03}; + cbor_item_t *item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring(data, 3)))); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring(data, 2)))); + assert_describe_result( + item, + "[CBOR_TYPE_BYTESTRING] Indefinite, Chunks: 2, Chunk data:\n" + " [CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n" + " 010203\n" + " [CBOR_TYPE_BYTESTRING] Definite, Length: 2B, Data:\n" + " 0102\n"); + cbor_decref(&item); +} + +static void test_definite_string(void **_CBOR_UNUSED(_state)) { + char *string = "Hello!"; + cbor_item_t *item = cbor_build_string(string); + assert_describe_result( + item, + "[CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 6, Data:\n" + " Hello!\n"); + cbor_decref(&item); +} + +static void test_indefinite_string(void **_CBOR_UNUSED(_state)) { + char *string = "Hello!"; + cbor_item_t *item = cbor_new_indefinite_string(); + assert_true( + cbor_string_add_chunk(item, cbor_move(cbor_build_string(string)))); + assert_true( + cbor_string_add_chunk(item, cbor_move(cbor_build_string(string)))); + assert_describe_result( + item, + "[CBOR_TYPE_STRING] Indefinite, Chunks: 2, Chunk data:\n" + " [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 6, Data:\n" + " Hello!\n" + " [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 6, Data:\n" + " Hello!\n"); + cbor_decref(&item); +} + +static void test_multibyte_string(void **_CBOR_UNUSED(_state)) { + // "Štěstíčko" in UTF-8 + char *string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko"; + cbor_item_t *item = cbor_build_string(string); + assert_describe_result( + item, + "[CBOR_TYPE_STRING] Definite, Length: 13B, Codepoints: 9, Data:\n" + " \xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko\n"); + cbor_decref(&item); +} + +static void test_definite_array(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_definite_array(2); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2)))); + assert_describe_result(item, + "[CBOR_TYPE_ARRAY] Definite, Size: 2, Contents:\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_indefinite_array(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2)))); + assert_describe_result(item, + "[CBOR_TYPE_ARRAY] Indefinite, Size: 2, Contents:\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_definite_map(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_definite_map(1); + assert_true(cbor_map_add( + item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)), + .value = cbor_move(cbor_build_uint8(2))})); + assert_describe_result(item, + "[CBOR_TYPE_MAP] Definite, Size: 1, Contents:\n" + " Map entry 0\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_indefinite_map(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_indefinite_map(); + assert_true(cbor_map_add( + item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)), + .value = cbor_move(cbor_build_uint8(2))})); + assert_describe_result(item, + "[CBOR_TYPE_MAP] Indefinite, Size: 1, Contents:\n" + " Map entry 0\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 2\n"); + cbor_decref(&item); +} + +static void test_tag(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_build_tag(42, cbor_move(cbor_build_uint8(1))); + assert_describe_result(item, + "[CBOR_TYPE_TAG] Value: 42\n" + " [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"); + cbor_decref(&item); +} + +static void test_floats(void **_CBOR_UNUSED(_state)) { + cbor_item_t *item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_bool(true)))); + assert_true( + cbor_array_push(item, cbor_move(cbor_build_ctrl(CBOR_CTRL_UNDEF)))); + assert_true( + cbor_array_push(item, cbor_move(cbor_build_ctrl(CBOR_CTRL_NULL)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_ctrl(24)))); + assert_true(cbor_array_push(item, cbor_move(cbor_build_float4(3.14f)))); + assert_describe_result( + item, + "[CBOR_TYPE_ARRAY] Indefinite, Size: 5, Contents:\n" + " [CBOR_TYPE_FLOAT_CTRL] Bool: true\n" + " [CBOR_TYPE_FLOAT_CTRL] Undefined\n" + " [CBOR_TYPE_FLOAT_CTRL] Null\n" + " [CBOR_TYPE_FLOAT_CTRL] Simple value: 24\n" + " [CBOR_TYPE_FLOAT_CTRL] Width: 4B, Value: 3.140000\n"); + cbor_decref(&item); +} + int main(void) { - const struct CMUnitTest tests[] = {cmocka_unit_test(test_pretty_printer)}; + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_uint), + cmocka_unit_test(test_negint), + cmocka_unit_test(test_definite_bytestring), + cmocka_unit_test(test_indefinite_bytestring), + cmocka_unit_test(test_definite_string), + cmocka_unit_test(test_indefinite_string), + cmocka_unit_test(test_multibyte_string), + cmocka_unit_test(test_definite_array), + cmocka_unit_test(test_indefinite_array), + cmocka_unit_test(test_definite_map), + cmocka_unit_test(test_indefinite_map), + cmocka_unit_test(test_tag), + cmocka_unit_test(test_floats), + }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/string_test.c b/test/string_test.c index 991aa578a26e..c3079b449838 100644 --- a/test/string_test.c +++ b/test/string_test.c @@ -217,6 +217,22 @@ static void test_short_indef_string(void **_CBOR_UNUSED(_state)) { assert_null(string); } +static void test_invalid_utf(void **_CBOR_UNUSED(_state)) { + /* 0x60 + 1 | 0xC5 (invalid unfinished 2B codepoint) */ + unsigned char string_data[] = {0x61, 0xC5}; + string = cbor_load(string_data, 2, &res); + + assert_non_null(string); + assert_true(cbor_typeof(string) == CBOR_TYPE_STRING); + assert_true(cbor_isa_string(string)); + assert_size_equal(cbor_string_length(string), 1); + assert_size_equal(cbor_string_codepoint_count(string), 0); + assert_true(cbor_string_is_definite(string)); + assert_true(res.read == 2); + + cbor_decref(&string); +} + static void test_inline_creation(void **_CBOR_UNUSED(_state)) { string = cbor_build_string("Hello!"); assert_memory_equal(cbor_string_handle(string), "Hello!", strlen("Hello!")); @@ -275,6 +291,53 @@ static void test_add_chunk_reallocation_overflow(void **_CBOR_UNUSED(_state)) { cbor_decref(&string); } +static void test_set_handle(void **_CBOR_UNUSED(_state)) { + string = cbor_new_definite_string(); + char *test_string = "Hello"; + unsigned char *string_data = malloc(strlen(test_string)); + memcpy(string_data, test_string, strlen(test_string)); + assert_ptr_not_equal(string_data, NULL); + cbor_string_set_handle(string, string_data, strlen(test_string)); + + assert_ptr_equal(cbor_string_handle(string), string_data); + assert_size_equal(cbor_string_length(string), 5); + assert_size_equal(cbor_string_codepoint_count(string), 5); + + cbor_decref(&string); +} + +static void test_set_handle_multibyte_codepoint(void **_CBOR_UNUSED(_state)) { + string = cbor_new_definite_string(); + // "Štěstíčko" in UTF-8 + char *test_string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko"; + unsigned char *string_data = malloc(strlen(test_string)); + memcpy(string_data, test_string, strlen(test_string)); + assert_ptr_not_equal(string_data, NULL); + cbor_string_set_handle(string, string_data, strlen(test_string)); + + assert_ptr_equal(cbor_string_handle(string), string_data); + assert_size_equal(cbor_string_length(string), 13); + assert_size_equal(cbor_string_codepoint_count(string), 9); + + cbor_decref(&string); +} + +static void test_set_handle_invalid_utf(void **_CBOR_UNUSED(_state)) { + string = cbor_new_definite_string(); + // Invalid multi-byte character (missing the second byte). + char *test_string = "Test: \xc5"; + unsigned char *string_data = malloc(strlen(test_string)); + memcpy(string_data, test_string, strlen(test_string)); + assert_ptr_not_equal(string_data, NULL); + cbor_string_set_handle(string, string_data, strlen(test_string)); + + assert_ptr_equal(cbor_string_handle(string), string_data); + assert_size_equal(cbor_string_length(string), 7); + assert_size_equal(cbor_string_codepoint_count(string), 0); + + cbor_decref(&string); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_empty_string), @@ -285,10 +348,14 @@ int main(void) { cmocka_unit_test(test_int32_string), cmocka_unit_test(test_int64_string), cmocka_unit_test(test_short_indef_string), + cmocka_unit_test(test_invalid_utf), cmocka_unit_test(test_inline_creation), cmocka_unit_test(test_string_creation), cmocka_unit_test(test_string_add_chunk), cmocka_unit_test(test_add_chunk_reallocation_overflow), + cmocka_unit_test(test_set_handle), + cmocka_unit_test(test_set_handle_multibyte_codepoint), + cmocka_unit_test(test_set_handle_invalid_utf), }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/tag_test.c b/test/tag_test.c index 4bf6c718f0b9..4bce10589803 100644 --- a/test/tag_test.c +++ b/test/tag_test.c @@ -102,6 +102,28 @@ static void test_nested_tag(void **_CBOR_UNUSED(_state)) { assert_null(nested_tag); } +static void test_all_tag_values_supported(void **_CBOR_UNUSED(_state)) { + /* Test all items in the protected range of + * https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml */ + for (int64_t tag_value = 0; tag_value <= 32767; tag_value++) { + cbor_item_t *tag_item = + cbor_build_tag(tag_value, cbor_move(cbor_build_uint8(42))); + unsigned char *serialized_tag; + size_t serialized_tag_size = + cbor_serialize_alloc(tag_item, &serialized_tag, NULL); + assert_true(serialized_tag_size > 0); + tag = cbor_load(serialized_tag, serialized_tag_size, &res); + assert_true(res.read == serialized_tag_size); + assert_true(cbor_typeof(tag) == CBOR_TYPE_TAG); + assert_true(cbor_tag_value(tag) == tag_value); + cbor_decref(&tag); + assert_null(tag); + cbor_decref(&tag_item); + assert_null(tag_item); + free(serialized_tag); + } +} + static void test_build_tag(void **_CBOR_UNUSED(_state)) { tag = cbor_build_tag(1, cbor_move(cbor_build_uint8(42))); @@ -134,6 +156,7 @@ int main(void) { cmocka_unit_test(test_int32_tag), cmocka_unit_test(test_int64_tag), cmocka_unit_test(test_nested_tag), + cmocka_unit_test(test_all_tag_values_supported), cmocka_unit_test(test_build_tag), cmocka_unit_test(test_build_tag_failure), cmocka_unit_test(test_tag_creation), diff --git a/test/test_allocator.c b/test/test_allocator.c index 72ccf6591d25..a2f98efa22f7 100644 --- a/test/test_allocator.c +++ b/test/test_allocator.c @@ -29,7 +29,7 @@ void finalize_mock_malloc(void) { free(expectations); } -void print_backtrace() { +void print_backtrace(void) { #if HAS_EXECINFO void *buffer[128]; int frames = backtrace(buffer, 128);