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

8
.gitignore vendored
View File

@ -6,3 +6,11 @@ doxygen_docs
*/*.out
cmake-build-debug
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
---------------------
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)
---------------------
- 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)
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)
SET(CBOR_VERSION_MAJOR "0")
SET(CBOR_VERSION_MINOR "8")
SET(CBOR_VERSION_PATCH "0")
SET(CBOR_VERSION_MINOR "10")
SET(CBOR_VERSION_PATCH "2")
SET(CBOR_VERSION ${CBOR_VERSION_MAJOR}.${CBOR_VERSION_MINOR}.${CBOR_VERSION_PATCH})
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
@ -18,6 +18,13 @@ if(BIG_ENDIAN)
endif()
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)
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")
@ -85,7 +92,7 @@ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "-g")
include(CheckTypeSize)
check_type_size("size_t" SIZEOF_SIZE_T)
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()
add_definitions(-DEIGHT_BYTE_SIZE_T)
endif()
@ -99,19 +106,36 @@ add_custom_target(coverage
COMMAND ctest
COMMAND lcov --capture --directory . --output-file coverage.info
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)
option(COVERAGE "Enable code coverage instrumentation" OFF)
option(c "Enable code coverage instrumentation" OFF)
if (COVERAGE)
message("Configuring code coverage instrumentation")
if(NOT CMAKE_C_COMPILER MATCHES "gcc")
message(WARNING "Gcov instrumentation only works with GCC")
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
# 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()
# 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)
@ -139,20 +163,20 @@ else()
message(STATUS "LTO is not enabled")
endif(use_lto)
subdirs(src)
add_subdirectory(src)
if(use_lto)
set_property(DIRECTORY src PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif(use_lto)
if (WITH_TESTS)
subdirs(test)
add_subdirectory(test)
if(use_lto)
set_property(DIRECTORY test PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif(use_lto)
endif (WITH_TESTS)
if (WITH_EXAMPLES)
subdirs(examples)
add_subdirectory(examples)
if(use_lto)
set_property(DIRECTORY examples PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
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
HINTS ${PC_CMOCKA_INCLUDEDIR} ${PC_CMOCKA_INCLUDE_DIRS}
NAMES
cmocka.h
PATHS
@ -29,6 +36,7 @@ find_path(CMOCKA_INCLUDE_DIR
)
find_library(CMOCKA_LIBRARY
HINTS ${PC_CMOCKA_LIBDIR} ${PC_CMOCKA_LIBRARY_DIRS}
NAMES
cmocka cmocka_shared
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)
[![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)
[![Documentation Status](https://readthedocs.org/projects/libcbor/badge/?version=latest)](https://readthedocs.org/projects/libcbor/?badge=latest)
[![latest packaged version(s)](https://repology.org/badge/latest-versions/libcbor.svg)](https://repology.org/project/libcbor/versions)
[![codecov](https://codecov.io/gh/PJK/libcbor/branch/master/graph/badge.svg)](https://codecov.io/gh/PJK/libcbor)
**libcbor** is a C library for parsing and generating [CBOR](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
- Complete RFC conformance
@ -25,7 +25,7 @@
```bash
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 install
```
@ -63,29 +63,29 @@ yum install libcbor-devel
#include <cbor.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
/* Preallocate the map structure */
cbor_item_t * root = cbor_new_definite_map(2);
/* Add the content */
cbor_map_add(root, (struct cbor_pair) {
.key = cbor_move(cbor_build_string("Is CBOR awesome?")),
.value = cbor_move(cbor_build_bool(true))
});
cbor_map_add(root, (struct cbor_pair) {
.key = cbor_move(cbor_build_uint8(42)),
.value = cbor_move(cbor_build_string("Is the answer"))
});
/* Output: `length` bytes of data in the `buffer` */
unsigned char * buffer;
size_t buffer_size,
length = cbor_serialize_alloc(root, &buffer, &buffer_size);
int main(void) {
/* Preallocate the map structure */
cbor_item_t* root = cbor_new_definite_map(2);
/* Add the content */
bool success = cbor_map_add(
root, (struct cbor_pair){
.key = cbor_move(cbor_build_string("Is CBOR awesome?")),
.value = cbor_move(cbor_build_bool(true))});
success &= cbor_map_add(
root, (struct cbor_pair){
.key = cbor_move(cbor_build_uint8(42)),
.value = cbor_move(cbor_build_string("Is the answer"))});
if (!success) return 1;
/* Output: `length` bytes of data in the `buffer` */
unsigned char* buffer;
size_t buffer_size;
cbor_serialize_alloc(root, &buffer, &buffer_size);
fwrite(buffer, 1, length, stdout);
free(buffer);
fwrite(buffer, 1, buffer_size, stdout);
free(buffer);
fflush(stdout);
cbor_decref(&root);
fflush(stdout);
cbor_decref(&root);
}
```
@ -94,7 +94,7 @@ Get the latest documentation at [libcbor.readthedocs.org](http://libcbor.readthe
## 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)!

View File

@ -1,27 +1,14 @@
image: Visual Studio 2015
image: Visual Studio 2022
version: '{build}'
branches:
except:
- gh-pages
platform: x64
environment:
matrix:
- CMAKE_GENERATOR: "Visual Studio 14 2015 Win64"
# Via https://github.com/apitrace/apitrace/blob/master/appveyor.yml
skip_branch_with_pr: true
before_build:
- cmake -H. -Bbuild -G "%CMAKE_GENERATOR%"
- C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64\CL.exe /?
- cmake -H. -Bbuild
build_script:
- if "%APPVEYOR_REPO_TAG%"=="true" (set CONFIGURATION=RelWithDebInfo) else (set CONFIGURATION=Debug)
- cmake --build build --config "%CONFIGURATION%"
# TODO enable CMocka tests, maybe package the binaries
# 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 "*.cpp")"
# TravisCI workaround to use new clang-format while avoiding painful aliasing
# into the subshell
if which clang-format-8; then
clang-format-8 $@ -style=file -i ${SOURCES}
else
clang-format $@ -style=file -i ${SOURCES}
fi
clang-format $@ -style=file -i ${SOURCES}

View File

@ -1,2 +1,9 @@
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 *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.
@ -28,6 +28,8 @@ The API is designed to allow both very tight control & flexibility and general c
api/item_reference_counting
api/decoding
api/encoding
api/streaming_decoding
api/streaming_encoding
api/type_0_1
api/type_2
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_alloc
To determine the number of bytes needed to serialize an item, use :func:`cbor_serialized_size`:
.. doxygenfunction:: cbor_serialized_size
Type-specific serializers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
===============================================
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.
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
#if CBOR_CUSTOM_ALLOC
cbor_set_allocs(malloc, realloc, free);
#else
#error "libcbor built with support for custom allocation is required"
#endif
cbor_set_allocs(malloc, realloc, free);
.. 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
^^^^^^^^^^^^^^^^^^^^^
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

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
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
@ -16,13 +17,6 @@ When building custom sets of callbacks, feel free to start from
.. doxygenvariable:: cbor_empty_callbacks
Related structures
~~~~~~~~~~~~~~~~~~~~~
.. doxygenenum:: cbor_decoder_status
.. doxygenstruct:: cbor_decoder_result
:members:
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
~~~~~~~~~~~~~~~~~

View File

@ -1,7 +1,7 @@
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``
@ -12,10 +12,6 @@ Storage requirements (definite) ``sizeof(cbor_item_t) + length(handle)``
Storage requirements (indefinite) ``sizeof(cbor_item_t) * (1 + chunk_count) + chunks``
================================== ======================================================
Streaming indefinite strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please refer to :doc:`/streaming`.
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.
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``
@ -28,10 +33,6 @@ Examples
0x20 Unsigned integer 32
... 32 items follow
Streaming indefinite arrays
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please refer to :doc:`/streaming`.
Getting metadata
~~~~~~~~~~~~~~~~~

View File

@ -1,9 +1,18 @@
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``
@ -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``
================================== =====================================================================================
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
~~~~~~~~~~~~~~~~~

View File

@ -65,11 +65,4 @@ Manipulating existing items
Half floats
~~~~~~~~~~~~
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.
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
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.

View File

@ -76,8 +76,8 @@
# built documents.
#
# The short X.Y version.
version = '0.8'
release = '0.8.0'
version = '0.10'
release = '0.10.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# 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``
======================== ======================================================= ====================== =====================================================================================================================
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
------------------------ ------------------------------------------------------- ---------------------- ---------------------------------------------------------------------------------------------------------------------
``CBOR_CUSTOM_ALLOC`` Enable custom allocator support ``OFF`` ``ON``, ``OFF``
``CBOR_PRETTY_PRINTER`` Include a pretty-printing routine ``ON`` ``ON``, ``OFF``
``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>`_.
.. 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**
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
using
api
streaming
tests
rfc_conformance
internal

View File

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

View File

@ -1,13 +1,13 @@
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
-------------------
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
---------------------------------

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/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/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/ints.h`` - :doc:`api/type_0_1`
- ``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)),
.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;
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);
fflush(stdout);

