diff options
| author | Ed Maste <emaste@FreeBSD.org> | 2026-03-22 14:31:52 +0000 |
|---|---|---|
| committer | Ed Maste <emaste@FreeBSD.org> | 2026-03-22 14:32:11 +0000 |
| commit | 0427abbc78cf28d98d8f1393669ba96e4018a77b (patch) | |
| tree | 2829de4b4ccdc1c7c36c38d40266a14b3bafd5c7 | |
| parent | aa1599ed2bad271ece23ac2d2ca14c6540fa5ffa (diff) | |
Vendor import of libcbor 0.13.0vendor/libcbor/0.13.0vendor/libcbor
Sponsored by: The FreeBSD Foundation
40 files changed, 1334 insertions, 374 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d3d1ea8fcc1..ea030d6b7b8e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,7 +44,9 @@ commands: - run: make -j 16 VERBOSE=1 test: steps: - - run: ctest -VV + - run: ctest -VV --output-junit ctest_out.xml + - store_test_results: + path: ctest_out.xml orbs: codecov: codecov/codecov@3.2.2 @@ -238,6 +240,17 @@ jobs: docker: - image: dockcross/linux-mipsel-lts + + build-and-test-riscv64: &dockcross-job + docker: + - image: dockcross/linux-riscv64 + steps: + - checkout + - attach_workspace: + at: /home/circleci/project + - build-with-cmocka-from-source + - test + workflows: build-and-test: jobs: @@ -250,6 +263,7 @@ workflows: - build-and-test-win - build-and-test-mips - build-and-test-mipsel + - build-and-test-riscv64 - build-bazel - llvm-coverage # OSX builds are expensive, run only on master diff --git a/.cirrus.yml b/.cirrus.yml index d3be21bc1d4c..beaea2a0b6ee 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -5,7 +5,6 @@ freebsd_task: - mkdir build - cd build - cmake -GNinja -DWITH_TESTS=ON - -DCBOR_CUSTOM_ALLOC=ON -DCMAKE_BUILD_TYPE=Debug -DSANITIZE=OFF .. diff --git a/.gitignore b/.gitignore index a927c86360a3..cfc2f906bd0a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,8 @@ doxygen_docs cmake-build-debug venv **.DS_Store -.vscode +.vscode/tmp +.vscode/c_cpp_properties.json doc/build # No top-level requirements, see doc/source requirements.txt diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000000..1efd1526d101 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "C_Cpp.clang_format_style": "file", + "editor.formatOnSave": true, + "cmake.configureOnOpen": true, + "cmake.buildDirectory": "${workspaceFolder}/.vscode/tmp/build_${buildType}", +}
\ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8acd3265e490..7509569b414f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,21 +1,36 @@ Template: + - [Fix issue X in feature Y](https://github.com/PJK/libcbor/pull/XXX) (by [YYY](https://github.com/YYY)) Next --------------------- +0.13.0 (2025-08-30) +--------------------- + +- [Fix `cbor_is_null`, `cbor_is_undef`, `cbor_is_bool` assertion failing on non-ctrl floats in debug mode](https://github.com/PJK/libcbor/issues/352) (bug discovered by <https://github.com/psturm-swift>) +- [Add an example for handling of CBOR Sequences](https://github.com/PJK/libcbor/pull/358) +- [Use C23/c2x if available](https://github.com/PJK/libcbor/pull/361) + - libcbor remains C99 compatible + - When the compiler does not support new standard, C99 will be used, so the change should be backwards compatible +- [Improved introduction documentation and examples](https://github.com/PJK/libcbor/pull/363) +- [Add cbor_copy_definite to turn indefinite items into definite equivalents](https://github.com/PJK/libcbor/pull/364/files) (proposed by Jacob Teplitsky) +- BUILD BREAKING: [Minimum CMake version set to 3.5](https://github.com/PJK/libcbor/pull/355) to [be compatible with CMake 4](https://github.com/eclipse-ecal/ecal/issues/2041) ([suggestion](https://github.com/PJK/libcbor/commit/1183292d4695300785b272532c1e02d68840e4b8#commitcomment-164507943) by <https://github.com/hnyman>) + - See <https://repology.org/project/cmake/versions> for support; the vast majority of users should not be affected. + 0.12.0 (2025-03-16) --------------------- -- BUILD BREAKING: [Respect `INTERPROCEDURAL_OPTIMIZATION` and use the default value](https://github.com/PJK/libcbor/issues/315) + +- BUILD BREAKING: [Respect `INTERPROCEDURAL_OPTIMIZATION` and use the default value](https://github.com/PJK/libcbor/issues/315) - BREAKING: Changes to NaN encoding - [Fix NaN encoding on Windows](https://github.com/PJK/libcbor/issues/271) - [Fix NaN encoding on mips/mipsel](https://github.com/PJK/libcbor/issues/329) - [Signaling NaNs will from now on be encoded as canonical quiet NaNs](https://github.com/PJK/libcbor/pull/335). This was already the existing behavior for half-precision floats - Decoding is unchanged - - Please note that this is an intermediate state and likely to be revisited (https://github.com/PJK/libcbor/issues/336) + - Please note that this is an intermediate state and likely to be revisited (<https://github.com/PJK/libcbor/issues/336>) - [Make build compatible with CMake FetchContent](https://github.com/PJK/libcbor/pull/341) (by [Jan200101](https://github.com/Jan200101)) - [Support Bzlmod for Bazel builds](https://github.com/PJK/libcbor/pull/340) - - This should significantly simplify including libcbor as a dependency/module in Bazel projects, see https://bazel.build/external/migration + - This should significantly simplify including libcbor as a dependency/module in Bazel projects, see <https://bazel.build/external/migration> - Code quality improvements - [Fix compiler pragmas](https://github.com/PJK/libcbor/pull/347) (by [brooksdavis](https://github.com/brooksdavis)) - [Fix code style issues](https://github.com/PJK/libcbor/pull/321) @@ -23,35 +38,39 @@ 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 + - 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)) - Actual libcbor functionality not affected, bug was in the test suite - [Made tests platform-independent](https://github.com/PJK/libcbor/pull/272) 0.10.1 (2022-12-30) --------------------- + - [Fix a regression in `cbor_serialize_alloc` that caused serialization of zero-length strings and bytestrings or byte/strings with zero-length chunks to fail](https://github.com/PJK/libcbor/pull/260) (discovered by [martelletto](https://github.com/martelletto)) 0.10.0 (2022-12-29) --------------------- + - Make the buffer_size optional in `cbor_serialize_alloc` [[#205]](https://github.com/PJK/libcbor/pull/205) (by [hughsie](https://github.com/hughsie)) - BREAKING: Improved half-float encoding for denormalized numbers. [[#208]](https://github.com/PJK/libcbor/pull/208) (by [ranvis](https://github.com/ranvis)) - Denormalized half-floats will now preserve data in the mantissa - - Note: Half-float NaNs still lose data (https://github.com/PJK/libcbor/issues/215) + - Note: Half-float NaNs still lose data (<https://github.com/PJK/libcbor/issues/215>) - BUILD BREAKING: Minimum CMake version is 3.0 [[#201]](https://github.com/PJK/libcbor/pull/201) (by [thewtex@](https://github.com/thewtex)) - - See https://repology.org/project/cmake/versions for support; the vast majority of users should not be affected. + - See <https://repology.org/project/cmake/versions> for support; the vast majority of users should not be affected. - Fix a potential memory leak when the allocator fails during array or map decoding [[#224]](https://github.com/PJK/libcbor/pull/224) (by [James-ZHANG](https://github.com/James-ZHANG)) - [Fix a memory leak when the allocator fails when adding chunks to indefinite bytestrings.](https://github.com/PJK/libcbor/pull/242) ([discovered](https://github.com/PJK/libcbor/pull/228) by [James-ZHANG](https://github.com/James-ZHANG)) - [Fix a memory leak when the allocator fails when adding chunks to indefinite strings](https://github.com/PJK/libcbor/pull/246) @@ -70,57 +89,62 @@ Next 0.9.0 (2021-11-14) --------------------- + - Improved pkg-config paths handling [[#164]](https://github.com/PJK/libcbor/pull/164) (by [jtojnar@](https://github.com/jtojnar)) - Use explicit math.h linkage [[#170]](https://github.com/PJK/libcbor/pull/170) - BREAKING: Fixed handling of items that exceed the host size_t range [[#186]](https://github.com/PJK/libcbor/pull/186hg) - - Callbacks for bytestrings, strings, arrays, and maps use uint64_t instead of size_t to allow handling of large items that exceed size_t even if size_t < uint64_t - - cbor_decode explicitly checks size to avoid overflows (previously broken, potentially resulting in erroneous decoding on affected systems) - - The change should be a noop for 64b systems + - Callbacks for bytestrings, strings, arrays, and maps use uint64_t instead of size_t to allow handling of large items that exceed size_t even if size_t < uint64_t + - cbor_decode explicitly checks size to avoid overflows (previously broken, potentially resulting in erroneous decoding on affected systems) + - The change should be a noop for 64b systems - Added a [Bazel](https://bazel.build/) build example [[#196]](https://github.com/PJK/libcbor/pull/196) (by [andyjgf@](https://github.com/andyjgf)) 0.8.0 (2020-09-20) --------------------- + - BUILD BREAKING: Use BUILD_SHARED_LIBS to determine how to build libraries (fixed Windows linkage) [[#148]](https://github.com/PJK/libcbor/pull/148) (by [intelligide@](https://github.com/intelligide)) - BREAKING: Fix `cbor_tag_item` not increasing the reference count on the tagged item reference it returns [[Fixes #109](https://github.com/PJK/libcbor/issues/109)] (discovered bt [JohnGilmour](https://github.com/JohnGilmour)) - If you have previously relied on the broken behavior, you can use `cbor_move` to emulate as long as the returned handle is an "rvalue" - BREAKING: [`CBOR_DECODER_EBUFFER` removed from `cbor_decoder_status`](https://github.com/PJK/libcbor/pull/156) - - `cbor_stream_decode` will set `CBOR_DECODER_NEDATA` instead if the input buffer is empty + - `cbor_stream_decode` will set `CBOR_DECODER_NEDATA` instead if the input buffer is empty - [Fix `cbor_stream_decode`](https://github.com/PJK/libcbor/pull/156) to set `cbor_decoder_result.required` to the minimum number of input bytes necessary to receive the next callback (as long as at least one byte was passed) (discovered by [woefulwabbit](https://github.com/woefulwabbit)) - Fixed several minor manpage issues [[#159]](https://github.com/PJK/libcbor/pull/159) (discovered by [kloczek@](https://github.com/kloczek)) 0.7.0 (2020-04-25) --------------------- + - Fix bad encoding of NaN half-floats [[Fixes #53]](https://github.com/PJK/libcbor/issues/53) (discovered by [BSipos-RKF](https://github.com/BSipos-RKF)) - - **Warning**: Previous versions encoded NaNs as `0xf9e700` instead of `0xf97e00`; if you rely on the broken behavior, this will be a breaking change + - **Warning**: Previous versions encoded NaNs as `0xf9e700` instead of `0xf97e00`; if you rely on the broken behavior, this will be a breaking change - Fix potentially bad encoding of negative half-float with exponent < -14 [[Fixes #112]](https://github.com/PJK/libcbor/issues/112) (discovered by [yami36](https://github.com/yami36)) - BREAKING: Improved bool support [[Fixes #63]](https://github.com/PJK/libcbor/issues/63) - - Rename `cbor_ctrl_is_bool` to `cbor_get_bool` and fix the behavior - - Add `cbor_set_bool` + - Rename `cbor_ctrl_is_bool` to `cbor_get_bool` and fix the behavior + - Add `cbor_set_bool` - Fix memory_allocation_test breaking the build without CBOR_CUSTOM_ALLOC [[Fixes #128]](https://github.com/PJK/libcbor/issues/128) (by [panlinux](https://github.com/panlinux)) - [Fix a potential build issue where cJSON includes may be misconfigured](https://github.com/PJK/libcbor/pull/132) - Breaking: [Add a limit on the size of the decoding context stack](https://github.com/PJK/libcbor/pull/138) (by [James-ZHANG](https://github.com/James-ZHANG)) - - If your usecase requires parsing very deeply nested structures, you might need to increase the default 2k limit via `CBOR_MAX_STACK_SIZE` + - If your usecase requires parsing very deeply nested structures, you might need to increase the default 2k limit via `CBOR_MAX_STACK_SIZE` - Enable LTO/IPO based on [CheckIPOSupported](https://cmake.org/cmake/help/latest/module/CheckIPOSupported.html#module:CheckIPOSupported) [[#143]](https://github.com/PJK/libcbor/pull/143) (by [xanderlent](https://github.com/xanderlent)) - - If you rely on LTO being enabled and use CMake version older than 3.9, you will need to re-enable it manually or upgrade your CMake + - If you rely on LTO being enabled and use CMake version older than 3.9, you will need to re-enable it manually or upgrade your CMake 0.6.1 (2020-03-26) --------------------- + - [Fix bad shared library version number](https://github.com/PJK/libcbor/pull/131) - - **Warning**: Shared library built from the 0.6.0 release is erroneously marked as version "0.6.0", which makes it incompatible with future releases *including the v0.6.X line* even though they may be compatible API/ABI-wise. Refer to the documentation for the new SO versioning scheme. + - **Warning**: Shared library built from the 0.6.0 release is erroneously marked as version "0.6.0", which makes it incompatible with future releases *including the v0.6.X line* even though they may be compatible API/ABI-wise. Refer to the documentation for the new SO versioning scheme. 0.6.0 (2020-03-15) --------------------- -- Correctly set .so version [[Fixes #52]](https://github.com/PJK/libcbor/issues/52). - - **Warning**: All previous releases will be identified as 0.0 by the linker. + +- Correctly set .so version [[Fixes #52]](https://github.com/PJK/libcbor/issues/52). + - **Warning**: All previous releases will be identified as 0.0 by the linker. - Fix & prevent heap overflow error in example code [[#74]](https://github.com/PJK/libcbor/pull/74) [[#76]](https://github.com/PJK/libcbor/pull/76) (by @nevun) - Correctly set OSX dynamic library version [[Fixes #75]](https://github.com/PJK/libcbor/issues/75) - [Fix misplaced 0xFF bytes in maps possibly causing memory corruption](https://github.com/PJK/libcbor/pull/82) - BREAKING: Fix handling & cleanup of failed memory allocation in constructor and builder helper functions [[Fixes #84]](https://github.com/PJK/libcbor/issues/84) - - All cbor_new_* and cbor_build_* functions will now explicitly return NULL when memory allocation fails + - All cbor_new_*and cbor_build_* functions will now explicitly return NULL when memory allocation fails - It is up to the client to handle such cases - Globally enforced code style [[Fixes #83]](https://github.com/PJK/libcbor/issues/83) -- Fix issue possible memory corruption bug on repeated +- Fix issue possible memory corruption bug on repeated cbor_(byte)string_add_chunk calls with intermittently failing realloc calls - Fix possibly misaligned reads and writes when endian.h is uses or when running on a big-endian machine [[Fixes #99](https://github.com/PJK/libcbor/issues/99), [#100](https://github.com/PJK/libcbor/issues/100)] @@ -129,6 +153,7 @@ Next 0.5.0 (2017-02-06) --------------------- + - Remove cmocka from the subtree (always rely on system or user-provided version) - Windows CI - Only build tests if explicitly enabled (`-DWITH_TESTS=ON`) @@ -144,6 +169,7 @@ Next 0.4.0 (2015-12-25) --------------------- + Breaks build & header compatibility due to: - Improved build configuration and feature check macros @@ -154,6 +180,7 @@ Breaks build & header compatibility due to: 0.3.1 (2015-05-21) --------------------- + - documentation and comments improvements, mostly for the API reference 0.3.0 (2015-05-21) @@ -169,6 +196,7 @@ Breaks build & header compatibility due to: 0.2.1 (2015-05-17) --------------------- + - C99 support 0.2.0 (2015-05-17) diff --git a/CMakeLists.txt b/CMakeLists.txt index 815675edd81a..a7e133a3e888 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,13 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) -project(libcbor) +project(libcbor LANGUAGES C CXX) 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 "12") +set(CBOR_VERSION_MINOR "13") set(CBOR_VERSION_PATCH "0") set(CBOR_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.${CBOR_VERSION_PATCH}) @@ -79,13 +79,44 @@ set(CPACK_PACKAGE_VERSION_PATCH ${CBOR_VERSION_PATCH}) include(CPack) +# +# Configure compilation flags and language features +# + +include(CheckCSourceCompiles) + +check_c_source_compiles(" + #include <stdio.h> + [[nodiscard]] int f(void) { return 42; } + int main(void) { return f(); } +" HAS_NODISCARD_ATTRIBUTE) + +if (HAS_NODISCARD_ATTRIBUTE) + message(STATUS "[[nodiscard]] is supported.") + add_definitions(-D_CBOR_HAS_NODISCARD_ATTRIBUTE) + # Assume that if we have [[nodiscard]], we have some C23 support. May fail. + if(NOT DEFINED CMAKE_C_STANDARD) + message(STATUS "Switching to C23-like mode. To prevent this, pass -DCMAKE_C_STANDARD explicitly.") + # On Clang 16, this is resolved to -std=c2x + set(CMAKE_C_STANDARD 23 CACHE STRING "C language standard") + endif() +endif() + if(MINGW) # https://github.com/PJK/libcbor/issues/13 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") elseif(NOT MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -pedantic") + # Default to C99 + if(NOT DEFINED CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 99 CACHE STRING "C language standard") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic") endif() +# CMAKE_C_STANDARD set above +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + if(MSVC) # This just doesn't work right -- # https://msdn.microsoft.com/en-us/library/5ft82fed.aspx @@ -98,8 +129,8 @@ else() set(CBOR_RESTRICT_SPECIFIER "restrict") set(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} -O0 -pedantic -Wall -Wextra -g -ggdb -DDEBUG=true") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -pedantic -Wall -Wextra -DNDEBUG") + "${CMAKE_C_FLAGS_DEBUG} -O0 -Wall -Wextra -g -ggdb -DDEBUG=true") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wall -Wextra -DNDEBUG") if(SANITIZE) set(CMAKE_C_FLAGS_DEBUG @@ -124,8 +155,6 @@ else() add_definitions(-DEIGHT_BYTE_SIZE_T) endif() -include(CheckCSourceCompiles) - check_c_source_compiles(" int main() { __builtin_unreachable(); @@ -137,6 +166,36 @@ if (HAS_BUILTIN_UNREACHABLE) add_definitions(-D_CBOR_HAS_BUILTIN_UNREACHABLE) endif() +# CMake >= 3.9.0 enables LTO for GCC and Clang with INTERPROCEDURAL_OPTIMIZATION +# Policy CMP0069 enables this behavior when we set the minimum CMake version < +# 3.9.0 Checking for LTO support before setting INTERPROCEDURAL_OPTIMIZATION is +# mandatory with CMP0069 set to NEW. +set(LTO_SUPPORTED FALSE) +if(${CMAKE_VERSION} VERSION_GREATER "3.9.0" OR ${CMAKE_VERSION} VERSION_EQUAL + "3.9.0") + cmake_policy(SET CMP0069 NEW) + # Require LTO support to build libcbor with newer CMake versions + include(CheckIPOSupported) + check_ipo_supported(RESULT LTO_SUPPORTED) +endif() + +if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +endif() + +if(LTO_SUPPORTED) + message( + STATUS + "LTO is supported and CMAKE_INTERPROCEDURAL_OPTIMIZATION=${CMAKE_INTERPROCEDURAL_OPTIMIZATION}" + ) +else() + message(STATUS "LTO is not supported") +endif() + +# +# Testing and validation +# + enable_testing() set(CTEST_MEMORYCHECK_COMMAND "/usr/bin/valgrind") @@ -180,8 +239,6 @@ add_custom_target( VERBATIM COMMENT "Generate coverage report using the LLVM toolchain") -include_directories(src) - option(COVERAGE "Enable code coverage instrumentation" OFF) if(COVERAGE) message("Configuring code coverage instrumentation") @@ -205,6 +262,12 @@ if(COVERAGE) endif() endif() +# +# Configure build and targets +# + +include_directories(src) + # 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. @@ -213,32 +276,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/cbor/configuration.h.in install(FILES ${PROJECT_BINARY_DIR}/cbor/configuration.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cbor) -# CMake >= 3.9.0 enables LTO for GCC and Clang with INTERPROCEDURAL_OPTIMIZATION -# Policy CMP0069 enables this behavior when we set the minimum CMake version < -# 3.9.0 Checking for LTO support before setting INTERPROCEDURAL_OPTIMIZATION is -# mandatory with CMP0069 set to NEW. -set(LTO_SUPPORTED FALSE) -if(${CMAKE_VERSION} VERSION_GREATER "3.9.0" OR ${CMAKE_VERSION} VERSION_EQUAL - "3.9.0") - cmake_policy(SET CMP0069 NEW) - # Require LTO support to build libcbor with newer CMake versions - include(CheckIPOSupported) - check_ipo_supported(RESULT LTO_SUPPORTED) -endif() - -if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) - set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) -endif() - -if(LTO_SUPPORTED) - message( - STATUS - "LTO is supported and CMAKE_INTERPROCEDURAL_OPTIMIZATION=${CMAKE_INTERPROCEDURAL_OPTIMIZATION}" - ) -else() - message(STATUS "LTO is not supported") -endif() - add_subdirectory(src) if(LTO_SUPPORTED) set_property(DIRECTORY src PROPERTY INTERPROCEDURAL_OPTIMIZATION CMAKE_INTERPROCEDURAL_OPTIMIZATION) @@ -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.12.0 +PROJECT_NUMBER = 0.13.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 ea54bed9437b..62d37b6b957d 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,35 @@ **libcbor** is a C library for parsing and generating [CBOR](https://cbor.io/), the general-purpose schema-less binary data format. ## Main features - - 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 - - Proper handling of UTF-8 - - Full support for streams & incremental processing - - Extensive documentation and test suite - - No runtime dependencies, small footprint - + +- Complete CBOR [IETF RFC 8949 (STD 94)](https://www.rfc-editor.org/info/std94) specification conformance (previously known as [RFC 7049](https://www.rfc-editor.org/info/rfc7049)) +- Supports CBOR Sequences ([RFC 8742](https://datatracker.ietf.org/doc/html/rfc8742)) +- Robust platform-independent C99 implementation, tested on + - Linux, OS X, Windows, BSD + - x86(_64), arm(64), mips(el), riscv64 +- Layered architecture offers both control and convenience +- Flexible memory management +- No shared global state - threading friendly +- Proper handling of UTF-8 +- Full support for streams & incremental processing +- Extensive documentation and test suite +- No runtime dependencies, small footprint + +## References + +libcbor is most prominently used in: + +- Yubico's [libfido2](https://developers.yubico.com/libfido2/) 2FA security key implementation +- Amazon's [AWS C SDK](https://github.com/awslabs/aws-c-common) +- Gnome [fwdup](https://github.com/fwupd/fwupd/blob/main/meson.build#L339) +- Alibaba's [Inclavare librats](https://github.com/inclavare-containers/librats) +- [QEMU](https://wiki.qemu.org/ChangeLog/9.2) +- [ITK](https://docs.itk.org/projects/wasm/en/latest/introduction/parts.html) + +It found its way into many open source an proprietary projects. If you run among others [OpenSSH](https://www.matbra.com/2020/02/17/using-fido2-with-ssh.html), [Microsoft PowerShell](https://github.com/PowerShell/libcbor), [SteamOS](https://github.com/randombk/steamos-teardown/blob/5a37d977fae55d9c41eaf1d07528fa965740bb26/docs/packages.md?plain=1#L461), or [MySQL](https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-34.html) -- you might be indirectly running libcbor too. + +Also, thank you for the shout out in <https://github.com/oz123/awesome-c?tab=readme-ov-file#others>! + ## Getting started ### Compile from source @@ -48,7 +67,15 @@ sudo apt-get install libcbor-dev yum install libcbor-devel ``` -### Others +### Include git repository using using CMake + +See e.g. <https://github.com/inclavare-containers/librats/blob/master/cmake/LibCBOR.cmake>. + +## Include git repository using Bazel + +See <https://github.com/PJK/libcbor/tree/master/examples/bazel>. + +### Others <details> <summary>Packaged libcbor is available from 15+ major repositories. Click here for more detail</summary> @@ -89,6 +116,9 @@ int main(void) { ``` ## Documentation + +Crash course: <https://libcbor.readthedocs.io/en/latest/tutorial.html#crash-course> + Get the latest documentation at [libcbor.readthedocs.org](http://libcbor.readthedocs.org/) ## Contributions @@ -98,6 +128,7 @@ Bug reports and contributions are welcome. Please see [CONTRIBUTING.md](https:// Kudos to all the [contributors](https://github.com/PJK/libcbor/graphs/contributors)! ## License + The MIT License (MIT) Copyright (c) Pavel Kalvoda, 2014-2020 diff --git a/doc/source/api/item_reference_counting.rst b/doc/source/api/item_reference_counting.rst index 70075cb67e5b..f590ac2e2292 100644 --- a/doc/source/api/item_reference_counting.rst +++ b/doc/source/api/item_reference_counting.rst @@ -36,3 +36,4 @@ The destruction is synchronous and renders any pointers to items with refcount z .. doxygenfunction:: cbor_refcount .. doxygenfunction:: cbor_move .. doxygenfunction:: cbor_copy +.. doxygenfunction:: cbor_copy_definite diff --git a/doc/source/conf.py b/doc/source/conf.py index 4574669505e8..52c19154d04e 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -77,8 +77,8 @@ copyright = '2014 - 2020, Pavel Kalvoda' # built documents. # # The short X.Y version. -version = '0.12' -release = '0.12.0' +version = '0.13' +release = '0.13.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/development.rst b/doc/source/development.rst index 5471047317af..9715476a55d6 100644 --- a/doc/source/development.rst +++ b/doc/source/development.rst @@ -108,13 +108,16 @@ Installing *sphinx* .. code-block:: bash - pip install sphinx - pip install sphinx_rtd_theme - pip install breathe - pip install https://github.com/lepture/python-livereload/archive/master.zip - pip install sphinx-autobuild + pip install -r doc/source/requirements.txt -Further instructions on configuring advanced features can be found at `<http://read-the-docs.readthedocs.org/en/latest/install.html>`_. + +To update the Python dependencies: + +.. code-block:: bash + + pip-compile --upgrade doc/source/requirements.in + +Sphinx reference: `<http://read-the-docs.readthedocs.org/en/latest/install.html>`_. Live preview of docs diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index ee57c094458d..5f67a86f66c1 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -102,6 +102,9 @@ The following configuration options will also be defined as macros [#]_ in ``<cb If you want to pass other custom configuration options, please refer to `<http://www.cmake.org/Wiki/CMake_Useful_Variables>`_. +.. note:: + When ``CMAKE_INTERPROCEDURAL_OPTIMIZATION`` is enabled, the generated static library (`libcbor.a`) should be used with an LTO-enabled linker downstream. On LLVM toolchains without bitcode embedding (`-fembed-bitcode`), the archive will contain LLVM IR only and linking without LTO `will not work <https://github.com/PJK/libcbor/issues/372>`_. + .. warning:: ``CBOR_CUSTOM_ALLOC`` has been `removed <https://github.com/PJK/libcbor/pull/237>`_. Custom allocators (historically a controlled by a build flag) are always enabled. diff --git a/doc/source/index.rst b/doc/source/index.rst index d3d62cf75c41..06ef1a059891 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -3,6 +3,8 @@ libcbor Documentation for version |release|, updated on |today|. +Git repo: https://github.com/PJK/libcbor + Overview -------- *libcbor* is a C library for parsing and generating CBOR_, the general-purpose schema-less binary data format. @@ -28,7 +30,7 @@ Contents .. toctree:: getting_started - using + tutorial api tests standard_conformance diff --git a/doc/source/requirements.in b/doc/source/requirements.in new file mode 100644 index 000000000000..000ba6286d17 --- /dev/null +++ b/doc/source/requirements.in @@ -0,0 +1,5 @@ +sphinx +sphinx_rtd_theme +breathe +livereload +sphinx-autobuild diff --git a/doc/source/requirements.txt b/doc/source/requirements.txt index 1b34e120b8b2..44b77ded4f36 100644 --- a/doc/source/requirements.txt +++ b/doc/source/requirements.txt @@ -1,47 +1,91 @@ +# +# This file is autogenerated by pip-compile with Python 3.13 +# by the following command: +# +# pip-compile doc/source/requirements.in +# alabaster==1.0.0 -anyio==4.8.0 + # via sphinx +anyio==4.9.0 + # via + # starlette + # watchfiles babel==2.17.0 + # via sphinx breathe==4.36.0 -build==1.2.2.post1 + # via -r doc/source/requirements.in certifi==2025.1.31 + # via requests charset-normalizer==3.4.1 + # via requests click==8.1.8 + # via uvicorn colorama==0.4.6 + # via sphinx-autobuild docutils==0.21.2 + # via + # sphinx + # sphinx-rtd-theme h11==0.14.0 + # via uvicorn idna==3.10 + # via + # anyio + # requests imagesize==1.4.1 -importlib_metadata==8.6.1 -Jinja2==3.1.6 -livereload @ https://github.com/lepture/python-livereload/archive/master.zip#sha256=95371213cf9107242808ea6e1353b524d7c38d96e299604e651e43271263352c -MarkupSafe==3.0.2 -packaging==24.2 -pip-tools==7.4.1 -Pygments==2.19.1 -pyparsing==3.2.1 -pyproject_hooks==1.2.0 -pytz==2025.1 + # via sphinx +jinja2==3.1.6 + # via sphinx +livereload==2.7.1 + # via -r doc/source/requirements.in +markupsafe==3.0.2 + # via jinja2 +packaging==25.0 + # via sphinx +pygments==2.19.1 + # via sphinx requests==2.32.3 + # via sphinx roman-numerals-py==3.1.0 -setuptools==75.8.2 -six==1.17.0 + # via sphinx sniffio==1.3.1 + # via anyio snowballstemmer==2.2.0 -Sphinx==8.2.3 + # via sphinx +sphinx==8.2.3 + # via + # -r doc/source/requirements.in + # breathe + # sphinx-autobuild + # sphinx-rtd-theme + # sphinxcontrib-jquery sphinx-autobuild==2024.10.3 + # via -r doc/source/requirements.in sphinx-rtd-theme==3.0.2 + # via -r doc/source/requirements.in sphinxcontrib-applehelp==2.0.0 + # via sphinx sphinxcontrib-devhelp==2.0.0 + # via sphinx sphinxcontrib-htmlhelp==2.1.0 + # via sphinx sphinxcontrib-jquery==4.1 + # via sphinx-rtd-theme sphinxcontrib-jsmath==1.0.1 + # via sphinx sphinxcontrib-qthelp==2.0.0 + # via sphinx sphinxcontrib-serializinghtml==2.0.0 -starlette==0.46.0 + # via sphinx +starlette==0.46.2 + # via sphinx-autobuild tornado==6.4.2 -urllib3==2.3.0 -uvicorn==0.34.0 -watchfiles==1.0.4 -websockets==15.0 -wheel==0.45.1 -zipp==3.21.0 + # via livereload +urllib3==2.4.0 + # via requests +uvicorn==0.34.2 + # via sphinx-autobuild +watchfiles==1.0.5 + # via sphinx-autobuild +websockets==15.0.1 + # via sphinx-autobuild diff --git a/doc/source/tutorial.rst b/doc/source/tutorial.rst new file mode 100644 index 000000000000..81859ccb2192 --- /dev/null +++ b/doc/source/tutorial.rst @@ -0,0 +1,66 @@ +Tutorial +=========================== + +*libcbor* is a C library to encode, decode, and manipulate CBOR data. It is to CBOR to what `cJSON <https://github.com/DaveGamble/cJSON>`_ is to JSON. We assume you are familiar with the CBOR standard. If not, we recommend `cbor.io <http://cbor.io/>`_. + + +Where to start +-------------- + +- Skim through the Crash course section below. +- Examples of of how to read, write, manipulate, and translate data to and from JSON using *libcbor* are in the `examples directory <https://github.com/PJK/libcbor/tree/master/examples>`_. +- The :doc:`API documentation <api>` is a complete reference of *libcbor*. + + +Crash course +---------------- + +CBOR data objects are ``cbor_item_t`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. literalinclude:: ../../examples/crash_course.c + :language: C + :start-after: // Part 1: Begin + :end-before: // Part 1: End + + +Objects can be serialized and deserialized +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. literalinclude:: ../../examples/crash_course.c + :language: C + :start-after: // Part 2: Begin + :end-before: // Part 2: End + + +Reference counting +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. literalinclude:: ../../examples/crash_course.c + :language: C + :start-after: // Part 3: Begin + :end-before: // Part 3: End + + +Moving intermediate values +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. literalinclude:: ../../examples/crash_course.c + :language: C + :start-after: // Part 4: Begin + :end-before: // Part 4: End + + +Ownership +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. literalinclude:: ../../examples/crash_course.c + :language: C + :start-after: // Part 5: Begin + :end-before: // Part 5: End + + +Streaming IO +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +See https://github.com/PJK/libcbor/blob/master/examples/streaming_array.c, https://github.com/PJK/libcbor/blob/master/examples/streaming_parser.c
\ No newline at end of file diff --git a/doc/source/using.rst b/doc/source/using.rst deleted file mode 100644 index c6688bbad7bd..000000000000 --- a/doc/source/using.rst +++ /dev/null @@ -1,174 +0,0 @@ -Usage & preliminaries -======================= - -Version information --------------------- - -libcbor exports its version using three self-explanatory macros: - - - ``CBOR_MAJOR_VERSION`` - - ``CBOR_MINOR_VERSION`` - - ``CBOR_PATCH_VERSION`` - -The ``CBOR_VERSION`` is a string concatenating these three identifiers into one (e.g. ``0.2.0``). - -In order to simplify version comparisons, the version is also exported as - -.. code-block:: c - - #define CBOR_HEX_VERSION ((CBOR_MAJOR_VERSION << 16) | (CBOR_MINOR_VERSION << 8) | CBOR_PATCH_VERSION) - -Since macros are difficult to work with through FFIs, the same information is also available through three ``uint8_t`` constants, -namely - - - ``cbor_major_version`` - - ``cbor_minor_version`` - - ``cbor_patch_version`` - - -Headers to include ---------------------- - -The ``cbor.h`` header includes all the symbols. If, for any reason, you don't want to include all the exported symbols, -feel free to use just some of the ``cbor/*.h`` headers: - - - ``cbor/arrays.h`` - :doc:`api/type_4_arrays` - - ``cbor/bytestrings.h`` - :doc:`api/type_2_byte_strings` - - ``cbor/callbacks.h`` - Callbacks used for :doc:`api/streaming_decoding` - - ``cbor/common.h`` - Common utilities - always transitively included - - ``cbor/data.h`` - Data types definitions - always transitively included - - ``cbor/encoding.h`` - Streaming encoders for :doc:`api/streaming_encoding` - - ``cbor/floats_ctrls.h`` - :doc:`api/type_7_floats_ctrls` - - ``cbor/ints.h`` - :doc:`api/type_0_1_integers` - - ``cbor/maps.h`` - :doc:`api/type_5_maps` - - ``cbor/serialization.h`` - High level serialization such as :func:`cbor_serialize` - - ``cbor/streaming.h`` - Home of :func:`cbor_stream_decode` - - ``cbor/strings.h`` - :doc:`api/type_3_strings` - - ``cbor/tags.h`` - :doc:`api/type_6_tags` - - -Using libcbor --------------- - -If you want to get more familiar with CBOR, we recommend the `cbor.io <http://cbor.io/>`_ website. Once you get the grasp -of what is it CBOR does, the examples (located in the ``examples`` directory) should give you a good feel of the API. The -:doc:`API documentation <api>` should then provide with all the information you may need. - - -**Creating and serializing items** - -.. code-block:: c - - #include "cbor.h" - #include <stdio.h> - - int main(int argc, char * argv[]) - { - /* Preallocate the map structure */ - cbor_item_t * root = cbor_new_definite_map(2); - /* Add the content */ - cbor_map_add(root, (struct cbor_pair) { - .key = cbor_move(cbor_build_string("Is CBOR awesome?")), - .value = cbor_move(cbor_build_bool(true)) - }); - cbor_map_add(root, (struct cbor_pair) { - .key = cbor_move(cbor_build_uint8(42)), - .value = cbor_move(cbor_build_string("Is the answer")) - }); - /* Output: `buffer_size` bytes of data in the `buffer` */ - unsigned char * buffer; - size_t buffer_size; - cbor_serialize_alloc(root, &buffer, &buffer_size); - - fwrite(buffer, 1, buffer_size, stdout); - free(buffer); - - fflush(stdout); - cbor_decref(&root); - } - - -**Reading serialized data** - -.. code-block:: c - - #include "cbor.h" - #include <stdio.h> - - /* - * Reads data from a file. Example usage: - * $ ./examples/readfile examples/data/nested_array.cbor - */ - - int main(int argc, char * argv[]) - { - FILE * f = fopen(argv[1], "rb"); - 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 `info.st_size` bytes of input data */ - struct cbor_load_result result; - cbor_item_t * item = cbor_load(buffer, length, &result); - /* Pretty-print the result */ - cbor_describe(item, stdout); - fflush(stdout); - /* Deallocate the result */ - cbor_decref(&item); - - fclose(f); - } - - -**Using the streaming parser** - -.. code-block:: c - - #include "cbor.h" - #include <stdio.h> - #include <string.h> - - /* - * Illustrates how one might skim through a map (which is assumed to have - * string keys and values only), looking for the value of a specific key - * - * Use the examples/data/map.cbor input to test this. - */ - - const char * key = "a secret key"; - bool key_found = false; - - void find_string(void * _ctx, cbor_data buffer, size_t len) - { - if (key_found) { - printf("Found the value: %*s\n", (int) len, buffer); - key_found = false; - } else if (len == strlen(key)) { - key_found = (memcmp(key, buffer, len) == 0); - } - } - - int main(int argc, char * argv[]) - { - FILE * f = fopen(argv[1], "rb"); - 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); - - struct cbor_callbacks callbacks = cbor_empty_callbacks; - struct cbor_decoder_result decode_result; - size_t bytes_read = 0; - callbacks.string = find_string; - while (bytes_read < length) { - decode_result = cbor_stream_decode(buffer + bytes_read, - length - bytes_read, - &callbacks, NULL); - bytes_read += decode_result.read; - } - - fclose(f); - } diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5ef9162c12d1..f0d9e9749963 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -16,6 +16,12 @@ target_link_libraries(sort cbor) add_executable(hello hello.c) target_link_libraries(hello cbor) +add_executable(cbor_sequence cbor_sequence.c) +target_link_libraries(cbor_sequence cbor) + +add_executable(crash_course crash_course.c) +target_link_libraries(crash_course cbor) + find_package(CJSON) if(CJSON_FOUND) diff --git a/examples/bazel/third_party/libcbor/cbor/configuration.h b/examples/bazel/third_party/libcbor/cbor/configuration.h index 070b54d9c2ae..c0122ff9fcfc 100644 --- a/examples/bazel/third_party/libcbor/cbor/configuration.h +++ b/examples/bazel/third_party/libcbor/cbor/configuration.h @@ -2,7 +2,7 @@ #define LIBCBOR_CONFIGURATION_H #define CBOR_MAJOR_VERSION 0 -#define CBOR_MINOR_VERSION 12 +#define CBOR_MINOR_VERSION 13 #define CBOR_PATCH_VERSION 0 #define CBOR_BUFFER_GROWTH 2 diff --git a/examples/cbor_sequence.c b/examples/cbor_sequence.c new file mode 100644 index 000000000000..02c43185ff58 --- /dev/null +++ b/examples/cbor_sequence.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com> + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <stdio.h> +#include <string.h> + +#include "cbor.h" + +void write_cbor_sequence(const char* filename) { + FILE* file = fopen(filename, "wb"); + if (!file) { + fprintf(stderr, "Error: Could not open file %s for writing\n", filename); + return; + } + + // Create example CBOR items + cbor_item_t* int_item = cbor_build_uint32(42); + cbor_item_t* string_item = cbor_build_string("Hello, CBOR!"); + cbor_item_t* array_item = cbor_new_definite_array(2); + assert(cbor_array_push(array_item, cbor_build_uint8(1))); + assert(cbor_array_push(array_item, cbor_build_uint8(2))); + + // Serialize and write items to the file + unsigned char* buffer; + size_t buffer_size; + + cbor_serialize_alloc(int_item, &buffer, &buffer_size); + fwrite(buffer, 1, buffer_size, file); + free(buffer); + cbor_decref(&int_item); + + cbor_serialize_alloc(string_item, &buffer, &buffer_size); + fwrite(buffer, 1, buffer_size, file); + free(buffer); + cbor_decref(&string_item); + + cbor_serialize_alloc(array_item, &buffer, &buffer_size); + fwrite(buffer, 1, buffer_size, file); + free(buffer); + cbor_decref(&array_item); + + fclose(file); + printf("CBOR sequence written to %s\n", filename); +} + +void read_cbor_sequence(const char* filename) { + FILE* file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, "Error: Could not open file %s\n", filename); + return; + } + + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + unsigned char* buffer = malloc(file_size); + if (!buffer) { + fprintf(stderr, "Error: Could not allocate memory\n"); + fclose(file); + return; + } + + fread(buffer, 1, file_size, file); + fclose(file); + + struct cbor_load_result result; + size_t offset = 0; + + while (offset < file_size) { + cbor_item_t* item = cbor_load(buffer + offset, file_size - offset, &result); + if (result.error.code != CBOR_ERR_NONE) { + fprintf(stderr, "Error: Failed to parse CBOR item at offset %zu\n", + offset); + break; + } + + cbor_describe(item, stdout); + printf("\n"); + + offset += result.read; + cbor_decref(&item); + } + + free(buffer); +} + +int main(int argc, char* argv[]) { + if (argc != 3) { + fprintf(stderr, "Usage: %s <r|w> <file>\n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "w") == 0) { + write_cbor_sequence(argv[2]); + } else if (strcmp(argv[1], "r") == 0) { + read_cbor_sequence(argv[2]); + } else { + fprintf(stderr, + "Error: First argument must be 'r' (read) or 'w' (write)\n"); + return 1; + } + + return 0; +} diff --git a/examples/crash_course.c b/examples/crash_course.c new file mode 100644 index 000000000000..4bd9f9379d26 --- /dev/null +++ b/examples/crash_course.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com> + * + * libcbor is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include "cbor.h" + +// Part 1: Begin +void item_examples() { + // A cbor_item_t can contain any CBOR data type + cbor_item_t* float_item = cbor_build_float4(3.14f); + cbor_item_t* string_item = cbor_build_string("Hello World!"); + cbor_item_t* array_item = cbor_new_indefinite_array(); + + // They can be inspected + assert(cbor_is_float(float_item)); + assert(cbor_typeof(string_item) == CBOR_TYPE_STRING); + assert(cbor_array_is_indefinite(array_item)); + assert(cbor_array_size(array_item) == 0); + + // The data can be accessed + assert(cbor_float_get_float4(float_item) == 3.14f); + assert(memcmp(cbor_string_handle(string_item), "Hello World!", + cbor_string_length(string_item)) == 0); + + // And they can be modified + assert(cbor_array_push(array_item, float_item)); + assert(cbor_array_push(array_item, string_item)); + assert(cbor_array_size(array_item) == 2); + + // At the end of their lifetime, items must be freed + cbor_decref(&float_item); + cbor_decref(&string_item); + cbor_decref(&array_item); +} +// Part 1: End + +// Part 2: Begin +void encode_decode() { + cbor_item_t* item = cbor_build_uint8(42); + + // Serialize the item to a buffer (it will be allocated by libcbor) + unsigned char* buffer; + size_t buffer_size; + cbor_serialize_alloc(item, &buffer, &buffer_size); + assert(buffer_size == 2); + assert(buffer[0] == 0x18); // Encoding byte for uint8 + assert(buffer[1] == 42); // The value itself + + // And deserialize bytes back to an item + struct cbor_load_result result; + cbor_item_t* decoded_item = cbor_load(buffer, buffer_size, &result); + assert(result.error.code == CBOR_ERR_NONE); + assert(cbor_isa_uint(decoded_item)); + assert(cbor_get_uint8(decoded_item) == 42); + + // Free the allocated buffer and items + free(buffer); + cbor_decref(&decoded_item); + cbor_decref(&item); +} +// Part 2: End + +// Part 3: Begin +void reference_counting() { + // cbor_item_t is a reference counted pointer under the hood + cbor_item_t* item = cbor_build_uint8(42); + + // Reference count starts at 1 + assert(cbor_refcount(item) == 1); + + // Most operations have reference semantics + cbor_item_t* array_item = cbor_new_definite_array(1); + assert(cbor_array_push(array_item, item)); + assert(cbor_refcount(item) == 2); // item and array_item reference it + cbor_item_t* first_array_element = cbor_array_get(array_item, 0); + assert(first_array_element == item); // same item under the hood + assert(cbor_refcount(item) == + 3); // and now first_array_element also points to it + + // To release the reference, use cbor_decref + cbor_decref(&first_array_element); + + // When reference count reaches 0, the item is freed + assert(cbor_refcount(array_item) == 1); + cbor_decref(&array_item); + assert(array_item == NULL); + assert(cbor_refcount(item) == 1); + + // Be careful, loops leak memory! + + // Deep copy copies the whole item tree + cbor_item_t* item_copy = cbor_copy(item); + assert(cbor_refcount(item) == 1); + assert(cbor_refcount(item_copy) == 1); + assert(item_copy != item); + cbor_decref(&item); + cbor_decref(&item_copy); +} +// Part 3: End + +// Part 4: Begin +void moving_values() { + { + // Move the "42" into an array. + cbor_item_t* array_item = cbor_new_definite_array(1); + // The line below leaks memory! + assert(cbor_array_push(array_item, cbor_build_uint8(42))); + cbor_item_t* first_array_element = cbor_array_get(array_item, 0); + assert(cbor_refcount(first_array_element) == 3); // Should be 2! + cbor_decref(&first_array_element); + cbor_decref(&array_item); + assert(cbor_refcount(first_array_element) == 1); // Shouldn't exist! + // Clean up + cbor_decref(&first_array_element); + } + + { + // A correct way to move values is to decref them in the caller scope. + cbor_item_t* array_item = cbor_new_definite_array(1); + cbor_item_t* item = cbor_build_uint8(42); + assert(cbor_array_push(array_item, item)); + assert(cbor_refcount(item) == 2); + // "Give up" the item + cbor_decref(&item); + cbor_decref(&array_item); + // item is a dangling pointer at this point + } + + { + // cbor_move avoids the need to decref and the dangling pointer + cbor_item_t* array_item = cbor_new_definite_array(1); + assert(cbor_array_push(array_item, cbor_move(cbor_build_uint8(42)))); + cbor_item_t* first_array_element = cbor_array_get(array_item, 0); + assert(cbor_refcount(first_array_element) == 2); + cbor_decref(&first_array_element); + cbor_decref(&array_item); + } +} +// Part 4: End + +// Part 5: Begin +// Refcount can be managed in conjunction with ownership +static cbor_item_t* global_item = NULL; + +// This function takes shared ownership of the item +void borrow_item(cbor_item_t* item) { + global_item = item; + // Mark the extra reference + cbor_incref(item); +} + +void return_item() { + cbor_decref(&global_item); + global_item = NULL; +} + +void reference_ownership() { + cbor_item_t* item = cbor_build_uint8(42); + + // Lend the item + borrow_item(item); + assert(cbor_refcount(item) == 2); + cbor_decref(&item); + + // Release the shared ownership. return_item will deallocate the item. + return_item(); +} +// Part 5: End + +int main(void) { + item_examples(); + encode_decode(); + reference_counting(); + moving_values(); + reference_ownership(); + return 0; +} diff --git a/misc/asan_suppressions.osx.supp b/misc/asan_suppressions.osx.supp new file mode 100644 index 000000000000..5503d9412a53 --- /dev/null +++ b/misc/asan_suppressions.osx.supp @@ -0,0 +1,3 @@ +leak:initializeNonMetaClass +# via _cmocka_run_group_tests +leak:tlv_get_addr
\ No newline at end of file diff --git a/release.sh b/release.sh index e16bcbc99b31..790c650de4f0 100755 --- a/release.sh +++ b/release.sh @@ -80,7 +80,6 @@ git checkout master git pull prompt "Will proceed to tag the release with $TAG_NAME." -git commit -a -m "Release $TAG_NAME" git tag "$TAG_NAME" git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD) git push --tags diff --git a/src/cbor.c b/src/cbor.c index 819ff6077c34..93a3709f5423 100644 --- a/src/cbor.c +++ b/src/cbor.c @@ -5,6 +5,9 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include <stdbool.h> +#include <string.h> + #include "cbor.h" #include "cbor/internal/builder_callbacks.h" #include "cbor/internal/loaders.h" @@ -115,6 +118,9 @@ error: } static cbor_item_t* _cbor_copy_int(cbor_item_t* item, bool negative) { + CBOR_ASSERT(cbor_isa_uint(item) || cbor_isa_negint(item)); + CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && + cbor_int_get_width(item) <= CBOR_INT_64); cbor_item_t* res = NULL; switch (cbor_int_get_width(item)) { case CBOR_INT_8: @@ -137,6 +143,9 @@ static cbor_item_t* _cbor_copy_int(cbor_item_t* item, bool negative) { } static cbor_item_t* _cbor_copy_float_ctrl(cbor_item_t* item) { + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 && + cbor_float_get_width(item) <= CBOR_FLOAT_64); switch (cbor_float_get_width(item)) { case CBOR_FLOAT_0: return cbor_build_ctrl(cbor_ctrl_value(item)); @@ -146,13 +155,14 @@ static cbor_item_t* _cbor_copy_float_ctrl(cbor_item_t* item) { return cbor_build_float4(cbor_float_get_float4(item)); case CBOR_FLOAT_64: return cbor_build_float8(cbor_float_get_float8(item)); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return NULL; + return NULL; // LCOV_EXCL_START } } cbor_item_t* cbor_copy(cbor_item_t* item) { + CBOR_ASSERT_VALID_TYPE(cbor_typeof(item)); switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: return _cbor_copy_int(item, false); @@ -283,9 +293,138 @@ cbor_item_t* cbor_copy(cbor_item_t* item) { } case CBOR_TYPE_FLOAT_CTRL: return _cbor_copy_float_ctrl(item); - default: + default: // LCOV_EXCL_START + _CBOR_UNREACHABLE; + return NULL; // LCOV_EXCL_STOP + } +} + +cbor_item_t* cbor_copy_definite(cbor_item_t* item) { + CBOR_ASSERT_VALID_TYPE(cbor_typeof(item)); + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: + case CBOR_TYPE_NEGINT: + return cbor_copy(item); + case CBOR_TYPE_BYTESTRING: + if (cbor_bytestring_is_definite(item)) { + return cbor_copy(item); + } else { + size_t total_length = 0; + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) { + total_length += + cbor_bytestring_length(cbor_bytestring_chunks_handle(item)[i]); + } + + unsigned char* combined_data = _cbor_malloc(total_length); + if (combined_data == NULL) { + return NULL; + } + + size_t offset = 0; + for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) { + cbor_item_t* chunk = cbor_bytestring_chunks_handle(item)[i]; + memcpy(combined_data + offset, cbor_bytestring_handle(chunk), + cbor_bytestring_length(chunk)); + offset += cbor_bytestring_length(chunk); + } + + cbor_item_t* res = cbor_new_definite_bytestring(); + cbor_bytestring_set_handle(res, combined_data, total_length); + return res; + } + case CBOR_TYPE_STRING: + if (cbor_string_is_definite(item)) { + return cbor_copy(item); + } else { + size_t total_length = 0; + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) { + total_length += + cbor_string_length(cbor_string_chunks_handle(item)[i]); + } + + unsigned char* combined_data = _cbor_malloc(total_length); + if (combined_data == NULL) { + return NULL; + } + + size_t offset = 0; + for (size_t i = 0; i < cbor_string_chunk_count(item); i++) { + cbor_item_t* chunk = cbor_string_chunks_handle(item)[i]; + memcpy(combined_data + offset, cbor_string_handle(chunk), + cbor_string_length(chunk)); + offset += cbor_string_length(chunk); + } + + cbor_item_t* res = cbor_new_definite_string(); + cbor_string_set_handle(res, combined_data, total_length); + return res; + } + case CBOR_TYPE_ARRAY: { + cbor_item_t* res = cbor_new_definite_array(cbor_array_size(item)); + if (res == NULL) { + return NULL; + } + + for (size_t i = 0; i < cbor_array_size(item); i++) { + cbor_item_t* entry_copy = + cbor_copy_definite(cbor_array_handle(item)[i]); + if (entry_copy == NULL) { + cbor_decref(&res); + return NULL; + } + // Cannot fail since we have a definite array preallocated + // cppcheck-suppress syntaxError + const bool item_pushed _CBOR_UNUSED = cbor_array_push(res, entry_copy); + CBOR_ASSERT(item_pushed); + cbor_decref(&entry_copy); + } + return res; + } + case CBOR_TYPE_MAP: { + cbor_item_t* res; + res = cbor_new_definite_map(cbor_map_size(item)); + if (res == NULL) { + return NULL; + } + + struct cbor_pair* it = cbor_map_handle(item); + for (size_t i = 0; i < cbor_map_size(item); i++) { + cbor_item_t* key_copy = cbor_copy_definite(it[i].key); + if (key_copy == NULL) { + cbor_decref(&res); + return NULL; + } + cbor_item_t* value_copy = cbor_copy_definite(it[i].value); + if (value_copy == NULL) { + cbor_decref(&res); + cbor_decref(&key_copy); + return NULL; + } + // Cannot fail since we have a definite map preallocated + // cppcheck-suppress syntaxError + const bool item_added _CBOR_UNUSED = cbor_map_add( + res, (struct cbor_pair){.key = key_copy, .value = value_copy}); + CBOR_ASSERT(item_added); + cbor_decref(&key_copy); + cbor_decref(&value_copy); + } + return res; + } + case CBOR_TYPE_TAG: { + cbor_item_t* item_copy = + cbor_copy_definite(cbor_move(cbor_tag_item(item))); + if (item_copy == NULL) { + return NULL; + } + cbor_item_t* tag = cbor_build_tag(cbor_tag_value(item), item_copy); + cbor_decref(&item_copy); + return tag; + } + case CBOR_TYPE_FLOAT_CTRL: + return cbor_copy(item); + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return NULL; + return NULL; // LCOV_EXCL_STOP } } @@ -309,6 +448,8 @@ static void _cbor_type_marquee(FILE* out, char* label, int indent) { } static void _cbor_nested_describe(cbor_item_t* item, FILE* out, int indent) { + CBOR_ASSERT(cbor_typeof(item) >= CBOR_TYPE_UINT && + cbor_typeof(item) <= CBOR_TYPE_FLOAT_CTRL); const int indent_offset = 4; switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: { diff --git a/src/cbor.h b/src/cbor.h index 46ef8f267ac9..b2bb9771d13e 100644 --- a/src/cbor.h +++ b/src/cbor.h @@ -61,6 +61,17 @@ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_load( */ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_copy(cbor_item_t* item); +/** Copy the item with all items converted to definite length equivalents + * + * Deep copy semantics follow #cbor_copy + * + * @param item item to copy + * @return Reference to the new item. The item's reference count is initialized + * to one. + * @return `NULL` if memory allocation fails + */ +_CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_copy_definite(cbor_item_t* item); + #if CBOR_PRETTY_PRINTER #include <stdio.h> diff --git a/src/cbor/arrays.c b/src/cbor/arrays.c index 174c97e9a61e..5433037cfdb8 100644 --- a/src/cbor/arrays.c +++ b/src/cbor/arrays.c @@ -5,8 +5,9 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include <stdbool.h> + #include "arrays.h" -#include <string.h> #include "internal/memory_utils.h" size_t cbor_array_size(const cbor_item_t* item) { diff --git a/src/cbor/arrays.h b/src/cbor/arrays.h index db19e59d0624..891061ef765a 100644 --- a/src/cbor/arrays.h +++ b/src/cbor/arrays.h @@ -49,7 +49,8 @@ CBOR_EXPORT cbor_item_t* cbor_array_get(const cbor_item_t* item, size_t index); * returned. Creating arrays with holes is not possible. * * @param item An array - * @param value The item to assign + * @param value The item to assign. Its reference count will be increased by + * one. * @param index The index (zero-based) * @return `true` on success, `false` on allocation failure. */ diff --git a/src/cbor/common.c b/src/cbor/common.c index 704dd0c3205d..1931b572fb32 100644 --- a/src/cbor/common.c +++ b/src/cbor/common.c @@ -19,56 +19,62 @@ bool _cbor_enable_assert = true; #endif +cbor_type cbor_typeof(const cbor_item_t* item) { + CBOR_ASSERT(item != NULL); + CBOR_ASSERT_VALID_TYPE(item->type); + return item->type; +} + bool cbor_isa_uint(const cbor_item_t* item) { - return item->type == CBOR_TYPE_UINT; + return cbor_typeof(item) == CBOR_TYPE_UINT; } bool cbor_isa_negint(const cbor_item_t* item) { - return item->type == CBOR_TYPE_NEGINT; + return cbor_typeof(item) == CBOR_TYPE_NEGINT; } bool cbor_isa_bytestring(const cbor_item_t* item) { - return item->type == CBOR_TYPE_BYTESTRING; + return cbor_typeof(item) == CBOR_TYPE_BYTESTRING; } bool cbor_isa_string(const cbor_item_t* item) { - return item->type == CBOR_TYPE_STRING; + return cbor_typeof(item) == CBOR_TYPE_STRING; } bool cbor_isa_array(const cbor_item_t* item) { - return item->type == CBOR_TYPE_ARRAY; + return cbor_typeof(item) == CBOR_TYPE_ARRAY; } bool cbor_isa_map(const cbor_item_t* item) { - return item->type == CBOR_TYPE_MAP; + return cbor_typeof(item) == CBOR_TYPE_MAP; } bool cbor_isa_tag(const cbor_item_t* item) { - return item->type == CBOR_TYPE_TAG; + return cbor_typeof(item) == CBOR_TYPE_TAG; } bool cbor_isa_float_ctrl(const cbor_item_t* item) { - return item->type == CBOR_TYPE_FLOAT_CTRL; + return cbor_typeof(item) == CBOR_TYPE_FLOAT_CTRL; } -cbor_type cbor_typeof(const cbor_item_t* item) { return item->type; } - bool cbor_is_int(const cbor_item_t* item) { return cbor_isa_uint(item) || cbor_isa_negint(item); } bool cbor_is_bool(const cbor_item_t* item) { - return cbor_isa_float_ctrl(item) && + return cbor_isa_float_ctrl(item) && cbor_float_ctrl_is_ctrl(item) && (cbor_ctrl_value(item) == CBOR_CTRL_FALSE || cbor_ctrl_value(item) == CBOR_CTRL_TRUE); } bool cbor_is_null(const cbor_item_t* item) { - return cbor_isa_float_ctrl(item) && cbor_ctrl_value(item) == CBOR_CTRL_NULL; + return cbor_isa_float_ctrl(item) && cbor_float_ctrl_is_ctrl(item) && + cbor_ctrl_value(item) == CBOR_CTRL_NULL; } bool cbor_is_undef(const cbor_item_t* item) { - return cbor_isa_float_ctrl(item) && cbor_ctrl_value(item) == CBOR_CTRL_UNDEF; + return cbor_isa_float_ctrl(item) && cbor_float_ctrl_is_ctrl(item) && + cbor_ctrl_value(item) == CBOR_CTRL_UNDEF; } bool cbor_is_float(const cbor_item_t* item) { diff --git a/src/cbor/common.h b/src/cbor/common.h index 89968db95442..d16002d9b651 100644 --- a/src/cbor/common.h +++ b/src/cbor/common.h @@ -77,14 +77,25 @@ extern bool _cbor_enable_assert; } while (0) #endif +#define CBOR_ASSERT_VALID_TYPE(item_type) \ + CBOR_ASSERT(item_type >= CBOR_TYPE_UINT && item_type <= CBOR_TYPE_FLOAT_CTRL); + #define _CBOR_TO_STR_(x) #x #define _CBOR_TO_STR(x) _CBOR_TO_STR_(x) /* enables proper double expansion */ +#ifdef CBOR_HAS_NODISCARD_ATTRIBUTE +#define CBOR_NODISCARD [[nodiscard]] +#else +#define CBOR_NODISCARD +#endif + #ifdef __GNUC__ #define _CBOR_UNUSED __attribute__((__unused__)) -// TODO(https://github.com/PJK/libcbor/issues/247): Prefer [[nodiscard]] if -// available +// Fall back to __attribute__((warn_unused_result)) if we don't have +// [[nodiscard]] +#ifndef CBOR_HAS_NODISCARD_ATTRIBUTE #define _CBOR_NODISCARD __attribute__((warn_unused_result)) +#endif #elif defined(_MSC_VER) #define _CBOR_UNUSED __pragma(warning(suppress : 4100 4101)) #define _CBOR_NODISCARD @@ -115,7 +126,8 @@ CBOR_EXPORT extern _cbor_free_t _cbor_free; } \ } while (0) -// Macro to short-circuit builders when memory allocation of nested data fails +// Macro to short-circuit builders when memory allocation of nested data +// fails #define _CBOR_DEPENDENT_NOTNULL(cbor_item, pointer) \ do { \ if (pointer == NULL) { \ @@ -126,18 +138,21 @@ CBOR_EXPORT extern _cbor_free_t _cbor_free; /** Sets the memory management routines to use. * - * By default, libcbor will use the standard library `malloc`, `realloc`, and - * `free`. + * By default, libcbor will use the standard library `malloc`, `realloc`, + * and `free`. * * \rst - * .. warning:: This function modifies the global state and should therefore be - * used accordingly. Changing the memory handlers while allocated items exist - * will result in a ``free``/``malloc`` mismatch. This function is not thread - * safe with respect to both itself and all the other *libcbor* functions that - * work with the heap. + * .. warning:: + * This function modifies the global state and should + * therefore be used accordingly. Changing the memory handlers while + * allocated items exist will result in a ``free``/``malloc`` mismatch. + * This function is not thread safe with respect to both itself and all + * the other *libcbor* functions that work with the heap. + * + * .. note:: + * `realloc` implementation must correctly support `NULL` + * reallocation (see e.g. http://en.cppreference.com/w/c/memory/realloc) * - * .. note:: `realloc` implementation must correctly support `NULL` reallocation - * (see e.g. http://en.cppreference.com/w/c/memory/realloc) * \endrst * * @param custom_malloc malloc implementation @@ -247,8 +262,9 @@ CBOR_EXPORT bool cbor_is_bool(const cbor_item_t* item); /** Does this item represent `null` * * \rst - * .. warning:: This is in no way related to the value of the pointer. Passing a - * null pointer will most likely result in a crash. + * .. warning:: + * This is in no way related to the value of the pointer. + * Passing a null pointer will most likely result in a crash. * \endrst * * @param item the item @@ -260,8 +276,8 @@ CBOR_EXPORT bool cbor_is_null(const cbor_item_t* item); /** Does this item represent `undefined` * * \rst - * .. warning:: Care must be taken to distinguish nulls and undefined values in - * C. + * .. warning:: + * Care must be taken to distinguish nulls and undefined values in C. * \endrst * * @param item the item @@ -278,8 +294,8 @@ CBOR_EXPORT bool cbor_is_undef(const cbor_item_t* item); /** Increases the item's reference count by one * - * Constant complexity; items referring to this one or items being referred to - * are not updated. + * Constant complexity; items referring to this one or items being + * referred to are not updated. * * This function can be used to extend reference counting to client code. * @@ -288,20 +304,22 @@ CBOR_EXPORT bool cbor_is_undef(const cbor_item_t* item); */ CBOR_EXPORT cbor_item_t* cbor_incref(cbor_item_t* item); -/** Decreases the item's reference count by one, deallocating the item if needed +/** Decreases the item's reference count by one, deallocating the item if + * needed * - * In case the item is deallocated, the reference count of all items this item - * references will also be #cbor_decref 'ed recursively. + * In case the item is deallocated, the reference count of all items this + * item references will also be #cbor_decref 'ed recursively. * * @param item Reference to an item. Will be set to `NULL` if deallocated */ CBOR_EXPORT void cbor_decref(cbor_item_t** item); -/** Decreases the item's reference count by one, deallocating the item if needed - * - * Convenience wrapper for #cbor_decref when its set-to-null behavior is not +/** Decreases the item's reference count by one, deallocating the item if * needed * + * Convenience wrapper for #cbor_decref when its set-to-null behavior is + * not needed + * * @param item Reference to an item */ CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t* item); @@ -309,7 +327,8 @@ CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t* item); /** Get the item's reference count * * \rst - * .. warning:: This does *not* account for transitive references. + * .. warning:: + * This does *not* account for transitive references. * \endrst * * @todo Add some inline examples for reference counting @@ -322,14 +341,15 @@ CBOR_EXPORT size_t cbor_refcount(const cbor_item_t* item); /** Provides CPP-like move construct * - * Decreases the reference count by one, but does not deallocate the item even - * if its refcount reaches zero. This is useful for passing intermediate values - * to functions that increase reference count. Should only be used with - * functions that `incref` their arguments. + * Decreases the reference count by one, but does not deallocate the item + * even if its refcount reaches zero. This is useful for passing + * intermediate values to functions that increase reference count. Should + * only be used with functions that `incref` their arguments. * * \rst - * .. warning:: If the item is moved without correctly increasing the reference - * count afterwards, the memory will be leaked. + * .. warning:: + * If the item is moved without correctly increasing the + * reference count afterwards, the memory will be leaked. * \endrst * * @param item Reference to an item diff --git a/src/cbor/floats_ctrls.c b/src/cbor/floats_ctrls.c index 705e78cb20ca..cae4abee2186 100644 --- a/src/cbor/floats_ctrls.c +++ b/src/cbor/floats_ctrls.c @@ -44,7 +44,9 @@ double cbor_float_get_float8(const cbor_item_t* item) { } double cbor_float_get_float(const cbor_item_t* item) { - CBOR_ASSERT(cbor_is_float(item)); + CBOR_ASSERT(cbor_isa_float_ctrl(item)); + CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 && + cbor_float_get_width(item) <= CBOR_FLOAT_64); switch (cbor_float_get_width(item)) { case CBOR_FLOAT_0: return NAN; @@ -54,9 +56,9 @@ double cbor_float_get_float(const cbor_item_t* item) { return cbor_float_get_float4(item); case CBOR_FLOAT_64: return cbor_float_get_float8(item); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } diff --git a/src/cbor/floats_ctrls.h b/src/cbor/floats_ctrls.h index 6a7925346951..1c1d0ba5b494 100644 --- a/src/cbor/floats_ctrls.h +++ b/src/cbor/floats_ctrls.h @@ -152,8 +152,9 @@ _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_build_bool(bool value); /** Assign a control value * * \rst - * .. warning:: It is possible to produce an invalid CBOR value by assigning a - * invalid value using this mechanism. Please consult the standard before use. + * .. warning:: + * It is possible to produce an invalid CBOR value by assigning an invalid + * value using this mechanism. Please consult the standard before use. * \endrst * * @param item A ctrl item diff --git a/src/cbor/ints.c b/src/cbor/ints.c index 272d82550c48..140d688f8215 100644 --- a/src/cbor/ints.c +++ b/src/cbor/ints.c @@ -38,7 +38,8 @@ uint64_t cbor_get_uint64(const cbor_item_t* item) { uint64_t cbor_get_int(const cbor_item_t* item) { CBOR_ASSERT(cbor_is_int(item)); - // cppcheck-suppress missingReturn + CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && + cbor_int_get_width(item) <= CBOR_INT_64); switch (cbor_int_get_width(item)) { case CBOR_INT_8: return cbor_get_uint8(item); @@ -48,9 +49,9 @@ uint64_t cbor_get_int(const cbor_item_t* item) { return cbor_get_uint32(item); case CBOR_INT_64: return cbor_get_uint64(item); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } diff --git a/src/cbor/serialization.c b/src/cbor/serialization.c index dbfc8c371382..b0f681c0c383 100644 --- a/src/cbor/serialization.c +++ b/src/cbor/serialization.c @@ -19,6 +19,7 @@ size_t cbor_serialize(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { + CBOR_ASSERT_VALID_TYPE(cbor_typeof(item)); switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: return cbor_serialize_uint(item, buffer, buffer_size); @@ -36,9 +37,9 @@ size_t cbor_serialize(const cbor_item_t* item, unsigned char* buffer, return cbor_serialize_tag(item, buffer, buffer_size); case CBOR_TYPE_FLOAT_CTRL: return cbor_serialize_float_ctrl(item, buffer, buffer_size); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } @@ -61,9 +62,12 @@ size_t _cbor_encoded_header_size(uint64_t size) { } size_t cbor_serialized_size(const cbor_item_t* item) { + CBOR_ASSERT_VALID_TYPE(cbor_typeof(item)); switch (cbor_typeof(item)) { case CBOR_TYPE_UINT: case CBOR_TYPE_NEGINT: + CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && + cbor_int_get_width(item) <= CBOR_INT_64); switch (cbor_int_get_width(item)) { case CBOR_INT_8: if (cbor_get_uint8(item) <= kMaxEmbeddedInt) return 1; @@ -74,9 +78,9 @@ size_t cbor_serialized_size(const cbor_item_t* item) { return 5; case CBOR_INT_64: return 9; - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } // Note: We do not _cbor_safe_signaling_add zero-length definite strings, // they would cause zeroes to propagate. All other items are at least one @@ -142,6 +146,8 @@ size_t cbor_serialized_size(const cbor_item_t* item) { cbor_serialized_size(cbor_move(cbor_tag_item(item)))); } case CBOR_TYPE_FLOAT_CTRL: + CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 && + cbor_float_get_width(item) <= CBOR_FLOAT_64); switch (cbor_float_get_width(item)) { case CBOR_FLOAT_0: return _cbor_encoded_header_size(cbor_ctrl_value(item)); @@ -151,13 +157,13 @@ size_t cbor_serialized_size(const cbor_item_t* item) { return 5; case CBOR_FLOAT_64: return 9; - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } @@ -184,6 +190,8 @@ size_t cbor_serialize_alloc(const cbor_item_t* item, unsigned char** buffer, size_t cbor_serialize_uint(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_uint(item)); + CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && + cbor_int_get_width(item) <= CBOR_INT_64); // cppcheck-suppress missingReturn switch (cbor_int_get_width(item)) { case CBOR_INT_8: @@ -194,15 +202,17 @@ size_t cbor_serialize_uint(const cbor_item_t* item, unsigned char* buffer, return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size); case CBOR_INT_64: return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } size_t cbor_serialize_negint(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_negint(item)); + CBOR_ASSERT(cbor_int_get_width(item) >= CBOR_INT_8 && + cbor_int_get_width(item) <= CBOR_INT_64); // cppcheck-suppress missingReturn switch (cbor_int_get_width(item)) { case CBOR_INT_8: @@ -213,9 +223,9 @@ size_t cbor_serialize_negint(const cbor_item_t* item, unsigned char* buffer, return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size); case CBOR_INT_64: return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } @@ -367,6 +377,8 @@ size_t cbor_serialize_tag(const cbor_item_t* item, unsigned char* buffer, size_t cbor_serialize_float_ctrl(const cbor_item_t* item, unsigned char* buffer, size_t buffer_size) { CBOR_ASSERT(cbor_isa_float_ctrl(item)); + CBOR_ASSERT(cbor_float_get_width(item) >= CBOR_FLOAT_0 && + cbor_float_get_width(item) <= CBOR_FLOAT_64); // cppcheck-suppress missingReturn switch (cbor_float_get_width(item)) { case CBOR_FLOAT_0: @@ -380,8 +392,8 @@ size_t cbor_serialize_float_ctrl(const cbor_item_t* item, unsigned char* buffer, case CBOR_FLOAT_64: return cbor_encode_double(cbor_float_get_float8(item), buffer, buffer_size); - default: + default: // LCOV_EXCL_START _CBOR_UNREACHABLE; - return 0; + return 0; // LCOV_EXCL_STOP } } diff --git a/src/cbor/serialization.h b/src/cbor/serialization.h index 10f5784a5b2f..66cb1ef4d696 100644 --- a/src/cbor/serialization.h +++ b/src/cbor/serialization.h @@ -51,8 +51,9 @@ cbor_serialized_size(const cbor_item_t* item); * ignore the return value. * * \rst - * .. warning:: It is the caller's responsibility to free the buffer using an - * appropriate ``free`` implementation. + * .. warning:: + * It is the caller's responsibility to free the buffer using an appropriate + * ``free`` implementation. * \endrst * * @param item A data item diff --git a/src/cbor/streaming.c b/src/cbor/streaming.c index 595c85805f19..426df78b7318 100644 --- a/src/cbor/streaming.c +++ b/src/cbor/streaming.c @@ -593,9 +593,9 @@ struct cbor_decoder_result cbor_stream_decode( /* Break */ callbacks->indef_break(context); return result; - default: + default: // LCOV_EXCL_START // Never happens, the switch statement is exhaustive on the 1B range _CBOR_UNREACHABLE; - return result; + return result; // LCOV_EXCL_STOP } } diff --git a/src/cbor/strings.h b/src/cbor/strings.h index a21133b30129..a026f4e5c95a 100644 --- a/src/cbor/strings.h +++ b/src/cbor/strings.h @@ -76,9 +76,10 @@ cbor_string_handle(const cbor_item_t* item); * and invalid, `cbor_string_codepoint_count` will return 0. * * \rst - * .. warning:: Using a pointer to a stack allocated constant is a common - * mistake. Lifetime of the string will expire when it goes out of scope and - * the CBOR item will be left inconsistent. + * .. warning:: + * Using a pointer to a stack allocated constant is a common mistake. + * Lifetime of the string will expire when it goes out of scope and the CBOR + * item will be left inconsistent. * \endrst * * @param item A definite string diff --git a/test/callbacks_test.c b/test/callbacks_test.c index c2b5ecfe8fe0..1045ed1bc254 100644 --- a/test/callbacks_test.c +++ b/test/callbacks_test.c @@ -33,8 +33,9 @@ unsigned char bytestring_data[] = {0x01, 0x02, 0x03}; static void test_builder_byte_string_callback_append( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null( - _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -73,8 +74,9 @@ static void test_builder_byte_string_callback_append( static void test_builder_byte_string_callback_append_alloc_failure( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null( - _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -105,8 +107,9 @@ static void test_builder_byte_string_callback_append_alloc_failure( static void test_builder_byte_string_callback_append_item_alloc_failure( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null( - _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -139,8 +142,9 @@ static void test_builder_byte_string_callback_append_item_alloc_failure( static void test_builder_byte_string_callback_append_parent_alloc_failure( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null( - _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_bytestring(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -173,7 +177,9 @@ static void test_builder_byte_string_callback_append_parent_alloc_failure( unsigned char string_data[] = {0x61, 0x62, 0x63}; static void test_builder_string_callback_append(void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null(_cbor_stack_push(&stack, cbor_new_indefinite_string(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_string(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -210,7 +216,9 @@ static void test_builder_string_callback_append(void** _state _CBOR_UNUSED) { static void test_builder_string_callback_append_alloc_failure( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null(_cbor_stack_push(&stack, cbor_new_indefinite_string(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_string(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -241,7 +249,9 @@ static void test_builder_string_callback_append_alloc_failure( static void test_builder_string_callback_append_item_alloc_failure( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null(_cbor_stack_push(&stack, cbor_new_indefinite_string(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_string(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -273,7 +283,9 @@ static void test_builder_string_callback_append_item_alloc_failure( static void test_builder_string_callback_append_parent_alloc_failure( void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null(_cbor_stack_push(&stack, cbor_new_indefinite_string(), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_string(), 0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -304,7 +316,9 @@ static void test_builder_string_callback_append_parent_alloc_failure( static void test_append_array_failure(void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null(_cbor_stack_push(&stack, cbor_new_definite_array(0), 0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_definite_array(0), 0); + assert_non_null(stack_top); stack.top->subitems = 1; struct _cbor_decoder_context context = { .creation_failed = false, @@ -333,8 +347,9 @@ static void test_append_array_failure(void** _state _CBOR_UNUSED) { static void test_append_map_failure(void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null( - _cbor_stack_push(&stack, cbor_new_indefinite_map(), /*subitems=*/0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_indefinite_map(), /*subitems=*/0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, @@ -373,7 +388,9 @@ static void test_invalid_indef_break(void** _state _CBOR_UNUSED) { static void test_invalid_state_indef_break(void** _state _CBOR_UNUSED) { struct _cbor_stack stack = _cbor_stack_init(); - assert_non_null(_cbor_stack_push(&stack, cbor_new_int8(), /*subitems=*/0)); + struct _cbor_stack_record* stack_top = + _cbor_stack_push(&stack, cbor_new_int8(), /*subitems=*/0); + assert_non_null(stack_top); struct _cbor_decoder_context context = { .creation_failed = false, .syntax_error = false, diff --git a/test/copy_test.c b/test/copy_test.c index ef58fecd5a9f..727791c2834d 100644 --- a/test/copy_test.c +++ b/test/copy_test.c @@ -182,6 +182,326 @@ static void test_floats(void** _state _CBOR_UNUSED) { cbor_decref(©); } +static void test_definite_uints(void** _state _CBOR_UNUSED) { + item = cbor_build_uint8(10); + assert_uint8(copy = cbor_copy_definite(item), 10); + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_negints(void** _state _CBOR_UNUSED) { + item = cbor_build_negint16(10); + assert_true(cbor_get_uint16(copy = cbor_copy_definite(item)) == 10); + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_bytestring(void** _state _CBOR_UNUSED) { + item = cbor_build_bytestring((cbor_data) "abc", 3); + assert_memory_equal(cbor_bytestring_handle(copy = cbor_copy_definite(item)), + cbor_bytestring_handle(item), 3); + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_indef_bytestring(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); + + assert_memory_equal(cbor_bytestring_handle(copy = cbor_copy_definite(item)), + "abc", 3); + assert_true(cbor_isa_bytestring(copy)); + assert_true(cbor_bytestring_is_definite(copy)); + assert_size_equal(cbor_bytestring_length(copy), 3); + + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_bytestring_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_bytestring(); + assert_true(cbor_bytestring_add_chunk( + item, cbor_move(cbor_build_bytestring((cbor_data) "abc", 3)))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy_definite(item)); }); + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_string(void** _state _CBOR_UNUSED) { + item = cbor_build_string("abc"); + assert_memory_equal(cbor_string_handle(copy = cbor_copy_definite(item)), + cbor_string_handle(item), 3); + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_indef_string(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_string(); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); + + assert_memory_equal(cbor_string_handle(copy = cbor_copy_definite(item)), + "abc", 3); + assert_true(cbor_isa_string(copy)); + assert_true(cbor_string_is_definite(copy)); + assert_size_equal(cbor_string_length(copy), 3); + + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_string_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_string(); + assert_true(cbor_string_add_chunk(item, cbor_move(cbor_build_string("abc")))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy_definite(item)); }); + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_array(void** _state _CBOR_UNUSED) { + item = cbor_new_definite_array(1); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_array(copy)); + assert_true(cbor_array_is_definite(copy)); + assert_size_equal(cbor_array_size(copy), 1); + assert_uint8(tmp = cbor_array_get(copy, 0), 42); + + cbor_decref(&item); + cbor_decref(©); + cbor_decref(&tmp); +} + +static void test_definite_indef_array(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_array(copy)); + assert_true(cbor_array_is_definite(copy)); + assert_uint8(tmp = cbor_array_get(copy, 0), 42); + + cbor_decref(&item); + cbor_decref(©); + cbor_decref(&tmp); +} + +static void test_definite_indef_array_nested(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_array(); + cbor_item_t* nested_array = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(nested_array))); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_array(copy)); + assert_true(cbor_array_is_definite(copy)); + assert_size_equal(cbor_array_size(copy), 1); + + tmp = cbor_array_get(copy, 0); + assert_true(cbor_isa_array(tmp)); + assert_true(cbor_array_is_definite(tmp)); + assert_size_equal(cbor_array_size(tmp), 0); + + cbor_decref(&item); + cbor_decref(©); + cbor_decref(&tmp); +} + +static void test_definite_array_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy_definite(item)); }); + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_array_item_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_array(); + assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(42)))); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy_definite(item)); }, 3, + // New array, new array data, item copy + MALLOC, MALLOC, MALLOC_FAIL); + + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_map(void** _state _CBOR_UNUSED) { + item = cbor_new_definite_map(1); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_map(copy)); + assert_true(cbor_map_is_definite(copy)); + assert_size_equal(cbor_map_size(copy), 1); + assert_uint8(cbor_map_handle(copy)[0].key, 42); + + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_indef_map(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_map(); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_map(copy)); + assert_true(cbor_map_is_definite(copy)); + assert_size_equal(cbor_map_size(copy), 1); + assert_uint8(cbor_map_handle(copy)[0].key, 42); + + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_indef_map_nested(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_map(); + cbor_item_t* key = cbor_new_indefinite_array(); + cbor_item_t* value = cbor_new_indefinite_array(); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(key), + .value = cbor_move(value), + })); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_map(copy)); + assert_true(cbor_map_is_definite(copy)); + assert_size_equal(cbor_map_size(copy), 1); + + assert_true(cbor_isa_array(cbor_map_handle(copy)[0].key)); + assert_true(cbor_array_is_definite(cbor_map_handle(copy)[0].key)); + assert_size_equal(cbor_array_size(cbor_map_handle(copy)[0].key), 0); + + assert_true(cbor_isa_array(cbor_map_handle(copy)[0].value)); + assert_true(cbor_array_is_definite(cbor_map_handle(copy)[0].value)); + assert_size_equal(cbor_array_size(cbor_map_handle(copy)[0].value), 0); + + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_map_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_map(); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy_definite(item)); }); + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_map_key_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_map(); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy_definite(item)); }, 3, + // New map, map data, key copy + MALLOC, MALLOC, MALLOC_FAIL); + + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_map_value_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_new_indefinite_map(); + assert_true(cbor_map_add(item, (struct cbor_pair){ + .key = cbor_move(cbor_build_uint8(42)), + .value = cbor_move(cbor_build_uint8(43)), + })); + + WITH_MOCK_MALLOC({ assert_null(cbor_copy_definite(item)); }, 4, + // New map, map data, key copy, value copy + MALLOC, MALLOC, MALLOC, MALLOC_FAIL); + + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_tag(void** _state _CBOR_UNUSED) { + item = cbor_build_tag(10, cbor_move(cbor_build_uint8(42))); + + copy = cbor_copy_definite(item); + assert_uint8(tmp = cbor_tag_item(copy), 42); + + cbor_decref(&item); + cbor_decref(©); + cbor_decref(&tmp); +} + +static void test_definite_tag_nested(void** _state _CBOR_UNUSED) { + item = cbor_build_tag(10, cbor_move(cbor_new_indefinite_array())); + + copy = cbor_copy_definite(item); + assert_true(cbor_isa_tag(copy)); + + tmp = cbor_tag_item(copy); + assert_true(cbor_isa_array(tmp)); + assert_true(cbor_array_is_definite(tmp)); + assert_size_equal(cbor_array_size(tmp), 0); + + cbor_decref(&item); + cbor_decref(©); + cbor_decref(&tmp); +} + +static void test_definite_tag_alloc_failure(void** _state _CBOR_UNUSED) { + item = cbor_build_tag(10, cbor_move(cbor_build_uint8(42))); + + WITH_FAILING_MALLOC({ assert_null(cbor_copy_definite(item)); }); + assert_size_equal(cbor_refcount(item), 1); + + cbor_decref(&item); +} + +static void test_definite_ctrls(void** _state _CBOR_UNUSED) { + item = cbor_new_null(); + assert_true(cbor_is_null(copy = cbor_copy_definite(item))); + cbor_decref(&item); + cbor_decref(©); +} + +static void test_definite_floats(void** _state _CBOR_UNUSED) { + item = cbor_build_float2(3.14f); + assert_true(cbor_float_get_float2(copy = cbor_copy_definite(item)) == + cbor_float_get_float2(item)); + cbor_decref(&item); + cbor_decref(©); + + item = cbor_build_float4(3.14f); + assert_true(cbor_float_get_float4(copy = cbor_copy_definite(item)) == + cbor_float_get_float4(item)); + cbor_decref(&item); + cbor_decref(©); + + item = cbor_build_float8(3.14); + assert_true(cbor_float_get_float8(copy = cbor_copy_definite(item)) == + cbor_float_get_float8(item)); + cbor_decref(&item); + cbor_decref(©); +} + static void test_alloc_failure_simple(void** _state _CBOR_UNUSED) { item = cbor_build_uint8(10); @@ -469,6 +789,30 @@ int main(void) { cmocka_unit_test(test_map_second_key_failure), cmocka_unit_test(test_tag_item_alloc_failure), cmocka_unit_test(test_tag_alloc_failure), + cmocka_unit_test(test_definite_uints), + cmocka_unit_test(test_definite_negints), + cmocka_unit_test(test_definite_bytestring), + cmocka_unit_test(test_definite_bytestring_alloc_failure), + cmocka_unit_test(test_definite_indef_bytestring), + cmocka_unit_test(test_definite_string), + cmocka_unit_test(test_definite_indef_string), + cmocka_unit_test(test_definite_string_alloc_failure), + cmocka_unit_test(test_definite_array), + cmocka_unit_test(test_definite_indef_array), + cmocka_unit_test(test_definite_indef_array_nested), + cmocka_unit_test(test_definite_array_alloc_failure), + cmocka_unit_test(test_definite_array_item_alloc_failure), + cmocka_unit_test(test_definite_map), + cmocka_unit_test(test_definite_indef_map), + cmocka_unit_test(test_definite_indef_map_nested), + cmocka_unit_test(test_definite_map_alloc_failure), + cmocka_unit_test(test_definite_map_key_alloc_failure), + cmocka_unit_test(test_definite_map_value_alloc_failure), + cmocka_unit_test(test_definite_tag), + cmocka_unit_test(test_definite_tag_nested), + cmocka_unit_test(test_definite_tag_alloc_failure), + cmocka_unit_test(test_definite_ctrls), + cmocka_unit_test(test_definite_floats), }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/float_ctrl_test.c b/test/float_ctrl_test.c index 927bf567bdc3..5a19d58b68e1 100644 --- a/test/float_ctrl_test.c +++ b/test/float_ctrl_test.c @@ -69,6 +69,8 @@ unsigned char null_data[] = {0xF6}; static void test_null(void** _state _CBOR_UNUSED) { float_ctrl = cbor_load(null_data, 1, &res); assert_true(cbor_isa_float_ctrl(float_ctrl)); + assert_true(cbor_float_ctrl_is_ctrl(float_ctrl)); + assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_0); assert_true(cbor_is_null(float_ctrl)); cbor_decref(&float_ctrl); assert_null(float_ctrl); @@ -79,6 +81,8 @@ unsigned char undef_data[] = {0xF7}; static void test_undef(void** _state _CBOR_UNUSED) { float_ctrl = cbor_load(undef_data, 1, &res); assert_true(cbor_isa_float_ctrl(float_ctrl)); + assert_true(cbor_float_ctrl_is_ctrl(float_ctrl)); + assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_0); assert_true(cbor_is_undef(float_ctrl)); cbor_decref(&float_ctrl); assert_null(float_ctrl); @@ -90,6 +94,8 @@ static void test_bool(void** _state _CBOR_UNUSED) { _CBOR_TEST_DISABLE_ASSERT({ float_ctrl = cbor_load(bool_data, 1, &res); assert_true(cbor_isa_float_ctrl(float_ctrl)); + assert_true(cbor_float_ctrl_is_ctrl(float_ctrl)); + assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_0); assert_true(cbor_is_bool(float_ctrl)); assert_false(cbor_get_bool(float_ctrl)); cbor_set_bool(float_ctrl, true); @@ -100,6 +106,8 @@ static void test_bool(void** _state _CBOR_UNUSED) { float_ctrl = cbor_load(bool_data + 1, 1, &res); assert_true(cbor_isa_float_ctrl(float_ctrl)); + assert_true(cbor_float_ctrl_is_ctrl(float_ctrl)); + assert_true(cbor_float_get_width(float_ctrl) == CBOR_FLOAT_0); assert_true(cbor_is_bool(float_ctrl)); assert_true(cbor_get_bool(float_ctrl)); cbor_set_bool(float_ctrl, false); @@ -125,6 +133,18 @@ static void test_float_ctrl_creation(void** _state _CBOR_UNUSED) { WITH_FAILING_MALLOC({ assert_null(cbor_build_ctrl(0xAF)); }); } +static void test_ctrl_on_float(void** _state _CBOR_UNUSED) { + float_ctrl = cbor_build_float4(3.14f); + assert_non_null(float_ctrl); + assert_true(cbor_is_float(float_ctrl)); + assert_false(cbor_float_ctrl_is_ctrl(float_ctrl)); + assert_false(cbor_is_null(float_ctrl)); + assert_false(cbor_is_undef(float_ctrl)); + assert_false(cbor_is_bool(float_ctrl)); + cbor_decref(&float_ctrl); + assert_null(float_ctrl); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_float2), @@ -134,6 +154,7 @@ int main(void) { cmocka_unit_test(test_undef), cmocka_unit_test(test_bool), cmocka_unit_test(test_float_ctrl_creation), + cmocka_unit_test(test_ctrl_on_float), }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/test/map_test.c b/test/map_test.c index 3c41456079a0..1e8492ef49ea 100644 --- a/test/map_test.c +++ b/test/map_test.c @@ -246,11 +246,24 @@ static void test_indef_map_decode(void** _state _CBOR_UNUSED) { map = cbor_load(test_indef_map, 6, &res); assert_null(map); - assert_size_equal(res.error.code, CBOR_ERR_MEMERROR); + assert_int_equal(res.error.code, CBOR_ERR_MEMERROR); }, 4, MALLOC, MALLOC, MALLOC, REALLOC_FAIL); } +// The value in the third pair is missing, 0xFF instead. +static unsigned char test_break_in_def_map[] = {0xA3, 0x30, 0x30, 0x30, + 0x30, 0x00, 0xFF}; +static void test_break_in_def_map_decode(void** _state _CBOR_UNUSED) { + cbor_item_t* map; + struct cbor_load_result res; + map = cbor_load(test_break_in_def_map, 7, &res); + + assert_null(map); + assert_int_equal(res.error.code, CBOR_ERR_SYNTAXERROR); + assert_size_equal(res.error.position, 7); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_empty_map), @@ -265,6 +278,7 @@ int main(void) { cmocka_unit_test(test_map_creation), cmocka_unit_test(test_map_add), cmocka_unit_test(test_indef_map_decode), + cmocka_unit_test(test_break_in_def_map_decode), }; return cmocka_run_group_tests(tests, NULL, NULL); } |
