Vendor import of libcbor 0.10.2

This commit is contained in:
Ed Maste 2023-04-20 19:17:42 -04:00
parent 5b2defbd2a
commit 058aa793d8
138 changed files with 5679 additions and 2672 deletions

224
.circleci/config.yml Normal file
View File

@ -0,0 +1,224 @@
version: 2.1
commands:
linux-setup:
steps:
- run: sudo apt-get update
- run: sudo apt-get install -y cmake ${TOOLCHAIN_PACKAGES}
- run: sudo apt install libcmocka-dev
build:
steps:
- run: >
cmake -DWITH_TESTS=ON \
-DCMAKE_BUILD_TYPE=Debug \
-DSANITIZE=OFF \
-DCOVERAGE="${CMAKE_COVERAGE:='OFF'}" \
.
- run: make -j 16 VERBOSE=1
build-release:
steps:
- run: >
cmake -DWITH_TESTS=ON \
-DCMAKE_BUILD_TYPE=Release \
.
- run: make -j 16 VERBOSE=1
test:
steps:
- run: ctest -VV
orbs:
codecov: codecov/codecov@3.2.2
jobs:
static-test:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: g++
steps:
- checkout
- linux-setup
- run: sudo apt-get install -y clang-format doxygen cppcheck
- run: cppcheck --inline-suppr --error-exitcode=1 .
- run: bash clang-format.sh --verbose
- run: >
if ! $(git diff-index --quiet HEAD); then
echo "Code formatting doesn't conform to clang-format"
echo "Please run clang-format.sh, commit the diff, and push to re-run CI"
echo "Excerpt of the diff follows"
git diff | head -n 20
fi
- run: >
if doxygen Doxyfile | grep 'warning: '; then
echo "Doxygen warning (see above) -- misformatted docs?"
exit 1
fi
- run: >
cd doc &&
pip3 install -r source/requirements.txt &&
make html
build-and-test:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: g++
CMAKE_COVERAGE: ON
steps:
- checkout
- linux-setup
- run: sudo apt-get install -y valgrind
- build
- test
- run: ctest -T Coverage
- codecov/upload
- run: ctest --output-on-failure -T memcheck | tee memcheck.out
- run: >
if grep -q 'Memory Leak\|IPW\|Uninitialized Memory Conditional\|Uninitialized Memory Read' memcheck.out; then
cat Testing/Temporary/MemoryChecker*
exit 1
fi;
build-and-test-clang:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: clang
CC: clang
CXX: clang++
steps:
- checkout
- linux-setup
- build
- test
build-and-test-32b:
machine:
image: ubuntu-2204:2022.10.2
steps:
- checkout
- run: sudo apt-get update
- run: sudo apt-get install -y cmake gcc-multilib g++-multilib libc6-dev-i386
# Make cmocka from source w/ 32b setup
- run: git clone https://git.cryptomilk.org/projects/cmocka.git ~/cmocka
- run: >
cd $(mktemp -d /tmp/build.XXXX) &&
cmake ~/cmocka -DCMAKE_TOOLCHAIN_FILE=~/cmocka/cmake/Toolchain-cross-m32.cmake &&
make &&
sudo make install
# Piggyback on the cmocka 32b toolchain
- run: >
cmake -DWITH_TESTS=ON \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_TOOLCHAIN_FILE=~/cmocka/cmake/Toolchain-cross-m32.cmake \
.
- run: make -j 16 VERBOSE=1
- test
build-and-test-release-clang:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: clang
CC: clang
CXX: clang++
steps:
- checkout
- linux-setup
- build-release
- test
llvm-coverage:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: clang
CC: clang
CXX: clang++
CMAKE_COVERAGE: ON
steps:
- checkout
- linux-setup
- build
- run: make llvm-coverage
build-and-test-arm:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: g++
resource_class: arm.medium
steps:
- checkout
- linux-setup
- build
- test
build-bazel:
machine:
image: ubuntu-2204:2022.10.2
environment:
TOOLCHAIN_PACKAGES: g++
steps:
- checkout
- linux-setup
- run: sudo apt install apt-transport-https curl gnupg
- run: curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
- run: sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
- run: echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
- run: sudo apt update
- run: sudo apt install bazel
- run: bazel --version
- run: >
pushd examples/bazel &&
bazel run -s src:hello
build-and-test-osx:
macos:
xcode: 12.5.1
steps:
- checkout
- run: bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- run: brew install cmocka cmake
- build
- test
build-and-test-win:
resource_class: 'windows.medium'
machine:
image: 'windows-server-2022-gui:current'
shell: 'bash.exe'
steps:
- checkout
- run: choco install cmake -y
- run: git clone https://git.cryptomilk.org/projects/cmocka.git
- run: cd cmocka && git checkout tags/cmocka-1.1.5
- run: /c/Program\ Files/Cmake/bin/cmake -S cmocka -B cmocka_build
- run: /c/Program\ Files/Cmake/bin/cmake --build cmocka_build
- run: /c/Program\ Files/Cmake/bin/cmake -S . -B libcbor_build -DWITH_TESTS=ON -DCMOCKA_INCLUDE_DIR=cmocka/include -DCMOCKA_LIBRARIES=$(pwd)/cmocka_build/src/Debug/cmocka.lib
- run: /c/Program\ Files/Cmake/bin/cmake --build libcbor_build
- run: >
export PATH="$(pwd)/cmocka_build/src/Debug/:$PATH" &&
/c/Program\ Files/Cmake/bin/ctest.exe --test-dir libcbor_build --output-on-failure
workflows:
build-and-test:
jobs:
- static-test
- build-and-test
- build-and-test-clang
- build-and-test-32b
- build-and-test-release-clang
- build-and-test-arm
- build-and-test-win
- build-bazel
- llvm-coverage
# OSX builds are expensive, run only on master
- build-and-test-osx:
requires:
- build-and-test
filters:
branches:
only: [master]

20
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: PJK
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior. If possible, please attach a runnable code snippet.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment**
libcbor version and build configuration flags (or source package version if using a package manager).

15
.github/PULL_REQUEST_TEMPLATE vendored Normal file
View File