View File

@ -7,6 +7,9 @@ target_link_libraries(create_items cbor)
add_executable(streaming_parser streaming_parser.c)
target_link_libraries(streaming_parser cbor)
add_executable(streaming_array streaming_array.c)
target_link_libraries(streaming_array cbor)
add_executable(sort sort.c)
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");
exit(1);
}
@ -133,10 +133,10 @@ int main(int argc, char *argv[]) {
/* Print out CBOR bytes */
unsigned char *buffer;
size_t buffer_size,
cbor_length = cbor_serialize_alloc(cbor, &buffer, &buffer_size);
size_t buffer_size;
cbor_serialize_alloc(cbor, &buffer, &buffer_size);
fwrite(buffer, 1, cbor_length, stdout);
fwrite(buffer, 1, buffer_size, stdout);
free(buffer);
fflush(stdout);

View File

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

View File

@ -8,9 +8,8 @@
#include <stdio.h>
#include "cbor.h"
int main(int argc, char* argv[]) {
int main(void) {
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("Buffer growth factor: %f\n", (float)CBOR_BUFFER_GROWTH);
}

View File

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

View File

@ -14,7 +14,7 @@
* 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),
bv = cbor_get_uint8(*(cbor_item_t **)b);
@ -26,15 +26,16 @@ int comparUint(const void *a, const void *b) {
return 1;
}
int main(int argc, char *argv[]) {
int main(void) {
cbor_item_t *array = cbor_new_definite_array(4);
cbor_array_push(array, cbor_move(cbor_build_uint8(4)));
cbor_array_push(array, cbor_move(cbor_build_uint8(3)));
cbor_array_push(array, cbor_move(cbor_build_uint8(1)));
cbor_array_push(array, cbor_move(cbor_build_uint8(2)));
bool success = cbor_array_push(array, cbor_move(cbor_build_uint8(4)));
success &= cbor_array_push(array, cbor_move(cbor_build_uint8(3)));
success &= cbor_array_push(array, cbor_move(cbor_build_uint8(1)));
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 *),
comparUint);
compareUint);
cbor_describe(array, 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 "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");
exit(1);
}
@ -24,7 +30,7 @@ void usage() {
const char* key = "a secret key";
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) {
printf("Found the value: %.*s\n", (int)len, buffer);
key_found = false;

View File

@ -1,5 +1,14 @@
import sys, re
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]
release_date = date.today().strftime('%Y-%m-%d')
@ -7,7 +16,10 @@
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:
f.write(updated)
@ -23,13 +35,22 @@ def replace(file_path, pattern, replacement):
# Update CMakeLists.txt
replace('CMakeLists.txt',
'''SET\\(CBOR_VERSION_MAJOR "0"\\)
SET\\(CBOR_VERSION_MINOR "7"\\)
SET\\(CBOR_VERSION_PATCH "0"\\)''',
'''SET\\(CBOR_VERSION_MAJOR "\d+"\\)
SET\\(CBOR_VERSION_MINOR "\d+"\\)
SET\\(CBOR_VERSION_PATCH "\d+"\\)''',
f'''SET(CBOR_VERSION_MAJOR "{major}")
SET(CBOR_VERSION_MINOR "{minor}")
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
replace('doc/source/conf.py',
"""version = '.*'

View File

@ -18,7 +18,7 @@
mkdir build
cd build
# 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 install

View File

@ -1,20 +1,68 @@
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <unordered_map>
#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) {
if (size > 1 << 24) {
if (size + allocated_mem > kMemoryLimit) {
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 {
FILE* fout;
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"
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?"
echo ">>>>> Checking Doxyfile"
@ -33,6 +33,10 @@ echo ">>>>> Checking CMakeLists"
grep -A 2 'SET(CBOR_VERSION_MAJOR' CMakeLists.txt
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"
grep 'version =\|release =' doc/source/conf.py
prompt "Are the versions correct?"
@ -61,7 +65,9 @@ ctest
popd
prompt "Will proceed to tag the release with $TAG_NAME."
git commit -a -m "Release $TAG_NAME"
git tag "$TAG_NAME"
git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD)
git push --tags
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(JoinPaths)
include(CheckFunctionExists)
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)
set(CMAKE_MACOSX_RPATH 0)
endif()
@ -14,6 +12,12 @@ endif()
add_library(cbor ${SOURCES})
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)
generate_export_header(cbor EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/cbor/cbor_export.h)
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
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)
# 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 "${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;
}
case CBOR_DECODER_ERROR:
/* Reserved/malformated item */
/* Reserved/malformed item */
{
result->error.code = CBOR_ERR_MALFORMATED;
goto error;
@ -100,9 +100,7 @@ cbor_item_t *cbor_load(cbor_data source, size_t source_size,
}
} while (stack.size > 0);
/* Move the result before free */
cbor_item_t *result_item = context.root;
return result_item;
return context.root;
error:
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:
res = cbor_build_uint64(cbor_get_uint64(item));
break;
default:
return NULL;
}
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) {
// cppcheck-suppress missingReturn
switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0:
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:
return cbor_build_float8(cbor_float_get_float8(item));
}
return NULL;
}
cbor_item_t *cbor_copy(cbor_item_t *item) {
// cppcheck-suppress missingReturn
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT:
return _cbor_copy_int(item, false);
@ -167,10 +163,24 @@ cbor_item_t *cbor_copy(cbor_item_t *item) {
cbor_bytestring_length(item));
} else {
cbor_item_t *res = cbor_new_indefinite_bytestring();
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
cbor_bytestring_add_chunk(
res,
cbor_move(cbor_copy(cbor_bytestring_chunks_handle(item)[i])));
if (res == NULL) {
return NULL;
}
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;
}
case CBOR_TYPE_STRING:
@ -179,46 +189,100 @@ cbor_item_t *cbor_copy(cbor_item_t *item) {
cbor_string_length(item));
} else {
cbor_item_t *res = cbor_new_indefinite_string();
for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
cbor_string_add_chunk(
res, cbor_move(cbor_copy(cbor_string_chunks_handle(item)[i])));
if (res == NULL) {
return NULL;
}
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;
}
case CBOR_TYPE_ARRAY: {
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));
else
} else {
res = cbor_new_indefinite_array();
}
if (res == NULL) {
return NULL;
}
for (size_t i = 0; i < cbor_array_size(item); i++)
cbor_array_push(
res, cbor_move(cbor_copy(cbor_move(cbor_array_get(item, i)))));
for (size_t i = 0; i < cbor_array_size(item); i++) {
cbor_item_t *entry_copy = 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;
}
case CBOR_TYPE_MAP: {
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));
else
} else {
res = cbor_new_indefinite_map();
}
if (res == NULL) {
return NULL;
}
struct cbor_pair *it = cbor_map_handle(item);
for (size_t i = 0; i < cbor_map_size(item); i++)
cbor_map_add(res, (struct cbor_pair){
.key = cbor_move(cbor_copy(it[i].key)),
.value = cbor_move(cbor_copy(it[i].value))});
for (size_t i = 0; i < cbor_map_size(item); i++) {
cbor_item_t *key_copy = cbor_copy(it[i].key);
if (key_copy == NULL) {
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;
}
case CBOR_TYPE_TAG:
return cbor_build_tag(
cbor_tag_value(item),
cbor_move(cbor_copy(cbor_move(cbor_tag_item(item)))));
case CBOR_TYPE_TAG: {
cbor_item_t *item_copy = cbor_copy(cbor_move(cbor_tag_item(item)));
if (item_copy == NULL) {
return NULL;
}
cbor_item_t *tag = cbor_build_tag(cbor_tag_value(item), item_copy);
cbor_decref(&item_copy);
return tag;
}
case CBOR_TYPE_FLOAT_CTRL:
return _cbor_copy_float_ctrl(item);
}
return NULL;
}
#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, "Value: %" PRIu64 "\n", cbor_get_int(item));
break;
};
}
case CBOR_TYPE_NEGINT: {
fprintf(out, "%*s[CBOR_TYPE_NEGINT] ", indent, " ");
fprintf(out, "Width: %dB, ", _pow(2, cbor_int_get_width(item)));
fprintf(out, "Value: -%" PRIu64 " -1\n", cbor_get_int(item));
break;
};
}
case CBOR_TYPE_BYTESTRING: {
fprintf(out, "%*s[CBOR_TYPE_BYTESTRING] ", indent, " ");
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));
}
break;
};
}
case CBOR_TYPE_STRING: {
fprintf(out, "%*s[CBOR_TYPE_STRING] ", indent, " ");
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");
}
break;
};
}
case CBOR_TYPE_ARRAY: {
fprintf(out, "%*s[CBOR_TYPE_ARRAY] ", indent, " ");
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++)
_cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4);
break;
};
}
case CBOR_TYPE_MAP: {
fprintf(out, "%*s[CBOR_TYPE_MAP] ", indent, " ");
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);
}
break;
};
}
case CBOR_TYPE_TAG: {
fprintf(out, "%*s[CBOR_TYPE_TAG] ", indent, " ");
fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
_cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4);
break;
};
}
case CBOR_TYPE_FLOAT_CTRL: {
fprintf(out, "%*s[CBOR_TYPE_FLOAT_CTRL] ", indent, " ");
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));
}
break;
};
}
}
}

View File

@ -39,21 +39,27 @@ extern "C" {
*
* @param source The buffer
* @param source_size
* @param result[out] Result indicator. #CBOR_ERR_NONE on success
* @return **new** CBOR item or `NULL` on failure. In that case, \p result
* contains location and description of the error.
* @param[out] result Result indicator. #CBOR_ERR_NONE on success
* @return Decoded CBOR item. The item's reference count is initialized to one.
* @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,
struct cbor_load_result* result);
_CBOR_NODISCARD CBOR_EXPORT cbor_item_t* cbor_load(
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
* @return **new** CBOR deep copy
* @param item item to copy
* @return Reference to the new item. The item's reference count is initialized
* to one.
* @return `NULL` if memory allocation fails
*/
CBOR_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
#include <stdio.h>

View File

@ -10,12 +10,12 @@
#include "internal/memory_utils.h"
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;
}
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;
}
@ -31,9 +31,6 @@ bool cbor_array_set(cbor_item_t *item, size_t index, cbor_item_t *value) {
} else {
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) {
@ -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) {
assert(cbor_isa_array(array));
CBOR_ASSERT(cbor_isa_array(array));
struct _cbor_array_metadata *metadata =
(struct _cbor_array_metadata *)&array->metadata;
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 */
if (metadata->end_ptr >= metadata->allocated) {
// Check for overflows first
// TODO: Explicitly test this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
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) {
assert(cbor_isa_array(item));
CBOR_ASSERT(cbor_isa_array(item));
return item->metadata.array_metadata.type == _CBOR_METADATA_DEFINITE;
}
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;
}
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;
}
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_item_t **data = _cbor_alloc_multiple(sizeof(cbor_item_t *), size);
_CBOR_DEPENDENT_NOTNULL(item, data);
@ -119,8 +115,8 @@ cbor_item_t *cbor_new_definite_array(size_t size) {
return item;
}
cbor_item_t *cbor_new_indefinite_array() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
cbor_item_t *cbor_new_indefinite_array(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item);
*item = (cbor_item_t){

View File

@ -17,62 +17,74 @@ extern "C" {
/** Get the number of members
*
* @param item[borrow] An array
* @param item An array
* @return The number of members
*/
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_array_size(const cbor_item_t* item);
/** 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)
*/
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_array_allocated(const cbor_item_t* item);
/** Get item by index
*
* @param item[borrow] An array
* @param index The index
* @return **incref** The item, or `NULL` in case of boundary violation
* @param item An array
* @param index The index (zero-based)
* @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);
/** 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 value[incref] The item to assign
* @param index The index, first item is 0.
* @return true on success, false on allocation failure.
* @param item An array
* @param value The item to assign
* @param index The index (zero-based)
* @return `true` on success, `false` on allocation failure.
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_set(cbor_item_t* item, size_t index,
cbor_item_t* value);
/** 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 value[incref] The item to assign
* @param index The index, first item is 0.
* @param item An array
* @param value The item to assign. Its reference count will be increased by
* one.
* @param index The index (zero-based)
* @return true on success, false on allocation failure.
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_replace(cbor_item_t* item, size_t index,
cbor_item_t* value);
/** Is the array definite?
*
* @param item[borrow] An array
* @param item An array
* @return Is the array definite?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_is_definite(const cbor_item_t* item);
/** Is the array indefinite?
*
* @param item[borrow] An array
* @param item An array
* @return Is the array indefinite?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_is_indefinite(const cbor_item_t* item);
/** 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
* consistent.
*
* @param item[borrow] An array
* @return #cbor_array_size items
* @param item An array item
* @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);
/** Create new definite array
*
* @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);
/** 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
*
* 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.
*
* @param array[borrow] An array
* @param pushee[incref] The item to push
* @return true on success, false on failure
* @param array An array
* @param pushee The item to push. Its reference count will be increased by
* one.
* @return `true` on success, `false` on failure
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_array_push(cbor_item_t* array, cbor_item_t* pushee);
#ifdef __cplusplus

View File

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

View File

@ -25,23 +25,26 @@ extern "C" {
*
* 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
*/
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_bytestring_length(const cbor_item_t *item);
/** Is the byte string definite?
*
* @param item[borrow] a byte string
* @param item a byte string
* @return Is the byte string definite?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_bytestring_is_definite(const cbor_item_t *item);
/** Is the byte string indefinite?
*
* @param item[borrow] a byte string
* @param item a byte string
* @return Is the byte string indefinite?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_bytestring_is_indefinite(const cbor_item_t *item);
/** 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
* takes responsibility for the effect on items this item might be a part of
*
* @param item[borrow] A definite byte string
* @return The address of the binary data. `NULL` if no data have been assigned
* @param item A definite byte string
* @return The address of the underlying binary data
* @return `NULL` if no data have been assigned
* yet.
*/
_CBOR_NODISCARD
CBOR_EXPORT cbor_mutable_data cbor_bytestring_handle(const cbor_item_t *item);
/** 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.
* 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
*/
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
* 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
*/
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t **cbor_bytestring_chunks_handle(
const cbor_item_t *item);
/** 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.
*/
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_bytestring_chunk_count(const cbor_item_t *item);
/** 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.
*
* @param item[borrow] An indefinite byte string
* @param item[incref] A definite byte string
* @param item An indefinite 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
* 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_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
*
* @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
*
* 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
*
@ -120,9 +136,11 @@ CBOR_EXPORT cbor_item_t *cbor_new_indefinite_bytestring();
*
* @param handle Block of binary data
* @param length Length of `data`
* @return A **new** byte string with content `handle`. `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_NODISCARD
CBOR_EXPORT cbor_item_t *cbor_build_bytestring(cbor_data handle, size_t length);
#ifdef __cplusplus

View File

@ -7,110 +7,115 @@
#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,
uint16_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_uint32_callback(void *_CBOR_UNUSED(_ctx),
uint32_t _CBOR_UNUSED(_val)) {}
void cbor_null_uint32_callback(void *_ctx,
uint32_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_uint64_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_uint64_callback(void *_ctx,
uint64_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_negint8_callback(void *_CBOR_UNUSED(_ctx),
uint8_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint8_callback(void *_ctx,
uint8_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_negint16_callback(void *_CBOR_UNUSED(_ctx),
uint16_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint16_callback(void *_ctx,
uint16_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_negint32_callback(void *_CBOR_UNUSED(_ctx),
uint32_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint32_callback(void *_ctx,
uint32_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_negint64_callback(void *_CBOR_UNUSED(_ctx),
uint64_t _CBOR_UNUSED(_val)) {}
void cbor_null_negint64_callback(void *_ctx,
uint64_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_string_callback(void *_CBOR_UNUSED(_ctx),
cbor_data _CBOR_UNUSED(_val),
uint64_t _CBOR_UNUSED(_val2)) {}
void cbor_null_string_callback(void *_ctx, cbor_data _val,
size_t _val2) CBOR_DUMMY_CALLBACK
void cbor_null_string_start_callback(void *_CBOR_UNUSED(_ctx)) {}
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,
size_t _val2) CBOR_DUMMY_CALLBACK
void cbor_null_byte_string_start_callback(void *_CBOR_UNUSED(_ctx)) {}
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,
size_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_indef_array_start_callback(void *_CBOR_UNUSED(_ctx)) {}
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,
size_t _val) CBOR_DUMMY_CALLBACK
void cbor_null_indef_map_start_callback(void *_CBOR_UNUSED(_ctx)) {}
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 0 - Unsigned integers */
.uint8 = cbor_null_uint8_callback,
.uint16 = cbor_null_uint16_callback,
.uint32 = cbor_null_uint32_callback,
.uint64 = cbor_null_uint64_callback,
/* Type 1 - Negative integers */
.negint8 = cbor_null_negint8_callback,
.negint16 = cbor_null_negint16_callback,
.negint32 = cbor_null_negint32_callback,
.negint64 = cbor_null_negint64_callback,
/* Type 1 - Negative integers */
.negint8 = cbor_null_negint8_callback,
.negint16 = cbor_null_negint16_callback,
.negint32 = cbor_null_negint32_callback,
.negint64 = cbor_null_negint64_callback,
/* Type 2 - Byte strings */
.byte_string_start = cbor_null_byte_string_start_callback,
.byte_string = cbor_null_byte_string_callback,
/* Type 2 - Byte strings */
.byte_string_start = cbor_null_byte_string_start_callback,
.byte_string = cbor_null_byte_string_callback,
/* Type 3 - Strings */
.string_start = cbor_null_string_start_callback,
.string = cbor_null_string_callback,
/* Type 3 - Strings */
.string_start = cbor_null_string_start_callback,
.string = cbor_null_string_callback,
/* Type 4 - Arrays */
.indef_array_start = cbor_null_indef_array_start_callback,
.array_start = cbor_null_array_start_callback,
/* Type 4 - Arrays */
.indef_array_start = cbor_null_indef_array_start_callback,
.array_start = cbor_null_array_start_callback,
/* Type 5 - Maps */
.indef_map_start = cbor_null_indef_map_start_callback,
.map_start = cbor_null_map_start_callback,
/* Type 5 - Maps */
.indef_map_start = cbor_null_indef_map_start_callback,
.map_start = cbor_null_map_start_callback,
/* Type 6 - Tags */
.tag = cbor_null_tag_callback,
/* Type 6 - Tags */
.tag = cbor_null_tag_callback,
/* Type 7 - Floats & misc */
/* 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 */
/* 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,
/* Shared indefinites */
.indef_break = cbor_null_indef_break_callback,
/* Shared indefinites */
.indef_break = cbor_null_indef_break_callback,
};

View File

@ -8,6 +8,8 @@
#ifndef LIBCBOR_CALLBACKS_H
#define LIBCBOR_CALLBACKS_H
#include <stdint.h>
#include "cbor/cbor_export.h"
#include "cbor/common.h"
@ -31,10 +33,10 @@ typedef void (*cbor_int64_callback)(void *, uint64_t);
typedef void (*cbor_simple_callback)(void *);
/** Callback prototype */
typedef void (*cbor_string_callback)(void *, cbor_data, size_t);
typedef void (*cbor_string_callback)(void *, cbor_data, uint64_t);
/** Callback prototype */
typedef void (*cbor_collection_callback)(void *, size_t);
typedef void (*cbor_collection_callback)(void *, uint64_t);
/** Callback prototype */
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);
/** 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 */
CBOR_EXPORT void cbor_null_string_start_callback(void *);
/** 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 */
CBOR_EXPORT void cbor_null_byte_string_start_callback(void *);
/** 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 */
CBOR_EXPORT void cbor_null_indef_array_start_callback(void *);
/** 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 */
CBOR_EXPORT void cbor_null_indef_map_start_callback(void *);

View File

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

View File

@ -13,6 +13,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "cbor/cbor_export.h"
#include "cbor/configuration.h"
#include "data.h"
@ -21,7 +22,7 @@
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
* 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_patch_version = CBOR_PATCH_VERSION;
#define CBOR_VERSION \
TO_STR(CBOR_MAJOR_VERSION) \
"." TO_STR(CBOR_MINOR_VERSION) "." TO_STR(CBOR_PATCH_VERSION)
#define CBOR_VERSION \
_CBOR_TO_STR(CBOR_MAJOR_VERSION) \
"." _CBOR_TO_STR(CBOR_MINOR_VERSION) "." _CBOR_TO_STR(CBOR_PATCH_VERSION)
#define CBOR_HEX_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
#include <stdio.h>
#define debug_print(fmt, ...) \
#define _cbor_debug_print(fmt, ...) \
do { \
if (DEBUG) \
fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__, \
__VA_ARGS__); \
} 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
#define debug_print(fmt, ...) \
do { \
} while (0)
#define CBOR_ASSERT(e)
#define _CBOR_TEST_DISABLE_ASSERT(block) \
do { \
block \
} while (0)
#endif
#define TO_STR_(x) #x
#define TO_STR(x) TO_STR_(x) /* enables proper double expansion */
#define _CBOR_TO_STR_(x) #x
#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
#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) \
do { \
if (pointer == NULL) { \
_CBOR_FREE(cbor_item); \
_cbor_free(cbor_item); \
return NULL; \
} \
} 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.
*
* Only available when `CBOR_CUSTOM_ALLOC` is truthy
* By default, libcbor will use the standard library `malloc`, `realloc`, and
* `free`.
*
* \rst
* .. 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_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
@ -135,80 +150,92 @@ CBOR_EXPORT void cbor_set_allocs(_cbor_malloc_t custom_malloc,
/** Get the type of the item
*
* @param item[borrow]
* @param item
* @return The type
*/
_CBOR_NODISCARD
CBOR_EXPORT cbor_type cbor_typeof(
const cbor_item_t *item); /* Will be inlined iff link-time opt is enabled */
/* Standard item types as described by the RFC */
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_uint(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_negint(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_bytestring(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_string(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_array(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_map(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_tag(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_isa_float_ctrl(const cbor_item_t *item);
/* Practical types with respect to their semantics (but not tag values) */
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_int(const cbor_item_t *item);
/** 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?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_float(const cbor_item_t *item);
/** Is the item an a boolean?
* @param item[borrow] the item
* @param item the item
* @return Is the item a boolean?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_bool(const cbor_item_t *item);
/** 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.
* \endrst
*
* @param item[borrow] the item
* @param item the item
* @return Is the item (CBOR logical) null?
*/
_CBOR_NODISCARD
CBOR_EXPORT bool cbor_is_null(const cbor_item_t *item);
/** Does this item represent `undefined`
@ -230,9 +258,10 @@ CBOR_EXPORT bool cbor_is_null(const cbor_item_t *item);
* C.
* \endrst
*
* @param item[borrow] the item
* @param item the item
* @return Is the item (CBOR logical) undefined?
*/
_CBOR_NODISCARD
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
* @return the input reference
* This function can be used to extend reference counting to client code.
*
* @param item Reference to an item
* @return The input \p 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
* is adjusted accordingly in a recursive manner.
* In case the item is deallocated, the reference count of all items this item
* 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);
/** 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
* needed
*
* @param item[take] the item
* @param item Reference to an item
*/
CBOR_EXPORT void cbor_intermediate_decref(cbor_item_t *item);
/** Get the reference count
/** Get the item's reference count
*
* \rst
* .. warning:: This does *not* account for transitive references.
* \endrst
*
* @param item[borrow] the item
* @todo Add some inline examples for reference counting
*
* @param item the item
* @return the reference count
*/
_CBOR_NODISCARD
CBOR_EXPORT size_t cbor_refcount(const cbor_item_t *item);
/** 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.
* \endrst
*
* @param item[take] the item
* @param item Reference to an item
* @return the item with reference count decreased by one
*/
_CBOR_NODISCARD
CBOR_EXPORT cbor_item_t *cbor_move(cbor_item_t *item);
#ifdef __cplusplus

View File

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

View File

@ -45,6 +45,8 @@ typedef enum {
CBOR_ERR_NONE,
CBOR_ERR_NOTENOUGHDATA,
CBOR_ERR_NODATA,
// TODO: Should be "malformed" or at least "malformatted". Retained for
// backwards compatibility.
CBOR_ERR_MALFORMATED,
CBOR_ERR_MEMERROR /** Memory error - item allocation failed. Is it too big for
your allocator? */
@ -86,6 +88,11 @@ typedef enum {
CBOR_CTRL_UNDEF = 23
} _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 */
struct _cbor_int_metadata {
cbor_int_width width;
@ -184,7 +191,7 @@ struct cbor_indefinite_string_data {
/** High-level decoding error */
struct cbor_error {
/** Aproximate position */
/** Approximate position */
size_t position;
/** Description */
cbor_error_code code;
@ -212,6 +219,8 @@ enum cbor_decoder_status {
*/
CBOR_DECODER_FINISHED,
/** Not enough data to invoke a callback */
// TODO: The name is inconsistent with CBOR_ERR_NOTENOUGHDATA. Retained for
// backwards compatibility.
CBOR_DECODER_NEDATA,
/** Bad data (reserved MTB, malformed value, etc.) */
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 */
if (exp == 0xFF) { /* Infinity or NaNs */
if (value != value) {
res = (uint16_t)0x007e00; /* Not IEEE semantics - required by CBOR
[s. 3.9] */
// We discard information bits in half-float NaNs. This is
// 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 {
res = (uint16_t)((val & 0x80000000u) >> 16u | 0x7C00u |
(mant ? 1u : 0u) << 15u);
// If the mantissa is non-zero, we have a NaN, but those are handled
// 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 */
res = (uint16_t)((val & 0x80000000u) >> 16u | mant >> 13u);
} else { /* Normal numbers */
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
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
absence of standard half-float in the language. */
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 {
res = (uint16_t)((val & 0x80000000u) >> 16u |
((((uint8_t)logical_exp) + 15u) << 10u) |

View File

@ -16,55 +16,87 @@ extern "C" {
#endif
/*
* ============================================================================
* Primitives encoding
* ============================================================================
* All cbor_encode_* methods take 2 or 3 arguments:
* - 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 *,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_encode_bytestring_start(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
*
@ -86,21 +118,20 @@ CBOR_EXPORT size_t cbor_encode_undef(unsigned char *, size_t);
* lost.
* - In all other cases, the sign bit, the exponent, and 10 most significant
* 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
}

View File

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

View File

@ -23,111 +23,131 @@ extern "C" {
/** 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?
*/
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
*
* @param item[borrow] A float or ctrl item
* @param item A float or ctrl item
* @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
*
* The item must have the corresponding width
*
* @param[borrow] A half precision float
* @param item A half precision float
* @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
*
* The item must have the corresponding width
*
* @param[borrow] A signle precision float
* @param item A single precision float
* @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
*
* The item must have the corresponding width
*
* @param[borrow] A double precision float
* @param item A double precision float
* @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
*
* Can be used regardless of the width.
*
* @param[borrow] Any float
* @param item Any float
* @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
*
* @param item[borrow] A ctrl item
* @param item A ctrl item
* @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
*
* 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
*
* 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
*
* 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
*
* 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
*
* @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
*
* @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
*
* @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
*
@ -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.
* \endrst
*
* @param item[borrow] A ctrl item
* @param item A ctrl item
* @param value The simple value to assign. Please consult the standard for
* 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
*
* @param item[borrow] A ctrl item
* @param item A ctrl item
* @param value The simple value to assign.
*/
CBOR_EXPORT void cbor_set_bool(cbor_item_t *item, bool value);
/** Assigns a float value
*
* @param item[borrow] A half precision float
* @param item A half precision float
* @param value The value to assign
*/
CBOR_EXPORT void cbor_set_float2(cbor_item_t *item, 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
*/
CBOR_EXPORT void cbor_set_float4(cbor_item_t *item, 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
*/
CBOR_EXPORT void cbor_set_float8(cbor_item_t *item, double value);
/** Reads the control value
*
* @param item[borrow] A ctrl item
* @param item A ctrl item
* @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
*
* @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
*
* @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
*
* @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
*
* @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
}

View File

@ -6,9 +6,12 @@
*/
#include "builder_callbacks.h"
#include <string.h>
#include "../arrays.h"
#include "../bytestrings.h"
#include "../common.h"
#include "../floats_ctrls.h"
#include "../ints.h"
#include "../maps.h"
@ -16,73 +19,96 @@
#include "../tags.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,
struct _cbor_decoder_context *ctx) {
if (ctx->stack->size == 0) {
/* Top level item */
ctx->root = item;
} else {
/* Part of a bigger structure */
switch (ctx->stack->top->item->type) {
case CBOR_TYPE_ARRAY: {
if (cbor_array_is_definite(ctx->stack->top->item)) {
/*
* We don't need an explicit check for whether the item still belongs
* into this array because if there are extra items, they will cause a
* syntax error when decoded.
*/
assert(ctx->stack->top->subitems > 0);
cbor_array_push(ctx->stack->top->item, 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);
}
cbor_decref(&item);
} else {
/* Indefinite array, don't bother with subitems */
cbor_array_push(ctx->stack->top->item, item);
return;
}
/* Part of a bigger structure */
switch (ctx->stack->top->item->type) {
// Handle Arrays and Maps since they can contain subitems of any type.
// Byte/string construction from chunks is handled in the respective chunk
// handlers.
case CBOR_TYPE_ARRAY: {
if (cbor_array_is_definite(ctx->stack->top->item)) {
// We don't need an explicit check for whether the item still belongs
// into this array because if there are extra items, they will cause a
// syntax error when decoded.
CBOR_ASSERT(ctx->stack->top->subitems > 0);
// This should never happen since the definite array should be
// preallocated for the expected number of items.
if (!cbor_array_push(ctx->stack->top->item, item)) {
ctx->creation_failed = true;
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);
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)
// 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) \
do { \
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) {
struct _cbor_decoder_context *ctx = context;
cbor_item_t *res = cbor_new_int16();
CHECK_RES(ctx, res);
cbor_mark_negint(res);
cbor_set_uint16(res, value);
_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,
size_t length) {
uint64_t length) {
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) {
ctx->creation_failed = true;
return;
}
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) {
_CBOR_FREE(new_handle);
if (new_chunk == NULL) {
_cbor_free(new_handle);
ctx->creation_failed = true;
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 (cbor_bytestring_is_indefinite(ctx->stack->top->item)) {
cbor_bytestring_add_chunk(ctx->stack->top->item, cbor_move(res));
} else {
cbor_decref(&res);
ctx->syntax_error = true;
// If an indef bytestring is on the stack, extend it (if it were closed, it
// would have been popped). Handle any syntax errors upstream.
if (ctx->stack->size > 0 && cbor_isa_bytestring(ctx->stack->top->item) &&
cbor_bytestring_is_indefinite(ctx->stack->top->item)) {
if (!cbor_bytestring_add_chunk(ctx->stack->top->item, new_chunk)) {
ctx->creation_failed = true;
}
cbor_decref(&new_chunk);
} 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,
size_t length) {
uint64_t length) {
struct _cbor_decoder_context *ctx = context;
CHECK_LENGTH(ctx, length);
struct _cbor_unicode_status unicode_status;
size_t codepoint_count =
uint64_t codepoint_count =
_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;
return;
}
CBOR_ASSERT(codepoint_count <= length);
unsigned char *new_handle = _CBOR_MALLOC(length);
unsigned char *new_handle = _cbor_malloc(length);
if (new_handle == NULL) {
ctx->creation_failed = true;
@ -234,25 +274,25 @@ void cbor_builder_string_callback(void *context, cbor_data data,
}
memcpy(new_handle, data, length);
cbor_item_t *res = cbor_new_definite_string();
if (res == NULL) {
_CBOR_FREE(new_handle);
cbor_item_t *new_chunk = cbor_new_definite_string();
if (new_chunk == NULL) {
_cbor_free(new_handle);
ctx->creation_failed = true;
return;
}
cbor_string_set_handle(res, new_handle, length);
res->metadata.string_metadata.codepoint_count = codepoint_count;
cbor_string_set_handle(new_chunk, new_handle, length);
new_chunk->metadata.string_metadata.codepoint_count = codepoint_count;
/* Careful here: order matters */
if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item)) {
if (cbor_string_is_indefinite(ctx->stack->top->item)) {
cbor_string_add_chunk(ctx->stack->top->item, cbor_move(res));
} else {
cbor_decref(&res);
ctx->syntax_error = true;
// If an indef string is on the stack, extend it (if it were closed, it would
// have been popped). Handle any syntax errors upstream.
if (ctx->stack->size > 0 && cbor_isa_string(ctx->stack->top->item) &&
cbor_string_is_indefinite(ctx->stack->top->item)) {
if (!cbor_string_add_chunk(ctx->stack->top->item, new_chunk)) {
ctx->creation_failed = true;
}
cbor_decref(&new_chunk);
} 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);
}
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;
CHECK_LENGTH(ctx, size);
cbor_item_t *res = cbor_new_definite_array(size);
CHECK_RES(ctx, res);
if (size > 0) {
@ -288,8 +329,9 @@ void cbor_builder_indef_map_start_callback(void *context) {
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;
CHECK_LENGTH(ctx, size);
cbor_item_t *res = cbor_new_definite_map(size);
CHECK_RES(ctx, res);
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) {
switch (item->type) {
case CBOR_TYPE_BYTESTRING:
return item->metadata.bytestring_metadata.type ==
_CBOR_METADATA_INDEFINITE;
return cbor_bytestring_is_indefinite(item);
case CBOR_TYPE_STRING:
return item->metadata.string_metadata.type == _CBOR_METADATA_INDEFINITE;
return cbor_string_is_indefinite(item);
case CBOR_TYPE_ARRAY:
return item->metadata.array_metadata.type == _CBOR_METADATA_INDEFINITE;
return cbor_array_is_indefinite(item);
case CBOR_TYPE_MAP:
return item->metadata.map_metadata.type == _CBOR_METADATA_INDEFINITE;
return cbor_map_is_indefinite(item);
default:
return false;
}
@ -340,6 +381,7 @@ void cbor_builder_indef_break_callback(void *context) {
void cbor_builder_float2_callback(void *context, float value) {
struct _cbor_decoder_context *ctx = context;
cbor_item_t *res = cbor_new_float2();
CHECK_RES(ctx, res);
cbor_set_float2(res, value);
_cbor_builder_append(res, ctx);
}

View File

@ -26,6 +26,10 @@ struct _cbor_decoder_context {
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_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_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_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_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_map_start_callback(void *, size_t);
void cbor_builder_map_start_callback(void *, uint64_t);
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
memcpy(buffer + 1, &value, 2);
#else
buffer[1] = value >> 8;
buffer[2] = value;
buffer[1] = (unsigned char)(value >> 8);
buffer[2] = (unsigned char)value;
#endif
return 3;
@ -50,10 +50,10 @@ size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer,
#ifdef IS_BIG_ENDIAN
memcpy(buffer + 1, &value, 4);
#else
buffer[1] = value >> 24;
buffer[2] = value >> 16;
buffer[3] = value >> 8;
buffer[4] = value;
buffer[1] = (unsigned char)(value >> 24);
buffer[2] = (unsigned char)(value >> 16);
buffer[3] = (unsigned char)(value >> 8);
buffer[4] = (unsigned char)value;
#endif
return 5;
@ -69,14 +69,14 @@ size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer,
#ifdef IS_BIG_ENDIAN
memcpy(buffer + 1, &value, 8);
#else
buffer[1] = value >> 56;
buffer[2] = value >> 48;
buffer[3] = value >> 40;
buffer[4] = value >> 32;
buffer[5] = value >> 24;
buffer[6] = value >> 16;
buffer[7] = value >> 8;
buffer[8] = value;
buffer[1] = (unsigned char)(value >> 56);
buffer[2] = (unsigned char)(value >> 48);
buffer[3] = (unsigned char)(value >> 40);
buffer[4] = (unsigned char)(value >> 32);
buffer[5] = (unsigned char)(value >> 24);
buffer[6] = (unsigned char)(value >> 16);
buffer[7] = (unsigned char)(value >> 8);
buffer[8] = (unsigned char)value;
#endif
return 9;

View File

@ -14,18 +14,23 @@
extern "C" {
#endif
_CBOR_NODISCARD
size_t _cbor_encode_uint8(uint8_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint16(uint16_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint32(uint32_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint64(uint64_t value, unsigned char *buffer,
size_t buffer_size, uint8_t offset);
_CBOR_NODISCARD
size_t _cbor_encode_uint(uint64_t value, unsigned char *buffer,
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);
}
double _cbor_load_half(cbor_data source) {
float _cbor_load_half(cbor_data source) {
/* Discard const */
return _cbor_decode_half((unsigned char *)source);
}

View File

@ -15,18 +15,25 @@ extern "C" {
#endif
/* Read the given uint from the given location, no questions asked */
_CBOR_NODISCARD
uint8_t _cbor_load_uint8(const unsigned char *source);
_CBOR_NODISCARD
uint16_t _cbor_load_uint16(const unsigned char *source);
_CBOR_NODISCARD
uint32_t _cbor_load_uint32(const unsigned char *source);
_CBOR_NODISCARD
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);
_CBOR_NODISCARD
double _cbor_load_double(cbor_data source);
#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) {
if (a <= 1 || b <= 1) return true;
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) {
if (_cbor_safe_to_multiply(item_size, item_count)) {
return _CBOR_MALLOC(item_size * item_count);
return _cbor_malloc(item_size * item_count);
} else {
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,
size_t 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 {
return NULL;
}

View File

@ -11,9 +11,20 @@
#include <stdbool.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);
/** 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
*
* @param item_size

View File

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

View File

@ -16,8 +16,18 @@ extern "C" {
/** Simple stack record for the parser */
struct _cbor_stack_record {
/** Pointer to the parent stack frame */
struct _cbor_stack_record *lower;
/** Item under construction */
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;
};
@ -27,10 +37,12 @@ struct _cbor_stack {
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 *);
_CBOR_NODISCARD
struct _cbor_stack_record *_cbor_stack_push(struct _cbor_stack *, cbor_item_t *,
size_t);

View File

@ -6,6 +6,7 @@
*/
#include "unicode.h"
#include <stdint.h>
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
@ -65,12 +66,12 @@ uint32_t _cbor_unicode_decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
return *state;
}
size_t _cbor_unicode_codepoint_count(cbor_data source, size_t source_length,
struct _cbor_unicode_status* status) {
uint64_t _cbor_unicode_codepoint_count(cbor_data source, uint64_t source_length,
struct _cbor_unicode_status* status) {
*status =
(struct _cbor_unicode_status){.location = 0, .status = _CBOR_UNICODE_OK};
uint32_t codepoint, state = UTF8_ACCEPT, res;
size_t pos = 0, count = 0;
uint64_t pos = 0, count = 0;
for (; pos < source_length; 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:
*status = (struct _cbor_unicode_status){.location = pos,
.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 */
struct _cbor_unicode_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,
struct _cbor_unicode_status* status);
_CBOR_NODISCARD
uint64_t _cbor_unicode_codepoint_count(cbor_data source, uint64_t source_length,
struct _cbor_unicode_status* status);
#ifdef __cplusplus
}

View File

@ -8,36 +8,37 @@
#include "ints.h"
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;
}
uint8_t cbor_get_uint8(const cbor_item_t *item) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_8);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_8);
return *item->data;
}
uint16_t cbor_get_uint16(const cbor_item_t *item) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_16);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_16);
return *(uint16_t *)item->data;
}
uint32_t cbor_get_uint32(const cbor_item_t *item) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_32);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_32);
return *(uint32_t *)item->data;
}
uint64_t cbor_get_uint64(const cbor_item_t *item) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_64);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_64);
return *(uint64_t *)item->data;
}
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)) {
case CBOR_INT_8:
return cbor_get_uint8(item);
@ -48,46 +49,44 @@ uint64_t cbor_get_int(const cbor_item_t *item) {
case CBOR_INT_64:
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) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_8);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_8);
*item->data = value;
}
void cbor_set_uint16(cbor_item_t *item, uint16_t value) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_16);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_16);
*(uint16_t *)item->data = value;
}
void cbor_set_uint32(cbor_item_t *item, uint32_t value) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_32);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_32);
*(uint32_t *)item->data = value;
}
void cbor_set_uint64(cbor_item_t *item, uint64_t value) {
assert(cbor_is_int(item));
assert(cbor_int_get_width(item) == CBOR_INT_64);
CBOR_ASSERT(cbor_is_int(item));
CBOR_ASSERT(cbor_int_get_width(item) == CBOR_INT_64);
*(uint64_t *)item->data = value;
}
void cbor_mark_uint(cbor_item_t *item) {
assert(cbor_is_int(item));
CBOR_ASSERT(cbor_is_int(item));
item->type = CBOR_TYPE_UINT;
}
void cbor_mark_negint(cbor_item_t *item) {
assert(cbor_is_int(item));
CBOR_ASSERT(cbor_is_int(item));
item->type = CBOR_TYPE_NEGINT;
}
cbor_item_t *cbor_new_int8() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 1);
cbor_item_t *cbor_new_int8(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 1);
_CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1,
@ -96,8 +95,8 @@ cbor_item_t *cbor_new_int8() {
return item;
}
cbor_item_t *cbor_new_int16() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 2);
cbor_item_t *cbor_new_int16(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 2);
_CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1,
@ -106,8 +105,8 @@ cbor_item_t *cbor_new_int16() {
return item;
}
cbor_item_t *cbor_new_int32() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 4);
cbor_item_t *cbor_new_int32(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 4);
_CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1,
@ -116,8 +115,8 @@ cbor_item_t *cbor_new_int32() {
return item;
}
cbor_item_t *cbor_new_int64() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t) + 8);
cbor_item_t *cbor_new_int64(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t) + 8);
_CBOR_NOTNULL(item);
*item = (cbor_item_t){.data = (unsigned char *)item + sizeof(cbor_item_t),
.refcount = 1,

View File

@ -23,42 +23,42 @@ extern "C" {
/** Extracts the integer value
*
* @param item[borrow] positive or negative integer
* @param item positive or negative integer
* @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
*
* @param item[borrow] positive or negative integer
* @param item positive or negative integer
* @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
*
* @param item[borrow] positive or negative integer
* @param item positive or negative integer
* @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
*
* @param item[borrow] positive or negative integer
* @param item positive or negative integer
* @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
*
* @param item[borrow] positive or negative integer
* @param item positive or negative integer
* @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
*
* @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
* `-value - 1`
*/
@ -66,7 +66,7 @@ CBOR_EXPORT void cbor_set_uint8(cbor_item_t *item, uint8_t 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
* `-value - 1`
*/
@ -74,7 +74,7 @@ CBOR_EXPORT void cbor_set_uint16(cbor_item_t *item, uint16_t 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
* `-value - 1`
*/
@ -82,7 +82,7 @@ CBOR_EXPORT void cbor_set_uint32(cbor_item_t *item, uint32_t 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
* `-value - 1`
*/
@ -90,16 +90,17 @@ CBOR_EXPORT void cbor_set_uint64(cbor_item_t *item, uint64_t value);
/** Queries the integer width
*
* @param item[borrow] positive or negative integer item
* @param item positive or negative integer item
* @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
*
* 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);
@ -107,7 +108,7 @@ CBOR_EXPORT void cbor_mark_uint(cbor_item_t *item);
*
* 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);
@ -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
* 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
*
@ -127,7 +128,7 @@ CBOR_EXPORT cbor_item_t *cbor_new_int8();
* @return **new** positive integer or `NULL` on memory allocation failure. The
* 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
*
@ -136,7 +137,7 @@ CBOR_EXPORT cbor_item_t *cbor_new_int16();
* @return **new** positive integer or `NULL` on memory allocation failure. The
* 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
*
@ -145,63 +146,63 @@ CBOR_EXPORT cbor_item_t *cbor_new_int32();
* @return **new** positive integer or `NULL` on memory allocation failure. The
* 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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
*
* @param value the value to use
* @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
}

View File

@ -9,17 +9,17 @@
#include "internal/memory_utils.h"
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;
}
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;
}
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);
*item = (cbor_item_t){
@ -34,8 +34,8 @@ cbor_item_t *cbor_new_definite_map(size_t size) {
return item;
}
cbor_item_t *cbor_new_indefinite_map() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
cbor_item_t *cbor_new_indefinite_map(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item);
*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) {
assert(cbor_isa_map(item));
CBOR_ASSERT(cbor_isa_map(item));
struct _cbor_map_metadata *metadata =
(struct _cbor_map_metadata *)&item->metadata;
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) {
/* Exponential realloc */
// Check for overflows first
// TODO: Explicitly test this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, metadata->allocated)) {
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) {
assert(cbor_isa_map(item));
CBOR_ASSERT(cbor_isa_map(item));
cbor_incref(value);
cbor_map_handle(item)[
/* 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) {
assert(cbor_isa_map(item));
CBOR_ASSERT(cbor_isa_map(item));
if (!_cbor_map_add_key(item, pair.key)) return false;
return _cbor_map_add_value(item, pair.value);
}
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;
}
@ -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) {
assert(cbor_isa_map(item));
CBOR_ASSERT(cbor_isa_map(item));
return (struct cbor_pair *)item->data;
}

View File

@ -23,87 +23,96 @@ extern "C" {
/** Get the number of pairs
*
* @param item[borrow] A map
* @param item A map
* @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
*
* @param item[borrow] A map
* @param item A map
* @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
*
* @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
*
* @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_indefinite_map();
_CBOR_NODISCARD CBOR_EXPORT cbor_item_t *cbor_new_indefinite_map(void);
/** Add a pair to the map
*
* For definite maps, items can only be added to the preallocated space. For
* indefinite maps, the storage will be expanded as needed
*
* @param item[borrow] A map
* @param pair[incref] The key-value pair to add (incref is member-wise)
* @return `true` on success, `false` if either reallocation failed or the
* preallcoated storage is full
* @param item A map
* @param pair The key-value pair to add. Reference count of the #cbor_pair.key
* and #cbor_pair.value will be increased by one.
* @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
*
* Sets the value to `NULL`. Internal API.
*
* @param item[borrow] A map
* @param key[incref] The key
* @param item A map
* @param key The key, Its reference count will be be increased by one.
* @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
*
* Assumes that #_cbor_map_add_key has been called. Internal API.
*
* @param item[borrow] A map
* @param key[incref] The value
* @param item A map
* @param value The value. Its reference count will be be increased by one.
* @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?
*
* @param item[borrow] A map
* @param item A map
* @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?
*
* @param item[borrow] A map
* @param item A map
* @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
*
* @param item[borrow] A map
* @param item A map
* @return Array of #cbor_map_size pairs. Manipulation is possible as long as
* 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
}

View File

@ -19,6 +19,7 @@
size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
// cppcheck-suppress missingReturn
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT:
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);
case CBOR_TYPE_FLOAT_CTRL:
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 *buffer_size) {
size_t bfr_size = 32;
unsigned char *bfr = _CBOR_MALLOC(bfr_size), *tmp_bfr;
if (bfr == NULL) {
*buffer = NULL;
size_t serialized_size = cbor_serialized_size(item);
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;
}
size_t written;
/* This is waaay too optimistic - figure out something smarter (eventually) */
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;
size_t written = cbor_serialize(item, *buffer, serialized_size);
CBOR_ASSERT(written == serialized_size);
if (buffer_size != NULL) *buffer_size = serialized_size;
return written;
}
size_t cbor_serialize_uint(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
assert(cbor_isa_uint(item));
CBOR_ASSERT(cbor_isa_uint(item));
// cppcheck-suppress missingReturn
switch (cbor_int_get_width(item)) {
case CBOR_INT_8:
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);
case CBOR_INT_64:
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 buffer_size) {
assert(cbor_isa_negint(item));
CBOR_ASSERT(cbor_isa_negint(item));
// cppcheck-suppress missingReturn
switch (cbor_int_get_width(item)) {
case CBOR_INT_8:
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);
case CBOR_INT_64:
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 buffer_size) {
assert(cbor_isa_bytestring(item));
CBOR_ASSERT(cbor_isa_bytestring(item));
if (cbor_bytestring_is_definite(item)) {
size_t length = cbor_bytestring_length(item);
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);
return written + length;
} else
return 0;
}
return 0;
} 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 written = cbor_encode_indef_bytestring_start(buffer, buffer_size);
if (written == 0) return 0;
cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
for (size_t i = 0; i < chunk_count; i++) {
size_t chunk_written = cbor_serialize_bytestring(
chunks[i], buffer + written, buffer_size - written);
if (chunk_written == 0)
return 0;
else
written += chunk_written;
if (chunk_written == 0) return 0;
written += chunk_written;
}
if (cbor_encode_break(buffer + written, buffer_size - written) > 0)
return written + 1;
else
return 0;
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
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 buffer_size) {
assert(cbor_isa_string(item));
CBOR_ASSERT(cbor_isa_string(item));
if (cbor_string_is_definite(item)) {
size_t length = cbor_string_length(item);
size_t written = cbor_encode_string_start(length, buffer, buffer_size);
if (written && (buffer_size - written >= length)) {
memcpy(buffer + written, cbor_string_handle(item), length);
return written + length;
} else
return 0;
}
return 0;
} 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 written = cbor_encode_indef_string_start(buffer, buffer_size);
if (written == 0) return 0;
cbor_item_t **chunks = cbor_string_chunks_handle(item);
for (size_t i = 0; i < chunk_count; i++) {
size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written,
buffer_size - written);
if (chunk_written == 0)
return 0;
else
written += chunk_written;
if (chunk_written == 0) return 0;
written += chunk_written;
}
if (cbor_encode_break(buffer + written, buffer_size - written) > 0)
return written + 1;
else
return 0;
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
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 buffer_size) {
assert(cbor_isa_array(item));
CBOR_ASSERT(cbor_isa_array(item));
size_t size = cbor_array_size(item), written = 0;
cbor_item_t **handle = cbor_array_handle(item);
if (cbor_array_is_definite(item)) {
written = cbor_encode_array_start(size, buffer, buffer_size);
} else {
assert(cbor_array_is_indefinite(item));
CBOR_ASSERT(cbor_array_is_indefinite(item));
written = cbor_encode_indef_array_start(buffer, buffer_size);
}
if (written == 0) return 0;
size_t item_written;
for (size_t i = 0; i < size; i++) {
item_written =
size_t item_written =
cbor_serialize(*(handle++), buffer + written, buffer_size - written);
if (item_written == 0)
return 0;
else
written += item_written;
if (item_written == 0) return 0;
written += item_written;
}
if (cbor_array_is_definite(item)) {
return written;
} else {
assert(cbor_array_is_indefinite(item));
item_written = cbor_encode_break(buffer + written, buffer_size - written);
if (item_written == 0)
return 0;
else
return written + 1;
CBOR_ASSERT(cbor_array_is_indefinite(item));
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
if (break_written == 0) return 0;
return written + break_written;
}
}
size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
assert(cbor_isa_map(item));
CBOR_ASSERT(cbor_isa_map(item));
size_t size = cbor_map_size(item), written = 0;
struct cbor_pair *handle = cbor_map_handle(item);
if (cbor_map_is_definite(item)) {
written = cbor_encode_map_start(size, buffer, buffer_size);
} else {
assert(cbor_map_is_indefinite(item));
CBOR_ASSERT(cbor_map_is_indefinite(item));
written = cbor_encode_indef_map_start(buffer, buffer_size);
}
if (written == 0) return 0;
size_t item_written;
for (size_t i = 0; i < size; i++) {
item_written =
size_t item_written =
cbor_serialize(handle->key, buffer + written, buffer_size - written);
if (item_written == 0)
if (item_written == 0) {
return 0;
else
written += item_written;
}
written += item_written;
item_written = cbor_serialize((handle++)->value, buffer + written,
buffer_size - written);
if (item_written == 0)
return 0;
else
written += item_written;
if (item_written == 0) return 0;
written += item_written;
}
if (cbor_map_is_definite(item)) {
return written;
} else {
assert(cbor_map_is_indefinite(item));
item_written = cbor_encode_break(buffer + written, buffer_size - written);
if (item_written == 0)
return 0;
else
return written + 1;
CBOR_ASSERT(cbor_map_is_indefinite(item));
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
if (break_written == 0) return 0;
return written + break_written;
}
}
size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer,
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);
if (written == 0) return 0;
size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)),
buffer + written, buffer_size - written);
if (item_written == 0)
return 0;
else
return written + item_written;
if (item_written == 0) return 0;
return written + item_written;
}
size_t cbor_serialize_float_ctrl(const cbor_item_t *item, unsigned char *buffer,
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)) {
case CBOR_FLOAT_0:
/* 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,
buffer_size);
}
/* Should never happen - make the compiler happy */
return 0;
}

View File

@ -23,110 +23,143 @@ extern "C" {
/** Serialize the given item
*
* @param item[borrow] A data item
* @param item A data item
* @param buffer Buffer to serialize to
* @param buffer_size Size of the \p buffer
* @return Length of the result. 0 on failure.
*/
CBOR_EXPORT size_t cbor_serialize(const cbor_item_t *item,
cbor_mutable_data buffer, size_t buffer_size);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize(const cbor_item_t *item,
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
*
* 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
* .. 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.
* \endrst
*
* @param item[borrow] A data item
* @param buffer[out] Buffer containing the result
* @param buffer_size[out] Size of the \p buffer
* @return Length of the result. 0 on failure, in which case \p buffer is
* ``NULL``.
* @param item A data item
* @param[out] buffer Buffer containing the result
* @param[out] buffer_size Size of the \p buffer, or 0 on memory allocation
* failure.
* @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_mutable_data *buffer,
unsigned char **buffer,
size_t *buffer_size);
/** Serialize an uint
*
* @param item[borrow] A uint
* @param buffer Buffer to serialize to
* @param item A uint
* @param[out] buffer Buffer to serialize to
* @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,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_uint(const cbor_item_t *item,
cbor_mutable_data buffer,
size_t buffer_size);
/** Serialize a negint
*
* @param item[borrow] A neging
* @param buffer Buffer to serialize to
* @param item A negint
* @param[out] buffer Buffer to serialize to
* @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,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_negint(
const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize a bytestring
*
* @param item[borrow] A bytestring
* @param buffer Buffer to serialize to
* @param item A bytestring
* @param[out] buffer Buffer to serialize to
* @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_mutable_data, size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_bytestring(
const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize a string
*
* @param item[borrow] A string
* @param buffer Buffer to serialize to
* @param item A string
* @param[out] buffer Buffer to serialize to
* @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,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_string(
const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize an array
*
* @param item[borrow] An array
* @param buffer Buffer to serialize to
* @param item An array
* @param[out] buffer Buffer to serialize to
* @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,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_array(
const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
/** Serialize a map
*
* @param item[borrow] A map
* @param buffer Buffer to serialize to
* @param item A map
* @param[out] buffer Buffer to serialize to
* @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,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_map(const cbor_item_t *item,
cbor_mutable_data buffer,
size_t buffer_size);
/** Serialize a tag
*
* @param item[borrow] A tag
* @param buffer Buffer to serialize to
* @param item A tag
* @param[out] buffer Buffer to serialize to
* @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,
size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_tag(const cbor_item_t *item,
cbor_mutable_data buffer,
size_t buffer_size);
/** Serialize a
*
* @param item[borrow] A float or ctrl
* @param buffer Buffer to serialize to
* @param item A float or ctrl
* @param[out] buffer Buffer to serialize to
* @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_mutable_data, size_t);
_CBOR_NODISCARD CBOR_EXPORT size_t cbor_serialize_float_ctrl(
const cbor_item_t *item, cbor_mutable_data buffer, size_t buffer_size);
#ifdef __cplusplus
}

View File

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

View File

@ -18,16 +18,16 @@ extern "C" {
/** 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.
*
* @param buffer Input buffer
* @param buffer_size Length of the buffer
* @param source Input buffer
* @param source_size Length of the buffer
* @param callbacks The callback bundle
* @param context An arbitrary pointer to allow for maintaining context.
*/
CBOR_EXPORT struct cbor_decoder_result cbor_stream_decode(
cbor_data buffer, size_t buffer_size,
_CBOR_NODISCARD CBOR_EXPORT struct cbor_decoder_result cbor_stream_decode(
cbor_data source, size_t source_size,
const struct cbor_callbacks* callbacks, void* context);
#ifdef __cplusplus

View File

@ -9,8 +9,8 @@
#include <string.h>
#include "internal/memory_utils.h"
cbor_item_t *cbor_new_definite_string() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
cbor_item_t *cbor_new_definite_string(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item);
*item = (cbor_item_t){
.refcount = 1,
@ -19,15 +19,15 @@ cbor_item_t *cbor_new_definite_string() {
return item;
}
cbor_item_t *cbor_new_indefinite_string() {
cbor_item_t *item = _CBOR_MALLOC(sizeof(cbor_item_t));
cbor_item_t *cbor_new_indefinite_string(void) {
cbor_item_t *item = _cbor_malloc(sizeof(cbor_item_t));
_CBOR_NOTNULL(item);
*item = (cbor_item_t){
.refcount = 1,
.type = CBOR_TYPE_STRING,
.metadata = {.string_metadata = {.type = _CBOR_METADATA_INDEFINITE,
.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);
*((struct cbor_indefinite_string_data *)item->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_NOTNULL(item);
size_t len = strlen(val);
unsigned char *handle = _CBOR_MALLOC(len);
unsigned char *handle = _cbor_malloc(len);
_CBOR_DEPENDENT_NOTNULL(item, handle);
memcpy(handle, val, 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 *item = cbor_new_definite_string();
_CBOR_NOTNULL(item);
unsigned char *handle = _CBOR_MALLOC(length);
unsigned char *handle = _cbor_malloc(length);
_CBOR_DEPENDENT_NOTNULL(item, handle);
memcpy(handle, val, 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,
cbor_mutable_data CBOR_RESTRICT_POINTER data,
size_t length) {
assert(cbor_isa_string(item));
assert(cbor_string_is_definite(item));
CBOR_ASSERT(cbor_isa_string(item));
CBOR_ASSERT(cbor_string_is_definite(item));
item->data = data;
item->metadata.string_metadata.length = length;
}
cbor_item_t **cbor_string_chunks_handle(const cbor_item_t *item) {
assert(cbor_isa_string(item));
assert(cbor_string_is_indefinite(item));
CBOR_ASSERT(cbor_isa_string(item));
CBOR_ASSERT(cbor_string_is_indefinite(item));
return ((struct cbor_indefinite_string_data *)item->data)->chunks;
}
size_t cbor_string_chunk_count(const cbor_item_t *item) {
assert(cbor_isa_string(item));
assert(cbor_string_is_indefinite(item));
CBOR_ASSERT(cbor_isa_string(item));
CBOR_ASSERT(cbor_string_is_indefinite(item));
return ((struct cbor_indefinite_string_data *)item->data)->chunk_count;
}
bool cbor_string_add_chunk(cbor_item_t *item, cbor_item_t *chunk) {
assert(cbor_isa_string(item));
assert(cbor_string_is_indefinite(item));
CBOR_ASSERT(cbor_isa_string(item));
CBOR_ASSERT(cbor_string_is_indefinite(item));
struct cbor_indefinite_string_data *data =
(struct cbor_indefinite_string_data *)item->data;
if (data->chunk_count == data->chunk_capacity) {
// TODO: Add a test for this
if (!_cbor_safe_to_multiply(CBOR_BUFFER_GROWTH, data->chunk_capacity)) {
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) {
assert(cbor_isa_string(item));
CBOR_ASSERT(cbor_isa_string(item));
return item->metadata.string_metadata.length;
}
unsigned char *cbor_string_handle(const cbor_item_t *item) {
assert(cbor_isa_string(item));
CBOR_ASSERT(cbor_isa_string(item));
return item->data;
}
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;
}
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;
}

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
*/
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
*
* 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
*/
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?
*
* @param item[borrow] a string
* @param item a string
* @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?
*
* @param item[borrow] a string
* @param item a string
* @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
*
* 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
*
* @param item[borrow] A definite string
* @return The address of the underlying string. `NULL` if no data have been
* assigned yet.
* @param item A definite string
* @return The address of the underlying string.
* @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
*
@ -73,7 +78,7 @@ CBOR_EXPORT cbor_mutable_data cbor_string_handle(const cbor_item_t *item);
* the CBOR item will be left inconsistent.
* \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.
* libcbor will deallocate it when appropriate using its free function
* @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
* 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
*/
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
*
* @param item[borrow] A indefinite string
* @param item A indefinite string
* @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
*
@ -105,46 +112,60 @@ CBOR_EXPORT size_t cbor_string_chunk_count(const cbor_item_t *item);
*
* May realloc the chunk storage.
*
* @param item[borrow] An indefinite string
* @param item[incref] A definite string
* @return true on success. false on realloc failure. In that case, the refcount
* of `chunk` is not increased and the `item` is left intact.
* @param item An indefinite string
* @param chunk A definite string item. Its reference count will be increased
* by one.
* @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
*
* 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
*
* 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
*
* The `val` will be copied to a newly allocated block
*
* @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
*
* 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)
* @return A **new** string with content `handle`. `NULL` on malloc failure.
* @param val A UTF-8 string, at least @p `length` long (excluding the null
* 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
}

View File

@ -8,7 +8,7 @@
#include "tags.h"
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);
*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) {
assert(cbor_isa_tag(item));
CBOR_ASSERT(cbor_isa_tag(item));
return cbor_incref(item->metadata.tag_metadata.tagged_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;
}
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);
item->metadata.tag_metadata.tagged_item = tagged_item;
}
cbor_item_t *cbor_build_tag(uint64_t value, cbor_item_t *item) {
cbor_item_t *res = cbor_new_tag(value);
if (res == NULL) {
return NULL;
}
cbor_tag_set_item(res, item);
return res;
}

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