@ -0,0 +1,15 @@
## Description
What changes and why
## Checklist
- [ ] I have read followed [CONTRIBUTING.md](https://github.com/PJK/libcbor/blob/master/CONTRIBUTING.md)
- [ ] I have added tests
- [ ] I have updated the documentation
- [ ] I have updated the CHANGELOG
- [ ] Are there any breaking changes?
- [ ] If yes: I have marked them in the CHANGELOG ([example](https://github.com/PJK/libcbor/blob/87e2d48a127968d39f158cbfc2b79d6285bd039d/CHANGELOG.md?plain=1#L16))
- [ ] Does this PR introduce any platform specific code?
- [ ] Security: Does this PR potentially affect security?
- [ ] Performance: Does this PR potentially affect performance?

25
.github/workflows/fuzz-pr.yml vendored Normal file
View File

@ -0,0 +1,25 @@
# A quick CIFuzz sanity check. Fuzzing time is kept low provide fast feedback
# on PRs; fuzz.yml schedules a daily long-running fuzzing job.
name: CIFuzz PR check
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
steps:
- name: Build Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'libcbor'
dry-run: false
- name: Smoke Test Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'libcbor'
fuzz-seconds: 10
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure()
with:
name: artifacts
path: ./out/artifacts

View File

@ -1,5 +1,7 @@
name: CIFuzz name: CIFuzz
on: [pull_request] on:
schedule:
- cron: '0 4 * * *'
jobs: jobs:
Fuzzing: Fuzzing:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -13,7 +15,7 @@ jobs:
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with: with:
oss-fuzz-project-name: 'libcbor' oss-fuzz-project-name: 'libcbor'
fuzz-seconds: 600 fuzz-seconds: 14400 # 4 hours
dry-run: false dry-run: false
- name: Upload Crash - name: Upload Crash
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1

8
.gitignore vendored
View File

@ -6,3 +6,11 @@ doxygen_docs
*/*.out */*.out
cmake-build-debug cmake-build-debug
venv venv
**.DS_Store
.vscode
# No top-level requirements, see doc/source
requirements.txt
examples/bazel/bazel-bazel
examples/bazel/bazel-bin
examples/bazel/bazel-out
examples/bazel/bazel-testlogs

View File

@ -1,66 +0,0 @@
language: c
matrix:
include:
- arch: amd64
os: linux
dist: bionic
compiler: clang
env: TRAVIS_ARCH="amd64"
- arch: amd64
os: linux
dist: bionic
compiler: gcc
env: TRAVIS_ARCH="amd64"
- arch: arm64
os: linux
dist: bionic
compiler: gcc
env: TRAVIS_ARCH="arm64"
- arch: ppc64le
os: linux
dist: bionic
compiler: gcc
env: TRAVIS_ARCH="ppc64le"
- arch: amd64
os: osx
compiler: gcc
env: TRAVIS_ARCH="amd64"
before_install:
- pushd ${HOME}
- git clone https://gitlab.com/cmocka/cmocka.git
- cd cmocka && mkdir build && cd build
- cmake .. && make -j2 && sudo make install
- cd .. && popd
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; sudo apt-get install -y clang-format-8 cppcheck; fi
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$CC" = "gcc" -a "$TRAVIS_ARCH" = "amd64" ]; then pip install --user codecov; export CFLAGS="-coverage"; fi
script:
- >
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
cppcheck . --error-exitcode=1
# Fail if re-formatting creates diffs (implying bad formatting)
/clang-format.sh --verbose
git diff-index --quiet HEAD
fi;
- mkdir build && cd build
- cmake -DWITH_TESTS=ON
-DCBOR_CUSTOM_ALLOC=ON
-DCMAKE_BUILD_TYPE=Debug
-DSANITIZE=OFF
..
- make VERBOSE=1
- ctest -VV
- ctest -T memcheck | tee memcheck.out
- >
if grep -q 'Memory Leak\|IPW\|Uninitialized Memory Conditional\|Uninitialized Memory Read' memcheck.out; then
exit 1
fi;
after_success:
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$CC" = "gcc" -a "$TRAVIS_ARCH" = "amd64" ]; then codecov; fi
notifications:
email: false

100
Bazel.md Normal file
View File

@ -0,0 +1,100 @@
# Use as a Bazel Dependency
To use libcbor in your
[Baze](https://bazel.build/)
project, first add the following section to your project's `WORKSPACE` file.
Note the location of the `third_party/libcbor.BUILD` file - you may use a
different location if you wish, but you the file must be make available to
`WORKSPACE`.
## WORKSPACE
Note, this imports version `0.8.0` - you may need to update the version and
the sha256 hash.
```python
# libcbor
http_archive(
name = "libcbor",
build_file = "//third_party:libcbor.BUILD",
sha256 = "dd04ea1a7df484217058d389e027e7a0143a4f245aa18a9f89a5dd3e1a4fcc9a",
strip_prefix = "libcbor-0.8.0",
urls = ["https://github.com/PJK/libcbor/archive/refs/tags/v0.8.0.zip"],
)
```
## third_party/libcbor.BUILD
Bazel will unzip the libcbor zip file, then copy this file in as `BUILD`.
Bazel will then use this file to compile libcbor.
[Cmake](https://cmake.org/)
is used in two passes: to create the Makefiles, and then to invoke Make to build
the `libcbor.a` static library. `libcbor.a` and the `.h` files are then made
available for other packages to use.
```python
genrule(
name = "cbor_cmake",
srcs = glob(["**"]),
outs = ["libcbor.a", "cbor.h", "cbor/arrays.h", "cbor/bytestrings.h",
"cbor/callbacks.h", "cbor/cbor_export.h", "cbor/common.h", "cbor/configuration.h", "cbor/data.h",
"cbor/encoding.h", "cbor/floats_ctrls.h", "cbor/ints.h", "cbor/maps.h",
"cbor/serialization.h", "cbor/streaming.h", "cbor/strings.h", "cbor/tags.h"],
cmd = " && ".join([
# Remember where output should go.
"INITIAL_WD=`pwd`",
# Build libcbor library.
"cd `dirname $(location CMakeLists.txt)`",
"cmake -DCMAKE_BUILD_TYPE=Release .",
"cmake --build .",
# Export the .a and .h files for cbor rule, below.
"cp src/libcbor.a src/cbor.h $$INITIAL_WD/$(RULEDIR)",
"cp src/cbor/*h cbor/configuration.h $$INITIAL_WD/$(RULEDIR)/cbor"]),
visibility = ["//visibility:private"],
)
cc_import(
name = "cbor",
hdrs = ["cbor.h", "cbor/arrays.h", "cbor/bytestrings.h",
"cbor/callbacks.h", "cbor/cbor_export.h", "cbor/common.h", "cbor/configuration.h", "cbor/data.h",
"cbor/encoding.h", "cbor/floats_ctrls.h", "cbor/ints.h", "cbor/maps.h",
"cbor/serialization.h", "cbor/streaming.h", "cbor/strings.h", "cbor/tags.h"],
static_library = "libcbor.a",
visibility = ["//visibility:public"],
)
```
## third_party/BUILD
The `libcbor.BUILD` file must be make available to the top-level `WORKSPACE`
file:
```python
exports_files(["libcbor.BUILD"]))
```
## Your BUILD File
Add libcbor dependency to your package's `BUILD` file like so:
```python
cc_library(
name = "...",
srcs = [ ... ],
hdrs = [ ... ],
deps = [
...
"@libcbor//:cbor",
],
)
```
## Your C File
Now you may simply include `cbor.h`:
```c
#include "cbor.h"
static const uint8_t version = cbor_major_version;
```

View File

@ -1,6 +1,53 @@
Template:
- [Fix issue X in feature Y](https://github.com/PJK/libcbor/pull/XXX) (by [YYY](https://github.com/YYY))
Next Next
--------------------- ---------------------
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)
- 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.
- 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)
- Potentially BUILD BREAKING: [Add nodiscard attributes to most functions](https://github.com/PJK/libcbor/pull/248)
- **Warning**: This may cause new build warnings and (in rare cases, depending on your configuration) errors
- BREAKING: [Fix `cbor_copy` leaking memory and creating invalid items when the allocator fails](https://github.com/PJK/libcbor/pull/249).
- Previously, the failures were not handled in the interface. Now, `cbor_copy` may return `NULL` upon failure; clients should check the return value
- [Fix `cbor_build_tag` illegal memory behavior when the allocator fails](https://github.com/PJK/libcbor/pull/249)
- [Add a new `cbor_serialized_size` API](https://github.com/PJK/libcbor/pull/250)
- [Reworked `cbor_serialize_alloc` to allocate the exact amount of memory necessary upfront](https://github.com/PJK/libcbor/pull/251)
- This should significantly speed up `cbor_serialize_alloc` for large items by avoiding multiple reallocation iterations
- Clients should not use the return value of `cbor_serialize_alloc`. It may be removed in the future.
- BUILD BREAKING: [Deprecate CBOR_CUSTOM_ALLOC](https://github.com/PJK/libcbor/pull/237)
- `cbor_set_allocs` will always be enabled from now on
- Note: The flag will be kept as a no-op triggering a warning when used for one version and then removed completely
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
- 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) 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)) - 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))

View File

@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 3.0)
project(libcbor) project(libcbor)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/")
include(CTest) include(CTest)
SET(CBOR_VERSION_MAJOR "0") SET(CBOR_VERSION_MAJOR "0")
SET(CBOR_VERSION_MINOR "8") SET(CBOR_VERSION_MINOR "10")
SET(CBOR_VERSION_PATCH "0") SET(CBOR_VERSION_PATCH "2")
SET(CBOR_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.${CBOR_VERSION_PATCH}) SET(CBOR_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.${CBOR_VERSION_PATCH})
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true) set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
@ -18,6 +18,13 @@ if(BIG_ENDIAN)
endif() endif()
option(CBOR_CUSTOM_ALLOC "Custom, dynamically defined allocator support" OFF) option(CBOR_CUSTOM_ALLOC "Custom, dynamically defined allocator support" OFF)
if(CBOR_CUSTOM_ALLOC)
message(WARNING
"CBOR_CUSTOM_ALLOC has been deprecated. Custom allocators are now enabled by default."
"The flag is a no-op and will be removed in the next version. "
"Please remove CBOR_CUSTOM_ALLOC from your build configuation.")
endif(CBOR_CUSTOM_ALLOC)
option(CBOR_PRETTY_PRINTER "Include a pretty-printing routine" ON) option(CBOR_PRETTY_PRINTER "Include a pretty-printing routine" ON)
set(CBOR_BUFFER_GROWTH "2" CACHE STRING "Factor for buffer growth & shrinking") set(CBOR_BUFFER_GROWTH "2" CACHE STRING "Factor for buffer growth & shrinking")
set(CBOR_MAX_STACK_SIZE "2048" CACHE STRING "maximum size for decoding context stack") set(CBOR_MAX_STACK_SIZE "2048" CACHE STRING "maximum size for decoding context stack")
@ -85,7 +92,7 @@ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-g")
include(CheckTypeSize) include(CheckTypeSize)
check_type_size("size_t" SIZEOF_SIZE_T) check_type_size("size_t" SIZEOF_SIZE_T)
if(SIZEOF_SIZE_T LESS 8) if(SIZEOF_SIZE_T LESS 8)
message(WARNING "Your size_t is less than 8 bytes. Long items with 64b length specifiers might not work as expected. Make sure to run the tests!") message(WARNING "Your size_t is less than 8 bytes. Decoding of huge items that would exceed the memory address space will always fail. Consider implementing a custom streaming decoder if you need to deal with huge items.")
else() else()
add_definitions(-DEIGHT_BYTE_SIZE_T) add_definitions(-DEIGHT_BYTE_SIZE_T)
endif() endif()
@ -99,19 +106,36 @@ add_custom_target(coverage
COMMAND ctest COMMAND ctest
COMMAND lcov --capture --directory . --output-file coverage.info COMMAND lcov --capture --directory . --output-file coverage.info
COMMAND genhtml coverage.info --highlight --legend --output-directory coverage_html COMMAND genhtml coverage.info --highlight --legend --output-directory coverage_html
COMMAND echo "Coverage report ready: file://${CMAKE_CURRENT_BINARY_DIR}/coverage_html/index.html") COMMAND echo "Coverage report ready: ${CMAKE_CURRENT_BINARY_DIR}/coverage_html/index.html")
add_custom_target(llvm-coverage
COMMAND make -j 16
COMMAND rm -rf coverage_profiles
COMMAND mkdir coverage_profiles
COMMAND bash -c [[ for TEST in $(ls test/*_test); do LLVM_PROFILE_FILE="coverage_profiles/$(basename -- ${TEST}).profraw" ./${TEST}; done ]]
# VERBATIM makes escaping working, but breaks shell expansions, so we need to explicitly use bash
COMMAND bash -c [[ llvm-profdata merge -sparse $(ls coverage_profiles/*.profraw) -o coverage_profiles/combined.profdata ]]
COMMAND bash -c [[ llvm-cov show -instr-profile=coverage_profiles/combined.profdata test/*_test -format=html > coverage_profiles/report.html ]]
COMMAND bash -c [[ llvm-cov report -instr-profile=coverage_profiles/combined.profdata test/*_test ]]
COMMAND echo "Coverage report ready: ${CMAKE_CURRENT_BINARY_DIR}/coverage_profiles/report.html"
VERBATIM)
include_directories(src) include_directories(src)
option(COVERAGE "Enable code coverage instrumentation" OFF) option(c "Enable code coverage instrumentation" OFF)
if (COVERAGE) if (COVERAGE)
message("Configuring code coverage instrumentation") message("Configuring code coverage instrumentation")
if(NOT CMAKE_C_COMPILER MATCHES "gcc") if(CMAKE_C_COMPILER_ID MATCHES "GNU")
message(WARNING "Gcov instrumentation only works with GCC") # https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fprofile-arcs -ftest-coverage --coverage")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage --coverage")
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-instr-generate -fcoverage-mapping")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fprofile-instr-generate")
else()
message(WARNING "Code coverage build not implemented for compiler ${CMAKE_C_COMPILER_ID}")
endif() endif()
# https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fprofile-arcs -ftest-coverage --coverage")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage --coverage")
endif (COVERAGE) endif (COVERAGE)
@ -139,20 +163,20 @@ else()
message(STATUS "LTO is not enabled") message(STATUS "LTO is not enabled")
endif(use_lto) endif(use_lto)
subdirs(src) add_subdirectory(src)
if(use_lto) if(use_lto)
set_property(DIRECTORY src PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) set_property(DIRECTORY src PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif(use_lto) endif(use_lto)
if (WITH_TESTS) if (WITH_TESTS)
subdirs(test) add_subdirectory(test)
if(use_lto) if(use_lto)
set_property(DIRECTORY test PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) set_property(DIRECTORY test PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif(use_lto) endif(use_lto)
endif (WITH_TESTS) endif (WITH_TESTS)
if (WITH_EXAMPLES) if (WITH_EXAMPLES)
subdirs(examples) add_subdirectory(examples)
if(use_lto) if(use_lto)
set_property(DIRECTORY examples PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) set_property(DIRECTORY examples PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif(use_lto) endif(use_lto)

View File

@ -21,7 +21,14 @@
#============================================================================= #=============================================================================
# #
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_CMOCKA QUIET cmocka)
set(CMOCKA_DEFINITIONS ${PC_CMOCKA_CFLAGS_OTHER})
endif()
find_path(CMOCKA_INCLUDE_DIR find_path(CMOCKA_INCLUDE_DIR
HINTS ${PC_CMOCKA_INCLUDEDIR} ${PC_CMOCKA_INCLUDE_DIRS}
NAMES NAMES
cmocka.h cmocka.h
PATHS PATHS
@ -29,6 +36,7 @@ find_path(CMOCKA_INCLUDE_DIR
) )
find_library(CMOCKA_LIBRARY find_library(CMOCKA_LIBRARY
HINTS ${PC_CMOCKA_LIBDIR} ${PC_CMOCKA_LIBRARY_DIRS}
NAMES NAMES
cmocka cmocka_shared cmocka cmocka_shared
PATHS PATHS

View File

@ -0,0 +1,23 @@
# This module provides function for joining paths
# known from most languages
#
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Pythons os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

35
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,35 @@
# Contributing to libcbor
libcbor is maintained by [@PJK](https://github.com/PJK) in his spare time on a best-effort basis.
Community contributions are welcome as long as they align with the [project priorities](https://github.com/PJK/libcbor#main-features) and [goals](https://libcbor.readthedocs.io/en/latest/development.html#goals) and follow the guidelines described belows.
## Principles
**Bug reports and questions:** Bug reports and specific technical questions are always welcome. Feel free to open an [issue](https://github.com/PJK/libcbor/issues).
**Incremental improvements:** Bug fixes (including build scripts, examples, test, typos, CI/CD config, etc.) and documentation improvements (fixes of typos, inaccuracies, out of date content, etc.) are always welcome. Feel free to open a [PR](https://github.com/PJK/libcbor/pulls).
**New features:** I am looking to keep the scope of libcbor small. If you would like to add a feature, please open an issue with your proposal (or reach out to me privately) to discuss if the feature fits into libcbor before opening a PR.
**Major changes:** Please open an issue with your proposal (or reach out to me privately) to discuss if the improvement fits into cbor before opening a PR.
## Pull Request Process
1. Ensure that all CI checks pass
2. Check that the PR is complete and of good quality
- Include a descriptive summary of the change. If the PR addresses an open issue, please link it.
- Code changes: Add tests
- If necessary: Update documentation, including any links
3. Code changes: Update [the changelog](https://github.com/PJK/libcbor/blob/master/CHANGELOG.md). Do *not* change the version number.
## Interactions
I work on libcbor on a best effort basis. The typical response time is a few days.
If you do not receive a response in a few weeks, feel free to ping the PR or issue.
## Resources
- [Development documentation](https://libcbor.readthedocs.io/en/latest/development.html)

778
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
# [libcbor](https://github.com/PJK/libcbor) # [libcbor](https://github.com/PJK/libcbor)
[![Build Status](https://travis-ci.org/PJK/libcbor.svg?branch=master)](https://travis-ci.org/PJK/libcbor) [![CircleCI](https://circleci.com/gh/PJK/libcbor/tree/master.svg?style=svg)](https://circleci.com/gh/PJK/libcbor/tree/master)
[![Build status](https://ci.appveyor.com/api/projects/status/8kkmvmefelsxp5u2?svg=true)](https://ci.appveyor.com/project/PJK/libcbor) [![Build status](https://ci.appveyor.com/api/projects/status/8kkmvmefelsxp5u2?svg=true)](https://ci.appveyor.com/project/PJK/libcbor)
[![Documentation Status](https://readthedocs.org/projects/libcbor/badge/?version=latest)](https://readthedocs.org/projects/libcbor/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/libcbor/badge/?version=latest)](https://readthedocs.org/projects/libcbor/?badge=latest)
[![latest packaged version(s)](https://repology.org/badge/latest-versions/libcbor.svg)](https://repology.org/project/libcbor/versions) [![latest packaged version(s)](https://repology.org/badge/latest-versions/libcbor.svg)](https://repology.org/project/libcbor/versions)
[![codecov](https://codecov.io/gh/PJK/libcbor/branch/master/graph/badge.svg)](https://codecov.io/gh/PJK/libcbor) [![codecov](https://codecov.io/gh/PJK/libcbor/branch/master/graph/badge.svg)](https://codecov.io/gh/PJK/libcbor)
**libcbor** is a C library for parsing and generating [CBOR](http://tools.ietf.org/html/rfc7049), the general-purpose schema-less binary data format. **libcbor** is a C library for parsing and generating [CBOR](https://tools.ietf.org/html/rfc7049), the general-purpose schema-less binary data format.
## Main features ## Main features
- Complete RFC conformance - Complete RFC conformance
@ -25,7 +25,7 @@
```bash ```bash
git clone https://github.com/PJK/libcbor git clone https://github.com/PJK/libcbor
cmake -DCMAKE_BUILD_TYPE=Release -DCBOR_CUSTOM_ALLOC=ON libcbor cmake -DCMAKE_BUILD_TYPE=Release libcbor
make make
make install make install
``` ```
@ -63,29 +63,29 @@ yum install libcbor-devel
#include <cbor.h> #include <cbor.h>
#include <stdio.h> #include <stdio.h>
int main(int argc, char * argv[]) int main(void) {
{ /* Preallocate the map structure */
/* Preallocate the map structure */ cbor_item_t* root = cbor_new_definite_map(2);
cbor_item_t * root = cbor_new_definite_map(2); /* Add the content */
/* Add the content */ bool success = cbor_map_add(
cbor_map_add(root, (struct cbor_pair) { root, (struct cbor_pair){
.key = cbor_move(cbor_build_string("Is CBOR awesome?")), .key = cbor_move(cbor_build_string("Is CBOR awesome?")),
.value = cbor_move(cbor_build_bool(true)) .value = cbor_move(cbor_build_bool(true))});
}); success &= cbor_map_add(
cbor_map_add(root, (struct cbor_pair) { root, (struct cbor_pair){
.key = cbor_move(cbor_build_uint8(42)), .key = cbor_move(cbor_build_uint8(42)),
.value = cbor_move(cbor_build_string("Is the answer")) .value = cbor_move(cbor_build_string("Is the answer"))});
}); if (!success) return 1;
/* Output: `length` bytes of data in the `buffer` */ /* Output: `length` bytes of data in the `buffer` */
unsigned char * buffer; unsigned char* buffer;
size_t buffer_size, size_t buffer_size;
length = cbor_serialize_alloc(root, &buffer, &buffer_size); cbor_serialize_alloc(root, &buffer, &buffer_size);
fwrite(buffer, 1, length, stdout); fwrite(buffer, 1, buffer_size, stdout);
free(buffer); free(buffer);
fflush(stdout); fflush(stdout);
cbor_decref(&root); cbor_decref(&root);
} }
``` ```
@ -94,7 +94,7 @@ Get the latest documentation at [libcbor.readthedocs.org](http://libcbor.readthe
## Contributions ## Contributions
All bug reports and contributions are welcome. Please see https://github.com/PJK/libcbor for more info. Bug reports and contributions are welcome. Please see [CONTRIBUTING.md](https://github.com/PJK/libcbor/blob/master/CONTRIBUTING.md) for more info.
Kudos to all the [contributors](https://github.com/PJK/libcbor/graphs/contributors)! Kudos to all the [contributors](https://github.com/PJK/libcbor/graphs/contributors)!

View File

@ -1,27 +1,14 @@
image: Visual Studio 2015 image: Visual Studio 2022
version: '{build}' version: '{build}'
branches:
except:
- gh-pages
platform: x64 platform: x64
skip_branch_with_pr: true
environment:
matrix:
- CMAKE_GENERATOR: "Visual Studio 14 2015 Win64"
# Via https://github.com/apitrace/apitrace/blob/master/appveyor.yml
before_build: before_build:
- cmake -H. -Bbuild -G "%CMAKE_GENERATOR%" - cmake -H. -Bbuild
- C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64\CL.exe /?
build_script: build_script:
- if "%APPVEYOR_REPO_TAG%"=="true" (set CONFIGURATION=RelWithDebInfo) else (set CONFIGURATION=Debug) - if "%APPVEYOR_REPO_TAG%"=="true" (set CONFIGURATION=RelWithDebInfo) else (set CONFIGURATION=Debug)
- cmake --build build --config "%CONFIGURATION%" - cmake --build build --config "%CONFIGURATION%"
# TODO enable CMocka tests, maybe package the binaries # TODO enable CMocka tests, maybe package the binaries
# TODO add MinGW
# TODO add older MSVC

View File

@ -7,11 +7,4 @@ SOURCES=$(find ${DIRS} -name "*.c")
SOURCES+=" $(find ${DIRS} -name "*.h")" SOURCES+=" $(find ${DIRS} -name "*.h")"
SOURCES+=" $(find ${DIRS} -name "*.cpp")" SOURCES+=" $(find ${DIRS} -name "*.cpp")"
# TravisCI workaround to use new clang-format while avoiding painful aliasing clang-format $@ -style=file -i ${SOURCES}
# into the subshell
if which clang-format-8; then
clang-format-8 $@ -style=file -i ${SOURCES}
else
clang-format $@ -style=file -i ${SOURCES}
fi

View File

@ -1,2 +1,9 @@
ignore: ignore:
- "test/stream_expectations.c" # Function pointers are not resolved correctly - "test"
- "examples"
coverage:
status:
project:
default:
target: auto
threshold: 1% # Don't fail CI on trivial changes and Codecov flakiness

View File

@ -10,7 +10,7 @@ The data API is centered around :type:`cbor_item_t`, a generic handle for any CB
The single most important thing to keep in mind is: :type:`cbor_item_t` **is an opaque type and should only be manipulated using the appropriate functions!** Think of it as an object. The single most important thing to keep in mind is: :type:`cbor_item_t` **is an opaque type and should only be manipulated using the appropriate functions!** Think of it as an object.
The *libcbor* API closely follows the semantics outlined by `CBOR standard <http://tools.ietf.org/html/rfc7049>`_. This part of the documentation provides a short overview of the CBOR constructs, as well as a general introduction to the *libcbor* API. Remaining reference can be found in the following files structured by data types. The *libcbor* API closely follows the semantics outlined by `CBOR standard <https://tools.ietf.org/html/rfc7049>`_. This part of the documentation provides a short overview of the CBOR constructs, as well as a general introduction to the *libcbor* API. Remaining reference can be found in the following files structured by data types.
The API is designed to allow both very tight control & flexibility and general convenience with sane defaults. [#]_ For example, client with very specific requirements (constrained environment, custom application protocol built on top of CBOR, etc.) may choose to take full control (and responsibility) of memory and data structures management by interacting directly with the decoder. Other clients might want to take control of specific aspects (streamed collections, hash maps storage), but leave other responsibilities to *libcbor*. More general clients might prefer to be abstracted away from all aforementioned details and only be presented complete data structures. The API is designed to allow both very tight control & flexibility and general convenience with sane defaults. [#]_ For example, client with very specific requirements (constrained environment, custom application protocol built on top of CBOR, etc.) may choose to take full control (and responsibility) of memory and data structures management by interacting directly with the decoder. Other clients might want to take control of specific aspects (streamed collections, hash maps storage), but leave other responsibilities to *libcbor*. More general clients might prefer to be abstracted away from all aforementioned details and only be presented complete data structures.
@ -28,6 +28,8 @@ The API is designed to allow both very tight control & flexibility and general c
api/item_reference_counting api/item_reference_counting
api/decoding api/decoding
api/encoding api/encoding
api/streaming_decoding
api/streaming_encoding
api/type_0_1 api/type_0_1
api/type_2 api/type_2
api/type_3 api/type_3

View File

@ -6,6 +6,10 @@ The easiest way to encode data items is using the :func:`cbor_serialize` or :fun
.. doxygenfunction:: cbor_serialize .. doxygenfunction:: cbor_serialize
.. doxygenfunction:: cbor_serialize_alloc .. doxygenfunction:: cbor_serialize_alloc
To determine the number of bytes needed to serialize an item, use :func:`cbor_serialized_size`:
.. doxygenfunction:: cbor_serialized_size
Type-specific serializers Type-specific serializers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In case you know the type of the item you want to serialize beforehand, you can use one In case you know the type of the item you want to serialize beforehand, you can use one

View File

@ -1,24 +1,21 @@
Memory management and reference counting Memory management and reference counting
=============================================== ===============================================
Due to the nature of its domain, *libcbor* will need to work with heap memory. The stateless decoder and encoder don't allocate any memory. Due to the nature of its domain, *libcbor* will need to work with heap memory. The stateless decoder and encoder doesn't allocate any memory.
If you have specific requirements, you should consider rolling your own driver for the stateless API. If you have specific requirements, you should consider rolling your own driver for the stateless API.
Using custom allocator Using custom allocator
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
*libcbor* gives you with the ability to provide your own implementations of ``malloc``, ``realloc``, and ``free``. This can be useful if you are using a custom allocator throughout your application, or if you want to implement custom policies (e.g. tighter restrictions on the amount of allocated memory). *libcbor* gives you with the ability to provide your own implementations of ``malloc``, ``realloc``, and ``free``.
This can be useful if you are using a custom allocator throughout your application,
or if you want to implement custom policies (e.g. tighter restrictions on the amount of allocated memory).
In order to use this feature, *libcbor* has to be compiled with the :doc:`appropriate flags </getting_started>`. You can verify the configuration using the ``CBOR_CUSTOM_ALLOC`` macro. A simple usage might be as follows:
.. code-block:: c .. code-block:: c
#if CBOR_CUSTOM_ALLOC cbor_set_allocs(malloc, realloc, free);
cbor_set_allocs(malloc, realloc, free);
#else
#error "libcbor built with support for custom allocation is required"
#endif
.. doxygenfunction:: cbor_set_allocs .. doxygenfunction:: cbor_set_allocs
@ -26,11 +23,11 @@ In order to use this feature, *libcbor* has to be compiled with the :doc:`approp
Reference counting Reference counting
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
As CBOR items may require complex cleanups at the end of their lifetime, there is a reference counting mechanism in place. This also enables very simple GC when integrating *libcbor* into managed environment. Every item starts its life (by either explicit creation, or as a result of parsing) with reference count set to 1. When the refcount reaches zero, it will be destroyed. As CBOR items may require complex cleanups at the end of their lifetime, there is a reference counting mechanism in place. This also enables a very simple GC when integrating *libcbor* into a managed environment. Every item starts its life (by either explicit creation, or as a result of parsing) with reference count set to 1. When the refcount reaches zero, it will be destroyed.
Items containing nested items will be destroyed recursively - refcount of every nested item will be decreased by one. Items containing nested items will be destroyed recursively - the refcount of every nested item will be decreased by one.
The destruction is synchronous and renders any pointers to items with refcount zero invalid immediately after calling the :func:`cbor_decref`. The destruction is synchronous and renders any pointers to items with refcount zero invalid immediately after calling :func:`cbor_decref`.
.. doxygenfunction:: cbor_incref .. doxygenfunction:: cbor_incref

View File

@ -1,11 +1,12 @@
Decoding Streaming Decoding
============================= =============================
Another way to decode data using libcbor is to specify a callbacks that will be invoked when upon finding certain items in the input. This API is provided by *libcbor* exposes a stateless decoder that reads a stream of input bytes from a buffer and invokes user-provided callbacks as it decodes the input:
.. doxygenfunction:: cbor_stream_decode .. doxygenfunction:: cbor_stream_decode
Usage example: https://github.com/PJK/libcbor/blob/master/examples/streaming_parser.c For example, when :func:`cbor_stream_decode` encounters a 1B unsigned integer, it will invoke the function pointer stored in ``cbor_callbacks.uint8``.
Complete usage example: `examples/streaming_parser.c <https://github.com/PJK/libcbor/blob/master/examples/streaming_parser.c>`_
The callbacks are defined by The callbacks are defined by
@ -16,13 +17,6 @@ When building custom sets of callbacks, feel free to start from
.. doxygenvariable:: cbor_empty_callbacks .. doxygenvariable:: cbor_empty_callbacks
Related structures
~~~~~~~~~~~~~~~~~~~~~
.. doxygenenum:: cbor_decoder_status
.. doxygenstruct:: cbor_decoder_result
:members:
Callback types definition Callback types definition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,65 @@
Streaming Encoding
=============================
`cbor/encoding.h <https://github.com/PJK/libcbor/blob/master/src/cbor/encoding.h>`_
exposes a low-level encoding API to encode CBOR objects on the fly. Unlike
:func:`cbor_serialize`, these functions take logical values (integers, floats,
strings, etc.) instead of :type:`cbor_item_t`. The client is responsible for
constructing the compound types correctly (e.g. terminating arrays).
Streaming encoding is typically used to create an streaming (indefinite length) CBOR :doc:`strings <type_2>`, :doc:`byte strings <type_3>`, :doc:`arrays <type_4>`, and :doc:`maps <type_5>`. Complete example: `examples/streaming_array.c <https://github.com/PJK/libcbor/blob/master/examples/streaming_array.c>`_
.. doxygenfunction:: cbor_encode_uint8
.. doxygenfunction:: cbor_encode_uint16
.. doxygenfunction:: cbor_encode_uint32
.. doxygenfunction:: cbor_encode_uint64
.. doxygenfunction:: cbor_encode_uint
.. doxygenfunction:: cbor_encode_negint8
.. doxygenfunction:: cbor_encode_negint16
.. doxygenfunction:: cbor_encode_negint32
.. doxygenfunction:: cbor_encode_negint64
.. doxygenfunction:: cbor_encode_negint
.. doxygenfunction:: cbor_encode_bytestring_start
.. doxygenfunction:: cbor_encode_indef_bytestring_start
.. doxygenfunction:: cbor_encode_string_start
.. doxygenfunction:: cbor_encode_indef_string_start
.. doxygenfunction:: cbor_encode_array_start
.. doxygenfunction:: cbor_encode_indef_array_start
.. doxygenfunction:: cbor_encode_map_start
.. doxygenfunction:: cbor_encode_indef_map_start
.. doxygenfunction:: cbor_encode_tag
.. doxygenfunction:: cbor_encode_bool
.. doxygenfunction:: cbor_encode_null
.. doxygenfunction:: cbor_encode_undef
.. doxygenfunction:: cbor_encode_half
.. doxygenfunction:: cbor_encode_single
.. doxygenfunction:: cbor_encode_double
.. doxygenfunction:: cbor_encode_break
.. doxygenfunction:: cbor_encode_ctrl

View File

@ -27,11 +27,6 @@ Storage requirements (indefinite) ``sizeof(cbor_item_t) * (1 + chunk_count) +
================================== ====================================================== ================================== ======================================================
Streaming indefinite byte strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please refer to :doc:`/streaming`.
Getting metadata Getting metadata
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~

View File

@ -1,7 +1,7 @@
Type 3 UTF-8 strings Type 3 UTF-8 strings
============================= =============================
CBOR strings work in much the same ways as :doc:`type_2`. CBOR strings have the same structure as :doc:`type_2`.
================================== ====================================================== ================================== ======================================================
Corresponding :type:`cbor_type` ``CBOR_TYPE_STRING`` Corresponding :type:`cbor_type` ``CBOR_TYPE_STRING``
@ -12,10 +12,6 @@ Storage requirements (definite) ``sizeof(cbor_item_t) + length(handle)``
Storage requirements (indefinite) ``sizeof(cbor_item_t) * (1 + chunk_count) + chunks`` Storage requirements (indefinite) ``sizeof(cbor_item_t) * (1 + chunk_count) + chunks``
================================== ====================================================== ================================== ======================================================
Streaming indefinite strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please refer to :doc:`/streaming`.
UTF-8 encoding validation UTF-8 encoding validation
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -2,6 +2,11 @@ Type 4 Arrays
============================= =============================
CBOR arrays, just like :doc:`byte strings <type_2>` and :doc:`strings <type_3>`, can be encoded either as definite, or as indefinite. CBOR arrays, just like :doc:`byte strings <type_2>` and :doc:`strings <type_3>`, can be encoded either as definite, or as indefinite.
Definite arrays have a fixed size which is stored in the header, whereas indefinite arrays do not and are terminated by a special "break" byte instead.
Arrays are explicitly created or decoded as definite or indefinite and will be encoded using the corresponding wire representation, regardless of whether the actual size is known at the time of encoding.
.. note:: Indefinite arrays can be conveniently used with streaming :doc:`decoding <streaming_decoding>` and :doc:`encoding <streaming_encoding>`.
================================== ===================================================================================== ================================== =====================================================================================
Corresponding :type:`cbor_type` ``CBOR_TYPE_ARRAY`` Corresponding :type:`cbor_type` ``CBOR_TYPE_ARRAY``
@ -28,10 +33,6 @@ Examples
0x20 Unsigned integer 32 0x20 Unsigned integer 32
... 32 items follow ... 32 items follow
Streaming indefinite arrays
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please refer to :doc:`/streaming`.
Getting metadata Getting metadata
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~

View File

@ -1,9 +1,18 @@
Type 5 Maps Type 5 Maps
============================= =============================
CBOR maps are the plain old associate hash maps known from JSON and many other formats and languages, with one exception: any CBOR data item can be a key, not just strings. This is somewhat unusual and you, as an application developer, should keep that in mind. CBOR maps are the plain old associative maps similar JSON objects or Python dictionaries.
Maps can be either definite or indefinite, in much the same way as :doc:`type_4`. Definite maps have a fixed size which is stored in the header, whereas indefinite maps do not and are terminated by a special "break" byte instead.
Map are explicitly created or decoded as definite or indefinite and will be encoded using the corresponding wire representation, regardless of whether the actual size is known at the time of encoding.
.. note::
Indefinite maps can be conveniently used with streaming :doc:`decoding <streaming_decoding>` and :doc:`encoding <streaming_encoding>`.
Keys and values can simply be output one by one, alternating keys and values.
.. warning:: Any CBOR data item is a legal map key (not just strings).
================================== ===================================================================================== ================================== =====================================================================================
Corresponding :type:`cbor_type` ``CBOR_TYPE_MAP`` Corresponding :type:`cbor_type` ``CBOR_TYPE_MAP``
@ -14,10 +23,19 @@ Storage requirements (definite) ``sizeof(cbor_pair) * size + sizeof(cbor_ite
Storage requirements (indefinite) ``<= sizeof(cbor_item_t) + sizeof(cbor_pair) * size * BUFFER_GROWTH`` Storage requirements (indefinite) ``<= sizeof(cbor_item_t) + sizeof(cbor_pair) * size * BUFFER_GROWTH``
================================== ===================================================================================== ================================== =====================================================================================
Streaming maps Examples
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please refer to :doc:`/streaming`. ::
0xbf Start indefinite map (represents {1: 2})
0x01 Unsigned integer 1 (key)
0x02 Unsigned integer 2 (value)
0xff "Break" control token
::
0xa0 Map of size 0
Getting metadata Getting metadata
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~

View File

@ -65,11 +65,4 @@ Manipulating existing items
Half floats Half floats
~~~~~~~~~~~~ ~~~~~~~~~~~~
CBOR supports two `bytes wide ("half-precision") <https://en.wikipedia.org/wiki/Half-precision_floating-point_format>`_ CBOR supports two `bytes wide ("half-precision") <https://en.wikipedia.org/wiki/Half-precision_floating-point_format>`_
floats which are not supported by the C language. *libcbor* represents them using `float <https://en.cppreference.com/w/c/language/type>` values throughout the API, which has important implications when manipulating these values. floats which are not supported by the C language. *libcbor* represents them using `float <https://en.cppreference.com/w/c/language/type>` values throughout the API. Encoding will be performed by :func:`cbor_encode_half`, which will handle any values that cannot be represented as a half-float.
In particular, if a user uses some of the manipulation APIs
(e.g. :func:`cbor_set_float2`, :func:`cbor_new_float2`)
to introduce a value that doesn't have an exect half-float representation,
the encoding semantics are given by :func:`cbor_encode_half` as follows:
.. doxygenfunction:: cbor_encode_half

View File

@ -76,8 +76,8 @@
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.8' version = '0.10'
release = '0.8.0' release = '0.10.2'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -50,12 +50,11 @@ Option Meaning
``WITH_TESTS`` Build unit tests (see :doc:`development`) ``OFF`` ``ON``, ``OFF`` ``WITH_TESTS`` Build unit tests (see :doc:`development`) ``OFF`` ``ON``, ``OFF``
======================== ======================================================= ====================== ===================================================================================================================== ======================== ======================================================= ====================== =====================================================================================================================
The following configuration options will also be defined as macros[#]_ in ``<cbor/common.h>`` and can therefore be used in client code: The following configuration options will also be defined as macros [#]_ in ``<cbor/common.h>`` and can therefore be used in client code:
======================== ======================================================= ====================== ===================================================================================================================== ======================== ======================================================= ====================== =====================================================================================================================
Option Meaning Default Possible values Option Meaning Default Possible values
------------------------ ------------------------------------------------------- ---------------------- --------------------------------------------------------------------------------------------------------------------- ------------------------ ------------------------------------------------------- ---------------------- ---------------------------------------------------------------------------------------------------------------------
``CBOR_CUSTOM_ALLOC`` Enable custom allocator support ``OFF`` ``ON``, ``OFF``
``CBOR_PRETTY_PRINTER`` Include a pretty-printing routine ``ON`` ``ON``, ``OFF`` ``CBOR_PRETTY_PRINTER`` Include a pretty-printing routine ``ON`` ``ON``, ``OFF``
``CBOR_BUFFER_GROWTH`` Factor for buffer growth & shrinking ``2`` Decimals > 1 ``CBOR_BUFFER_GROWTH`` Factor for buffer growth & shrinking ``2`` Decimals > 1
======================== ======================================================= ====================== ===================================================================================================================== ======================== ======================================================= ====================== =====================================================================================================================
@ -64,6 +63,10 @@ Option Meaning
If you want to pass other custom configuration options, please refer to `<http://www.cmake.org/Wiki/CMake_Useful_Variables>`_. If you want to pass other custom configuration options, please refer to `<http://www.cmake.org/Wiki/CMake_Useful_Variables>`_.
.. 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.
**Building using make** **Building using make**
CMake will generate a Makefile and other configuration files for the build. As a rule of thumb, you should configure the CMake will generate a Makefile and other configuration files for the build. As a rule of thumb, you should configure the

View File

@ -30,7 +30,6 @@ Contents
getting_started getting_started
using using
api api
streaming
tests tests
rfc_conformance rfc_conformance
internal internal

View File

@ -1,39 +1,31 @@
alabaster==0.7.12 alabaster==0.7.12
argh==0.26.2 Babel==2.9.1
Babel==2.8.0 breathe==4.33.1
breathe==4.14.1 certifi==2022.12.7
certifi==2019.11.28 charset-normalizer==2.0.12
chardet==3.0.4 colorama==0.4.4
Click==7.0 docutils==0.17.1
docutils==0.16 idna==3.3
Flask==1.1.1 imagesize==1.3.0
idna==2.9 importlib-metadata==4.11.3
imagesize==1.2.0 Jinja2==3.0.3
itsdangerous==1.1.0 livereload==2.6.3
Jinja2==2.10.1 MarkupSafe==2.1.1
livereload==2.6.1 packaging==21.3
MarkupSafe==1.1.1 Pygments==2.11.2
packaging==20.3 pyparsing==3.0.7
pathtools==0.1.2 pytz==2021.3
port-for==0.3.1 requests==2.27.1
Pygments==2.6.1 snowballstemmer==2.2.0
pyparsing==2.4.6 Sphinx==4.4.0
pytz==2019.3 sphinx-autobuild==2021.3.14
PyYAML==5.3 sphinx-rtd-theme==1.0.0
requests==2.23.0
six==1.14.0
snowballstemmer==2.0.0
sortedcontainers==2.1.0
Sphinx==2.4.4
sphinx-autobuild==0.7.1
sphinx-rtd-theme==0.4.3
sphinxcontrib-applehelp==1.0.2 sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2 sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==1.0.3 sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1 sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3 sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.4 sphinxcontrib-serializinghtml==1.1.5
tornado==6.0.4 tornado==6.1
urllib3==1.25.8 urllib3==1.26.9
watchdog==0.10.2 zipp==3.7.0
Werkzeug==0.15.5

View File

@ -1,13 +1,13 @@
RFC conformance RFC conformance
========================= =========================
*libcbor* is, generally speaking, very faithful implementation of `RFC 7049 <http://tools.ietf.org/html/rfc7049>`_. There are, however, some limitations imposed by technical constraints. *libcbor* is, generally speaking, very faithful implementation of `RFC 7049 <https://tools.ietf.org/html/rfc7049>`_. There are, however, some limitations imposed by technical constraints.
Bytestring length Bytestring length
------------------- -------------------
There is no explicit limitation of indefinite length byte strings. [#]_ *libcbor* will not handle byte strings with more chunks than the maximum value of :type:`size_t`. On any sane platform, such string would not fit in the memory anyway. It is, however, possible to process arbitrarily long strings and byte strings using the streaming decoder. There is no explicit limitation of indefinite length byte strings. [#]_ *libcbor* will not handle byte strings with more chunks than the maximum value of :type:`size_t`. On any sane platform, such string would not fit in the memory anyway. It is, however, possible to process arbitrarily long strings and byte strings using the streaming decoder.
.. [#] http://tools.ietf.org/html/rfc7049#section-2.2.2 .. [#] https://tools.ietf.org/html/rfc7049#section-2.2.2
"Half-precision" IEEE 754 floats "Half-precision" IEEE 754 floats
--------------------------------- ---------------------------------

View File

@ -1,13 +0,0 @@
Streaming & indefinite items
=============================
CBOR :doc:`strings <api/type_2>`, :doc:`byte strings <api/type_3>`, :doc:`arrays <api/type_4>`, and :doc:`maps <api/type_5>` can be encoded as *indefinite*, meaning their length or size is not specified. Instead, they are divided into *chunks* (:doc:`strings <api/type_2>`, :doc:`byte strings <api/type_3>`), or explicitly terminated (:doc:`arrays <api/type_4>`, :doc:`maps <api/type_5>`).
This is one of the most important (and due to poor implementations, underutilized) features of CBOR. It enables low-overhead streaming just about anywhere without dealing with channels or pub/sub mechanism. It is, however, important to recognize that CBOR streaming is not a substitute for Websockets [#]_ and similar technologies.
.. [#] :RFC:`6455`
.. toctree::
streaming/decoding
streaming/encoding

View File

@ -1,4 +0,0 @@
Encoding
=============================
TODO

View File

@ -34,10 +34,10 @@ feel free to use just some of the ``cbor/*.h`` headers:
- ``cbor/arrays.h`` - :doc:`api/type_4` - ``cbor/arrays.h`` - :doc:`api/type_4`
- ``cbor/bytestrings.h`` - :doc:`api/type_2` - ``cbor/bytestrings.h`` - :doc:`api/type_2`
- ``cbor/callbacks.h`` - Callbacks used for :doc:`streaming/decoding` - ``cbor/callbacks.h`` - Callbacks used for :doc:`api/streaming_decoding`
- ``cbor/common.h`` - Common utilities - always transitively included - ``cbor/common.h`` - Common utilities - always transitively included
- ``cbor/data.h`` - Data types definitions - always transitively included - ``cbor/data.h`` - Data types definitions - always transitively included
- ``cbor/encoding.h`` - Streaming encoders for :doc:`streaming/encoding` - ``cbor/encoding.h`` - Streaming encoders for :doc:`api/streaming_encoding`
- ``cbor/floats_ctrls.h`` - :doc:`api/type_7` - ``cbor/floats_ctrls.h`` - :doc:`api/type_7`
- ``cbor/ints.h`` - :doc:`api/type_0_1` - ``cbor/ints.h`` - :doc:`api/type_0_1`
- ``cbor/maps.h`` - :doc:`api/type_5` - ``cbor/maps.h`` - :doc:`api/type_5`
@ -75,11 +75,12 @@ of what is it CBOR does, the examples (located in the ``examples`` directory) sh
.key = cbor_move(cbor_build_uint8(42)), .key = cbor_move(cbor_build_uint8(42)),
.value = cbor_move(cbor_build_string("Is the answer")) .value = cbor_move(cbor_build_string("Is the answer"))
}); });
/* Output: `length` bytes of data in the `buffer` */ /* Output: `buffer_size` bytes of data in the `buffer` */
unsigned char * buffer; unsigned char * buffer;
size_t buffer_size, length = cbor_serialize_alloc(root, &buffer, &buffer_size); size_t buffer_size;
cbor_serialize_alloc(root, &buffer, &buffer_size);
fwrite(buffer, 1, length, stdout); fwrite(buffer, 1, buffer_size, stdout);
free(buffer); free(buffer);
fflush(stdout); fflush(stdout);

View File

@ -7,6 +7,9 @@ target_link_libraries(create_items cbor)
add_executable(streaming_parser streaming_parser.c) add_executable(streaming_parser streaming_parser.c)
target_link_libraries(streaming_parser cbor) target_link_libraries(streaming_parser cbor)
add_executable(streaming_array streaming_array.c)
target_link_libraries(streaming_array cbor)
add_executable(sort sort.c) add_executable(sort sort.c)
target_link_libraries(sort cbor) target_link_libraries(sort cbor)

33
examples/bazel/README.md Normal file
View File

@ -0,0 +1,33 @@
# Bazel Example
This directory shows an example of using LibCbor in a project that builds with Bazel.
## Compile
To build the project:
```shell
bazel build src:all
```
## Test
To test the code:
```shell
bazel test src:all
```
## Run
To run the demo:
```shell
bazel run src:hello
```
or
```shell
bazel-bin/src/hello
```

19
examples/bazel/WORKSPACE Normal file
View File

@ -0,0 +1,19 @@
workspace(name = "libcbor_bazel_example")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Google Test
http_archive(
name = "gtest",
sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91",
strip_prefix = "googletest-release-1.10.0",
url = "https://github.com/google/googletest/archive/release-1.10.0.zip",
)
# libcbor
new_local_repository(
name = "libcbor",
build_file = "//third_party:libcbor.BUILD",
path = "../../src",
)

46
examples/bazel/src/BUILD Normal file
View File

@ -0,0 +1,46 @@
load("@rules_cc//cc:defs.bzl", "cc_library")
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_library(
name = "src",
srcs = [
"hello.cc",
],
hdrs = [
"hello.h",
],
visibility = [
"//src:__pkg__",
],
deps = [
"@libcbor//:cbor",
],
)
cc_test(
name = "tests",
size = "small",
srcs = [
"hello_test.cc",
],
visibility = [
"//visibility:private",
],
deps = [
":src",
"@gtest//:gtest_main",
"@libcbor//:cbor",
],
)
cc_binary(
name = "hello",
srcs = [
"main.cc",
],
deps = [
":src",
],
)

View File

@ -0,0 +1,7 @@
#include "src/hello.h"
#include "cbor.h"
void print_cbor_version() {
printf("libcbor v%d.%d.%d\n", cbor_major_version, cbor_minor_version, cbor_patch_version);
}

View File

@ -0,0 +1,8 @@
#ifndef HELLO_H_
#define HELLO_H_
#include <cstdint>
void print_cbor_version(void);
#endif // HELLO_H_

View File

@ -0,0 +1,10 @@
#include "src/hello.h"
#include "gtest/gtest.h"
class HelloTest : public ::testing::Test {};
TEST_F(HelloTest, CborVersion) {
EXPECT_EQ(cbor_version(), 0);
}

View File

@ -0,0 +1,10 @@
#include "src/hello.h"
#include <stdio.h>
int main() {
print_cbor_version();
return 0;
}

1
examples/bazel/third_party/BUILD vendored Normal file
View File

@ -0,0 +1 @@
exports_files(["libcbor.BUILD"])

View File

@ -0,0 +1,21 @@
cc_library(
name = "cbor",
srcs = glob([
"src/**/*.h",
"src/**/*.c",
]),
hdrs = [
"cbor.h",
] + glob([
"cbor/*.h",
]),
includes = [
"src",
"src/cbor",
"src/cbor/internal",
],
visibility = ["//visibility:public"],
deps = [
"@libcbor_bazel_example//third_party/libcbor:config",
],
)

View File

@ -0,0 +1,11 @@
cc_library(
name = "config",
hdrs = [
"cbor/cbor_export.h",
"cbor/configuration.h",
],
includes = [
"./",
],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,42 @@
#ifndef CBOR_EXPORT_H
#define CBOR_EXPORT_H
#ifdef CBOR_STATIC_DEFINE
#define CBOR_EXPORT
#define CBOR_NO_EXPORT
#else
#ifndef CBOR_EXPORT
#ifdef cbor_EXPORTS
/* We are building this library */
#define CBOR_EXPORT
#else
/* We are using this library */
#define CBOR_EXPORT
#endif
#endif
#ifndef CBOR_NO_EXPORT
#define CBOR_NO_EXPORT
#endif
#endif
#ifndef CBOR_DEPRECATED
#define CBOR_DEPRECATED __attribute__((__deprecated__))
#endif
#ifndef CBOR_DEPRECATED_EXPORT
#define CBOR_DEPRECATED_EXPORT CBOR_EXPORT CBOR_DEPRECATED
#endif
#ifndef CBOR_DEPRECATED_NO_EXPORT
#define CBOR_DEPRECATED_NO_EXPORT CBOR_NO_EXPORT CBOR_DEPRECATED
#endif
#if 0 /* DEFINE_NO_DEPRECATED */
#ifndef CBOR_NO_DEPRECATED
#define CBOR_NO_DEPRECATED
#endif
#endif
#endif /* CBOR_EXPORT_H */

View File

@ -0,0 +1,15 @@
#ifndef LIBCBOR_CONFIGURATION_H
#define LIBCBOR_CONFIGURATION_H
#define CBOR_MAJOR_VERSION 0
#define CBOR_MINOR_VERSION 10
#define CBOR_PATCH_VERSION 2
#define CBOR_BUFFER_GROWTH 2
#define CBOR_MAX_STACK_SIZE 2048
#define CBOR_PRETTY_PRINTER 1
#define CBOR_RESTRICT_SPECIFIER restrict
#define CBOR_INLINE_SPECIFIER
#endif // LIBCBOR_CONFIGURATION_H

View File

@ -110,7 +110,7 @@ void cjson_cbor_stream_decode(cJSON *source,
} }
} }
void usage() { void usage(void) {
printf("Usage: cjson [input JSON file]\n"); printf("Usage: cjson [input JSON file]\n");
exit(1); exit(1);
} }
@ -133,10 +133,10 @@ int main(int argc, char *argv[]) {
/* Print out CBOR bytes */ /* Print out CBOR bytes */
unsigned char *buffer; unsigned char *buffer;
size_t buffer_size, size_t buffer_size;
cbor_length = cbor_serialize_alloc(cbor, &buffer, &buffer_size); cbor_serialize_alloc(cbor, &buffer, &buffer_size);
fwrite(buffer, 1, cbor_length, stdout); fwrite(buffer, 1, buffer_size, stdout);
free(buffer); free(buffer);
fflush(stdout); fflush(stdout);

View File

@ -8,24 +8,25 @@
#include <stdio.h> #include <stdio.h>
#include "cbor.h" #include "cbor.h"
int main(int argc, char* argv[]) { int main(void) {
/* Preallocate the map structure */ /* Preallocate the map structure */
cbor_item_t* root = cbor_new_definite_map(2); cbor_item_t* root = cbor_new_definite_map(2);
/* Add the content */ /* Add the content */
cbor_map_add(root, bool success = cbor_map_add(
(struct cbor_pair){ root, (struct cbor_pair){
.key = cbor_move(cbor_build_string("Is CBOR awesome?")), .key = cbor_move(cbor_build_string("Is CBOR awesome?")),
.value = cbor_move(cbor_build_bool(true))}); .value = cbor_move(cbor_build_bool(true))});
cbor_map_add(root, success &= cbor_map_add(
(struct cbor_pair){ root, (struct cbor_pair){
.key = cbor_move(cbor_build_uint8(42)), .key = cbor_move(cbor_build_uint8(42)),
.value = cbor_move(cbor_build_string("Is the answer"))}); .value = cbor_move(cbor_build_string("Is the answer"))});
if (!success) return 1;
/* Output: `length` bytes of data in the `buffer` */ /* Output: `length` bytes of data in the `buffer` */
unsigned char* buffer; unsigned char* buffer;
size_t buffer_size, size_t buffer_size;
length = cbor_serialize_alloc(root, &buffer, &buffer_size); cbor_serialize_alloc(root, &buffer, &buffer_size);
fwrite(buffer, 1, length, stdout); fwrite(buffer, 1, buffer_size, stdout);
free(buffer); free(buffer);
fflush(stdout); fflush(stdout);

View File

@ -8,9 +8,8 @@
#include <stdio.h> #include <stdio.h>
#include "cbor.h" #include "cbor.h"
int main(int argc, char* argv[]) { int main(void) {
printf("Hello from libcbor %s\n", CBOR_VERSION); printf("Hello from libcbor %s\n", CBOR_VERSION);
printf("Custom allocation support: %s\n", CBOR_CUSTOM_ALLOC ? "yes" : "no");
printf("Pretty-printer support: %s\n", CBOR_PRETTY_PRINTER ? "yes" : "no"); printf("Pretty-printer support: %s\n", CBOR_PRETTY_PRINTER ? "yes" : "no");
printf("Buffer growth factor: %f\n", (float)CBOR_BUFFER_GROWTH); printf("Buffer growth factor: %f\n", (float)CBOR_BUFFER_GROWTH);
} }

View File

@ -8,7 +8,7 @@
#include <stdio.h> #include <stdio.h>
#include "cbor.h" #include "cbor.h"
void usage() { void usage(void) {
printf("Usage: readfile [input file]\n"); printf("Usage: readfile [input file]\n");
exit(1); exit(1);
} }
@ -31,6 +31,7 @@ int main(int argc, char* argv[]) {
/* Assuming `buffer` contains `length` bytes of input data */ /* Assuming `buffer` contains `length` bytes of input data */
struct cbor_load_result result; struct cbor_load_result result;
cbor_item_t* item = cbor_load(buffer, length, &result); cbor_item_t* item = cbor_load(buffer, length, &result);
free(buffer);
if (result.error.code != CBOR_ERR_NONE) { if (result.error.code != CBOR_ERR_NONE) {
printf( printf(

View File

@ -14,7 +14,7 @@
* standard library functions. * standard library functions.
*/ */
int comparUint(const void *a, const void *b) { int compareUint(const void *a, const void *b) {
uint8_t av = cbor_get_uint8(*(cbor_item_t **)a), uint8_t av = cbor_get_uint8(*(cbor_item_t **)a),
bv = cbor_get_uint8(*(cbor_item_t **)b); bv = cbor_get_uint8(*(cbor_item_t **)b);
@ -26,15 +26,16 @@ int comparUint(const void *a, const void *b) {
return 1; return 1;
} }
int main(int argc, char *argv[]) { int main(void) {
cbor_item_t *array = cbor_new_definite_array(4); cbor_item_t *array = cbor_new_definite_array(4);
cbor_array_push(array, cbor_move(cbor_build_uint8(4))); bool success = cbor_array_push(array, cbor_move(cbor_build_uint8(4)));
cbor_array_push(array, cbor_move(cbor_build_uint8(3))); success &= cbor_array_push(array, cbor_move(cbor_build_uint8(3)));
cbor_array_push(array, cbor_move(cbor_build_uint8(1))); success &= cbor_array_push(array, cbor_move(cbor_build_uint8(1)));
cbor_array_push(array, cbor_move(cbor_build_uint8(2))); success &= cbor_array_push(array, cbor_move(cbor_build_uint8(2)));
if (!success) return 1;
qsort(cbor_array_handle(array), cbor_array_size(array), sizeof(cbor_item_t *), qsort(cbor_array_handle(array), cbor_array_size(array), sizeof(cbor_item_t *),
comparUint); compareUint);
cbor_describe(array, stdout); cbor_describe(array, stdout);
fflush(stdout); fflush(stdout);

View File

@ -0,0 +1,47 @@
/*
* 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 <stdlib.h>
#include "cbor.h"
void usage(void) {
printf("Usage: streaming_array <N>\n");
printf("Prints out serialized array [0, ..., N-1]\n");
exit(1);
}
#define BUFFER_SIZE 8
unsigned char buffer[BUFFER_SIZE];
FILE* out;
void flush(size_t bytes) {
if (bytes == 0) exit(1); // All items should be successfully encoded
if (fwrite(buffer, sizeof(unsigned char), bytes, out) != bytes) exit(1);
if (fflush(out)) exit(1);
}
/*
* Example of using the streaming encoding API to create an array of integers
* on the fly. Notice that a partial output is produced with every element.
*/
int main(int argc, char* argv[]) {
if (argc != 2) usage();
long n = strtol(argv[1], NULL, 10);
out = freopen(NULL, "wb", stdout);
if (!out) exit(1);
// Start an indefinite-length array
flush(cbor_encode_indef_array_start(buffer, BUFFER_SIZE));
// Write the array items one by one
for (size_t i = 0; i < n; i++) {
flush(cbor_encode_uint32(i, buffer, BUFFER_SIZE));
}
// Close the array
flush(cbor_encode_break(buffer, BUFFER_SIZE));
if (fclose(out)) exit(1);
}

View File

@ -9,7 +9,13 @@
#include <string.h> #include <string.h>
#include "cbor.h" #include "cbor.h"
void usage() { #ifdef __GNUC__
#define UNUSED(x) __attribute__((__unused__)) x
#else
#define UNUSED(x) x
#endif
void usage(void) {
printf("Usage: streaming_parser [input file]\n"); printf("Usage: streaming_parser [input file]\n");
exit(1); exit(1);
} }
@ -24,7 +30,7 @@ void usage() {
const char* key = "a secret key"; const char* key = "a secret key";
bool key_found = false; bool key_found = false;
void find_string(void* _ctx, cbor_data buffer, size_t len) { void find_string(void* UNUSED(_ctx), cbor_data buffer, uint64_t len) {
if (key_found) { if (key_found) {
printf("Found the value: %.*s\n", (int)len, buffer); printf("Found the value: %.*s\n", (int)len, buffer);
key_found = false; key_found = false;

View File

@ -1,5 +1,14 @@
import sys, re import sys, re
from datetime import date from datetime import date
import logging
logging.basicConfig(level=logging.INFO)
# Update version label in all configuration files
# Usage: python3 misc/update_version.py X.Y.Z
# When testing, reset local state using:
# git checkout -- CHANGELOG.md Doxyfile CMakeLists.txt doc/source/conf.py examples/bazel/third_party/libcbor/cbor/configuration.h
version = sys.argv[1] version = sys.argv[1]
release_date = date.today().strftime('%Y-%m-%d') release_date = date.today().strftime('%Y-%m-%d')
@ -7,7 +16,10 @@
def replace(file_path, pattern, replacement): def replace(file_path, pattern, replacement):
updated = re.sub(pattern, replacement, open(file_path).read()) logging.info(f'Updating {file_path}')
original = open(file_path).read()
updated = re.sub(pattern, replacement, original)
assert updated != original
with open(file_path, 'w') as f: with open(file_path, 'w') as f:
f.write(updated) f.write(updated)
@ -23,13 +35,22 @@ def replace(file_path, pattern, replacement):
# Update CMakeLists.txt # Update CMakeLists.txt
replace('CMakeLists.txt', replace('CMakeLists.txt',
'''SET\\(CBOR_VERSION_MAJOR "0"\\) '''SET\\(CBOR_VERSION_MAJOR "\d+"\\)
SET\\(CBOR_VERSION_MINOR "7"\\) SET\\(CBOR_VERSION_MINOR "\d+"\\)
SET\\(CBOR_VERSION_PATCH "0"\\)''', SET\\(CBOR_VERSION_PATCH "\d+"\\)''',
f'''SET(CBOR_VERSION_MAJOR "{major}") f'''SET(CBOR_VERSION_MAJOR "{major}")
SET(CBOR_VERSION_MINOR "{minor}") SET(CBOR_VERSION_MINOR "{minor}")
SET(CBOR_VERSION_PATCH "{patch}")''') SET(CBOR_VERSION_PATCH "{patch}")''')
# Update Basel build example
replace('examples/bazel/third_party/libcbor/cbor/configuration.h',
'''#define CBOR_MAJOR_VERSION \d+
#define CBOR_MINOR_VERSION \d+
#define CBOR_PATCH_VERSION \d+''',
f'''#define CBOR_MAJOR_VERSION {major}
#define CBOR_MINOR_VERSION {minor}
#define CBOR_PATCH_VERSION {patch}''')
# Update Sphinx # Update Sphinx
replace('doc/source/conf.py', replace('doc/source/conf.py',
"""version = '.*' """version = '.*'

View File

@ -18,7 +18,7 @@
mkdir build mkdir build
cd build cd build
# We disable libcbor's default sanitizers since we'll be configuring them ourselves via CFLAGS. # We disable libcbor's default sanitizers since we'll be configuring them ourselves via CFLAGS.
cmake -D CMAKE_BUILD_TYPE=Debug -D CMAKE_INSTALL_PREFIX="$WORK" -D CBOR_CUSTOM_ALLOC=ON -D SANITIZE=OFF .. cmake -D CMAKE_BUILD_TYPE=Debug -D CMAKE_INSTALL_PREFIX="$WORK" -D SANITIZE=OFF ..
make "-j$(nproc)" make "-j$(nproc)"
make install make install

View File

@ -1,20 +1,68 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <unordered_map>
#include "cbor.h" #include "cbor.h"
static size_t allocated_mem = 0;
static std::unordered_map<void*, size_t> allocated_len_map;
static constexpr size_t kMemoryLimit = 1 << 30;
void *limited_malloc(size_t size) { void *limited_malloc(size_t size) {
if (size > 1 << 24) { if (size + allocated_mem > kMemoryLimit) {
return nullptr; return nullptr;
} }
return malloc(size); if (size == 0) {
return nullptr;
}
void* m = malloc(size);
if (m != nullptr) {
allocated_mem += size;
allocated_len_map[m] = size;
}
return m;
}
void limited_free(void *ptr) {
if (ptr != NULL && allocated_len_map.find(ptr) == allocated_len_map.end()) {
abort();
}
free(ptr);
if (ptr != NULL) {
allocated_mem -= allocated_len_map[ptr];
allocated_len_map.erase(ptr);
}
}
void *limited_realloc(void *ptr, size_t size) {
if (ptr != NULL && allocated_len_map.find(ptr) == allocated_len_map.end()) {
abort();
}
if (ptr == NULL) {
return limited_malloc(size);
}
long delta = (long) size - allocated_len_map[ptr];
if (delta + allocated_mem > kMemoryLimit) {
return nullptr;
}
void* new_ptr = realloc(ptr, size);
if (size > 0 && new_ptr == nullptr) {
return nullptr;
}
allocated_mem += delta;
allocated_len_map.erase(ptr);
if (size > 0) {
allocated_len_map[new_ptr] = size;
}
return new_ptr;
} }
struct State { struct State {
FILE* fout; FILE* fout;
State() : fout(fopen("/dev/null", "r")) { State() : fout(fopen("/dev/null", "r")) {
cbor_set_allocs(limited_malloc, realloc, free); cbor_set_allocs(limited_malloc, limited_realloc, limited_free);
} }
}; };

View File

@ -22,7 +22,7 @@ cd $DIR
python3 misc/update_version.py "$1" python3 misc/update_version.py "$1"
echo ">>>>> Checking changelog" echo ">>>>> Checking changelog"
grep -A 5 -F "$1" CHANGELOG.md || true grep -A 10 -F "$1" CHANGELOG.md || true
prompt "Is the changelog correct and complete?" prompt "Is the changelog correct and complete?"
echo ">>>>> Checking Doxyfile" echo ">>>>> Checking Doxyfile"
@ -33,6 +33,10 @@ echo ">>>>> Checking CMakeLists"
grep -A 2 'SET(CBOR_VERSION_MAJOR' CMakeLists.txt grep -A 2 'SET(CBOR_VERSION_MAJOR' CMakeLists.txt
prompt "Is the CMake version correct?" prompt "Is the CMake version correct?"
echo ">>>>> Checking Bazel build"
grep -A 2 'CBOR_MAJOR_VERSION' examples/bazel/third_party/libcbor/cbor/configuration.h
prompt "Is the version correct?"
echo ">>>>> Checking docs" echo ">>>>> Checking docs"
grep 'version =\|release =' doc/source/conf.py grep 'version =\|release =' doc/source/conf.py
prompt "Are the versions correct?" prompt "Are the versions correct?"
@ -61,7 +65,9 @@ ctest
popd popd
prompt "Will proceed to tag the release with $TAG_NAME." prompt "Will proceed to tag the release with $TAG_NAME."
git commit -a -m "Release $TAG_NAME"
git tag "$TAG_NAME" git tag "$TAG_NAME"
git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD)
git push --tags git push --tags
set +x set +x

View File

@ -1,12 +1,10 @@
set(SOURCES cbor.c cbor/streaming.c cbor/internal/encoders.c cbor/internal/builder_callbacks.c cbor/internal/loaders.c cbor/internal/memory_utils.c cbor/internal/stack.c cbor/internal/unicode.c cbor/encoding.c cbor/serialization.c cbor/arrays.c cbor/common.c cbor/floats_ctrls.c cbor/bytestrings.c cbor/callbacks.c cbor/strings.c cbor/maps.c cbor/tags.c cbor/ints.c) set(SOURCES cbor.c allocators.c cbor/streaming.c cbor/internal/encoders.c cbor/internal/builder_callbacks.c cbor/internal/loaders.c cbor/internal/memory_utils.c cbor/internal/stack.c cbor/internal/unicode.c cbor/encoding.c cbor/serialization.c cbor/arrays.c cbor/common.c cbor/floats_ctrls.c cbor/bytestrings.c cbor/callbacks.c cbor/strings.c cbor/maps.c cbor/tags.c cbor/ints.c)
include(GNUInstallDirs) include(GNUInstallDirs)
include(JoinPaths)
include(CheckFunctionExists)
set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_SKIP_BUILD_RPATH FALSE)
if (CBOR_CUSTOM_ALLOC)
LIST(APPEND SOURCES allocators.c)
endif(CBOR_CUSTOM_ALLOC)
if (NOT DEFINED CMAKE_MACOSX_RPATH) if (NOT DEFINED CMAKE_MACOSX_RPATH)
set(CMAKE_MACOSX_RPATH 0) set(CMAKE_MACOSX_RPATH 0)
endif() endif()
@ -14,6 +12,12 @@ endif()
add_library(cbor ${SOURCES}) add_library(cbor ${SOURCES})
target_include_directories(cbor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(cbor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# Explicitly link math.h if necessary
check_function_exists(ldexp LDEXP_AVAILABLE)
if (NOT LDEXP_AVAILABLE)
target_link_libraries(cbor m)
endif()
include(GenerateExportHeader) include(GenerateExportHeader)
generate_export_header(cbor EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/cbor/cbor_export.h) generate_export_header(cbor EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/cbor/cbor_export.h)
target_include_directories(cbor PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(cbor PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
@ -28,6 +32,8 @@ set_target_properties(cbor PROPERTIES
MACHO_COMPATIBILITY_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.0 MACHO_COMPATIBILITY_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.0
SOVERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}) SOVERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR})
join_paths(libdir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
configure_file(libcbor.pc.in libcbor.pc @ONLY) configure_file(libcbor.pc.in libcbor.pc @ONLY)
# http://www.cmake.org/Wiki/CMake:Install_Commands # http://www.cmake.org/Wiki/CMake:Install_Commands
@ -42,4 +48,4 @@ install(DIRECTORY cbor DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
install(FILES cbor.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES cbor.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libcbor.pc" install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libcbor.pc"
DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig") DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")

View File

@ -83,7 +83,7 @@ cbor_item_t *cbor_load(cbor_data source, size_t source_size,
goto error; goto error;
} }
case CBOR_DECODER_ERROR: case CBOR_DECODER_ERROR:
/* Reserved/malformated item */ /* Reserved/malformed item */
{ {
result->error.code = CBOR_ERR_MALFORMATED; result->error.code = CBOR_ERR_MALFORMATED;
goto error; goto error;
@ -100,9 +100,7 @@ cbor_item_t *cbor_load(cbor_data source, size_t source_size,
} }
} while (stack.size > 0); } while (stack.size > 0);
/* Move the result before free */ return context.root;
cbor_item_t *result_item = context.root;
return result_item;
error: error:
result->error.position = result->read; result->error.position = result->read;
@ -131,8 +129,6 @@ static cbor_item_t *_cbor_copy_int(cbor_item_t *item, bool negative) {
case CBOR_INT_64: case CBOR_INT_64:
res = cbor_build_uint64(cbor_get_uint64(item)); res = cbor_build_uint64(cbor_get_uint64(item));
break; break;
default:
return NULL;
} }
if (negative) cbor_mark_negint(res); if (negative) cbor_mark_negint(res);
@ -141,6 +137,7 @@ 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) { static cbor_item_t *_cbor_copy_float_ctrl(cbor_item_t *item) {
// cppcheck-suppress missingReturn
switch (cbor_float_get_width(item)) { switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0: case CBOR_FLOAT_0:
return cbor_build_ctrl(cbor_ctrl_value(item)); return cbor_build_ctrl(cbor_ctrl_value(item));
@ -151,11 +148,10 @@ static cbor_item_t *_cbor_copy_float_ctrl(cbor_item_t *item) {
case CBOR_FLOAT_64: case CBOR_FLOAT_64:
return cbor_build_float8(cbor_float_get_float8(item)); return cbor_build_float8(cbor_float_get_float8(item));
} }
return NULL;
} }
cbor_item_t *cbor_copy(cbor_item_t *item) { cbor_item_t *cbor_copy(cbor_item_t *item) {
// cppcheck-suppress missingReturn
switch (cbor_typeof(item)) { switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT: case CBOR_TYPE_UINT:
return _cbor_copy_int(item, false); return _cbor_copy_int(item, false);
@ -167,10 +163,24 @@ cbor_item_t *cbor_copy(cbor_item_t *item) {
cbor_bytestring_length(item)); cbor_bytestring_length(item));
} else { } else {
cbor_item_t *res = cbor_new_indefinite_bytestring(); cbor_item_t *res = cbor_new_indefinite_bytestring();
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) if (res == NULL) {
cbor_bytestring_add_chunk( return NULL;
res, }
cbor_move(cbor_copy(cbor_bytestring_chunks_handle(item)[i])));
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
cbor_item_t *chunk_copy =
cbor_copy(cbor_bytestring_chunks_handle(item)[i]);
if (chunk_copy == NULL) {
cbor_decref(&res);
return NULL;
}
if (!cbor_bytestring_add_chunk(res, chunk_copy)) {
cbor_decref(&chunk_copy);
cbor_decref(&res);
return NULL;
}
cbor_decref(&chunk_copy);
}
return res; return res;
} }
case CBOR_TYPE_STRING: case CBOR_TYPE_STRING:
@ -179,46 +189,100 @@ cbor_item_t *cbor_copy(cbor_item_t *item) {
cbor_string_length(item)); cbor_string_length(item));
} else { } else {
cbor_item_t *res = cbor_new_indefinite_string(); cbor_item_t *res = cbor_new_indefinite_string();
for (size_t i = 0; i < cbor_string_chunk_count(item); i++) if (res == NULL) {
cbor_string_add_chunk( return NULL;
res, cbor_move(cbor_copy(cbor_string_chunks_handle(item)[i]))); }
for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
cbor_item_t *chunk_copy =
cbor_copy(cbor_string_chunks_handle(item)[i]);
if (chunk_copy == NULL) {
cbor_decref(&res);
return NULL;
}
if (!cbor_string_add_chunk(res, chunk_copy)) {
cbor_decref(&chunk_copy);
cbor_decref(&res);
return NULL;
}
cbor_decref(&chunk_copy);
}
return res; return res;
} }
case CBOR_TYPE_ARRAY: { case CBOR_TYPE_ARRAY: {
cbor_item_t *res; cbor_item_t *res;
if (cbor_array_is_definite(item)) if (cbor_array_is_definite(item)) {
res = cbor_new_definite_array(cbor_array_size(item)); res = cbor_new_definite_array(cbor_array_size(item));
else } else {
res = cbor_new_indefinite_array(); res = cbor_new_indefinite_array();
}
if (res == NULL) {
return NULL;
}
for (size_t i = 0; i < cbor_array_size(item); i++) for (size_t i = 0; i < cbor_array_size(item); i++) {
cbor_array_push( cbor_item_t *entry_copy = cbor_copy(cbor_move(cbor_array_get(item, i)));
res, cbor_move(cbor_copy(cbor_move(cbor_array_get(item, i))))); if (entry_copy == NULL) {
cbor_decref(&res);
return NULL;
}
if (!cbor_array_push(res, entry_copy)) {
cbor_decref(&entry_copy);
cbor_decref(&res);
return NULL;
}
cbor_decref(&entry_copy);
}
return res; return res;
} }
case CBOR_TYPE_MAP: { case CBOR_TYPE_MAP: {
cbor_item_t *res; cbor_item_t *res;
if (cbor_map_is_definite(item)) if (cbor_map_is_definite(item)) {
res = cbor_new_definite_map(cbor_map_size(item)); res = cbor_new_definite_map(cbor_map_size(item));
else } else {
res = cbor_new_indefinite_map(); res = cbor_new_indefinite_map();
}
if (res == NULL) {
return NULL;
}
struct cbor_pair *it = cbor_map_handle(item); struct cbor_pair *it = cbor_map_handle(item);
for (size_t i = 0; i < cbor_map_size(item); i++) for (size_t i = 0; i < cbor_map_size(item); i++) {
cbor_map_add(res, (struct cbor_pair){ cbor_item_t *key_copy = cbor_copy(it[i].key);
.key = cbor_move(cbor_copy(it[i].key)), if (key_copy == NULL) {
.value = cbor_move(cbor_copy(it[i].value))}); cbor_decref(&res);
return NULL;
}
cbor_item_t *value_copy = cbor_copy(it[i].value);
if (value_copy == NULL) {
cbor_decref(&res);
cbor_decref(&key_copy);
return NULL;
}
if (!cbor_map_add(res, (struct cbor_pair){.key = key_copy,
.value = value_copy})) {
cbor_decref(&res);
cbor_decref(&key_copy);
cbor_decref(&value_copy);
return NULL;
}
cbor_decref(&key_copy);
cbor_decref(&value_copy);
}
return res; return res;
} }
case CBOR_TYPE_TAG: case CBOR_TYPE_TAG: {
return cbor_build_tag( cbor_item_t *item_copy = cbor_copy(cbor_move(cbor_tag_item(item)));
cbor_tag_value(item), if (item_copy == NULL) {
cbor_move(cbor_copy(cbor_move(cbor_tag_item(item))))); 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: case CBOR_TYPE_FLOAT_CTRL:
return _cbor_copy_float_ctrl(item); return _cbor_copy_float_ctrl(item);
} }
return NULL;
} }
#if CBOR_PRETTY_PRINTER #if CBOR_PRETTY_PRINTER
@ -245,13 +309,13 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item))); fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item)); fprintf(out, "Value: %" PRIu64 "\n", cbor_get_int(item));
break; break;
}; }
case CBOR_TYPE_NEGINT: { case CBOR_TYPE_NEGINT: {
fprintf(out, "%*s[CBOR_TYPE_NEGINT] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_NEGINT] ", indent, " ");
fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item))); fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
fprintf(out, "Value: -%" PRIu64 " -1\n", cbor_get_int(item)); fprintf(out, "Value: -%" PRIu64 " -1\n", cbor_get_int(item));
break; break;
}; }
case CBOR_TYPE_BYTESTRING: { case CBOR_TYPE_BYTESTRING: {
fprintf(out, "%*s[CBOR_TYPE_BYTESTRING] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_BYTESTRING] ", indent, " ");
if (cbor_bytestring_is_indefinite(item)) { if (cbor_bytestring_is_indefinite(item)) {
@ -264,7 +328,7 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item)); fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item));
} }
break; break;
}; }
case CBOR_TYPE_STRING: { case CBOR_TYPE_STRING: {
fprintf(out, "%*s[CBOR_TYPE_STRING] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_STRING] ", indent, " ");
if (cbor_string_is_indefinite(item)) { if (cbor_string_is_indefinite(item)) {
@ -285,7 +349,7 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
fprintf(out, "\n"); fprintf(out, "\n");
} }
break; break;
}; }
case CBOR_TYPE_ARRAY: { case CBOR_TYPE_ARRAY: {
fprintf(out, "%*s[CBOR_TYPE_ARRAY] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_ARRAY] ", indent, " ");
if (cbor_array_is_definite(item)) { if (cbor_array_is_definite(item)) {
@ -297,7 +361,7 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
for (size_t i = 0; i < cbor_array_size(item); i++) for (size_t i = 0; i < cbor_array_size(item); i++)
_cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4); _cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4);
break; break;
}; }
case CBOR_TYPE_MAP: { case CBOR_TYPE_MAP: {
fprintf(out, "%*s[CBOR_TYPE_MAP] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_MAP] ", indent, " ");
if (cbor_map_is_definite(item)) { if (cbor_map_is_definite(item)) {
@ -311,13 +375,13 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
_cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4); _cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4);
} }
break; break;
}; }
case CBOR_TYPE_TAG: { case CBOR_TYPE_TAG: {
fprintf(out, "%*s[CBOR_TYPE_TAG] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_TAG] ", indent, " ");
fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item)); fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
_cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4); _cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4);
break; break;
}; }
case CBOR_TYPE_FLOAT_CTRL: { case CBOR_TYPE_FLOAT_CTRL: {
fprintf(out, "%*s[CBOR_TYPE_FLOAT_CTRL] ", indent, " "); fprintf(out, "%*s[CBOR_TYPE_FLOAT_CTRL] ", indent, " ");
if (cbor_float_ctrl_is_ctrl(item)) { if (cbor_float_ctrl_is_ctrl(item)) {
@ -334,7 +398,7 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
fprintf(out, "value: %lf\n", cbor_float_get_float(item)); fprintf(out, "value: %lf\n", cbor_float_get_float(item));
} }
break; break;
}; }
} }
} }

View File

@ -39,21 +39,27 @@ extern "C" {
* *
* @param source The buffer * @param source The buffer
* @param source_size * @param source_size
* @param result[out] Result indicator. #CBOR_ERR_NONE on success * @param[out] result Result indicator. #CBOR_ERR_NONE on success
* @return **new** CBOR item or `NULL` on failure. In that case, \p result * @return Decoded CBOR item. The item's reference count is initialized to one.
* contains location and description of the error. * @return `NULL` on failure. In that case, \p result contains the location and
* description of the error.
*/ */
CBOR_EXPORT cbor_item_t* cbor_load(cbor_data source, size_t source_size, _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_load(
struct cbor_load_result* result); cbor_data source, size_t source_size, struct cbor_load_result* result);
/** Deep copy of an item /** Take a deep copy of an item
* *
* All the reference counts in the new structure are set to one. * All items this item points to (array and map members, string chunks, tagged
* items) will be copied recursively using #cbor_copy. The new item doesn't
* alias or point to any items from the original \p item. All the reference
* counts in the new structure are set to one.
* *
* @param item[borrow] item to copy * @param item item to copy
* @return **new** CBOR deep copy * @return Reference to the new item. The item's reference count is initialized
* to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t* cbor_copy(cbor_item_t* item); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_copy(cbor_item_t* item);
#if CBOR_PRETTY_PRINTER #if CBOR_PRETTY_PRINTER
#include <stdio.h> #include <stdio.h>

View File

@ -10,12 +10,12 @@
#include "internal/memory_utils.h" #include "internal/memory_utils.h"
size_t cbor_array_size(const cbor_item_t *item) { size_t cbor_array_size(const cbor_item_t *item) {
assert(cbor_isa_array(item)); CBOR_ASSERT(cbor_isa_array(item));
return item->metadata.array_metadata.end_ptr; return item->metadata.array_metadata.end_ptr;
} }
size_t cbor_array_allocated(const cbor_item_t *item) { size_t cbor_array_allocated(const cbor_item_t *item) {
assert(cbor_isa_array(item)); CBOR_ASSERT(cbor_isa_array(item));
return item->metadata.array_metadata.allocated; return item->metadata.array_metadata.allocated;
} }
@ -31,9 +31,6 @@ bool cbor_array_set(cbor_item_t *item, size_t index, cbor_item_t *value) {
} else { } else {
return false; return false;
} }
// TODO: This is unreachable and the index checking logic above seems
// suspicious -- out of bounds index is a caller error. Figure out & fix.
return true;
} }
bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value) { bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value) {
@ -45,7 +42,7 @@ bool cbor_array_replace(cbor_item_t *item, size_t index, cbor_item_t *value) {
} }
bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) { bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) {
assert(cbor_isa_array(array)); CBOR_ASSERT(cbor_isa_array(array));
struct _cbor_array_metadata *metadata = struct _cbor_array_metadata *metadata =
(struct _cbor_array_metadata *)&array->metadata; (struct _cbor_array_metadata *)&array->metadata;
cbor_item_t **data = (cbor_item_t **)array->data; cbor_item_t **data = (cbor_item_t **)array->data;
@ -59,7 +56,6 @@ bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) {
/* Exponential realloc */ /* Exponential realloc */
if (metadata->end_ptr >= metadata->allocated) { if (metadata->end_ptr >= metadata->allocated) {
// Check for overflows first // Check for overflows first
// TODO: Explicitly test this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) { if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
return false; return false;
} }
@ -84,22 +80,22 @@ bool cbor_array_push(cbor_item_t *array, cbor_item_t *pushee) {
} }
bool cbor_array_is_definite(const cbor_item_t *item) { bool cbor_array_is_definite(const cbor_item_t *item) {
assert(cbor_isa_array(item)); CBOR_ASSERT(cbor_isa_array(item));
return item->metadata.array_metadata.type == _CBOR_METADATA_DEFINITE; return item->metadata.array_metadata.type == _CBOR_METADATA_DEFINITE;
} }
bool cbor_array_is_indefinite(const cbor_item_t *item) { bool cbor_array_is_indefinite(const cbor_item_t *item) {
assert(cbor_isa_array(item)); CBOR_ASSERT(cbor_isa_array(item));
return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE; return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE;
} }
cbor_item_t **cbor_array_handle(const cbor_item_t *item) { cbor_item_t **cbor_array_handle(const cbor_item_t *item) {
assert(cbor_isa_array(item)); CBOR_ASSERT(cbor_isa_array(item));
return (cbor_item_t **)item->data; return (cbor_item_t **)item->data;
} }
cbor_item_t *cbor_new_definite_array(size_t size) { cbor_item_t *cbor_new_definite_array(size_t size) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
cbor_item_t **data = _cbor_alloc_multiple(sizeof(cbor_item_t *), size); cbor_item_t **data = _cbor_alloc_multiple(sizeof(cbor_item_t *), size);
_CBOR_DEPENDENT_NOTNULL(item, data); _CBOR_DEPENDENT_NOTNULL(item, data);
@ -119,8 +115,8 @@ cbor_item_t *cbor_new_definite_array(size_t size) {
return item; return item;
} }
cbor_item_t *cbor_new_indefinite_array() { cbor_item_t *cbor_new_indefinite_array(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){

View File

@ -17,62 +17,74 @@ extern "C" {
/** Get the number of members /** Get the number of members
* *
* @param item[borrow] An array * @param item An array
* @return The number of members * @return The number of members
*/ */
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_array_size(const cbor_item_t* item); CBOR_EXPORT size_t cbor_array_size(const cbor_item_t* item);
/** Get the size of the allocated storage /** Get the size of the allocated storage
* *
* @param item[borrow] An array * @param item An array
* @return The size of the allocated storage (number of items) * @return The size of the allocated storage (number of items)
*/ */
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_array_allocated(const cbor_item_t* item); CBOR_EXPORT size_t cbor_array_allocated(const cbor_item_t* item);
/** Get item by index /** Get item by index
* *
* @param item[borrow] An array * @param item An array
* @param index The index * @param index The index (zero-based)
* @return **incref** The item, or `NULL` in case of boundary violation * @return Reference to the item, or `NULL` in case of boundary violation.
*
* Increases the reference count of the underlying item. The returned reference
* must be released using #cbor_decref.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t* cbor_array_get(const cbor_item_t* item, size_t index); CBOR_EXPORT cbor_item_t* cbor_array_get(const cbor_item_t* item, size_t index);
/** Set item by index /** Set item by index
* *
* Creating arrays with holes is not possible * If the index is out of bounds, the array is not modified and false is
* returned. Creating arrays with holes is not possible.
* *
* @param item[borrow] An array * @param item An array
* @param value[incref] The item to assign * @param value The item to assign
* @param index The index, first item is 0. * @param index The index (zero-based)
* @return true on success, false on allocation failure. * @return `true` on success, `false` on allocation failure.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_set(cbor_item_t* item, size_t index, CBOR_EXPORT bool cbor_array_set(cbor_item_t* item, size_t index,
cbor_item_t* value); cbor_item_t* value);
/** Replace item at an index /** Replace item at an index
* *
* The item being replace will be #cbor_decref 'ed. * The reference to the item being replaced will be released using #cbor_decref.
* *
* @param item[borrow] An array * @param item An array
* @param value[incref] The item to assign * @param value The item to assign. Its reference count will be increased by
* @param index The index, first item is 0. * one.
* @param index The index (zero-based)
* @return true on success, false on allocation failure. * @return true on success, false on allocation failure.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_replace(cbor_item_t* item, size_t index, CBOR_EXPORT bool cbor_array_replace(cbor_item_t* item, size_t index,
cbor_item_t* value); cbor_item_t* value);
/** Is the array definite? /** Is the array definite?
* *
* @param item[borrow] An array * @param item An array
* @return Is the array definite? * @return Is the array definite?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_is_definite(const cbor_item_t* item); CBOR_EXPORT bool cbor_array_is_definite(const cbor_item_t* item);
/** Is the array indefinite? /** Is the array indefinite?
* *
* @param item[borrow] An array * @param item An array
* @return Is the array indefinite? * @return Is the array indefinite?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_is_indefinite(const cbor_item_t* item); CBOR_EXPORT bool cbor_array_is_indefinite(const cbor_item_t* item);
/** Get the array contents /** Get the array contents
@ -80,33 +92,42 @@ CBOR_EXPORT bool cbor_array_is_indefinite(const cbor_item_t* item);
* The items may be reordered and modified as long as references remain * The items may be reordered and modified as long as references remain
* consistent. * consistent.
* *
* @param item[borrow] An array * @param item An array item
* @return #cbor_array_size items * @return An array of #cbor_item_t pointers of size #cbor_array_size.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t** cbor_array_handle(const cbor_item_t* item); CBOR_EXPORT cbor_item_t** cbor_array_handle(const cbor_item_t* item);
/** Create new definite array /** Create new definite array
* *
* @param size Number of slots to preallocate * @param size Number of slots to preallocate
* @return **new** array or `NULL` upon malloc failure * @return Reference to the new array item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t* cbor_new_definite_array(size_t size); CBOR_EXPORT cbor_item_t* cbor_new_definite_array(size_t size);
/** Create new indefinite array /** Create new indefinite array
* *
* @return **new** array or `NULL` upon malloc failure * @return Reference to the new array item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t* cbor_new_indefinite_array(); _CBOR_NODISCARD
CBOR_EXPORT cbor_item_t* cbor_new_indefinite_array(void);
/** Append to the end /** Append to the end
* *
* For indefinite items, storage may be realloacted. For definite items, only * For indefinite items, storage may be reallocated. For definite items, only
* the preallocated capacity is available. * the preallocated capacity is available.
* *
* @param array[borrow] An array * @param array An array
* @param pushee[incref] The item to push * @param pushee The item to push. Its reference count will be increased by
* @return true on success, false on failure * one.
* @return `true` on success, `false` on failure
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_push(cbor_item_t* array, cbor_item_t* pushee); CBOR_EXPORT bool cbor_array_push(cbor_item_t* array, cbor_item_t* pushee);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -10,17 +10,17 @@
#include "internal/memory_utils.h" #include "internal/memory_utils.h"
size_t cbor_bytestring_length(const cbor_item_t *item) { size_t cbor_bytestring_length(const cbor_item_t *item) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
return item->metadata.bytestring_metadata.length; return item->metadata.bytestring_metadata.length;
} }
unsigned char *cbor_bytestring_handle(const cbor_item_t *item) { unsigned char *cbor_bytestring_handle(const cbor_item_t *item) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
return item->data; return item->data;
} }
bool cbor_bytestring_is_definite(const cbor_item_t *item) { bool cbor_bytestring_is_definite(const cbor_item_t *item) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
return item->metadata.bytestring_metadata.type == _CBOR_METADATA_DEFINITE; return item->metadata.bytestring_metadata.type == _CBOR_METADATA_DEFINITE;
} }
@ -28,25 +28,26 @@ bool cbor_bytestring_is_indefinite(const cbor_item_t *item) {
return !cbor_bytestring_is_definite(item); return !cbor_bytestring_is_definite(item);
} }
cbor_item_t *cbor_new_definite_bytestring() { cbor_item_t *cbor_new_definite_bytestring(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
.refcount = 1, .refcount = 1,
.type = CBOR_TYPE_BYTESTRING, .type = CBOR_TYPE_BYTESTRING,
.metadata = {.bytestring_metadata = {_CBOR_METADATA_DEFINITE, 0}}}; .metadata = {.bytestring_metadata = {.type = _CBOR_METADATA_DEFINITE,
.length = 0}}};
return item; return item;
} }
cbor_item_t *cbor_new_indefinite_bytestring() { cbor_item_t *cbor_new_indefinite_bytestring(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
.refcount = 1, .refcount = 1,
.type = CBOR_TYPE_BYTESTRING, .type = CBOR_TYPE_BYTESTRING,
.metadata = {.bytestring_metadata = {.type = _CBOR_METADATA_INDEFINITE, .metadata = {.bytestring_metadata = {.type = _CBOR_METADATA_INDEFINITE,
.length = 0}}, .length = 0}},
.data = _CBOR_MALLOC(sizeof(struct cbor_indefinite_string_data))}; .data = _cbor_malloc(sizeof(struct cbor_indefinite_string_data))};
_CBOR_DEPENDENT_NOTNULL(item, item->data); _CBOR_DEPENDENT_NOTNULL(item, item->data);
*((struct cbor_indefinite_string_data *)item->data) = *((struct cbor_indefinite_string_data *)item->data) =
(struct cbor_indefinite_string_data){ (struct cbor_indefinite_string_data){
@ -60,7 +61,7 @@ cbor_item_t *cbor_new_indefinite_bytestring() {
cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length) { cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length) {
cbor_item_t *item = cbor_new_definite_bytestring(); cbor_item_t *item = cbor_new_definite_bytestring();
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
void *content = _CBOR_MALLOC(length); void *content = _cbor_malloc(length);
_CBOR_DEPENDENT_NOTNULL(item, content); _CBOR_DEPENDENT_NOTNULL(item, content);
memcpy(content, handle, length); memcpy(content, handle, length);
cbor_bytestring_set_handle(item, content, length); cbor_bytestring_set_handle(item, content, length);
@ -70,31 +71,32 @@ cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length) {
void cbor_bytestring_set_handle(cbor_item_t *item, void cbor_bytestring_set_handle(cbor_item_t *item,
cbor_mutable_data CBOR_RESTRICT_POINTER data, cbor_mutable_data CBOR_RESTRICT_POINTER data,
size_t length) { size_t length) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
assert(cbor_bytestring_is_definite(item)); CBOR_ASSERT(cbor_bytestring_is_definite(item));
item->data = data; item->data = data;
item->metadata.bytestring_metadata.length = length; item->metadata.bytestring_metadata.length = length;
} }
cbor_item_t **cbor_bytestring_chunks_handle(const cbor_item_t *item) { cbor_item_t **cbor_bytestring_chunks_handle(const cbor_item_t *item) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
assert(cbor_bytestring_is_indefinite(item)); CBOR_ASSERT(cbor_bytestring_is_indefinite(item));
return ((struct cbor_indefinite_string_data *)item->data)->chunks; return ((struct cbor_indefinite_string_data *)item->data)->chunks;
} }
size_t cbor_bytestring_chunk_count(const cbor_item_t *item) { size_t cbor_bytestring_chunk_count(const cbor_item_t *item) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
assert(cbor_bytestring_is_indefinite(item)); CBOR_ASSERT(cbor_bytestring_is_indefinite(item));
return ((struct cbor_indefinite_string_data *)item->data)->chunk_count; return ((struct cbor_indefinite_string_data *)item->data)->chunk_count;
} }
bool cbor_bytestring_add_chunk(cbor_item_t *item, cbor_item_t *chunk) { bool cbor_bytestring_add_chunk(cbor_item_t *item, cbor_item_t *chunk) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
assert(cbor_bytestring_is_indefinite(item)); CBOR_ASSERT(cbor_bytestring_is_indefinite(item));
CBOR_ASSERT(cbor_isa_bytestring(chunk));
CBOR_ASSERT(cbor_bytestring_is_definite(chunk));
struct cbor_indefinite_string_data *data = struct cbor_indefinite_string_data *data =
(struct cbor_indefinite_string_data *)item->data; (struct cbor_indefinite_string_data *)item->data;
if (data->chunk_count == data->chunk_capacity) { if (data->chunk_count == data->chunk_capacity) {
// TODO: Add a test for this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) { if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) {
return false; return false;
} }

View File

@ -25,23 +25,26 @@ extern "C" {
* *
* For definite byte strings only * For definite byte strings only
* *
* @param item[borrow] a definite bytestring * @param item a definite bytestring
* @return length of the binary data. Zero if no chunk has been attached yet * @return length of the binary data. Zero if no chunk has been attached yet
*/ */
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_bytestring_length(const cbor_item_t *item); CBOR_EXPORT size_t cbor_bytestring_length(const cbor_item_t *item);
/** Is the byte string definite? /** Is the byte string definite?
* *
* @param item[borrow] a byte string * @param item a byte string
* @return Is the byte string definite? * @return Is the byte string definite?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_bytestring_is_definite(const cbor_item_t *item); CBOR_EXPORT bool cbor_bytestring_is_definite(const cbor_item_t *item);
/** Is the byte string indefinite? /** Is the byte string indefinite?
* *
* @param item[borrow] a byte string * @param item a byte string
* @return Is the byte string indefinite? * @return Is the byte string indefinite?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_bytestring_is_indefinite(const cbor_item_t *item); CBOR_EXPORT bool cbor_bytestring_is_indefinite(const cbor_item_t *item);
/** Get the handle to the binary data /** Get the handle to the binary data
@ -49,17 +52,20 @@ CBOR_EXPORT bool cbor_bytestring_is_indefinite(const cbor_item_t *item);
* Definite items only. Modifying the data is allowed. In that case, the caller * Definite items only. Modifying the data is allowed. In that case, the caller
* takes responsibility for the effect on items this item might be a part of * takes responsibility for the effect on items this item might be a part of
* *
* @param item[borrow] A definite byte string * @param item A definite byte string
* @return The address of the binary data. `NULL` if no data have been assigned * @return The address of the underlying binary data
* @return `NULL` if no data have been assigned
* yet. * yet.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_mutable_data cbor_bytestring_handle(const cbor_item_t *item); CBOR_EXPORT cbor_mutable_data cbor_bytestring_handle(const cbor_item_t *item);
/** Set the handle to the binary data /** Set the handle to the binary data
* *
* @param item[borrow] A definite byte string * @param item A definite byte string
* @param data The memory block. The caller gives up the ownership of the block. * @param data The memory block. The caller gives up the ownership of the block.
* libcbor will deallocate it when appropriate using its free function * libcbor will deallocate it when appropriate using the `free` implementation
* configured using #cbor_set_allocs
* @param length Length of the data block * @param length Length of the data block
*/ */
CBOR_EXPORT void cbor_bytestring_set_handle( CBOR_EXPORT void cbor_bytestring_set_handle(
@ -71,17 +77,19 @@ CBOR_EXPORT void cbor_bytestring_set_handle(
* Manipulations with the memory block (e.g. sorting it) are allowed, but the * Manipulations with the memory block (e.g. sorting it) are allowed, but the
* validity and the number of chunks must be retained. * validity and the number of chunks must be retained.
* *
* @param item[borrow] A indefinite byte string * @param item A indefinite byte string
* @return array of #cbor_bytestring_chunk_count definite bytestrings * @return array of #cbor_bytestring_chunk_count definite bytestrings
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t **cbor_bytestring_chunks_handle( CBOR_EXPORT cbor_item_t **cbor_bytestring_chunks_handle(
const cbor_item_t *item); const cbor_item_t *item);
/** Get the number of chunks this string consist of /** Get the number of chunks this string consist of
* *
* @param item[borrow] A indefinite bytestring * @param item A indefinite bytestring
* @return The chunk count. 0 for freshly created items. * @return The chunk count. 0 for freshly created items.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_bytestring_chunk_count(const cbor_item_t *item); CBOR_EXPORT size_t cbor_bytestring_chunk_count(const cbor_item_t *item);
/** Appends a chunk to the bytestring /** Appends a chunk to the bytestring
@ -90,11 +98,13 @@ CBOR_EXPORT size_t cbor_bytestring_chunk_count(const cbor_item_t *item);
* *
* May realloc the chunk storage. * May realloc the chunk storage.
* *
* @param item[borrow] An indefinite byte string * @param item An indefinite byte string
* @param item[incref] A definite byte string * @param chunk A definite byte string. Its reference count will be be increased
* by one.
* @return true on success, false on realloc failure. In that case, the refcount * @return true on success, false on realloc failure. In that case, the refcount
* of `chunk` is not increased and the `item` is left intact. * of `chunk` is not increased and the `item` is left intact.
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_bytestring_add_chunk(cbor_item_t *item, CBOR_EXPORT bool cbor_bytestring_add_chunk(cbor_item_t *item,
cbor_item_t *chunk); cbor_item_t *chunk);
@ -102,17 +112,23 @@ CBOR_EXPORT bool cbor_bytestring_add_chunk(cbor_item_t *item,
* *
* The handle is initialized to `NULL` and length to 0 * The handle is initialized to `NULL` and length to 0
* *
* @return **new** definite bytestring. `NULL` on malloc failure. * @return Reference to the new bytestring item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_definite_bytestring(); _CBOR_NODISCARD
CBOR_EXPORT cbor_item_t *cbor_new_definite_bytestring(void);
/** Creates a new indefinite byte string /** Creates a new indefinite byte string
* *
* The chunks array is initialized to `NULL` and chunkcount to 0 * The chunks array is initialized to `NULL` and chunk count to 0
* *
* @return **new** indefinite bytestring. `NULL` on malloc failure. * @return Reference to the new bytestring item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_indefinite_bytestring(); _CBOR_NODISCARD
CBOR_EXPORT cbor_item_t *cbor_new_indefinite_bytestring(void);
/** Creates a new byte string and initializes it /** Creates a new byte string and initializes it
* *
@ -120,9 +136,11 @@ CBOR_EXPORT cbor_item_t *cbor_new_indefinite_bytestring();
* *
* @param handle Block of binary data * @param handle Block of binary data
* @param length Length of `data` * @param length Length of `data`
* @return A **new** byte string with content `handle`. `NULL` on malloc * @return Reference to the new bytestring item. The item's reference count is
* failure. * initialized to one.
* @return `NULL` if memory allocation fails
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length); CBOR_EXPORT cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -7,110 +7,115 @@
#include "callbacks.h" #include "callbacks.h"
#define CBOR_DUMMY_CALLBACK \ void cbor_null_uint8_callback(void *_CBOR_UNUSED(_ctx),
{} uint8_t _CBOR_UNUSED(_val)) {}
void cbor_null_uint8_callback(void *_ctx, uint8_t _val) CBOR_DUMMY_CALLBACK void cbor_null_uint16_callback(void *_CBOR_UNUSED(_ctx),
uint16_t _CBOR_UNUSED(_val)) {}
void cbor_null_uint16_callback(void *_ctx, void cbor_null_uint32_callback(void *_CBOR_UNUSED(_ctx),
uint16_t _val) CBOR_DUMMY_CALLBACK uint32_t _CBOR_UNUSED(_val)) {}
void cbor_null_uint32_callback(void *_ctx, void cbor_null_uint64_callback(void *_CBOR_UNUSED(_ctx),
uint32_t _val) CBOR_DUMMY_CALLBACK uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_uint64_callback(void *_ctx, void cbor_null_negint8_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _val) CBOR_DUMMY_CALLBACK uint8_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint8_callback(void *_ctx, void cbor_null_negint16_callback(void *_CBOR_UNUSED(_ctx),
uint8_t _val) CBOR_DUMMY_CALLBACK uint16_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint16_callback(void *_ctx, void cbor_null_negint32_callback(void *_CBOR_UNUSED(_ctx),
uint16_t _val) CBOR_DUMMY_CALLBACK uint32_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint32_callback(void *_ctx, void cbor_null_negint64_callback(void *_CBOR_UNUSED(_ctx),
uint32_t _val) CBOR_DUMMY_CALLBACK uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint64_callback(void *_ctx, void cbor_null_string_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _val) CBOR_DUMMY_CALLBACK cbor_data _CBOR_UNUSED(_val),
uint64_t _CBOR_UNUSED(_val2)) {}
void cbor_null_string_callback(void *_ctx, cbor_data _val, void cbor_null_string_start_callback(void *_CBOR_UNUSED(_ctx)) {}
size_t _val2) CBOR_DUMMY_CALLBACK
void cbor_null_string_start_callback(void *_ctx) CBOR_DUMMY_CALLBACK void cbor_null_byte_string_callback(void *_CBOR_UNUSED(_ctx),
cbor_data _CBOR_UNUSED(_val),
uint64_t _CBOR_UNUSED(_val2)) {}
void cbor_null_byte_string_callback(void *_ctx, cbor_data _val, void cbor_null_byte_string_start_callback(void *_CBOR_UNUSED(_ctx)) {}
size_t _val2) CBOR_DUMMY_CALLBACK
void cbor_null_byte_string_start_callback(void *_ctx) CBOR_DUMMY_CALLBACK void cbor_null_array_start_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_array_start_callback(void *_ctx, void cbor_null_indef_array_start_callback(void *_CBOR_UNUSED(_ctx)) {}
size_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_indef_array_start_callback(void *_ctx) CBOR_DUMMY_CALLBACK void cbor_null_map_start_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_map_start_callback(void *_ctx, void cbor_null_indef_map_start_callback(void *_CBOR_UNUSED(_ctx)) {}
size_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_indef_map_start_callback(void *_ctx) CBOR_DUMMY_CALLBACK void cbor_null_tag_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_tag_callback(void *_ctx, uint64_t _val) CBOR_DUMMY_CALLBACK void cbor_null_float2_callback(void *_CBOR_UNUSED(_ctx),
float _CBOR_UNUSED(_val)) {}
void cbor_null_float2_callback(void *_ctx, float _val) CBOR_DUMMY_CALLBACK void cbor_null_float4_callback(void *_CBOR_UNUSED(_ctx),
float _CBOR_UNUSED(_val)) {}
void cbor_null_float4_callback(void *_ctx, float _val) CBOR_DUMMY_CALLBACK void cbor_null_float8_callback(void *_CBOR_UNUSED(_ctx),
double _CBOR_UNUSED(_val)) {}
void cbor_null_float8_callback(void *_ctx, double _val) CBOR_DUMMY_CALLBACK void cbor_null_null_callback(void *_CBOR_UNUSED(_ctx)) {}
void cbor_null_null_callback(void *_ctx) CBOR_DUMMY_CALLBACK void cbor_null_undefined_callback(void *_CBOR_UNUSED(_ctx)) {}
void cbor_null_undefined_callback(void *_ctx) CBOR_DUMMY_CALLBACK void cbor_null_boolean_callback(void *_CBOR_UNUSED(_ctx),
bool _CBOR_UNUSED(_val)) {}
void cbor_null_boolean_callback(void *_ctx, bool _val) CBOR_DUMMY_CALLBACK void cbor_null_indef_break_callback(void *_CBOR_UNUSED(_ctx)) {}
void cbor_null_indef_break_callback(void *_ctx) CBOR_DUMMY_CALLBACK CBOR_EXPORT const struct cbor_callbacks cbor_empty_callbacks = {
/* Type 0 - Unsigned integers */
.uint8 = cbor_null_uint8_callback,
.uint16 = cbor_null_uint16_callback,
.uint32 = cbor_null_uint32_callback,
.uint64 = cbor_null_uint64_callback,
CBOR_EXPORT const struct cbor_callbacks cbor_empty_callbacks = { /* Type 1 - Negative integers */
/* Type 0 - Unsigned integers */ .negint8 = cbor_null_negint8_callback,
.uint8 = cbor_null_uint8_callback, .negint16 = cbor_null_negint16_callback,
.uint16 = cbor_null_uint16_callback, .negint32 = cbor_null_negint32_callback,
.uint32 = cbor_null_uint32_callback, .negint64 = cbor_null_negint64_callback,
.uint64 = cbor_null_uint64_callback,
/* Type 1 - Negative integers */ /* Type 2 - Byte strings */
.negint8 = cbor_null_negint8_callback, .byte_string_start = cbor_null_byte_string_start_callback,
.negint16 = cbor_null_negint16_callback, .byte_string = cbor_null_byte_string_callback,
.negint32 = cbor_null_negint32_callback,
.negint64 = cbor_null_negint64_callback,
/* Type 2 - Byte strings */ /* Type 3 - Strings */
.byte_string_start = cbor_null_byte_string_start_callback, .string_start = cbor_null_string_start_callback,
.byte_string = cbor_null_byte_string_callback, .string = cbor_null_string_callback,
/* Type 3 - Strings */ /* Type 4 - Arrays */
.string_start = cbor_null_string_start_callback, .indef_array_start = cbor_null_indef_array_start_callback,
.string = cbor_null_string_callback, .array_start = cbor_null_array_start_callback,
/* Type 4 - Arrays */ /* Type 5 - Maps */
.indef_array_start = cbor_null_indef_array_start_callback, .indef_map_start = cbor_null_indef_map_start_callback,
.array_start = cbor_null_array_start_callback, .map_start = cbor_null_map_start_callback,
/* Type 5 - Maps */ /* Type 6 - Tags */
.indef_map_start = cbor_null_indef_map_start_callback, .tag = cbor_null_tag_callback,
.map_start = cbor_null_map_start_callback,
/* Type 6 - Tags */ /* Type 7 - Floats & misc */
.tag = cbor_null_tag_callback, /* Type names cannot be member names */
.float2 = cbor_null_float2_callback,
/* 2B float is not supported in standard C */
.float4 = cbor_null_float4_callback,
.float8 = cbor_null_float8_callback,
.undefined = cbor_null_undefined_callback,
.null = cbor_null_null_callback,
.boolean = cbor_null_boolean_callback,
/* Type 7 - Floats & misc */ /* Shared indefinites */
/* Type names cannot be member names */ .indef_break = cbor_null_indef_break_callback,
.float2 = cbor_null_float2_callback,
/* 2B float is not supported in standard C */
.float4 = cbor_null_float4_callback,
.float8 = cbor_null_float8_callback,
.undefined = cbor_null_undefined_callback,
.null = cbor_null_null_callback,
.boolean = cbor_null_boolean_callback,
/* Shared indefinites */
.indef_break = cbor_null_indef_break_callback,
}; };

View File

@ -8,6 +8,8 @@
#ifndef LIBCBOR_CALLBACKS_H #ifndef LIBCBOR_CALLBACKS_H
#define LIBCBOR_CALLBACKS_H #define LIBCBOR_CALLBACKS_H
#include <stdint.h>
#include "cbor/cbor_export.h" #include "cbor/cbor_export.h"
#include "cbor/common.h" #include "cbor/common.h"
@ -31,10 +33,10 @@ typedef void (*cbor_int64_callback)(void *, uint64_t);
typedef void (*cbor_simple_callback)(void *); typedef void (*cbor_simple_callback)(void *);
/** Callback prototype */ /** Callback prototype */
typedef void (*cbor_string_callback)(void *, cbor_data, size_t); typedef void (*cbor_string_callback)(void *, cbor_data, uint64_t);
/** Callback prototype */ /** Callback prototype */
typedef void (*cbor_collection_callback)(void *, size_t); typedef void (*cbor_collection_callback)(void *, uint64_t);
/** Callback prototype */ /** Callback prototype */
typedef void (*cbor_float_callback)(void *, float); typedef void (*cbor_float_callback)(void *, float);
@ -130,25 +132,25 @@ CBOR_EXPORT void cbor_null_negint32_callback(void *, uint32_t);
CBOR_EXPORT void cbor_null_negint64_callback(void *, uint64_t); CBOR_EXPORT void cbor_null_negint64_callback(void *, uint64_t);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_string_callback(void *, cbor_data, size_t); CBOR_EXPORT void cbor_null_string_callback(void *, cbor_data, uint64_t);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_string_start_callback(void *); CBOR_EXPORT void cbor_null_string_start_callback(void *);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_byte_string_callback(void *, cbor_data, size_t); CBOR_EXPORT void cbor_null_byte_string_callback(void *, cbor_data, uint64_t);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_byte_string_start_callback(void *); CBOR_EXPORT void cbor_null_byte_string_start_callback(void *);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_array_start_callback(void *, size_t); CBOR_EXPORT void cbor_null_array_start_callback(void *, uint64_t);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_indef_array_start_callback(void *); CBOR_EXPORT void cbor_null_indef_array_start_callback(void *);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_map_start_callback(void *, size_t); CBOR_EXPORT void cbor_null_map_start_callback(void *, uint64_t);
/** Dummy callback implementation - does nothing */ /** Dummy callback implementation - does nothing */
CBOR_EXPORT void cbor_null_indef_map_start_callback(void *); CBOR_EXPORT void cbor_null_indef_map_start_callback(void *);

View File

@ -15,6 +15,10 @@
#include "strings.h" #include "strings.h"
#include "tags.h" #include "tags.h"
#ifdef DEBUG
bool _cbor_enable_assert = true;
#endif
bool cbor_isa_uint(const cbor_item_t *item) { bool cbor_isa_uint(const cbor_item_t *item) {
return item->type == CBOR_TYPE_UINT; return item->type == CBOR_TYPE_UINT;
} }
@ -78,7 +82,7 @@ cbor_item_t *cbor_incref(cbor_item_t *item) {
void cbor_decref(cbor_item_t **item_ref) { void cbor_decref(cbor_item_t **item_ref) {
cbor_item_t *item = *item_ref; cbor_item_t *item = *item_ref;
assert(item->refcount > 0); CBOR_ASSERT(item->refcount > 0);
if (--item->refcount == 0) { if (--item->refcount == 0) {
switch (item->type) { switch (item->type) {
case CBOR_TYPE_UINT: case CBOR_TYPE_UINT:
@ -88,29 +92,29 @@ void cbor_decref(cbor_item_t **item_ref) {
{ break; } { break; }
case CBOR_TYPE_BYTESTRING: { case CBOR_TYPE_BYTESTRING: {
if (cbor_bytestring_is_definite(item)) { if (cbor_bytestring_is_definite(item)) {
_CBOR_FREE(item->data); _cbor_free(item->data);
} else { } else {
/* We need to decref all chunks */ /* We need to decref all chunks */
cbor_item_t **handle = cbor_bytestring_chunks_handle(item); cbor_item_t **handle = cbor_bytestring_chunks_handle(item);
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
cbor_decref(&handle[i]); cbor_decref(&handle[i]);
_CBOR_FREE( _cbor_free(
((struct cbor_indefinite_string_data *)item->data)->chunks); ((struct cbor_indefinite_string_data *)item->data)->chunks);
_CBOR_FREE(item->data); _cbor_free(item->data);
} }
break; break;
} }
case CBOR_TYPE_STRING: { case CBOR_TYPE_STRING: {
if (cbor_string_is_definite(item)) { if (cbor_string_is_definite(item)) {
_CBOR_FREE(item->data); _cbor_free(item->data);
} else { } else {
/* We need to decref all chunks */ /* We need to decref all chunks */
cbor_item_t **handle = cbor_string_chunks_handle(item); cbor_item_t **handle = cbor_string_chunks_handle(item);
for (size_t i = 0; i < cbor_string_chunk_count(item); i++) for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
cbor_decref(&handle[i]); cbor_decref(&handle[i]);
_CBOR_FREE( _cbor_free(
((struct cbor_indefinite_string_data *)item->data)->chunks); ((struct cbor_indefinite_string_data *)item->data)->chunks);
_CBOR_FREE(item->data); _cbor_free(item->data);
} }
break; break;
} }
@ -120,7 +124,7 @@ void cbor_decref(cbor_item_t **item_ref) {
size_t size = cbor_array_size(item); size_t size = cbor_array_size(item);
for (size_t i = 0; i < size; i++) for (size_t i = 0; i < size; i++)
if (handle[i] != NULL) cbor_decref(&handle[i]); if (handle[i] != NULL) cbor_decref(&handle[i]);
_CBOR_FREE(item->data); _cbor_free(item->data);
break; break;
} }
case CBOR_TYPE_MAP: { case CBOR_TYPE_MAP: {
@ -130,13 +134,13 @@ void cbor_decref(cbor_item_t **item_ref) {
cbor_decref(&handle->key); cbor_decref(&handle->key);
if (handle->value != NULL) cbor_decref(&handle->value); if (handle->value != NULL) cbor_decref(&handle->value);
} }
_CBOR_FREE(item->data); _cbor_free(item->data);
break; break;
}; }
case CBOR_TYPE_TAG: { case CBOR_TYPE_TAG: {
if (item->metadata.tag_metadata.tagged_item != NULL) if (item->metadata.tag_metadata.tagged_item != NULL)
cbor_decref(&item->metadata.tag_metadata.tagged_item); cbor_decref(&item->metadata.tag_metadata.tagged_item);
_CBOR_FREE(item->data); _cbor_free(item->data);
break; break;
} }
case CBOR_TYPE_FLOAT_CTRL: { case CBOR_TYPE_FLOAT_CTRL: {
@ -144,8 +148,7 @@ void cbor_decref(cbor_item_t **item_ref) {
break; break;
} }
} }
_CBOR_FREE(item); _cbor_free(item);
// TODO
*item_ref = NULL; *item_ref = NULL;
} }
} }

View File

@ -13,6 +13,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "cbor/cbor_export.h" #include "cbor/cbor_export.h"
#include "cbor/configuration.h" #include "cbor/configuration.h"
#include "data.h" #include "data.h"
@ -21,7 +22,7 @@
extern "C" { extern "C" {
/** /**
* C++ is not a subset of C99 -- 'restrict' qualifier is not a part of the * C99 is not a subset of C++ -- 'restrict' qualifier is not a part of the
* language. This is a workaround to keep it in C headers -- compilers allow * language. This is a workaround to keep it in C headers -- compilers allow
* linking non-restrict signatures with restrict implementations. * linking non-restrict signatures with restrict implementations.
* *
@ -40,9 +41,9 @@ static const uint8_t cbor_major_version = CBOR_MAJOR_VERSION;
static const uint8_t cbor_minor_version = CBOR_MINOR_VERSION; static const uint8_t cbor_minor_version = CBOR_MINOR_VERSION;
static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION; static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION;
#define CBOR_VERSION \ #define CBOR_VERSION \
TO_STR(CBOR_MAJOR_VERSION) \ _CBOR_TO_STR(CBOR_MAJOR_VERSION) \
"." TO_STR(CBOR_MINOR_VERSION) "." TO_STR(CBOR_PATCH_VERSION) "." _CBOR_TO_STR(CBOR_MINOR_VERSION) "." _CBOR_TO_STR(CBOR_PATCH_VERSION)
#define CBOR_HEX_VERSION \ #define CBOR_HEX_VERSION \
((CBOR_MAJOR_VERSION << 16) | (CBOR_MINOR_VERSION << 8) | CBOR_PATCH_VERSION) ((CBOR_MAJOR_VERSION << 16) | (CBOR_MINOR_VERSION << 8) | CBOR_PATCH_VERSION)
@ -50,20 +51,55 @@ static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION;
*/ */
#ifdef DEBUG #ifdef DEBUG
#include <stdio.h> #include <stdio.h>
#define debug_print(fmt, ...) \ #define _cbor_debug_print(fmt, ...) \
do { \ do { \
if (DEBUG) \ if (DEBUG) \
fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, \ fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, \
__VA_ARGS__); \ __VA_ARGS__); \
} while (0) } while (0)
extern bool _cbor_enable_assert;
// Like `assert`, but can be dynamically disabled in tests to allow testing
// invalid behaviors.
#define CBOR_ASSERT(e) assert(!_cbor_enable_assert || (e))
#define _CBOR_TEST_DISABLE_ASSERT(block) \
do { \
_cbor_enable_assert = false; \
block _cbor_enable_assert = true; \
} while (0)
#else #else
#define debug_print(fmt, ...) \ #define debug_print(fmt, ...) \
do { \ do { \
} while (0) } while (0)
#define CBOR_ASSERT(e)
#define _CBOR_TEST_DISABLE_ASSERT(block) \
do { \
block \
} while (0)
#endif #endif
#define TO_STR_(x) #x #define _CBOR_TO_STR_(x) #x
#define TO_STR(x) TO_STR_(x) /* enables proper double expansion */ #define _CBOR_TO_STR(x) _CBOR_TO_STR_(x) /* enables proper double expansion */
#ifdef __GNUC__
#define _CBOR_UNUSED(x) __attribute__((__unused__)) x
// TODO(https://github.com/PJK/libcbor/issues/247): Prefer [[nodiscard]] if
// available
#define _CBOR_NODISCARD __attribute__((warn_unused_result))
#elif defined(_MSC_VER)
#define _CBOR_UNUSED(x) __pragma(warning(suppress : 4100 4101)) x
#define _CBOR_NODISCARD
#else
#define _CBOR_UNUSED(x) x
#define _CBOR_NODISCARD
#endif
typedef void *(*_cbor_malloc_t)(size_t);
typedef void *(*_cbor_realloc_t)(void *, size_t);
typedef void (*_cbor_free_t)(void *);
CBOR_EXPORT extern _cbor_malloc_t _cbor_malloc;
CBOR_EXPORT extern _cbor_realloc_t _cbor_realloc;
CBOR_EXPORT extern _cbor_free_t _cbor_free;
// Macro to short-circuit builder functions when memory allocation fails // Macro to short-circuit builder functions when memory allocation fails
#define _CBOR_NOTNULL(cbor_item) \ #define _CBOR_NOTNULL(cbor_item) \
@ -77,24 +113,15 @@ static const uint8_t cbor_patch_version = CBOR_PATCH_VERSION;
#define _CBOR_DEPENDENT_NOTNULL(cbor_item, pointer) \ #define _CBOR_DEPENDENT_NOTNULL(cbor_item, pointer) \
do { \ do { \
if (pointer == NULL) { \ if (pointer == NULL) { \
_CBOR_FREE(cbor_item); \ _cbor_free(cbor_item); \
return NULL; \ return NULL; \
} \ } \
} while (0) } while (0)
#if CBOR_CUSTOM_ALLOC
typedef void *(*_cbor_malloc_t)(size_t);
typedef void *(*_cbor_realloc_t)(void *, size_t);
typedef void (*_cbor_free_t)(void *);
CBOR_EXPORT extern _cbor_malloc_t _cbor_malloc;
CBOR_EXPORT extern _cbor_realloc_t _cbor_realloc;
CBOR_EXPORT extern _cbor_free_t _cbor_free;
/** Sets the memory management routines to use. /** Sets the memory management routines to use.
* *
* Only available when `CBOR_CUSTOM_ALLOC` is truthy * By default, libcbor will use the standard library `malloc`, `realloc`, and
* `free`.
* *
* \rst * \rst
* .. warning:: This function modifies the global state and should therefore be * .. warning:: This function modifies the global state and should therefore be
@ -115,18 +142,6 @@ CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc,
_cbor_realloc_t custom_realloc, _cbor_realloc_t custom_realloc,
_cbor_free_t custom_free); _cbor_free_t custom_free);
#define _CBOR_MALLOC _cbor_malloc
#define _CBOR_REALLOC _cbor_realloc
#define _CBOR_FREE _cbor_free
#else
#define _CBOR_MALLOC malloc
#define _CBOR_REALLOC realloc
#define _CBOR_FREE free
#endif
/* /*
* ============================================================================ * ============================================================================
* Type manipulation * Type manipulation
@ -135,80 +150,92 @@ CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc,
/** Get the type of the item /** Get the type of the item
* *
* @param item[borrow] * @param item
* @return The type * @return The type
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_type cbor_typeof( CBOR_EXPORT cbor_type cbor_typeof(
const cbor_item_t *item); /* Will be inlined iff link-time opt is enabled */ const cbor_item_t *item); /* Will be inlined iff link-time opt is enabled */
/* Standard item types as described by the RFC */ /* Standard item types as described by the RFC */
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item an #CBOR_TYPE_UINT? * @return Is the item an #CBOR_TYPE_UINT?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item a #CBOR_TYPE_NEGINT? * @return Is the item a #CBOR_TYPE_NEGINT?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item a #CBOR_TYPE_BYTESTRING? * @return Is the item a #CBOR_TYPE_BYTESTRING?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item a #CBOR_TYPE_STRING? * @return Is the item a #CBOR_TYPE_STRING?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_string(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_string(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item an #CBOR_TYPE_ARRAY? * @return Is the item an #CBOR_TYPE_ARRAY?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_array(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_array(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item a #CBOR_TYPE_MAP? * @return Is the item a #CBOR_TYPE_MAP?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_map(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_map(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item a #CBOR_TYPE_TAG? * @return Is the item a #CBOR_TYPE_TAG?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t *item);
/** Does the item have the appropriate major type? /** Does the item have the appropriate major type?
* @param item[borrow] the item * @param item the item
* @return Is the item a #CBOR_TYPE_FLOAT_CTRL? * @return Is the item a #CBOR_TYPE_FLOAT_CTRL?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t *item); CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t *item);
/* Practical types with respect to their semantics (but not tag values) */ /* Practical types with respect to their semantics (but not tag values) */
/** Is the item an integer, either positive or negative? /** Is the item an integer, either positive or negative?
* @param item[borrow] the item * @param item the item
* @return Is the item an integer, either positive or negative? * @return Is the item an integer, either positive or negative?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_int(const cbor_item_t *item); CBOR_EXPORT bool cbor_is_int(const cbor_item_t *item);
/** Is the item an a floating point number? /** Is the item an a floating point number?
* @param item[borrow] the item * @param item the item
* @return Is the item a floating point number? * @return Is the item a floating point number?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_float(const cbor_item_t *item); CBOR_EXPORT bool cbor_is_float(const cbor_item_t *item);
/** Is the item an a boolean? /** Is the item an a boolean?
* @param item[borrow] the item * @param item the item
* @return Is the item a boolean? * @return Is the item a boolean?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_bool(const cbor_item_t *item); CBOR_EXPORT bool cbor_is_bool(const cbor_item_t *item);
/** Does this item represent `null` /** Does this item represent `null`
@ -218,9 +245,10 @@ CBOR_EXPORT bool cbor_is_bool(const cbor_item_t *item);
* null pointer will most likely result in a crash. * null pointer will most likely result in a crash.
* \endrst * \endrst
* *
* @param item[borrow] the item * @param item the item
* @return Is the item (CBOR logical) null? * @return Is the item (CBOR logical) null?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_null(const cbor_item_t *item); CBOR_EXPORT bool cbor_is_null(const cbor_item_t *item);
/** Does this item represent `undefined` /** Does this item represent `undefined`
@ -230,9 +258,10 @@ CBOR_EXPORT bool cbor_is_null(const cbor_item_t *item);
* C. * C.
* \endrst * \endrst
* *
* @param item[borrow] the item * @param item the item
* @return Is the item (CBOR logical) undefined? * @return Is the item (CBOR logical) undefined?
*/ */
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_undef(const cbor_item_t *item); CBOR_EXPORT bool cbor_is_undef(const cbor_item_t *item);
/* /*
@ -241,42 +270,48 @@ CBOR_EXPORT bool cbor_is_undef(const cbor_item_t *item);
* ============================================================================ * ============================================================================
*/ */
/** Increases the reference count by one /** Increases the item's reference count by one
* *
* No dependent items are affected. * Constant complexity; items referring to this one or items being referred to
* are not updated.
* *
* @param item[incref] item the item * This function can be used to extend reference counting to client code.
* @return the input reference *
* @param item Reference to an item
* @return The input \p item
*/ */
CBOR_EXPORT cbor_item_t *cbor_incref(cbor_item_t *item); CBOR_EXPORT cbor_item_t *cbor_incref(cbor_item_t *item);
/** Decreases the 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 any dependent items * In case the item is deallocated, the reference count of all items this item
* is adjusted accordingly in a recursive manner. * references will also be #cbor_decref 'ed recursively.
* *
* @param item[take] the item. Set to `NULL` if deallocated * @param item Reference to an item. Will be set to `NULL` if deallocated
*/ */
CBOR_EXPORT void cbor_decref(cbor_item_t **item); CBOR_EXPORT void cbor_decref(cbor_item_t **item);
/** Decreases the reference count by one, deallocating the item if needed /** 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 * Convenience wrapper for #cbor_decref when its set-to-null behavior is not
* needed * needed
* *
* @param item[take] the item * @param item Reference to an item
*/ */
CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t *item); CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t *item);
/** Get the reference count /** Get the item's reference count
* *
* \rst * \rst
* .. warning:: This does *not* account for transitive references. * .. warning:: This does *not* account for transitive references.
* \endrst * \endrst
* *
* @param item[borrow] the item * @todo Add some inline examples for reference counting
*
* @param item the item
* @return the reference count * @return the reference count
*/ */
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_refcount(const cbor_item_t *item); CBOR_EXPORT size_t cbor_refcount(const cbor_item_t *item);
/** Provides CPP-like move construct /** Provides CPP-like move construct
@ -291,9 +326,10 @@ CBOR_EXPORT size_t cbor_refcount(const cbor_item_t *item);
* count afterwards, the memory will be leaked. * count afterwards, the memory will be leaked.
* \endrst * \endrst
* *
* @param item[take] the item * @param item Reference to an item
* @return the item with reference count decreased by one * @return the item with reference count decreased by one
*/ */
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t *cbor_move(cbor_item_t *item); CBOR_EXPORT cbor_item_t *cbor_move(cbor_item_t *item);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -5,7 +5,6 @@
#define CBOR_MINOR_VERSION ${CBOR_VERSION_MINOR} #define CBOR_MINOR_VERSION ${CBOR_VERSION_MINOR}
#define CBOR_PATCH_VERSION ${CBOR_VERSION_PATCH} #define CBOR_PATCH_VERSION ${CBOR_VERSION_PATCH}
#cmakedefine01 CBOR_CUSTOM_ALLOC
#define CBOR_BUFFER_GROWTH ${CBOR_BUFFER_GROWTH} #define CBOR_BUFFER_GROWTH ${CBOR_BUFFER_GROWTH}
#define CBOR_MAX_STACK_SIZE ${CBOR_MAX_STACK_SIZE} #define CBOR_MAX_STACK_SIZE ${CBOR_MAX_STACK_SIZE}
#cmakedefine01 CBOR_PRETTY_PRINTER #cmakedefine01 CBOR_PRETTY_PRINTER

View File

@ -45,6 +45,8 @@ typedef enum {
CBOR_ERR_NONE, CBOR_ERR_NONE,
CBOR_ERR_NOTENOUGHDATA, CBOR_ERR_NOTENOUGHDATA,
CBOR_ERR_NODATA, CBOR_ERR_NODATA,
// TODO: Should be "malformed" or at least "malformatted". Retained for
// backwards compatibility.
CBOR_ERR_MALFORMATED, CBOR_ERR_MALFORMATED,
CBOR_ERR_MEMERROR /** Memory error - item allocation failed. Is it too big for CBOR_ERR_MEMERROR /** Memory error - item allocation failed. Is it too big for
your allocator? */ your allocator? */
@ -86,6 +88,11 @@ typedef enum {
CBOR_CTRL_UNDEF = 23 CBOR_CTRL_UNDEF = 23
} _cbor_ctrl; } _cbor_ctrl;
// Metadata items use size_t (instead of uint64_t) because items in memory take
// up at least 1B per entry or string byte, so if size_t is narrower than
// uint64_t, we wouldn't be able to create them in the first place and can save
// some space.
/** Integers specific metadata */ /** Integers specific metadata */
struct _cbor_int_metadata { struct _cbor_int_metadata {
cbor_int_width width; cbor_int_width width;
@ -184,7 +191,7 @@ struct cbor_indefinite_string_data {
/** High-level decoding error */ /** High-level decoding error */
struct cbor_error { struct cbor_error {
/** Aproximate position */ /** Approximate position */
size_t position; size_t position;
/** Description */ /** Description */
cbor_error_code code; cbor_error_code code;
@ -212,6 +219,8 @@ enum cbor_decoder_status {
*/ */
CBOR_DECODER_FINISHED, CBOR_DECODER_FINISHED,
/** Not enough data to invoke a callback */ /** Not enough data to invoke a callback */
// TODO: The name is inconsistent with CBOR_ERR_NOTENOUGHDATA. Retained for
// backwards compatibility.
CBOR_DECODER_NEDATA, CBOR_DECODER_NEDATA,
/** Bad data (reserved MTB, malformed value, etc.) */ /** Bad data (reserved MTB, malformed value, etc.) */
CBOR_DECODER_ERROR CBOR_DECODER_ERROR

View File

@ -135,17 +135,23 @@ size_t cbor_encode_half(float value, unsigned char *buffer,
val & 0x7FFFFFu; /* 0b0000_0000_0111_1111_1111_1111_1111_1111 */ val & 0x7FFFFFu; /* 0b0000_0000_0111_1111_1111_1111_1111_1111 */
if (exp == 0xFF) { /* Infinity or NaNs */ if (exp == 0xFF) { /* Infinity or NaNs */
if (value != value) { if (value != value) {
res = (uint16_t)0x007e00; /* Not IEEE semantics - required by CBOR // We discard information bits in half-float NaNs. This is
[s. 3.9] */ // not required for the core CBOR protocol (it is only a suggestion in
// Section 3.9).
// See https://github.com/PJK/libcbor/issues/215
res = (uint16_t)0x007e00;
} else { } else {
res = (uint16_t)((val & 0x80000000u) >> 16u | 0x7C00u | // If the mantissa is non-zero, we have a NaN, but those are handled
(mant ? 1u : 0u) << 15u); // above. See
// https://en.wikipedia.org/wiki/Half-precision_floating-point_format
CBOR_ASSERT(mant == 0u);
res = (uint16_t)((val & 0x80000000u) >> 16u | 0x7C00u);
} }
} else if (exp == 0x00) { /* Zeroes or subnorms */ } else if (exp == 0x00) { /* Zeroes or subnorms */
res = (uint16_t)((val & 0x80000000u) >> 16u | mant >> 13u); res = (uint16_t)((val & 0x80000000u) >> 16u | mant >> 13u);
} else { /* Normal numbers */ } else { /* Normal numbers */
int8_t logical_exp = (int8_t)(exp - 127); int8_t logical_exp = (int8_t)(exp - 127);
assert(logical_exp == exp - 127); CBOR_ASSERT(logical_exp == exp - 127);
// Now we know that 2^exp <= 0 logically // Now we know that 2^exp <= 0 logically
if (logical_exp < -24) { if (logical_exp < -24) {
@ -158,7 +164,9 @@ size_t cbor_encode_half(float value, unsigned char *buffer,
value is lost. This is an implementation decision that works around the value is lost. This is an implementation decision that works around the
absence of standard half-float in the language. */ absence of standard half-float in the language. */
res = (uint16_t)((val & 0x80000000u) >> 16u) | // Extract sign bit res = (uint16_t)((val & 0x80000000u) >> 16u) | // Extract sign bit
(uint16_t)(1u << (24u + logical_exp)); ((uint16_t)(1u << (24u + logical_exp)) +
(uint16_t)(((mant >> (-logical_exp - 2)) + 1) >>
1)); // Round half away from zero for simplicity
} else { } else {
res = (uint16_t)((val & 0x80000000u) >> 16u | res = (uint16_t)((val & 0x80000000u) >> 16u |
((((uint8_t)logical_exp) + 15u) << 10u) | ((((uint8_t)logical_exp) + 15u) << 10u) |

View File

@ -16,55 +16,87 @@ extern "C" {
#endif #endif
/* /*
* ============================================================================ * All cbor_encode_* methods take 2 or 3 arguments:
* Primitives encoding * - a logical `value` to encode (except for trivial items such as NULLs)
* ============================================================================ * - an output `buffer` pointer
* - a `buffer_size` specification
*
* They serialize the `value` into one or more bytes and write the bytes to the
* output `buffer` and return either the number of bytes written, or 0 if the
* `buffer_size` was too small to small to fit the serialized value (in which
* case it is not modified).
*/ */
CBOR_EXPORT size_t cbor_encode_uint8(uint8_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint8(uint8_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_uint16(uint16_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint16(uint16_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_uint32(uint32_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint32(uint32_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_uint64(uint64_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint64(uint64_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_uint(uint64_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_uint(uint64_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_negint8(uint8_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint8(uint8_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_negint16(uint16_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint16(uint16_t,
unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_negint32(uint32_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint32(uint32_t,
unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_negint64(uint64_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint64(uint64_t,
unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_negint(uint64_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_negint(uint64_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_bytestring_start(size_t, unsigned char *, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_bytestring_start(size_t,
size_t); unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_indef_bytestring_start(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t
cbor_encode_indef_bytestring_start(unsigned char *, size_t);
CBOR_EXPORT size_t cbor_encode_string_start(size_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_string_start(size_t,
unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_indef_string_start(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t
cbor_encode_indef_string_start(unsigned char *, size_t);
CBOR_EXPORT size_t cbor_encode_array_start(size_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_array_start(size_t,
unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_indef_array_start(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t
cbor_encode_indef_array_start(unsigned char *, size_t);
CBOR_EXPORT size_t cbor_encode_map_start(size_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_map_start(size_t,
unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_indef_map_start(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_indef_map_start(unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_tag(uint64_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_tag(uint64_t, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_bool(bool, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_bool(bool, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_null(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_null(unsigned char *, size_t);
CBOR_EXPORT size_t cbor_encode_undef(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_undef(unsigned char *, size_t);
/** Encodes a half-precision float /** Encodes a half-precision float
* *
@ -86,21 +118,20 @@ CBOR_EXPORT size_t cbor_encode_undef(unsigned char *, size_t);
* lost. * lost.
* - In all other cases, the sign bit, the exponent, and 10 most significant * - In all other cases, the sign bit, the exponent, and 10 most significant
* bits of the significand are kept * bits of the significand are kept
*
* @param value
* @param buffer Target buffer
* @param buffer_size Available space in the buffer
* @return number of bytes written
*/ */
CBOR_EXPORT size_t cbor_encode_half(float, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_half(float, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_single(float, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_single(float, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_double(double, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_double(double, unsigned char *,
size_t);
CBOR_EXPORT size_t cbor_encode_break(unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_break(unsigned char *, size_t);
CBOR_EXPORT size_t cbor_encode_ctrl(uint8_t, unsigned char *, size_t); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_ctrl(uint8_t, unsigned char *,
size_t);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -10,41 +10,42 @@
#include "assert.h" #include "assert.h"
cbor_float_width cbor_float_get_width(const cbor_item_t *item) { cbor_float_width cbor_float_get_width(const cbor_item_t *item) {
assert(cbor_isa_float_ctrl(item)); CBOR_ASSERT(cbor_isa_float_ctrl(item));
return item->metadata.float_ctrl_metadata.width; return item->metadata.float_ctrl_metadata.width;
} }
uint8_t cbor_ctrl_value(const cbor_item_t *item) { uint8_t cbor_ctrl_value(const cbor_item_t *item) {
assert(cbor_isa_float_ctrl(item)); CBOR_ASSERT(cbor_isa_float_ctrl(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_0); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_0);
return item->metadata.float_ctrl_metadata.ctrl; return item->metadata.float_ctrl_metadata.ctrl;
} }
bool cbor_float_ctrl_is_ctrl(const cbor_item_t *item) { bool cbor_float_ctrl_is_ctrl(const cbor_item_t *item) {
assert(cbor_isa_float_ctrl(item)); CBOR_ASSERT(cbor_isa_float_ctrl(item));
return cbor_float_get_width(item) == CBOR_FLOAT_0; return cbor_float_get_width(item) == CBOR_FLOAT_0;
} }
float cbor_float_get_float2(const cbor_item_t *item) { float cbor_float_get_float2(const cbor_item_t *item) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_16); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_16);
return *(float *)item->data; return *(float *)item->data;
} }
float cbor_float_get_float4(const cbor_item_t *item) { float cbor_float_get_float4(const cbor_item_t *item) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_32); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_32);
return *(float *)item->data; return *(float *)item->data;
} }
double cbor_float_get_float8(const cbor_item_t *item) { double cbor_float_get_float8(const cbor_item_t *item) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_64); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_64);
return *(double *)item->data; return *(double *)item->data;
} }
double cbor_float_get_float(const cbor_item_t *item) { double cbor_float_get_float(const cbor_item_t *item) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
// cppcheck-suppress missingReturn
switch (cbor_float_get_width(item)) { switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0: case CBOR_FLOAT_0:
return NAN; return NAN;
@ -55,46 +56,45 @@ double cbor_float_get_float(const cbor_item_t *item) {
case CBOR_FLOAT_64: case CBOR_FLOAT_64:
return cbor_float_get_float8(item); return cbor_float_get_float8(item);
} }
return NAN; /* Compiler complaints */
} }
bool cbor_get_bool(const cbor_item_t *item) { bool cbor_get_bool(const cbor_item_t *item) {
assert(cbor_is_bool(item)); CBOR_ASSERT(cbor_is_bool(item));
return item->metadata.float_ctrl_metadata.ctrl == CBOR_CTRL_TRUE; return item->metadata.float_ctrl_metadata.ctrl == CBOR_CTRL_TRUE;
} }
void cbor_set_float2(cbor_item_t *item, float value) { void cbor_set_float2(cbor_item_t *item, float value) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_16); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_16);
*((float *)item->data) = value; *((float *)item->data) = value;
} }
void cbor_set_float4(cbor_item_t *item, float value) { void cbor_set_float4(cbor_item_t *item, float value) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_32); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_32);
*((float *)item->data) = value; *((float *)item->data) = value;
} }
void cbor_set_float8(cbor_item_t *item, double value) { void cbor_set_float8(cbor_item_t *item, double value) {
assert(cbor_is_float(item)); CBOR_ASSERT(cbor_is_float(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_64); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_64);
*((double *)item->data) = value; *((double *)item->data) = value;
} }
void cbor_set_ctrl(cbor_item_t *item, uint8_t value) { void cbor_set_ctrl(cbor_item_t *item, uint8_t value) {
assert(cbor_isa_float_ctrl(item)); CBOR_ASSERT(cbor_isa_float_ctrl(item));
assert(cbor_float_get_width(item) == CBOR_FLOAT_0); CBOR_ASSERT(cbor_float_get_width(item) == CBOR_FLOAT_0);
item->metadata.float_ctrl_metadata.ctrl = value; item->metadata.float_ctrl_metadata.ctrl = value;
} }
void cbor_set_bool(cbor_item_t *item, bool value) { void cbor_set_bool(cbor_item_t *item, bool value) {
assert(cbor_is_bool(item)); CBOR_ASSERT(cbor_is_bool(item));
item->metadata.float_ctrl_metadata.ctrl = item->metadata.float_ctrl_metadata.ctrl =
value ? CBOR_CTRL_TRUE : CBOR_CTRL_FALSE; value ? CBOR_CTRL_TRUE : CBOR_CTRL_FALSE;
} }
cbor_item_t *cbor_new_ctrl() { cbor_item_t *cbor_new_ctrl(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -106,8 +106,8 @@ cbor_item_t *cbor_new_ctrl() {
return item; return item;
} }
cbor_item_t *cbor_new_float2() { cbor_item_t *cbor_new_float2(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 4); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -118,8 +118,8 @@ cbor_item_t *cbor_new_float2() {
return item; return item;
} }
cbor_item_t *cbor_new_float4() { cbor_item_t *cbor_new_float4(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 4); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -130,8 +130,8 @@ cbor_item_t *cbor_new_float4() {
return item; return item;
} }
cbor_item_t *cbor_new_float8() { cbor_item_t *cbor_new_float8(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 8); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 8);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -142,14 +142,14 @@ cbor_item_t *cbor_new_float8() {
return item; return item;
} }
cbor_item_t *cbor_new_null() { cbor_item_t *cbor_new_null(void) {
cbor_item_t *item = cbor_new_ctrl(); cbor_item_t *item = cbor_new_ctrl();
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
cbor_set_ctrl(item, CBOR_CTRL_NULL); cbor_set_ctrl(item, CBOR_CTRL_NULL);
return item; return item;
} }
cbor_item_t *cbor_new_undef() { cbor_item_t *cbor_new_undef(void) {
cbor_item_t *item = cbor_new_ctrl(); cbor_item_t *item = cbor_new_ctrl();
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
cbor_set_ctrl(item, CBOR_CTRL_UNDEF); cbor_set_ctrl(item, CBOR_CTRL_UNDEF);

View File

@ -23,111 +23,131 @@ extern "C" {
/** Is this a ctrl value? /** Is this a ctrl value?
* *
* @param item[borrow] A float or ctrl item * @param item A float or ctrl item
* @return Is this a ctrl value? * @return Is this a ctrl value?
*/ */
CBOR_EXPORT bool cbor_float_ctrl_is_ctrl(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT bool cbor_float_ctrl_is_ctrl(
const cbor_item_t *item);
/** Get the float width /** Get the float width
* *
* @param item[borrow] A float or ctrl item * @param item A float or ctrl item
* @return The width. * @return The width.
*/ */
CBOR_EXPORT cbor_float_width cbor_float_get_width(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT cbor_float_width
cbor_float_get_width(const cbor_item_t *item);
/** Get a half precision float /** Get a half precision float
* *
* The item must have the corresponding width * The item must have the corresponding width
* *
* @param[borrow] A half precision float * @param item A half precision float
* @return half precision value * @return half precision value
*/ */
CBOR_EXPORT float cbor_float_get_float2(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT float cbor_float_get_float2(
const cbor_item_t *item);
/** Get a single precision float /** Get a single precision float
* *
* The item must have the corresponding width * The item must have the corresponding width
* *
* @param[borrow] A signle precision float * @param item A single precision float
* @return single precision value * @return single precision value
*/ */
CBOR_EXPORT float cbor_float_get_float4(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT float cbor_float_get_float4(
const cbor_item_t *item);
/** Get a double precision float /** Get a double precision float
* *
* The item must have the corresponding width * The item must have the corresponding width
* *
* @param[borrow] A double precision float * @param item A double precision float
* @return double precision value * @return double precision value
*/ */
CBOR_EXPORT double cbor_float_get_float8(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT double cbor_float_get_float8(
const cbor_item_t *item);
/** Get the float value represented as double /** Get the float value represented as double
* *
* Can be used regardless of the width. * Can be used regardless of the width.
* *
* @param[borrow] Any float * @param item Any float
* @return double precision value * @return double precision value
*/ */
CBOR_EXPORT double cbor_float_get_float(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT double cbor_float_get_float(
const cbor_item_t *item);
/** Get value from a boolean ctrl item /** Get value from a boolean ctrl item
* *
* @param item[borrow] A ctrl item * @param item A ctrl item
* @return boolean value * @return boolean value
*/ */
CBOR_EXPORT bool cbor_get_bool(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT bool cbor_get_bool(const cbor_item_t *item);
/** Constructs a new ctrl item /** Constructs a new ctrl item
* *
* The width cannot be changed once the item is created * The width cannot be changed once the item is created
* *
* @return **new** 1B ctrl or `NULL` upon memory allocation failure * @return Reference to the new ctrl item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_ctrl(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_ctrl(void);
/** Constructs a new float item /** Constructs a new float item
* *
* The width cannot be changed once the item is created * The width cannot be changed once the item is created
* *
* @return **new** 2B float or `NULL` upon memory allocation failure * @return Reference to the new float item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_float2(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_float2(void);
/** Constructs a new float item /** Constructs a new float item
* *
* The width cannot be changed once the item is created * The width cannot be changed once the item is created
* *
* @return **new** 4B float or `NULL` upon memory allocation failure * @return Reference to the new float item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_float4(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_float4(void);
/** Constructs a new float item /** Constructs a new float item
* *
* The width cannot be changed once the item is created * The width cannot be changed once the item is created
* *
* @return **new** 8B float or `NULL` upon memory allocation failure * @return Reference to the new float item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_float8(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_float8(void);
/** Constructs new null ctrl item /** Constructs new null ctrl item
* *
* @return **new** null ctrl item or `NULL` upon memory allocation failure * @return Reference to the new null item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_null(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_null(void);
/** Constructs new undef ctrl item /** Constructs new undef ctrl item
* *
* @return **new** undef ctrl item or `NULL` upon memory allocation failure * @return Reference to the new undef item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_undef(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_undef(void);
/** Constructs new boolean ctrl item /** Constructs new boolean ctrl item
* *
* @param value The value to use * @param value The value to use
* @return **new** boolen ctrl item or `NULL` upon memory allocation failure * @return Reference to the new boolean item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_bool(bool value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_bool(bool value);
/** Assign a control value /** Assign a control value
* *
@ -136,7 +156,7 @@ CBOR_EXPORT cbor_item_t *cbor_build_bool(bool value);
* invalid value using this mechanism. Please consult the standard before use. * invalid value using this mechanism. Please consult the standard before use.
* \endrst * \endrst
* *
* @param item[borrow] A ctrl item * @param item A ctrl item
* @param value The simple value to assign. Please consult the standard for * @param value The simple value to assign. Please consult the standard for
* allowed values * allowed values
*/ */
@ -144,66 +164,74 @@ CBOR_EXPORT void cbor_set_ctrl(cbor_item_t *item, uint8_t value);
/** Assign a boolean value to a boolean ctrl item /** Assign a boolean value to a boolean ctrl item
* *
* @param item[borrow] A ctrl item * @param item A ctrl item
* @param value The simple value to assign. * @param value The simple value to assign.
*/ */
CBOR_EXPORT void cbor_set_bool(cbor_item_t *item, bool value); CBOR_EXPORT void cbor_set_bool(cbor_item_t *item, bool value);
/** Assigns a float value /** Assigns a float value
* *
* @param item[borrow] A half precision float * @param item A half precision float
* @param value The value to assign * @param value The value to assign
*/ */
CBOR_EXPORT void cbor_set_float2(cbor_item_t *item, float value); CBOR_EXPORT void cbor_set_float2(cbor_item_t *item, float value);
/** Assigns a float value /** Assigns a float value
* *
* @param item[borrow] A single precision float * @param item A single precision float
* @param value The value to assign * @param value The value to assign
*/ */
CBOR_EXPORT void cbor_set_float4(cbor_item_t *item, float value); CBOR_EXPORT void cbor_set_float4(cbor_item_t *item, float value);
/** Assigns a float value /** Assigns a float value
* *
* @param item[borrow] A double precision float * @param item A double precision float
* @param value The value to assign * @param value The value to assign
*/ */
CBOR_EXPORT void cbor_set_float8(cbor_item_t *item, double value); CBOR_EXPORT void cbor_set_float8(cbor_item_t *item, double value);
/** Reads the control value /** Reads the control value
* *
* @param item[borrow] A ctrl item * @param item A ctrl item
* @return the simple value * @return the simple value
*/ */
CBOR_EXPORT uint8_t cbor_ctrl_value(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT uint8_t cbor_ctrl_value(const cbor_item_t *item);
/** Constructs a new float /** Constructs a new float
* *
* @param value the value to use * @param value the value to use
* @return **new** float * @return Reference to the new float item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_float2(float value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_float2(float value);
/** Constructs a new float /** Constructs a new float
* *
* @param value the value to use * @param value the value to use
* @return **new** float or `NULL` upon memory allocation failure * @return Reference to the new float item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_float4(float value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_float4(float value);
/** Constructs a new float /** Constructs a new float
* *
* @param value the value to use * @param value the value to use
* @return **new** float or `NULL` upon memory allocation failure * @return Reference to the new float item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_float8(double value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_float8(double value);
/** Constructs a ctrl item /** Constructs a ctrl item
* *
* @param value the value to use * @param value the value to use
* @return **new** ctrl item or `NULL` upon memory allocation failure * @return Reference to the new ctrl item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_ctrl(uint8_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_ctrl(uint8_t value);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -6,9 +6,12 @@
*/ */
#include "builder_callbacks.h" #include "builder_callbacks.h"
#include <string.h> #include <string.h>
#include "../arrays.h" #include "../arrays.h"
#include "../bytestrings.h" #include "../bytestrings.h"
#include "../common.h"
#include "../floats_ctrls.h" #include "../floats_ctrls.h"
#include "../ints.h" #include "../ints.h"
#include "../maps.h" #include "../maps.h"
@ -16,73 +19,96 @@
#include "../tags.h" #include "../tags.h"
#include "unicode.h" #include "unicode.h"
// `_cbor_builder_append` takes ownership of `item`. If adding the item to
// parent container fails, `item` will be deallocated to prevent memory.
void _cbor_builder_append(cbor_item_t *item, void _cbor_builder_append(cbor_item_t *item,
struct _cbor_decoder_context *ctx) { struct _cbor_decoder_context *ctx) {
if (ctx->stack->size == 0) { if (ctx->stack->size == 0) {
/* Top level item */ /* Top level item */
ctx->root = item; ctx->root = item;
} else { return;
/* Part of a bigger structure */ }
switch (ctx->stack->top->item->type) { /* Part of a bigger structure */
case CBOR_TYPE_ARRAY: { switch (ctx->stack->top->item->type) {
if (cbor_array_is_definite(ctx->stack->top->item)) { // Handle Arrays and Maps since they can contain subitems of any type.
/* // Byte/string construction from chunks is handled in the respective chunk
* We don't need an explicit check for whether the item still belongs // handlers.
* into this array because if there are extra items, they will cause a case CBOR_TYPE_ARRAY: {
* syntax error when decoded. if (cbor_array_is_definite(ctx->stack->top->item)) {
*/ // We don't need an explicit check for whether the item still belongs
assert(ctx->stack->top->subitems > 0); // into this array because if there are extra items, they will cause a
cbor_array_push(ctx->stack->top->item, item); // syntax error when decoded.
ctx->stack->top->subitems--; CBOR_ASSERT(ctx->stack->top->subitems > 0);
if (ctx->stack->top->subitems == 0) { // This should never happen since the definite array should be
cbor_item_t *item = ctx->stack->top->item; // preallocated for the expected number of items.
_cbor_stack_pop(ctx->stack); if (!cbor_array_push(ctx->stack->top->item, item)) {
_cbor_builder_append(item, ctx); ctx->creation_failed = true;
}
cbor_decref(&item);
} else {
/* Indefinite array, don't bother with subitems */
cbor_array_push(ctx->stack->top->item, item);
cbor_decref(&item); cbor_decref(&item);
break;
}
cbor_decref(&item);
ctx->stack->top->subitems--;
if (ctx->stack->top->subitems == 0) {
cbor_item_t *stack_item = ctx->stack->top->item;
_cbor_stack_pop(ctx->stack);
_cbor_builder_append(stack_item, ctx);
}
} else {
/* Indefinite array, don't bother with subitems */
if (!cbor_array_push(ctx->stack->top->item, item)) {
ctx->creation_failed = true;
} }
break;
}
case CBOR_TYPE_MAP: {
/* We use 0 and 1 subitems to distinguish between keys and values in
* indefinite items */
if (ctx->stack->top->subitems % 2) {
/* Odd record, this is a value */
_cbor_map_add_value(ctx->stack->top->item, cbor_move(item));
} else {
/* Even record, this is a key */
_cbor_map_add_key(ctx->stack->top->item, cbor_move(item));
}
if (cbor_map_is_definite(ctx->stack->top->item)) {
ctx->stack->top->subitems--;
if (ctx->stack->top->subitems == 0) {
cbor_item_t *item = ctx->stack->top->item;
_cbor_stack_pop(ctx->stack);
_cbor_builder_append(item, ctx);
}
} else {
ctx->stack->top->subitems ^=
1; /* Flip the indicator for indefinite items */
}
break;
}
case CBOR_TYPE_TAG: {
assert(ctx->stack->top->subitems == 1);
cbor_tag_set_item(ctx->stack->top->item, item);
cbor_decref(&item); /* Give up on our reference */
cbor_item_t *item = ctx->stack->top->item;
_cbor_stack_pop(ctx->stack);
_cbor_builder_append(item, ctx);
break;
}
default: {
cbor_decref(&item); cbor_decref(&item);
ctx->syntax_error = true;
} }
break;
}
case CBOR_TYPE_MAP: {
// Handle both definite and indefinite maps the same initially.
// Note: We use 0 and 1 subitems to distinguish between keys and values in
// indefinite items
if (ctx->stack->top->subitems % 2) {
/* Odd record, this is a value */
if (!_cbor_map_add_value(ctx->stack->top->item, item)) {
ctx->creation_failed = true;
cbor_decref(&item);
break;
}
} else {
/* Even record, this is a key */
if (!_cbor_map_add_key(ctx->stack->top->item, item)) {
ctx->creation_failed = true;
cbor_decref(&item);
break;
}
}
cbor_decref(&item);
if (cbor_map_is_definite(ctx->stack->top->item)) {
CBOR_ASSERT(ctx->stack->top->subitems > 0);
ctx->stack->top->subitems--;
if (ctx->stack->top->subitems == 0) {
cbor_item_t *map_entry = ctx->stack->top->item;
_cbor_stack_pop(ctx->stack);
_cbor_builder_append(map_entry, ctx);
}
} else {
ctx->stack->top->subitems ^=
1; /* Flip the indicator for indefinite items */
}
break;
}
case CBOR_TYPE_TAG: {
CBOR_ASSERT(ctx->stack->top->subitems == 1);
cbor_tag_set_item(ctx->stack->top->item, item);
cbor_decref(&item); /* Give up on our reference */
cbor_item_t *tagged_item = ctx->stack->top->item;
_cbor_stack_pop(ctx->stack);
_cbor_builder_append(tagged_item, ctx);
break;
}
// We have an item to append but nothing to append it to.
default: {
cbor_decref(&item);
ctx->syntax_error = true;
} }
} }
} }
@ -95,6 +121,16 @@ void _cbor_builder_append(cbor_item_t *item,
} \ } \
} while (0) } while (0)
// Check that the length fits into size_t. If not, we cannot possibly allocate
// the required memory and should fail fast.
#define CHECK_LENGTH(ctx, length) \
do { \
if (length > SIZE_MAX) { \
ctx->creation_failed = true; \
return; \
} \
} while (0)
#define PUSH_CTX_STACK(ctx, res, subitems) \ #define PUSH_CTX_STACK(ctx, res, subitems) \
do { \ do { \
if (_cbor_stack_push(ctx->stack, res, subitems) == NULL) { \ if (_cbor_stack_push(ctx->stack, res, subitems) == NULL) { \
@ -151,6 +187,7 @@ void cbor_builder_negint8_callback(void *context, uint8_t value) {
void cbor_builder_negint16_callback(void *context, uint16_t value) { void cbor_builder_negint16_callback(void *context, uint16_t value) {
struct _cbor_decoder_context *ctx = context; struct _cbor_decoder_context *ctx = context;
cbor_item_t *res = cbor_new_int16(); cbor_item_t *res = cbor_new_int16();
CHECK_RES(ctx, res);
cbor_mark_negint(res); cbor_mark_negint(res);
cbor_set_uint16(res, value); cbor_set_uint16(res, value);
_cbor_builder_append(res, ctx); _cbor_builder_append(res, ctx);
@ -175,34 +212,36 @@ void cbor_builder_negint64_callback(void *context, uint64_t value) {
} }
void cbor_builder_byte_string_callback(void *context, cbor_data data, void cbor_builder_byte_string_callback(void *context, cbor_data data,
size_t length) { uint64_t length) {
struct _cbor_decoder_context *ctx = context; struct _cbor_decoder_context *ctx = context;
unsigned char *new_handle = _CBOR_MALLOC(length); CHECK_LENGTH(ctx, length);
unsigned char *new_handle = _cbor_malloc(length);
if (new_handle == NULL) { if (new_handle == NULL) {
ctx->creation_failed = true; ctx->creation_failed = true;
return; return;
} }
memcpy(new_handle, data, length); memcpy(new_handle, data, length);
cbor_item_t *res = cbor_new_definite_bytestring(); cbor_item_t *new_chunk = cbor_new_definite_bytestring();
if (res == NULL) { if (new_chunk == NULL) {
_CBOR_FREE(new_handle); _cbor_free(new_handle);
ctx->creation_failed = true; ctx->creation_failed = true;
return; return;
} }
cbor_bytestring_set_handle(res, new_handle, length); cbor_bytestring_set_handle(new_chunk, new_handle, length);
if (ctx->stack->size > 0 && cbor_isa_bytestring(ctx->stack->top->item)) { // If an indef bytestring is on the stack, extend it (if it were closed, it
if (cbor_bytestring_is_indefinite(ctx->stack->top->item)) { // would have been popped). Handle any syntax errors upstream.
cbor_bytestring_add_chunk(ctx->stack->top->item, cbor_move(res)); if (ctx->stack->size > 0 && cbor_isa_bytestring(ctx->stack->top->item) &&
} else { cbor_bytestring_is_indefinite(ctx->stack->top->item)) {
cbor_decref(&res); if (!cbor_bytestring_add_chunk(ctx->stack->top->item, new_chunk)) {
ctx->syntax_error = true; ctx->creation_failed = true;
} }
cbor_decref(&new_chunk);
} else { } else {
_cbor_builder_append(res, ctx); _cbor_builder_append(new_chunk, ctx);
} }
} }
@ -214,19 +253,20 @@ void cbor_builder_byte_string_start_callback(void *context) {
} }
void cbor_builder_string_callback(void *context, cbor_data data, void cbor_builder_string_callback(void *context, cbor_data data,
size_t length) { uint64_t length) {
struct _cbor_decoder_context *ctx = context; struct _cbor_decoder_context *ctx = context;
CHECK_LENGTH(ctx, length);
struct _cbor_unicode_status unicode_status; struct _cbor_unicode_status unicode_status;
uint64_t codepoint_count =
size_t codepoint_count =
_cbor_unicode_codepoint_count(data, length, &unicode_status); _cbor_unicode_codepoint_count(data, length, &unicode_status);
if (unicode_status.status == _CBOR_UNICODE_BADCP) { if (unicode_status.status != _CBOR_UNICODE_OK) {
ctx->syntax_error = true; ctx->syntax_error = true;
return; return;
} }
CBOR_ASSERT(codepoint_count <= length);
unsigned char *new_handle = _CBOR_MALLOC(length); unsigned char *new_handle = _cbor_malloc(length);
if (new_handle == NULL) { if (new_handle == NULL) {
ctx->creation_failed = true; ctx->creation_failed = true;
@ -234,25 +274,25 @@ void cbor_builder_string_callback(void *context, cbor_data data,
} }
memcpy(new_handle, data, length); memcpy(new_handle, data, length);
cbor_item_t *res = cbor_new_definite_string(); cbor_item_t *new_chunk = cbor_new_definite_string();
if (res == NULL) { if (new_chunk == NULL) {
_CBOR_FREE(new_handle); _cbor_free(new_handle);
ctx->creation_failed = true; ctx->creation_failed = true;
return; return;
} }
cbor_string_set_handle(res, new_handle, length); cbor_string_set_handle(new_chunk, new_handle, length);
res->metadata.string_metadata.codepoint_count = codepoint_count; new_chunk->metadata.string_metadata.codepoint_count = codepoint_count;
/* Careful here: order matters */ // If an indef string is on the stack, extend it (if it were closed, it would
if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item)) { // have been popped). Handle any syntax errors upstream.
if (cbor_string_is_indefinite(ctx->stack->top->item)) { if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item) &&
cbor_string_add_chunk(ctx->stack->top->item, cbor_move(res)); cbor_string_is_indefinite(ctx->stack->top->item)) {
} else { if (!cbor_string_add_chunk(ctx->stack->top->item, new_chunk)) {
cbor_decref(&res); ctx->creation_failed = true;
ctx->syntax_error = true;
} }
cbor_decref(&new_chunk);
} else { } else {
_cbor_builder_append(res, ctx); _cbor_builder_append(new_chunk, ctx);
} }
} }
@ -263,8 +303,9 @@ void cbor_builder_string_start_callback(void *context) {
PUSH_CTX_STACK(ctx, res, 0); PUSH_CTX_STACK(ctx, res, 0);
} }
void cbor_builder_array_start_callback(void *context, size_t size) { void cbor_builder_array_start_callback(void *context, uint64_t size) {
struct _cbor_decoder_context *ctx = context; struct _cbor_decoder_context *ctx = context;
CHECK_LENGTH(ctx, size);
cbor_item_t *res = cbor_new_definite_array(size); cbor_item_t *res = cbor_new_definite_array(size);
CHECK_RES(ctx, res); CHECK_RES(ctx, res);
if (size > 0) { if (size > 0) {
@ -288,8 +329,9 @@ void cbor_builder_indef_map_start_callback(void *context) {
PUSH_CTX_STACK(ctx, res, 0); PUSH_CTX_STACK(ctx, res, 0);
} }
void cbor_builder_map_start_callback(void *context, size_t size) { void cbor_builder_map_start_callback(void *context, uint64_t size) {
struct _cbor_decoder_context *ctx = context; struct _cbor_decoder_context *ctx = context;
CHECK_LENGTH(ctx, size);
cbor_item_t *res = cbor_new_definite_map(size); cbor_item_t *res = cbor_new_definite_map(size);
CHECK_RES(ctx, res); CHECK_RES(ctx, res);
if (size > 0) { if (size > 0) {
@ -305,14 +347,13 @@ void cbor_builder_map_start_callback(void *context, size_t size) {
bool _cbor_is_indefinite(cbor_item_t *item) { bool _cbor_is_indefinite(cbor_item_t *item) {
switch (item->type) { switch (item->type) {
case CBOR_TYPE_BYTESTRING: case CBOR_TYPE_BYTESTRING:
return item->metadata.bytestring_metadata.type == return cbor_bytestring_is_indefinite(item);
_CBOR_METADATA_INDEFINITE;
case CBOR_TYPE_STRING: case CBOR_TYPE_STRING:
return item->metadata.string_metadata.type == _CBOR_METADATA_INDEFINITE; return cbor_string_is_indefinite(item);
case CBOR_TYPE_ARRAY: case CBOR_TYPE_ARRAY:
return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE; return cbor_array_is_indefinite(item);
case CBOR_TYPE_MAP: case CBOR_TYPE_MAP:
return item->metadata.map_metadata.type == _CBOR_METADATA_INDEFINITE; return cbor_map_is_indefinite(item);
default: default:
return false; return false;
} }
@ -340,6 +381,7 @@ void cbor_builder_indef_break_callback(void *context) {
void cbor_builder_float2_callback(void *context, float value) { void cbor_builder_float2_callback(void *context, float value) {
struct _cbor_decoder_context *ctx = context; struct _cbor_decoder_context *ctx = context;
cbor_item_t *res = cbor_new_float2(); cbor_item_t *res = cbor_new_float2();
CHECK_RES(ctx, res);
cbor_set_float2(res, value); cbor_set_float2(res, value);
_cbor_builder_append(res, ctx); _cbor_builder_append(res, ctx);
} }

View File

@ -26,6 +26,10 @@ struct _cbor_decoder_context {
struct _cbor_stack *stack; struct _cbor_stack *stack;
}; };
/** Internal helper: Append item to the top of the stack while handling errors.
*/
void _cbor_builder_append(cbor_item_t *item, struct _cbor_decoder_context *ctx);
void cbor_builder_uint8_callback(void *, uint8_t); void cbor_builder_uint8_callback(void *, uint8_t);
void cbor_builder_uint16_callback(void *, uint16_t); void cbor_builder_uint16_callback(void *, uint16_t);
@ -42,19 +46,19 @@ void cbor_builder_negint32_callback(void *, uint32_t);
void cbor_builder_negint64_callback(void *, uint64_t); void cbor_builder_negint64_callback(void *, uint64_t);
void cbor_builder_string_callback(void *, cbor_data, size_t); void cbor_builder_string_callback(void *, cbor_data, uint64_t);
void cbor_builder_string_start_callback(void *); void cbor_builder_string_start_callback(void *);
void cbor_builder_byte_string_callback(void *, cbor_data, size_t); void cbor_builder_byte_string_callback(void *, cbor_data, uint64_t);
void cbor_builder_byte_string_start_callback(void *); void cbor_builder_byte_string_start_callback(void *);
void cbor_builder_array_start_callback(void *, size_t); void cbor_builder_array_start_callback(void *, uint64_t);
void cbor_builder_indef_array_start_callback(void *); void cbor_builder_indef_array_start_callback(void *);
void cbor_builder_map_start_callback(void *, size_t); void cbor_builder_map_start_callback(void *, uint64_t);
void cbor_builder_indef_map_start_callback(void *); void cbor_builder_indef_map_start_callback(void *);

View File

@ -33,8 +33,8 @@ size_t _cbor_encode_uint16(uint16_t value, unsigned char *buffer,
#ifdef IS_BIG_ENDIAN #ifdef IS_BIG_ENDIAN
memcpy(buffer + 1, &value, 2); memcpy(buffer + 1, &value, 2);
#else #else
buffer[1] = value >> 8; buffer[1] = (unsigned char)(value >> 8);
buffer[2] = value; buffer[2] = (unsigned char)value;
#endif #endif
return 3; return 3;
@ -50,10 +50,10 @@ size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer,
#ifdef IS_BIG_ENDIAN #ifdef IS_BIG_ENDIAN
memcpy(buffer + 1, &value, 4); memcpy(buffer + 1, &value, 4);
#else #else
buffer[1] = value >> 24; buffer[1] = (unsigned char)(value >> 24);
buffer[2] = value >> 16; buffer[2] = (unsigned char)(value >> 16);
buffer[3] = value >> 8; buffer[3] = (unsigned char)(value >> 8);
buffer[4] = value; buffer[4] = (unsigned char)value;
#endif #endif
return 5; return 5;
@ -69,14 +69,14 @@ size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer,
#ifdef IS_BIG_ENDIAN #ifdef IS_BIG_ENDIAN
memcpy(buffer + 1, &value, 8); memcpy(buffer + 1, &value, 8);
#else #else
buffer[1] = value >> 56; buffer[1] = (unsigned char)(value >> 56);
buffer[2] = value >> 48; buffer[2] = (unsigned char)(value >> 48);
buffer[3] = value >> 40; buffer[3] = (unsigned char)(value >> 40);
buffer[4] = value >> 32; buffer[4] = (unsigned char)(value >> 32);
buffer[5] = value >> 24; buffer[5] = (unsigned char)(value >> 24);
buffer[6] = value >> 16; buffer[6] = (unsigned char)(value >> 16);
buffer[7] = value >> 8; buffer[7] = (unsigned char)(value >> 8);
buffer[8] = value; buffer[8] = (unsigned char)value;
#endif #endif
return 9; return 9;

View File

@ -14,18 +14,23 @@
extern "C" { extern "C" {
#endif #endif
_CBOR_NODISCARD
size_t _cbor_encode_uint8(uint8_t value, unsigned char *buffer, size_t _cbor_encode_uint8(uint8_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset); size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint16(uint16_t value, unsigned char *buffer, size_t _cbor_encode_uint16(uint16_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset); size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer, size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset); size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer, size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset); size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint(uint64_t value, unsigned char *buffer, size_t _cbor_encode_uint(uint64_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset); size_t buffer_size, uint8_t offset);

View File

@ -64,7 +64,7 @@ float _cbor_decode_half(unsigned char *halfp) {
return (float)(half & 0x8000 ? -val : val); return (float)(half & 0x8000 ? -val : val);
} }
double _cbor_load_half(cbor_data source) { float _cbor_load_half(cbor_data source) {
/* Discard const */ /* Discard const */
return _cbor_decode_half((unsigned char *)source); return _cbor_decode_half((unsigned char *)source);
} }

View File

@ -15,18 +15,25 @@ extern "C" {
#endif #endif
/* Read the given uint from the given location, no questions asked */ /* Read the given uint from the given location, no questions asked */
_CBOR_NODISCARD
uint8_t _cbor_load_uint8(const unsigned char *source); uint8_t _cbor_load_uint8(const unsigned char *source);
_CBOR_NODISCARD
uint16_t _cbor_load_uint16(const unsigned char *source); uint16_t _cbor_load_uint16(const unsigned char *source);
_CBOR_NODISCARD
uint32_t _cbor_load_uint32(const unsigned char *source); uint32_t _cbor_load_uint32(const unsigned char *source);
_CBOR_NODISCARD
uint64_t _cbor_load_uint64(const unsigned char *source); uint64_t _cbor_load_uint64(const unsigned char *source);
double _cbor_load_half(cbor_data source); _CBOR_NODISCARD
float _cbor_load_half(cbor_data source);
_CBOR_NODISCARD
float _cbor_load_float(cbor_data source); float _cbor_load_float(cbor_data source);
_CBOR_NODISCARD
double _cbor_load_double(cbor_data source); double _cbor_load_double(cbor_data source);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -23,12 +23,25 @@ size_t _cbor_highest_bit(size_t number) {
} }
bool _cbor_safe_to_multiply(size_t a, size_t b) { bool _cbor_safe_to_multiply(size_t a, size_t b) {
if (a <= 1 || b <= 1) return true;
return _cbor_highest_bit(a) + _cbor_highest_bit(b) <= sizeof(size_t) * 8; return _cbor_highest_bit(a) + _cbor_highest_bit(b) <= sizeof(size_t) * 8;
} }
bool _cbor_safe_to_add(size_t a, size_t b) {
// Unsigned integer overflow doesn't constitute UB
size_t sum = a + b;
return sum >= a && sum >= b;
}
size_t _cbor_safe_signaling_add(size_t a, size_t b) {
if (a == 0 || b == 0) return 0;
if (_cbor_safe_to_add(a, b)) return a + b;
return 0;
}
void* _cbor_alloc_multiple(size_t item_size, size_t item_count) { void* _cbor_alloc_multiple(size_t item_size, size_t item_count) {
if (_cbor_safe_to_multiply(item_size, item_count)) { if (_cbor_safe_to_multiply(item_size, item_count)) {
return _CBOR_MALLOC(item_size * item_count); return _cbor_malloc(item_size * item_count);
} else { } else {
return NULL; return NULL;
} }
@ -37,7 +50,7 @@ void* _cbor_alloc_multiple(size_t item_size, size_t item_count) {
void* _cbor_realloc_multiple(void* pointer, size_t item_size, void* _cbor_realloc_multiple(void* pointer, size_t item_size,
size_t item_count) { size_t item_count) {
if (_cbor_safe_to_multiply(item_size, item_count)) { if (_cbor_safe_to_multiply(item_size, item_count)) {
return _CBOR_REALLOC(pointer, item_size * item_count); return _cbor_realloc(pointer, item_size * item_count);
} else { } else {
return NULL; return NULL;
} }

View File

@ -11,9 +11,20 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
/** Can a and b be multiplied without overflowing size_t? */ #include "cbor/common.h"
/** Can `a` and `b` be multiplied without overflowing size_t? */
_CBOR_NODISCARD
bool _cbor_safe_to_multiply(size_t a, size_t b); bool _cbor_safe_to_multiply(size_t a, size_t b);
/** Can `a` and `b` be added without overflowing size_t? */
_CBOR_NODISCARD
bool _cbor_safe_to_add(size_t a, size_t b);
/** Adds `a` and `b`, propagating zeros and returing 0 on overflow. */
_CBOR_NODISCARD
size_t _cbor_safe_signaling_add(size_t a, size_t b);
/** Overflow-proof contiguous array allocation /** Overflow-proof contiguous array allocation
* *
* @param item_size * @param item_size

View File

@ -7,14 +7,14 @@
#include "stack.h" #include "stack.h"
struct _cbor_stack _cbor_stack_init() { struct _cbor_stack _cbor_stack_init(void) {
return (struct _cbor_stack){.top = NULL, .size = 0}; return (struct _cbor_stack){.top = NULL, .size = 0};
} }
void _cbor_stack_pop(struct _cbor_stack *stack) { void _cbor_stack_pop(struct _cbor_stack *stack) {
struct _cbor_stack_record *top = stack->top; struct _cbor_stack_record *top = stack->top;
stack->top = stack->top->lower; stack->top = stack->top->lower;
_CBOR_FREE(top); _cbor_free(top);
stack->size--; stack->size--;
} }
@ -23,7 +23,7 @@ struct _cbor_stack_record *_cbor_stack_push(struct _cbor_stack *stack,
size_t subitems) { size_t subitems) {
if (stack->size == CBOR_MAX_STACK_SIZE) return NULL; if (stack->size == CBOR_MAX_STACK_SIZE) return NULL;
struct _cbor_stack_record *new_top = struct _cbor_stack_record *new_top =
_CBOR_MALLOC(sizeof(struct _cbor_stack_record)); _cbor_malloc(sizeof(struct _cbor_stack_record));
if (new_top == NULL) return NULL; if (new_top == NULL) return NULL;
*new_top = (struct _cbor_stack_record){stack->top, item, subitems}; *new_top = (struct _cbor_stack_record){stack->top, item, subitems};

View File

@ -16,8 +16,18 @@ extern "C" {
/** Simple stack record for the parser */ /** Simple stack record for the parser */
struct _cbor_stack_record { struct _cbor_stack_record {
/** Pointer to the parent stack frame */
struct _cbor_stack_record *lower; struct _cbor_stack_record *lower;
/** Item under construction */
cbor_item_t *item; cbor_item_t *item;
/**
* How many outstanding subitems are expected.
*
* For example, when we see a new definite array, `subitems` is initialized to
* the array length. With every item added, the counter is decreased. When it
* reaches zero, the stack is popped and the complete item is propagated
* upwards.
*/
size_t subitems; size_t subitems;
}; };
@ -27,10 +37,12 @@ struct _cbor_stack {
size_t size; size_t size;
}; };
struct _cbor_stack _cbor_stack_init(); _CBOR_NODISCARD
struct _cbor_stack _cbor_stack_init(void);
void _cbor_stack_pop(struct _cbor_stack *); void _cbor_stack_pop(struct _cbor_stack *);
_CBOR_NODISCARD
struct _cbor_stack_record *_cbor_stack_push(struct _cbor_stack *, cbor_item_t *, struct _cbor_stack_record *_cbor_stack_push(struct _cbor_stack *, cbor_item_t *,
size_t); size_t);

View File

@ -6,6 +6,7 @@
*/ */
#include "unicode.h" #include "unicode.h"
#include <stdint.h>
#define UTF8_ACCEPT 0 #define UTF8_ACCEPT 0
#define UTF8_REJECT 1 #define UTF8_REJECT 1
@ -65,12 +66,12 @@ uint32_t _cbor_unicode_decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
return *state; return *state;
} }
size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length, uint64_t _cbor_unicode_codepoint_count(cbor_data source, uint64_t source_length,
struct _cbor_unicode_status* status) { struct _cbor_unicode_status* status) {
*status = *status =
(struct _cbor_unicode_status){.location = 0, .status = _CBOR_UNICODE_OK}; (struct _cbor_unicode_status){.location = 0, .status = _CBOR_UNICODE_OK};
uint32_t codepoint, state = UTF8_ACCEPT, res; uint32_t codepoint, state = UTF8_ACCEPT, res;
size_t pos = 0, count = 0; uint64_t pos = 0, count = 0;
for (; pos < source_length; pos++) { for (; pos < source_length; pos++) {
res = _cbor_unicode_decode(&state, &codepoint, source[pos]); res = _cbor_unicode_decode(&state, &codepoint, source[pos]);
@ -90,5 +91,5 @@ size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length,
error: error:
*status = (struct _cbor_unicode_status){.location = pos, *status = (struct _cbor_unicode_status){.location = pos,
.status = _CBOR_UNICODE_BADCP}; .status = _CBOR_UNICODE_BADCP};
return -1; return 0;
} }

View File

@ -19,11 +19,12 @@ enum _cbor_unicode_status_error { _CBOR_UNICODE_OK, _CBOR_UNICODE_BADCP };
/** Signals unicode validation error and possibly its location */ /** Signals unicode validation error and possibly its location */
struct _cbor_unicode_status { struct _cbor_unicode_status {
enum _cbor_unicode_status_error status; enum _cbor_unicode_status_error status;
size_t location; uint64_t location;
}; };
size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length, _CBOR_NODISCARD
struct _cbor_unicode_status* status); uint64_t _cbor_unicode_codepoint_count(cbor_data source, uint64_t source_length,
struct _cbor_unicode_status* status);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -8,36 +8,37 @@
#include "ints.h" #include "ints.h"
cbor_int_width cbor_int_get_width(const cbor_item_t *item) { cbor_int_width cbor_int_get_width(const cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
return item->metadata.int_metadata.width; return item->metadata.int_metadata.width;
} }
uint8_t cbor_get_uint8(const cbor_item_t *item) { uint8_t cbor_get_uint8(const cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_8); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_8);
return *item->data; return *item->data;
} }
uint16_t cbor_get_uint16(const cbor_item_t *item) { uint16_t cbor_get_uint16(const cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_16); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_16);
return *(uint16_t *)item->data; return *(uint16_t *)item->data;
} }
uint32_t cbor_get_uint32(const cbor_item_t *item) { uint32_t cbor_get_uint32(const cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_32); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_32);
return *(uint32_t *)item->data; return *(uint32_t *)item->data;
} }
uint64_t cbor_get_uint64(const cbor_item_t *item) { uint64_t cbor_get_uint64(const cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_64); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_64);
return *(uint64_t *)item->data; return *(uint64_t *)item->data;
} }
uint64_t cbor_get_int(const cbor_item_t *item) { uint64_t cbor_get_int(const cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
// cppcheck-suppress missingReturn
switch (cbor_int_get_width(item)) { switch (cbor_int_get_width(item)) {
case CBOR_INT_8: case CBOR_INT_8:
return cbor_get_uint8(item); return cbor_get_uint8(item);
@ -48,46 +49,44 @@ uint64_t cbor_get_int(const cbor_item_t *item) {
case CBOR_INT_64: case CBOR_INT_64:
return cbor_get_uint64(item); return cbor_get_uint64(item);
} }
// TODO: This should be handled in a default branch
return 0xDEADBEEF; /* Compiler complaints */
} }
void cbor_set_uint8(cbor_item_t *item, uint8_t value) { void cbor_set_uint8(cbor_item_t *item, uint8_t value) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_8); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_8);
*item->data = value; *item->data = value;
} }
void cbor_set_uint16(cbor_item_t *item, uint16_t value) { void cbor_set_uint16(cbor_item_t *item, uint16_t value) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_16); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_16);
*(uint16_t *)item->data = value; *(uint16_t *)item->data = value;
} }
void cbor_set_uint32(cbor_item_t *item, uint32_t value) { void cbor_set_uint32(cbor_item_t *item, uint32_t value) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_32); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_32);
*(uint32_t *)item->data = value; *(uint32_t *)item->data = value;
} }
void cbor_set_uint64(cbor_item_t *item, uint64_t value) { void cbor_set_uint64(cbor_item_t *item, uint64_t value) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_64); CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_64);
*(uint64_t *)item->data = value; *(uint64_t *)item->data = value;
} }
void cbor_mark_uint(cbor_item_t *item) { void cbor_mark_uint(cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
item->type = CBOR_TYPE_UINT; item->type = CBOR_TYPE_UINT;
} }
void cbor_mark_negint(cbor_item_t *item) { void cbor_mark_negint(cbor_item_t *item) {
assert(cbor_is_int(item)); CBOR_ASSERT(cbor_is_int(item));
item->type = CBOR_TYPE_NEGINT; item->type = CBOR_TYPE_NEGINT;
} }
cbor_item_t *cbor_new_int8() { cbor_item_t *cbor_new_int8(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 1); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 1);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1, .refcount = 1,
@ -96,8 +95,8 @@ cbor_item_t *cbor_new_int8() {
return item; return item;
} }
cbor_item_t *cbor_new_int16() { cbor_item_t *cbor_new_int16(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 2); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 2);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1, .refcount = 1,
@ -106,8 +105,8 @@ cbor_item_t *cbor_new_int16() {
return item; return item;
} }
cbor_item_t *cbor_new_int32() { cbor_item_t *cbor_new_int32(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 4); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1, .refcount = 1,
@ -116,8 +115,8 @@ cbor_item_t *cbor_new_int32() {
return item; return item;
} }
cbor_item_t *cbor_new_int64() { cbor_item_t *cbor_new_int64(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 8); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 8);
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t), *item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1, .refcount = 1,

View File

@ -23,42 +23,42 @@ extern "C" {
/** Extracts the integer value /** Extracts the integer value
* *
* @param item[borrow] positive or negative integer * @param item positive or negative integer
* @return the value * @return the value
*/ */
CBOR_EXPORT uint8_t cbor_get_uint8(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT uint8_t cbor_get_uint8(const cbor_item_t *item);
/** Extracts the integer value /** Extracts the integer value
* *
* @param item[borrow] positive or negative integer * @param item positive or negative integer
* @return the value * @return the value
*/ */
CBOR_EXPORT uint16_t cbor_get_uint16(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT uint16_t cbor_get_uint16(const cbor_item_t *item);
/** Extracts the integer value /** Extracts the integer value
* *
* @param item[borrow] positive or negative integer * @param item positive or negative integer
* @return the value * @return the value
*/ */
CBOR_EXPORT uint32_t cbor_get_uint32(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT uint32_t cbor_get_uint32(const cbor_item_t *item);
/** Extracts the integer value /** Extracts the integer value
* *
* @param item[borrow] positive or negative integer * @param item positive or negative integer
* @return the value * @return the value
*/ */
CBOR_EXPORT uint64_t cbor_get_uint64(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT uint64_t cbor_get_uint64(const cbor_item_t *item);
/** Extracts the integer value /** Extracts the integer value
* *
* @param item[borrow] positive or negative integer * @param item positive or negative integer
* @return the value, extended to `uint64_t` * @return the value, extended to `uint64_t`
*/ */
CBOR_EXPORT uint64_t cbor_get_int(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT uint64_t cbor_get_int(const cbor_item_t *item);
/** Assigns the integer value /** Assigns the integer value
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
* @param value the value to assign. For negative integer, the logical value is * @param value the value to assign. For negative integer, the logical value is
* `-value - 1` * `-value - 1`
*/ */
@ -66,7 +66,7 @@ CBOR_EXPORT void cbor_set_uint8(cbor_item_t *item, uint8_t value);
/** Assigns the integer value /** Assigns the integer value
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
* @param value the value to assign. For negative integer, the logical value is * @param value the value to assign. For negative integer, the logical value is
* `-value - 1` * `-value - 1`
*/ */
@ -74,7 +74,7 @@ CBOR_EXPORT void cbor_set_uint16(cbor_item_t *item, uint16_t value);
/** Assigns the integer value /** Assigns the integer value
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
* @param value the value to assign. For negative integer, the logical value is * @param value the value to assign. For negative integer, the logical value is
* `-value - 1` * `-value - 1`
*/ */
@ -82,7 +82,7 @@ CBOR_EXPORT void cbor_set_uint32(cbor_item_t *item, uint32_t value);
/** Assigns the integer value /** Assigns the integer value
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
* @param value the value to assign. For negative integer, the logical value is * @param value the value to assign. For negative integer, the logical value is
* `-value - 1` * `-value - 1`
*/ */
@ -90,16 +90,17 @@ CBOR_EXPORT void cbor_set_uint64(cbor_item_t *item, uint64_t value);
/** Queries the integer width /** Queries the integer width
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
* @return the width * @return the width
*/ */
CBOR_EXPORT cbor_int_width cbor_int_get_width(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT cbor_int_width
cbor_int_get_width(const cbor_item_t *item);
/** Marks the integer item as a positive integer /** Marks the integer item as a positive integer
* *
* The data value is not changed * The data value is not changed
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
*/ */
CBOR_EXPORT void cbor_mark_uint(cbor_item_t *item); CBOR_EXPORT void cbor_mark_uint(cbor_item_t *item);
@ -107,7 +108,7 @@ CBOR_EXPORT void cbor_mark_uint(cbor_item_t *item);
* *
* The data value is not changed * The data value is not changed
* *
* @param item[borrow] positive or negative integer item * @param item positive or negative integer item
*/ */
CBOR_EXPORT void cbor_mark_negint(cbor_item_t *item); CBOR_EXPORT void cbor_mark_negint(cbor_item_t *item);
@ -118,7 +119,7 @@ CBOR_EXPORT void cbor_mark_negint(cbor_item_t *item);
* @return **new** positive integer or `NULL` on memory allocation failure. The * @return **new** positive integer or `NULL` on memory allocation failure. The
* value is not initialized * value is not initialized
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_int8(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int8(void);
/** Allocates new integer with 2B width /** Allocates new integer with 2B width
* *
@ -127,7 +128,7 @@ CBOR_EXPORT cbor_item_t *cbor_new_int8();
* @return **new** positive integer or `NULL` on memory allocation failure. The * @return **new** positive integer or `NULL` on memory allocation failure. The
* value is not initialized * value is not initialized
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_int16(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int16(void);
/** Allocates new integer with 4B width /** Allocates new integer with 4B width
* *
@ -136,7 +137,7 @@ CBOR_EXPORT cbor_item_t *cbor_new_int16();
* @return **new** positive integer or `NULL` on memory allocation failure. The * @return **new** positive integer or `NULL` on memory allocation failure. The
* value is not initialized * value is not initialized
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_int32(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int32(void);
/** Allocates new integer with 8B width /** Allocates new integer with 8B width
* *
@ -145,63 +146,63 @@ CBOR_EXPORT cbor_item_t *cbor_new_int32();
* @return **new** positive integer or `NULL` on memory allocation failure. The * @return **new** positive integer or `NULL` on memory allocation failure. The
* value is not initialized * value is not initialized
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_int64(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_int64(void);
/** Constructs a new positive integer /** Constructs a new positive integer
* *
* @param value the value to use * @param value the value to use
* @return **new** positive integer or `NULL` on memory allocation failure * @return **new** positive integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_uint8(uint8_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint8(uint8_t value);
/** Constructs a new positive integer /** Constructs a new positive integer
* *
* @param value the value to use * @param value the value to use
* @return **new** positive integer or `NULL` on memory allocation failure * @return **new** positive integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_uint16(uint16_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint16(uint16_t value);
/** Constructs a new positive integer /** Constructs a new positive integer
* *
* @param value the value to use * @param value the value to use
* @return **new** positive integer or `NULL` on memory allocation failure * @return **new** positive integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_uint32(uint32_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint32(uint32_t value);
/** Constructs a new positive integer /** Constructs a new positive integer
* *
* @param value the value to use * @param value the value to use
* @return **new** positive integer or `NULL` on memory allocation failure * @return **new** positive integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_uint64(uint64_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_uint64(uint64_t value);
/** Constructs a new negative integer /** Constructs a new negative integer
* *
* @param value the value to use * @param value the value to use
* @return **new** negative integer or `NULL` on memory allocation failure * @return **new** negative integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_negint8(uint8_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint8(uint8_t value);
/** Constructs a new negative integer /** Constructs a new negative integer
* *
* @param value the value to use * @param value the value to use
* @return **new** negative integer or `NULL` on memory allocation failure * @return **new** negative integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_negint16(uint16_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint16(uint16_t value);
/** Constructs a new negative integer /** Constructs a new negative integer
* *
* @param value the value to use * @param value the value to use
* @return **new** negative integer or `NULL` on memory allocation failure * @return **new** negative integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_negint32(uint32_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint32(uint32_t value);
/** Constructs a new negative integer /** Constructs a new negative integer
* *
* @param value the value to use * @param value the value to use
* @return **new** negative integer or `NULL` on memory allocation failure * @return **new** negative integer or `NULL` on memory allocation failure
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_negint64(uint64_t value); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_negint64(uint64_t value);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -9,17 +9,17 @@
#include "internal/memory_utils.h" #include "internal/memory_utils.h"
size_t cbor_map_size(const cbor_item_t *item) { size_t cbor_map_size(const cbor_item_t *item) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
return item->metadata.map_metadata.end_ptr; return item->metadata.map_metadata.end_ptr;
} }
size_t cbor_map_allocated(const cbor_item_t *item) { size_t cbor_map_allocated(const cbor_item_t *item) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
return item->metadata.map_metadata.allocated; return item->metadata.map_metadata.allocated;
} }
cbor_item_t *cbor_new_definite_map(size_t size) { cbor_item_t *cbor_new_definite_map(size_t size) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -34,8 +34,8 @@ cbor_item_t *cbor_new_definite_map(size_t size) {
return item; return item;
} }
cbor_item_t *cbor_new_indefinite_map() { cbor_item_t *cbor_new_indefinite_map(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -50,7 +50,7 @@ cbor_item_t *cbor_new_indefinite_map() {
} }
bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key) { bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
struct _cbor_map_metadata *metadata = struct _cbor_map_metadata *metadata =
(struct _cbor_map_metadata *)&item->metadata; (struct _cbor_map_metadata *)&item->metadata;
if (cbor_map_is_definite(item)) { if (cbor_map_is_definite(item)) {
@ -66,7 +66,6 @@ bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key) {
if (metadata->end_ptr >= metadata->allocated) { if (metadata->end_ptr >= metadata->allocated) {
/* Exponential realloc */ /* Exponential realloc */
// Check for overflows first // Check for overflows first
// TODO: Explicitly test this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) { if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
return false; return false;
} }
@ -94,7 +93,7 @@ bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key) {
} }
bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) { bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
cbor_incref(value); cbor_incref(value);
cbor_map_handle(item)[ cbor_map_handle(item)[
/* Move one back since we are assuming _add_key (which increased the ptr) /* Move one back since we are assuming _add_key (which increased the ptr)
@ -105,13 +104,13 @@ bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value) {
} }
bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair) { bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
if (!_cbor_map_add_key(item, pair.key)) return false; if (!_cbor_map_add_key(item, pair.key)) return false;
return _cbor_map_add_value(item, pair.value); return _cbor_map_add_value(item, pair.value);
} }
bool cbor_map_is_definite(const cbor_item_t *item) { bool cbor_map_is_definite(const cbor_item_t *item) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
return item->metadata.map_metadata.type == _CBOR_METADATA_DEFINITE; return item->metadata.map_metadata.type == _CBOR_METADATA_DEFINITE;
} }
@ -120,6 +119,6 @@ bool cbor_map_is_indefinite(const cbor_item_t *item) {
} }
struct cbor_pair *cbor_map_handle(const cbor_item_t *item) { struct cbor_pair *cbor_map_handle(const cbor_item_t *item) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
return (struct cbor_pair *)item->data; return (struct cbor_pair *)item->data;
} }

View File

@ -23,87 +23,96 @@ extern "C" {
/** Get the number of pairs /** Get the number of pairs
* *
* @param item[borrow] A map * @param item A map
* @return The number of pairs * @return The number of pairs
*/ */
CBOR_EXPORT size_t cbor_map_size(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_map_size(const cbor_item_t *item);
/** Get the size of the allocated storage /** Get the size of the allocated storage
* *
* @param item[borrow] A map * @param item A map
* @return Allocated storage size (as the number of #cbor_pair items) * @return Allocated storage size (as the number of #cbor_pair items)
*/ */
CBOR_EXPORT size_t cbor_map_allocated(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_map_allocated(const cbor_item_t *item);
/** Create a new definite map /** Create a new definite map
* *
* @param size The number of slots to preallocate * @param size The number of slots to preallocate
* @return **new** definite map. `NULL` on malloc failure. * @return Reference to the new map item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_definite_map(size_t size); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_definite_map(size_t size);
/** Create a new indefinite map /** Create a new indefinite map
* *
* @param size The number of slots to preallocate * @return Reference to the new map item. The item's reference count is
* @return **new** definite map. `NULL` on malloc failure. * initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_indefinite_map(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_indefinite_map(void);
/** Add a pair to the map /** Add a pair to the map
* *
* For definite maps, items can only be added to the preallocated space. For * For definite maps, items can only be added to the preallocated space. For
* indefinite maps, the storage will be expanded as needed * indefinite maps, the storage will be expanded as needed
* *
* @param item[borrow] A map * @param item A map
* @param pair[incref] The key-value pair to add (incref is member-wise) * @param pair The key-value pair to add. Reference count of the #cbor_pair.key
* @return `true` on success, `false` if either reallocation failed or the * and #cbor_pair.value will be increased by one.
* preallcoated storage is full * @return `true` on success, `false` if memory allocation failed (indefinite
* maps) or the preallocated storage is full (definite maps)
*/ */
CBOR_EXPORT bool cbor_map_add(cbor_item_t *item, struct cbor_pair pair); _CBOR_NODISCARD CBOR_EXPORT bool cbor_map_add(cbor_item_t *item,
struct cbor_pair pair);
/** Add a key to the map /** Add a key to the map
* *
* Sets the value to `NULL`. Internal API. * Sets the value to `NULL`. Internal API.
* *
* @param item[borrow] A map * @param item A map
* @param key[incref] The key * @param key The key, Its reference count will be be increased by one.
* @return `true` on success, `false` if either reallocation failed or the * @return `true` on success, `false` if either reallocation failed or the
* preallcoated storage is full * preallocated storage is full
*/ */
CBOR_EXPORT bool _cbor_map_add_key(cbor_item_t *item, cbor_item_t *key); _CBOR_NODISCARD CBOR_EXPORT bool _cbor_map_add_key(cbor_item_t *item,
cbor_item_t *key);
/** Add a value to the map /** Add a value to the map
* *
* Assumes that #_cbor_map_add_key has been called. Internal API. * Assumes that #_cbor_map_add_key has been called. Internal API.
* *
* @param item[borrow] A map * @param item A map
* @param key[incref] The value * @param value The value. Its reference count will be be increased by one.
* @return `true` on success, `false` if either reallocation failed or the * @return `true` on success, `false` if either reallocation failed or the
* preallcoated storage is full * preallocated storage is full
*/ */
CBOR_EXPORT bool _cbor_map_add_value(cbor_item_t *item, cbor_item_t *value); _CBOR_NODISCARD CBOR_EXPORT bool _cbor_map_add_value(cbor_item_t *item,
cbor_item_t *value);
/** Is this map definite? /** Is this map definite?
* *
* @param item[borrow] A map * @param item A map
* @return Is this map definite? * @return Is this map definite?
*/ */
CBOR_EXPORT bool cbor_map_is_definite(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT bool cbor_map_is_definite(const cbor_item_t *item);
/** Is this map indefinite? /** Is this map indefinite?
* *
* @param item[borrow] A map * @param item A map
* @return Is this map indefinite? * @return Is this map indefinite?
*/ */
CBOR_EXPORT bool cbor_map_is_indefinite(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT bool cbor_map_is_indefinite(
const cbor_item_t *item);
/** Get the pairs storage /** Get the pairs storage
* *
* @param item[borrow] A map * @param item A map
* @return Array of #cbor_map_size pairs. Manipulation is possible as long as * @return Array of #cbor_map_size pairs. Manipulation is possible as long as
* references remain valid. * references remain valid.
*/ */
CBOR_EXPORT struct cbor_pair *cbor_map_handle(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT struct cbor_pair *cbor_map_handle(
const cbor_item_t *item);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -19,6 +19,7 @@
size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
// cppcheck-suppress missingReturn
switch (cbor_typeof(item)) { switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT: case CBOR_TYPE_UINT:
return cbor_serialize_uint(item, buffer, buffer_size); return cbor_serialize_uint(item, buffer, buffer_size);
@ -36,44 +37,144 @@ size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer,
return cbor_serialize_tag(item, buffer, buffer_size); return cbor_serialize_tag(item, buffer, buffer_size);
case CBOR_TYPE_FLOAT_CTRL: case CBOR_TYPE_FLOAT_CTRL:
return cbor_serialize_float_ctrl(item, buffer, buffer_size); return cbor_serialize_float_ctrl(item, buffer, buffer_size);
default: }
return 0; }
/** Largest integer that can be encoded as embedded in the item leading byte. */
const uint64_t kMaxEmbeddedInt = 23;
/** How many bytes will a tag for a nested item of a given `size` take when
* encoded.*/
size_t _cbor_encoded_header_size(uint64_t size) {
if (size <= kMaxEmbeddedInt)
return 1;
else if (size <= UINT8_MAX)
return 2;
else if (size <= UINT16_MAX)
return 3;
else if (size <= UINT32_MAX)
return 5;
else
return 9;
}
size_t cbor_serialized_size(const cbor_item_t *item) {
// cppcheck-suppress missingReturn
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT:
case CBOR_TYPE_NEGINT:
switch (cbor_int_get_width(item)) {
case CBOR_INT_8:
if (cbor_get_uint8(item) <= kMaxEmbeddedInt) return 1;
return 2;
case CBOR_INT_16:
return 3;
case CBOR_INT_32:
return 5;
case CBOR_INT_64:
return 9;
}
// 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
// byte.
case CBOR_TYPE_BYTESTRING: {
if (cbor_bytestring_is_definite(item)) {
size_t header_size =
_cbor_encoded_header_size(cbor_bytestring_length(item));
if (cbor_bytestring_length(item) == 0) return header_size;
return _cbor_safe_signaling_add(header_size,
cbor_bytestring_length(item));
}
size_t indef_bytestring_size = 2; // Leading byte + break
cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
indef_bytestring_size = _cbor_safe_signaling_add(
indef_bytestring_size, cbor_serialized_size(chunks[i]));
}
return indef_bytestring_size;
}
case CBOR_TYPE_STRING: {
if (cbor_string_is_definite(item)) {
size_t header_size =
_cbor_encoded_header_size(cbor_string_length(item));
if (cbor_string_length(item) == 0) return header_size;
return _cbor_safe_signaling_add(header_size, cbor_string_length(item));
}
size_t indef_string_size = 2; // Leading byte + break
cbor_item_t **chunks = cbor_string_chunks_handle(item);
for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
indef_string_size = _cbor_safe_signaling_add(
indef_string_size, cbor_serialized_size(chunks[i]));
}
return indef_string_size;
}
case CBOR_TYPE_ARRAY: {
size_t array_size = cbor_array_is_definite(item)
? _cbor_encoded_header_size(cbor_array_size(item))
: 2; // Leading byte + break
cbor_item_t **items = cbor_array_handle(item);
for (size_t i = 0; i < cbor_array_size(item); i++) {
array_size = _cbor_safe_signaling_add(array_size,
cbor_serialized_size(items[i]));
}
return array_size;
}
case CBOR_TYPE_MAP: {
size_t map_size = cbor_map_is_definite(item)
? _cbor_encoded_header_size(cbor_map_size(item))
: 2; // Leading byte + break
struct cbor_pair *items = cbor_map_handle(item);
for (size_t i = 0; i < cbor_map_size(item); i++) {
map_size = _cbor_safe_signaling_add(
map_size,
_cbor_safe_signaling_add(cbor_serialized_size(items[i].key),
cbor_serialized_size(items[i].value)));
}
return map_size;
}
case CBOR_TYPE_TAG: {
return _cbor_safe_signaling_add(
_cbor_encoded_header_size(cbor_tag_value(item)),
cbor_serialized_size(cbor_move(cbor_tag_item(item))));
}
case CBOR_TYPE_FLOAT_CTRL:
switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0:
return _cbor_encoded_header_size(cbor_ctrl_value(item));
case CBOR_FLOAT_16:
return 3;
case CBOR_FLOAT_32:
return 5;
case CBOR_FLOAT_64:
return 9;
}
} }
} }
size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer, size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer,
size_t *buffer_size) { size_t *buffer_size) {
size_t bfr_size = 32; *buffer = NULL;
unsigned char *bfr = _CBOR_MALLOC(bfr_size), *tmp_bfr; size_t serialized_size = cbor_serialized_size(item);
if (bfr == NULL) { if (serialized_size == 0) {
if (buffer_size != NULL) *buffer_size = 0;
return 0;
}
*buffer = _cbor_malloc(serialized_size);
if (*buffer == NULL) {
if (buffer_size != NULL) *buffer_size = 0;
return 0; return 0;
} }
size_t written; size_t written = cbor_serialize(item, *buffer, serialized_size);
CBOR_ASSERT(written == serialized_size);
/* This is waaay too optimistic - figure out something smarter (eventually) */ if (buffer_size != NULL) *buffer_size = serialized_size;
while ((written = cbor_serialize(item, bfr, bfr_size)) == 0) {
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, bfr_size)) {
_CBOR_FREE(bfr);
return 0;
}
tmp_bfr = _CBOR_REALLOC(bfr, bfr_size *= 2);
if (tmp_bfr == NULL) {
_CBOR_FREE(bfr);
return 0;
}
bfr = tmp_bfr;
}
*buffer = bfr;
*buffer_size = bfr_size;
return written; return written;
} }
size_t cbor_serialize_uint(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) { size_t buffer_size) {
assert(cbor_isa_uint(item)); CBOR_ASSERT(cbor_isa_uint(item));
// cppcheck-suppress missingReturn
switch (cbor_int_get_width(item)) { switch (cbor_int_get_width(item)) {
case CBOR_INT_8: case CBOR_INT_8:
return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size); return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size);
@ -83,14 +184,13 @@ size_t cbor_serialize_uint(const cbor_item_t *item, unsigned char *buffer,
return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size); return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size);
case CBOR_INT_64: case CBOR_INT_64:
return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size); return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size);
default:
return 0;
} }
} }
size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
assert(cbor_isa_negint(item)); CBOR_ASSERT(cbor_isa_negint(item));
// cppcheck-suppress missingReturn
switch (cbor_int_get_width(item)) { switch (cbor_int_get_width(item)) {
case CBOR_INT_8: case CBOR_INT_8:
return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size); return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size);
@ -100,173 +200,158 @@ size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer,
return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size); return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size);
case CBOR_INT_64: case CBOR_INT_64:
return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size); return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size);
default:
return 0;
} }
} }
size_t cbor_serialize_bytestring(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize_bytestring(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
assert(cbor_isa_bytestring(item)); CBOR_ASSERT(cbor_isa_bytestring(item));
if (cbor_bytestring_is_definite(item)) { if (cbor_bytestring_is_definite(item)) {
size_t length = cbor_bytestring_length(item); size_t length = cbor_bytestring_length(item);
size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size); size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size);
if (written && (buffer_size - written >= length)) { if (written > 0 && (buffer_size - written >= length)) {
memcpy(buffer + written, cbor_bytestring_handle(item), length); memcpy(buffer + written, cbor_bytestring_handle(item), length);
return written + length; return written + length;
} else }
return 0; return 0;
} else { } else {
assert(cbor_bytestring_is_indefinite(item)); CBOR_ASSERT(cbor_bytestring_is_indefinite(item));
size_t chunk_count = cbor_bytestring_chunk_count(item); size_t chunk_count = cbor_bytestring_chunk_count(item);
size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size); size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size);
if (written == 0) return 0; if (written == 0) return 0;
cbor_item_t **chunks = cbor_bytestring_chunks_handle(item); cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
for (size_t i = 0; i < chunk_count; i++) { for (size_t i = 0; i < chunk_count; i++) {
size_t chunk_written = cbor_serialize_bytestring( size_t chunk_written = cbor_serialize_bytestring(
chunks[i], buffer + written, buffer_size - written); chunks[i], buffer + written, buffer_size - written);
if (chunk_written == 0) if (chunk_written == 0) return 0;
return 0; written += chunk_written;
else
written += chunk_written;
} }
if (cbor_encode_break(buffer + written, buffer_size - written) > 0)
return written + 1; size_t break_written =
else cbor_encode_break(buffer + written, buffer_size - written);
return 0; if (break_written == 0) return 0;
return written + break_written;
} }
} }
size_t cbor_serialize_string(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize_string(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
if (cbor_string_is_definite(item)) { if (cbor_string_is_definite(item)) {
size_t length = cbor_string_length(item); size_t length = cbor_string_length(item);
size_t written = cbor_encode_string_start(length, buffer, buffer_size); size_t written = cbor_encode_string_start(length, buffer, buffer_size);
if (written && (buffer_size - written >= length)) { if (written && (buffer_size - written >= length)) {
memcpy(buffer + written, cbor_string_handle(item), length); memcpy(buffer + written, cbor_string_handle(item), length);
return written + length; return written + length;
} else }
return 0; return 0;
} else { } else {
assert(cbor_string_is_indefinite(item)); CBOR_ASSERT(cbor_string_is_indefinite(item));
size_t chunk_count = cbor_string_chunk_count(item); size_t chunk_count = cbor_string_chunk_count(item);
size_t written = cbor_encode_indef_string_start(buffer, buffer_size); size_t written = cbor_encode_indef_string_start(buffer, buffer_size);
if (written == 0) return 0; if (written == 0) return 0;
cbor_item_t **chunks = cbor_string_chunks_handle(item); cbor_item_t **chunks = cbor_string_chunks_handle(item);
for (size_t i = 0; i < chunk_count; i++) { for (size_t i = 0; i < chunk_count; i++) {
size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written, size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written,
buffer_size - written); buffer_size - written);
if (chunk_written == 0) if (chunk_written == 0) return 0;
return 0; written += chunk_written;
else
written += chunk_written;
} }
if (cbor_encode_break(buffer + written, buffer_size - written) > 0)
return written + 1; size_t break_written =
else cbor_encode_break(buffer + written, buffer_size - written);
return 0; if (break_written == 0) return 0;
return written + break_written;
} }
} }
size_t cbor_serialize_array(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize_array(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
assert(cbor_isa_array(item)); CBOR_ASSERT(cbor_isa_array(item));
size_t size = cbor_array_size(item), written = 0; size_t size = cbor_array_size(item), written = 0;
cbor_item_t **handle = cbor_array_handle(item); cbor_item_t **handle = cbor_array_handle(item);
if (cbor_array_is_definite(item)) { if (cbor_array_is_definite(item)) {
written = cbor_encode_array_start(size, buffer, buffer_size); written = cbor_encode_array_start(size, buffer, buffer_size);
} else { } else {
assert(cbor_array_is_indefinite(item)); CBOR_ASSERT(cbor_array_is_indefinite(item));
written = cbor_encode_indef_array_start(buffer, buffer_size); written = cbor_encode_indef_array_start(buffer, buffer_size);
} }
if (written == 0) return 0; if (written == 0) return 0;
size_t item_written;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
item_written = size_t item_written =
cbor_serialize(*(handle++), buffer + written, buffer_size - written); cbor_serialize(*(handle++), buffer + written, buffer_size - written);
if (item_written == 0) if (item_written == 0) return 0;
return 0; written += item_written;
else
written += item_written;
} }
if (cbor_array_is_definite(item)) { if (cbor_array_is_definite(item)) {
return written; return written;
} else { } else {
assert(cbor_array_is_indefinite(item)); CBOR_ASSERT(cbor_array_is_indefinite(item));
item_written = cbor_encode_break(buffer + written, buffer_size - written); size_t break_written =
if (item_written == 0) cbor_encode_break(buffer + written, buffer_size - written);
return 0; if (break_written == 0) return 0;
else return written + break_written;
return written + 1;
} }
} }
size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
assert(cbor_isa_map(item)); CBOR_ASSERT(cbor_isa_map(item));
size_t size = cbor_map_size(item), written = 0; size_t size = cbor_map_size(item), written = 0;
struct cbor_pair *handle = cbor_map_handle(item); struct cbor_pair *handle = cbor_map_handle(item);
if (cbor_map_is_definite(item)) { if (cbor_map_is_definite(item)) {
written = cbor_encode_map_start(size, buffer, buffer_size); written = cbor_encode_map_start(size, buffer, buffer_size);
} else { } else {
assert(cbor_map_is_indefinite(item)); CBOR_ASSERT(cbor_map_is_indefinite(item));
written = cbor_encode_indef_map_start(buffer, buffer_size); written = cbor_encode_indef_map_start(buffer, buffer_size);
} }
if (written == 0) return 0; if (written == 0) return 0;
size_t item_written;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
item_written = size_t item_written =
cbor_serialize(handle->key, buffer + written, buffer_size - written); cbor_serialize(handle->key, buffer + written, buffer_size - written);
if (item_written == 0) if (item_written == 0) {
return 0; return 0;
else }
written += item_written; written += item_written;
item_written = cbor_serialize((handle++)->value, buffer + written, item_written = cbor_serialize((handle++)->value, buffer + written,
buffer_size - written); buffer_size - written);
if (item_written == 0) if (item_written == 0) return 0;
return 0; written += item_written;
else
written += item_written;
} }
if (cbor_map_is_definite(item)) { if (cbor_map_is_definite(item)) {
return written; return written;
} else { } else {
assert(cbor_map_is_indefinite(item)); CBOR_ASSERT(cbor_map_is_indefinite(item));
item_written = cbor_encode_break(buffer + written, buffer_size - written); size_t break_written =
if (item_written == 0) cbor_encode_break(buffer + written, buffer_size - written);
return 0; if (break_written == 0) return 0;
else return written + break_written;
return written + 1;
} }
} }
size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer, size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) { size_t buffer_size) {
assert(cbor_isa_tag(item)); CBOR_ASSERT(cbor_isa_tag(item));
size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size); size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size);
if (written == 0) return 0; if (written == 0) return 0;
size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)), size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)),
buffer + written, buffer_size - written); buffer + written, buffer_size - written);
if (item_written == 0) if (item_written == 0) return 0;
return 0; return written + item_written;
else
return written + item_written;
} }
size_t cbor_serialize_float_ctrl(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) { size_t buffer_size) {
assert(cbor_isa_float_ctrl(item)); CBOR_ASSERT(cbor_isa_float_ctrl(item));
// cppcheck-suppress missingReturn
switch (cbor_float_get_width(item)) { switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0: case CBOR_FLOAT_0:
/* CTRL - special treatment */ /* CTRL - special treatment */
@ -280,7 +365,4 @@ size_t cbor_serialize_float_ctrl(const cbor_item_t *item, unsigned char *buffer,
return cbor_encode_double(cbor_float_get_float8(item), buffer, return cbor_encode_double(cbor_float_get_float8(item), buffer,
buffer_size); buffer_size);
} }
/* Should never happen - make the compiler happy */
return 0;
} }

View File

@ -23,110 +23,143 @@ extern "C" {
/** Serialize the given item /** Serialize the given item
* *
* @param item[borrow] A data item * @param item A data item
* @param buffer Buffer to serialize to * @param buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result. 0 on failure.
*/ */
CBOR_EXPORT size_t cbor_serialize(const cbor_item_t *item, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize(const cbor_item_t *item,
cbor_mutable_data buffer, size_t buffer_size); cbor_mutable_data buffer,
size_t buffer_size);
/** Compute the length (in bytes) of the item when serialized using
* `cbor_serialize`.
*
* Time complexity is proportional to the number of nested items.
*
* @param item A data item
* @return Length (>= 1) of the item when serialized. 0 if the length overflows
* `size_t`.
*/
_CBOR_NODISCARD CBOR_EXPORT size_t
cbor_serialized_size(const cbor_item_t *item);
/** Serialize the given item, allocating buffers as needed /** Serialize the given item, allocating buffers as needed
*
* Since libcbor v0.10, the return value is always the same as `buffer_size` (if
* provided, see https://github.com/PJK/libcbor/pull/251/). New clients should
* ignore the return value.
* *
* \rst * \rst
* .. warning:: It is your responsibility to free the buffer using an * .. warning:: It is the caller's responsibility to free the buffer using an
* appropriate ``free`` implementation. * appropriate ``free`` implementation.
* \endrst * \endrst
* *
* @param item[borrow] A data item * @param item A data item
* @param buffer[out] Buffer containing the result * @param[out] buffer Buffer containing the result
* @param buffer_size[out] Size of the \p buffer * @param[out] buffer_size Size of the \p buffer, or 0 on memory allocation
* @return Length of the result. 0 on failure, in which case \p buffer is * failure.
* ``NULL``. * @return Length of the result in bytes
* @return 0 on memory allocation failure, in which case \p buffer is `NULL`.
*/ */
CBOR_EXPORT size_t cbor_serialize_alloc(const cbor_item_t *item, CBOR_EXPORT size_t cbor_serialize_alloc(const cbor_item_t *item,
cbor_mutable_data *buffer, unsigned char **buffer,
size_t *buffer_size); size_t *buffer_size);
/** Serialize an uint /** Serialize an uint
* *
* @param item[borrow] A uint * @param item A uint
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result
*/ */
CBOR_EXPORT size_t cbor_serialize_uint(const cbor_item_t *, cbor_mutable_data, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_uint(const cbor_item_t *item,
size_t); cbor_mutable_data buffer,
size_t buffer_size);
/** Serialize a negint /** Serialize a negint
* *
* @param item[borrow] A neging * @param item A negint
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result
*/ */
CBOR_EXPORT size_t cbor_serialize_negint(const cbor_item_t *, cbor_mutable_data, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_negint(
size_t); const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize a bytestring /** Serialize a bytestring
* *
* @param item[borrow] A bytestring * @param item A bytestring
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may
* still be modified
*/ */
CBOR_EXPORT size_t cbor_serialize_bytestring(const cbor_item_t *, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_bytestring(
cbor_mutable_data, size_t); const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize a string /** Serialize a string
* *
* @param item[borrow] A string * @param item A string
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may
* still be modified
*/ */
CBOR_EXPORT size_t cbor_serialize_string(const cbor_item_t *, cbor_mutable_data, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_string(
size_t); const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize an array /** Serialize an array
* *
* @param item[borrow] An array * @param item An array
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may
* still be modified
*/ */
CBOR_EXPORT size_t cbor_serialize_array(const cbor_item_t *, cbor_mutable_data, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_array(
size_t); const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize a map /** Serialize a map
* *
* @param item[borrow] A map * @param item A map
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may
* still be modified
*/ */
CBOR_EXPORT size_t cbor_serialize_map(const cbor_item_t *, cbor_mutable_data, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_map(const cbor_item_t *item,
size_t); cbor_mutable_data buffer,
size_t buffer_size);
/** Serialize a tag /** Serialize a tag
* *
* @param item[borrow] A tag * @param item A tag
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result. The \p buffer may
* still be modified
*/ */
CBOR_EXPORT size_t cbor_serialize_tag(const cbor_item_t *, cbor_mutable_data, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_tag(const cbor_item_t *item,
size_t); cbor_mutable_data buffer,
size_t buffer_size);
/** Serialize a /** Serialize a
* *
* @param item[borrow] A float or ctrl * @param item A float or ctrl
* @param buffer Buffer to serialize to * @param[out] buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer * @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure. * @return Length of the result
* @return 0 if the \p buffer_size doesn't fit the result
*/ */
CBOR_EXPORT size_t cbor_serialize_float_ctrl(const cbor_item_t *, _CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_float_ctrl(
cbor_mutable_data, size_t); const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -8,7 +8,7 @@
#include "streaming.h" #include "streaming.h"
#include "internal/loaders.h" #include "internal/loaders.h"
bool static claim_bytes(size_t required, size_t provided, static bool claim_bytes(size_t required, size_t provided,
struct cbor_decoder_result *result) { struct cbor_decoder_result *result) {
if (required > (provided - result->read)) { if (required > (provided - result->read)) {
result->required = required + result->read; result->required = required + result->read;
@ -22,6 +22,24 @@ bool static claim_bytes(size_t required, size_t provided,
} }
} }
// Use implicit capture as an exception to avoid the super long parameter list
#define CLAIM_BYTES_AND_INVOKE(callback_name, length, source_extra_offset) \
do { \
if (claim_bytes(length, source_size, &result)) { \
callbacks->callback_name(context, source + 1 + source_extra_offset, \
length); \
} \
} while (0)
#define READ_CLAIM_INVOKE(callback_name, length_reader, length_bytes) \
do { \
if (claim_bytes(length_bytes, source_size, &result)) { \
uint64_t length = length_reader(source + 1); \
CLAIM_BYTES_AND_INVOKE(callback_name, length, length_bytes); \
} \
return result; \
} while (0)
struct cbor_decoder_result cbor_stream_decode( struct cbor_decoder_result cbor_stream_decode(
cbor_data source, size_t source_size, cbor_data source, size_t source_size,
const struct cbor_callbacks *callbacks, void *context) { const struct cbor_callbacks *callbacks, void *context) {
@ -98,7 +116,7 @@ struct cbor_decoder_result cbor_stream_decode(
case 0x1E: /* Fallthrough */ case 0x1E: /* Fallthrough */
case 0x1F: case 0x1F:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0x20: /* Fallthrough */ case 0x20: /* Fallthrough */
case 0x21: /* Fallthrough */ case 0x21: /* Fallthrough */
case 0x22: /* Fallthrough */ case 0x22: /* Fallthrough */
@ -166,7 +184,7 @@ struct cbor_decoder_result cbor_stream_decode(
case 0x3E: /* Fallthrough */ case 0x3E: /* Fallthrough */
case 0x3F: case 0x3F:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0x40: /* Fallthrough */ case 0x40: /* Fallthrough */
case 0x41: /* Fallthrough */ case 0x41: /* Fallthrough */
case 0x42: /* Fallthrough */ case 0x42: /* Fallthrough */
@ -193,63 +211,27 @@ struct cbor_decoder_result cbor_stream_decode(
case 0x57: case 0x57:
/* Embedded length byte string */ /* Embedded length byte string */
{ {
size_t length = uint64_t length = _cbor_load_uint8(source) - 0x40; /* 0x40 offset */
(size_t)_cbor_load_uint8(source) - 0x40; /* 0x40 offset */ CLAIM_BYTES_AND_INVOKE(byte_string, length, 0);
if (claim_bytes(length, source_size, &result)) {
callbacks->byte_string(context, source + 1, length);
}
return result; return result;
} }
case 0x58: case 0x58:
/* One byte length byte string */ /* One byte length byte string */
// TODO template this? READ_CLAIM_INVOKE(byte_string, _cbor_load_uint8, 1);
{
if (claim_bytes(1, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint8(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->byte_string(context, source + 1 + 1, length);
}
}
return result;
}
case 0x59: case 0x59:
/* Two bytes length byte string */ /* Two bytes length byte string */
{ READ_CLAIM_INVOKE(byte_string, _cbor_load_uint16, 2);
if (claim_bytes(2, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint16(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->byte_string(context, source + 1 + 2, length);
}
}
return result;
}
case 0x5A: case 0x5A:
/* Four bytes length byte string */ /* Four bytes length byte string */
{ READ_CLAIM_INVOKE(byte_string, _cbor_load_uint32, 4);
if (claim_bytes(4, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint32(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->byte_string(context, source + 1 + 4, length);
}
}
return result;
}
case 0x5B: case 0x5B:
/* Eight bytes length byte string */ /* Eight bytes length byte string */
{ READ_CLAIM_INVOKE(byte_string, _cbor_load_uint64, 8);
if (claim_bytes(8, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint64(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->byte_string(context, source + 1 + 8, length);
}
}
return result;
}
case 0x5C: /* Fallthrough */ case 0x5C: /* Fallthrough */
case 0x5D: /* Fallthrough */ case 0x5D: /* Fallthrough */
case 0x5E: case 0x5E:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0x5F: case 0x5F:
/* Indefinite byte string */ /* Indefinite byte string */
{ {
@ -282,62 +264,27 @@ struct cbor_decoder_result cbor_stream_decode(
case 0x77: case 0x77:
/* Embedded one byte length string */ /* Embedded one byte length string */
{ {
size_t length = uint64_t length = _cbor_load_uint8(source) - 0x60; /* 0x60 offset */
(size_t)_cbor_load_uint8(source) - 0x60; /* 0x60 offset */ CLAIM_BYTES_AND_INVOKE(string, length, 0);
if (claim_bytes(length, source_size, &result)) {
callbacks->string(context, source + 1, length);
}
return result; return result;
} }
case 0x78: case 0x78:
/* One byte length string */ /* One byte length string */
{ READ_CLAIM_INVOKE(string, _cbor_load_uint8, 1);
if (claim_bytes(1, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint8(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->string(context, source + 1 + 1, length);
}
}
return result;
}
case 0x79: case 0x79:
/* Two bytes length string */ /* Two bytes length string */
{ READ_CLAIM_INVOKE(string, _cbor_load_uint16, 2);
if (claim_bytes(2, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint16(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->string(context, source + 1 + 2, length);
}
}
return result;
}
case 0x7A: case 0x7A:
/* Four bytes length string */ /* Four bytes length string */
{ READ_CLAIM_INVOKE(string, _cbor_load_uint32, 4);
if (claim_bytes(4, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint32(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->string(context, source + 1 + 4, length);
}
}
return result;
}
case 0x7B: case 0x7B:
/* Eight bytes length string */ /* Eight bytes length string */
{ READ_CLAIM_INVOKE(string, _cbor_load_uint64, 8);
if (claim_bytes(8, source_size, &result)) {
size_t length = (size_t)_cbor_load_uint64(source + 1);
if (claim_bytes(length, source_size, &result)) {
callbacks->string(context, source + 1 + 8, length);
}
}
return result;
}
case 0x7C: /* Fallthrough */ case 0x7C: /* Fallthrough */
case 0x7D: /* Fallthrough */ case 0x7D: /* Fallthrough */
case 0x7E: case 0x7E:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0x7F: case 0x7F:
/* Indefinite length string */ /* Indefinite length string */
{ {
@ -371,14 +318,14 @@ struct cbor_decoder_result cbor_stream_decode(
/* Embedded one byte length array */ /* Embedded one byte length array */
{ {
callbacks->array_start( callbacks->array_start(
context, (size_t)_cbor_load_uint8(source) - 0x80); /* 0x40 offset */ context, _cbor_load_uint8(source) - 0x80); /* 0x40 offset */
return result; return result;
} }
case 0x98: case 0x98:
/* One byte length array */ /* One byte length array */
{ {
if (claim_bytes(1, source_size, &result)) { if (claim_bytes(1, source_size, &result)) {
callbacks->array_start(context, (size_t)_cbor_load_uint8(source + 1)); callbacks->array_start(context, _cbor_load_uint8(source + 1));
} }
return result; return result;
} }
@ -386,8 +333,7 @@ struct cbor_decoder_result cbor_stream_decode(
/* Two bytes length array */ /* Two bytes length array */
{ {
if (claim_bytes(2, source_size, &result)) { if (claim_bytes(2, source_size, &result)) {
callbacks->array_start(context, callbacks->array_start(context, _cbor_load_uint16(source + 1));
(size_t)_cbor_load_uint16(source + 1));
} }
return result; return result;
} }
@ -395,8 +341,7 @@ struct cbor_decoder_result cbor_stream_decode(
/* Four bytes length array */ /* Four bytes length array */
{ {
if (claim_bytes(4, source_size, &result)) { if (claim_bytes(4, source_size, &result)) {
callbacks->array_start(context, callbacks->array_start(context, _cbor_load_uint32(source + 1));
(size_t)_cbor_load_uint32(source + 1));
} }
return result; return result;
} }
@ -404,8 +349,7 @@ struct cbor_decoder_result cbor_stream_decode(
/* Eight bytes length array */ /* Eight bytes length array */
{ {
if (claim_bytes(8, source_size, &result)) { if (claim_bytes(8, source_size, &result)) {
callbacks->array_start(context, callbacks->array_start(context, _cbor_load_uint64(source + 1));
(size_t)_cbor_load_uint64(source + 1));
} }
return result; return result;
} }
@ -413,7 +357,7 @@ struct cbor_decoder_result cbor_stream_decode(
case 0x9D: /* Fallthrough */ case 0x9D: /* Fallthrough */
case 0x9E: case 0x9E:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0x9F: case 0x9F:
/* Indefinite length array */ /* Indefinite length array */
{ {
@ -446,15 +390,15 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xB7: case 0xB7:
/* Embedded one byte length map */ /* Embedded one byte length map */
{ {
callbacks->map_start( callbacks->map_start(context,
context, (size_t)_cbor_load_uint8(source) - 0xA0); /* 0xA0 offset */ _cbor_load_uint8(source) - 0xA0); /* 0xA0 offset */
return result; return result;
} }
case 0xB8: case 0xB8:
/* One byte length map */ /* One byte length map */
{ {
if (claim_bytes(1, source_size, &result)) { if (claim_bytes(1, source_size, &result)) {
callbacks->map_start(context, (size_t)_cbor_load_uint8(source + 1)); callbacks->map_start(context, _cbor_load_uint8(source + 1));
} }
return result; return result;
} }
@ -462,7 +406,7 @@ struct cbor_decoder_result cbor_stream_decode(
/* Two bytes length map */ /* Two bytes length map */
{ {
if (claim_bytes(2, source_size, &result)) { if (claim_bytes(2, source_size, &result)) {
callbacks->map_start(context, (size_t)_cbor_load_uint16(source + 1)); callbacks->map_start(context, _cbor_load_uint16(source + 1));
} }
return result; return result;
} }
@ -470,7 +414,7 @@ struct cbor_decoder_result cbor_stream_decode(
/* Four bytes length map */ /* Four bytes length map */
{ {
if (claim_bytes(4, source_size, &result)) { if (claim_bytes(4, source_size, &result)) {
callbacks->map_start(context, (size_t)_cbor_load_uint32(source + 1)); callbacks->map_start(context, _cbor_load_uint32(source + 1));
} }
return result; return result;
} }
@ -478,7 +422,7 @@ struct cbor_decoder_result cbor_stream_decode(
/* Eight bytes length map */ /* Eight bytes length map */
{ {
if (claim_bytes(8, source_size, &result)) { if (claim_bytes(8, source_size, &result)) {
callbacks->map_start(context, (size_t)_cbor_load_uint64(source + 1)); callbacks->map_start(context, _cbor_load_uint64(source + 1));
} }
return result; return result;
} }
@ -486,7 +430,7 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xBD: /* Fallthrough */ case 0xBD: /* Fallthrough */
case 0xBE: case 0xBE:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0xBF: case 0xBF:
/* Indefinite length map */ /* Indefinite length map */
{ {
@ -506,8 +450,8 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xC5: case 0xC5:
/* Big float */ /* Big float */
{ {
callbacks->tag(context, callbacks->tag(context, (uint64_t)(_cbor_load_uint8(source) -
_cbor_load_uint8(source) - 0xC0); /* 0xC0 offset */ 0xC0)); /* 0xC0 offset */
return result; return result;
} }
case 0xC6: /* Fallthrough */ case 0xC6: /* Fallthrough */
@ -526,14 +470,14 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xD3: /* Fallthrough */ case 0xD3: /* Fallthrough */
case 0xD4: /* Unassigned tag value */ case 0xD4: /* Unassigned tag value */
{ {
return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR};
} }
case 0xD5: /* Expected b64url conversion tag - fallthrough */ case 0xD5: /* Expected b64url conversion tag - fallthrough */
case 0xD6: /* Expected b64 conversion tag - fallthrough */ case 0xD6: /* Expected b64 conversion tag - fallthrough */
case 0xD7: /* Expected b16 conversion tag */ case 0xD7: /* Expected b16 conversion tag */
{ {
callbacks->tag(context, callbacks->tag(context, (uint64_t)(_cbor_load_uint8(source) -
_cbor_load_uint8(source) - 0xC0); /* 0xC0 offset */ 0xC0)); /* 0xC0 offset */
return result; return result;
} }
case 0xD8: /* 1B tag */ case 0xD8: /* 1B tag */
@ -569,7 +513,7 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xDE: /* Fallthrough */ case 0xDE: /* Fallthrough */
case 0xDF: /* Reserved */ case 0xDF: /* Reserved */
{ {
return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR};
} }
case 0xE0: /* Fallthrough */ case 0xE0: /* Fallthrough */
case 0xE1: /* Fallthrough */ case 0xE1: /* Fallthrough */
@ -592,7 +536,7 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xF2: /* Fallthrough */ case 0xF2: /* Fallthrough */
case 0xF3: /* Simple value - unassigned */ case 0xF3: /* Simple value - unassigned */
{ {
return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR};
} }
case 0xF4: case 0xF4:
/* False */ /* False */
@ -620,7 +564,7 @@ struct cbor_decoder_result cbor_stream_decode(
} }
case 0xF8: case 0xF8:
/* 1B simple value, unassigned */ /* 1B simple value, unassigned */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0xF9: case 0xF9:
/* 2B float */ /* 2B float */
{ {
@ -649,16 +593,13 @@ struct cbor_decoder_result cbor_stream_decode(
case 0xFD: /* Fallthrough */ case 0xFD: /* Fallthrough */
case 0xFE: case 0xFE:
/* Reserved */ /* Reserved */
{ return (struct cbor_decoder_result){0, CBOR_DECODER_ERROR}; } { return (struct cbor_decoder_result){.status = CBOR_DECODER_ERROR}; }
case 0xFF: case 0xFF:
/* Break */ /* Break */
{ callbacks->indef_break(context);
callbacks->indef_break(context); // Never happens, the switch statement is exhaustive on the 1B range; make
return result; // compiler happy
} default:
default: /* Never happens - this shuts up the compiler */
{
return result; return result;
}
} }
} }

View File

@ -18,16 +18,16 @@ extern "C" {
/** Stateless decoder /** Stateless decoder
* *
* Will try parsing the \p buffer and will invoke the appropriate callback on * Will try parsing the \p source and will invoke the appropriate callback on
* success. Decodes one item at a time. No memory allocations occur. * success. Decodes one item at a time. No memory allocations occur.
* *
* @param buffer Input buffer * @param source Input buffer
* @param buffer_size Length of the buffer * @param source_size Length of the buffer
* @param callbacks The callback bundle * @param callbacks The callback bundle
* @param context An arbitrary pointer to allow for maintaining context. * @param context An arbitrary pointer to allow for maintaining context.
*/ */
CBOR_EXPORT struct cbor_decoder_result cbor_stream_decode( _CBOR_NODISCARD CBOR_EXPORT struct cbor_decoder_result cbor_stream_decode(
cbor_data buffer, size_t buffer_size, cbor_data source, size_t source_size,
const struct cbor_callbacks* callbacks, void* context); const struct cbor_callbacks* callbacks, void* context);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -9,8 +9,8 @@
#include <string.h> #include <string.h>
#include "internal/memory_utils.h" #include "internal/memory_utils.h"
cbor_item_t *cbor_new_definite_string() { cbor_item_t *cbor_new_definite_string(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
.refcount = 1, .refcount = 1,
@ -19,15 +19,15 @@ cbor_item_t *cbor_new_definite_string() {
return item; return item;
} }
cbor_item_t *cbor_new_indefinite_string() { cbor_item_t *cbor_new_indefinite_string(void) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
.refcount = 1, .refcount = 1,
.type = CBOR_TYPE_STRING, .type = CBOR_TYPE_STRING,
.metadata = {.string_metadata = {.type = _CBOR_METADATA_INDEFINITE, .metadata = {.string_metadata = {.type = _CBOR_METADATA_INDEFINITE,
.length = 0}}, .length = 0}},
.data = _CBOR_MALLOC(sizeof(struct cbor_indefinite_string_data))}; .data = _cbor_malloc(sizeof(struct cbor_indefinite_string_data))};
_CBOR_DEPENDENT_NOTNULL(item, item->data); _CBOR_DEPENDENT_NOTNULL(item, item->data);
*((struct cbor_indefinite_string_data *)item->data) = *((struct cbor_indefinite_string_data *)item->data) =
(struct cbor_indefinite_string_data){ (struct cbor_indefinite_string_data){
@ -42,7 +42,7 @@ cbor_item_t *cbor_build_string(const char *val) {
cbor_item_t *item = cbor_new_definite_string(); cbor_item_t *item = cbor_new_definite_string();
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
size_t len = strlen(val); size_t len = strlen(val);
unsigned char *handle = _CBOR_MALLOC(len); unsigned char *handle = _cbor_malloc(len);
_CBOR_DEPENDENT_NOTNULL(item, handle); _CBOR_DEPENDENT_NOTNULL(item, handle);
memcpy(handle, val, len); memcpy(handle, val, len);
cbor_string_set_handle(item, handle, len); cbor_string_set_handle(item, handle, len);
@ -52,7 +52,7 @@ cbor_item_t *cbor_build_string(const char *val) {
cbor_item_t *cbor_build_stringn(const char *val, size_t length) { cbor_item_t *cbor_build_stringn(const char *val, size_t length) {
cbor_item_t *item = cbor_new_definite_string(); cbor_item_t *item = cbor_new_definite_string();
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
unsigned char *handle = _CBOR_MALLOC(length); unsigned char *handle = _cbor_malloc(length);
_CBOR_DEPENDENT_NOTNULL(item, handle); _CBOR_DEPENDENT_NOTNULL(item, handle);
memcpy(handle, val, length); memcpy(handle, val, length);
cbor_string_set_handle(item, handle, length); cbor_string_set_handle(item, handle, length);
@ -62,31 +62,30 @@ cbor_item_t *cbor_build_stringn(const char *val, size_t length) {
void cbor_string_set_handle(cbor_item_t *item, void cbor_string_set_handle(cbor_item_t *item,
cbor_mutable_data CBOR_RESTRICT_POINTER data, cbor_mutable_data CBOR_RESTRICT_POINTER data,
size_t length) { size_t length) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
assert(cbor_string_is_definite(item)); CBOR_ASSERT(cbor_string_is_definite(item));
item->data = data; item->data = data;
item->metadata.string_metadata.length = length; item->metadata.string_metadata.length = length;
} }
cbor_item_t **cbor_string_chunks_handle(const cbor_item_t *item) { cbor_item_t **cbor_string_chunks_handle(const cbor_item_t *item) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
assert(cbor_string_is_indefinite(item)); CBOR_ASSERT(cbor_string_is_indefinite(item));
return ((struct cbor_indefinite_string_data *)item->data)->chunks; return ((struct cbor_indefinite_string_data *)item->data)->chunks;
} }
size_t cbor_string_chunk_count(const cbor_item_t *item) { size_t cbor_string_chunk_count(const cbor_item_t *item) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
assert(cbor_string_is_indefinite(item)); CBOR_ASSERT(cbor_string_is_indefinite(item));
return ((struct cbor_indefinite_string_data *)item->data)->chunk_count; return ((struct cbor_indefinite_string_data *)item->data)->chunk_count;
} }
bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk) { bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
assert(cbor_string_is_indefinite(item)); CBOR_ASSERT(cbor_string_is_indefinite(item));
struct cbor_indefinite_string_data *data = struct cbor_indefinite_string_data *data =
(struct cbor_indefinite_string_data *)item->data; (struct cbor_indefinite_string_data *)item->data;
if (data->chunk_count == data->chunk_capacity) { if (data->chunk_count == data->chunk_capacity) {
// TODO: Add a test for this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) { if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) {
return false; return false;
} }
@ -109,22 +108,22 @@ bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk) {
} }
size_t cbor_string_length(const cbor_item_t *item) { size_t cbor_string_length(const cbor_item_t *item) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
return item->metadata.string_metadata.length; return item->metadata.string_metadata.length;
} }
unsigned char *cbor_string_handle(const cbor_item_t *item) { unsigned char *cbor_string_handle(const cbor_item_t *item) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
return item->data; return item->data;
} }
size_t cbor_string_codepoint_count(const cbor_item_t *item) { size_t cbor_string_codepoint_count(const cbor_item_t *item) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
return item->metadata.string_metadata.codepoint_count; return item->metadata.string_metadata.codepoint_count;
} }
bool cbor_string_is_definite(const cbor_item_t *item) { bool cbor_string_is_definite(const cbor_item_t *item) {
assert(cbor_isa_string(item)); CBOR_ASSERT(cbor_isa_string(item));
return item->metadata.string_metadata.type == _CBOR_METADATA_DEFINITE; return item->metadata.string_metadata.type == _CBOR_METADATA_DEFINITE;
} }

View File

@ -21,48 +21,53 @@ extern "C" {
* ============================================================================ * ============================================================================
*/ */
/** Returns the length of the underlying string /** Returns the length of the underlying string in bytes
* *
* For definite strings only * There can be fewer unicode character than bytes (see
* `cbor_string_codepoint_count`). For definite strings only.
* *
* @param item[borrow] a definite string * @param item a definite string
* @return length of the string. Zero if no chunk has been attached yet * @return length of the string. Zero if no chunk has been attached yet
*/ */
CBOR_EXPORT size_t cbor_string_length(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT size_t cbor_string_length(const cbor_item_t *item);
/** The number of codepoints in this string /** The number of codepoints in this string
* *
* Might differ from length if there are multibyte ones * Might differ from length if there are multibyte ones
* *
* @param item[borrow] A string * @param item A string
* @return The number of codepoints in this string * @return The number of codepoints in this string
*/ */
CBOR_EXPORT size_t cbor_string_codepoint_count(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT size_t
cbor_string_codepoint_count(const cbor_item_t *item);
/** Is the string definite? /** Is the string definite?
* *
* @param item[borrow] a string * @param item a string
* @return Is the string definite? * @return Is the string definite?
*/ */
CBOR_EXPORT bool cbor_string_is_definite(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT bool cbor_string_is_definite(
const cbor_item_t *item);
/** Is the string indefinite? /** Is the string indefinite?
* *
* @param item[borrow] a string * @param item a string
* @return Is the string indefinite? * @return Is the string indefinite?
*/ */
CBOR_EXPORT bool cbor_string_is_indefinite(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT bool cbor_string_is_indefinite(
const cbor_item_t *item);
/** Get the handle to the underlying string /** Get the handle to the underlying string
* *
* Definite items only. Modifying the data is allowed. In that case, the caller * Definite items only. Modifying the data is allowed. In that case, the caller
* takes responsibility for the effect on items this item might be a part of * takes responsibility for the effect on items this item might be a part of
* *
* @param item[borrow] A definite string * @param item A definite string
* @return The address of the underlying string. `NULL` if no data have been * @return The address of the underlying string.
* assigned yet. * @return `NULL` if no data have been assigned yet.
*/ */
CBOR_EXPORT cbor_mutable_data cbor_string_handle(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT cbor_mutable_data
cbor_string_handle(const cbor_item_t *item);
/** Set the handle to the underlying string /** Set the handle to the underlying string
* *
@ -73,7 +78,7 @@ CBOR_EXPORT cbor_mutable_data cbor_string_handle(const cbor_item_t *item);
* the CBOR item will be left inconsistent. * the CBOR item will be left inconsistent.
* \endrst * \endrst
* *
* @param item[borrow] A definite string * @param item A definite string
* @param data The memory block. The caller gives up the ownership of the block. * @param data The memory block. The caller gives up the ownership of the block.
* libcbor will deallocate it when appropriate using its free function * libcbor will deallocate it when appropriate using its free function
* @param length Length of the data block * @param length Length of the data block
@ -87,17 +92,19 @@ CBOR_EXPORT void cbor_string_set_handle(
* Manipulations with the memory block (e.g. sorting it) are allowed, but the * Manipulations with the memory block (e.g. sorting it) are allowed, but the
* validity and the number of chunks must be retained. * validity and the number of chunks must be retained.
* *
* @param item[borrow] A indefinite string * @param item A indefinite string
* @return array of #cbor_string_chunk_count definite strings * @return array of #cbor_string_chunk_count definite strings
*/ */
CBOR_EXPORT cbor_item_t **cbor_string_chunks_handle(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t **cbor_string_chunks_handle(
const cbor_item_t *item);
/** Get the number of chunks this string consist of /** Get the number of chunks this string consist of
* *
* @param item[borrow] A indefinite string * @param item A indefinite string
* @return The chunk count. 0 for freshly created items. * @return The chunk count. 0 for freshly created items.
*/ */
CBOR_EXPORT size_t cbor_string_chunk_count(const cbor_item_t *item); _CBOR_NODISCARD CBOR_EXPORT size_t
cbor_string_chunk_count(const cbor_item_t *item);
/** Appends a chunk to the string /** Appends a chunk to the string
* *
@ -105,46 +112,60 @@ CBOR_EXPORT size_t cbor_string_chunk_count(const cbor_item_t *item);
* *
* May realloc the chunk storage. * May realloc the chunk storage.
* *
* @param item[borrow] An indefinite string * @param item An indefinite string
* @param item[incref] A definite string * @param chunk A definite string item. Its reference count will be increased
* @return true on success. false on realloc failure. In that case, the refcount * by one.
* of `chunk` is not increased and the `item` is left intact. * @return `true` on success. `false` on memory allocation failure. In that
* case, the refcount of @p `chunk` is not increased and the @p `item` is left
* intact.
*/ */
CBOR_EXPORT bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk); _CBOR_NODISCARD CBOR_EXPORT bool cbor_string_add_chunk(cbor_item_t *item,
cbor_item_t *chunk);
/** Creates a new definite string /** Creates a new definite string
* *
* The handle is initialized to `NULL` and length to 0 * The handle is initialized to `NULL` and length to 0
* *
* @return **new** definite string. `NULL` on malloc failure. * @return Reference to the new string item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_definite_string(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_definite_string(void);
/** Creates a new indefinite string /** Creates a new indefinite string
* *
* The chunks array is initialized to `NULL` and chunkcount to 0 * The chunks array is initialized to `NULL` and chunkcount to 0
* *
* @return **new** indefinite string. `NULL` on malloc failure. * @return Reference to the new string item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_new_indefinite_string(); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_indefinite_string(void);
/** Creates a new string and initializes it /** Creates a new string and initializes it
* *
* The `val` will be copied to a newly allocated block * The `val` will be copied to a newly allocated block
* *
* @param val A null-terminated UTF-8 string * @param val A null-terminated UTF-8 string
* @return A **new** string with content `handle`. `NULL` on malloc failure. * @return Reference to the new string item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_string(const char *val); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_string(const char *val);
/** Creates a new string and initializes it /** Creates a new string and initializes it
* *
* The `handle` will be copied to a newly allocated block * The `handle` will be copied to a newly allocated block
* *
* @param val A UTF-8 string, at least \p length long (excluding the null byte) * @param val A UTF-8 string, at least @p `length` long (excluding the null
* @return A **new** string with content `handle`. `NULL` on malloc failure. * byte)
* @param length Length (in bytes) of the string passed in @p `val`.
* @return Reference to the new string item. The item's reference count is
* initialized to one.
* @return `NULL` if memory allocation fails
*/ */
CBOR_EXPORT cbor_item_t *cbor_build_stringn(const char *val, size_t length); _CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_build_stringn(const char *val,
size_t length);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -8,7 +8,7 @@
#include "tags.h" #include "tags.h"
cbor_item_t *cbor_new_tag(uint64_t value) { cbor_item_t *cbor_new_tag(uint64_t value) {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t)); cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item); _CBOR_NOTNULL(item);
*item = (cbor_item_t){ *item = (cbor_item_t){
@ -21,23 +21,26 @@ cbor_item_t *cbor_new_tag(uint64_t value) {
} }
cbor_item_t *cbor_tag_item(const cbor_item_t *item) { cbor_item_t *cbor_tag_item(const cbor_item_t *item) {
assert(cbor_isa_tag(item)); CBOR_ASSERT(cbor_isa_tag(item));
return cbor_incref(item->metadata.tag_metadata.tagged_item); return cbor_incref(item->metadata.tag_metadata.tagged_item);
} }
uint64_t cbor_tag_value(const cbor_item_t *item) { uint64_t cbor_tag_value(const cbor_item_t *item) {
assert(cbor_isa_tag(item)); CBOR_ASSERT(cbor_isa_tag(item));
return item->metadata.tag_metadata.value; return item->metadata.tag_metadata.value;
} }
void cbor_tag_set_item(cbor_item_t *item, cbor_item_t *tagged_item) { void cbor_tag_set_item(cbor_item_t *item, cbor_item_t *tagged_item) {
assert(cbor_isa_tag(item)); CBOR_ASSERT(cbor_isa_tag(item));
cbor_incref(tagged_item); cbor_incref(tagged_item);
item->metadata.tag_metadata.tagged_item = tagged_item; item->metadata.tag_metadata.tagged_item = tagged_item;
} }
cbor_item_t *cbor_build_tag(uint64_t value, cbor_item_t *item) { cbor_item_t *cbor_build_tag(uint64_t value, cbor_item_t *item) {
cbor_item_t *res = cbor_new_tag(value); cbor_item_t *res = cbor_new_tag(value);
if (res == NULL) {
return NULL;
}
cbor_tag_set_item(res, item); cbor_tag_set_item(res, item);
return res; return res;
} }

Some files were not shown because too many files have changed in this diff Show More