├── .editorconfig ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Doxyfile.in ├── LICENSE ├── README.md ├── Simple-Makefile ├── build.sh ├── cmake ├── Coveralls.cmake ├── CoverallsClear.cmake ├── CoverallsGenerateGcov.cmake └── LCov.cmake ├── cn-cbor.pc.in ├── include ├── CMakeLists.txt └── cn-cbor │ └── cn-cbor.h ├── src ├── CMakeLists.txt ├── cbor.h ├── cn-cbor.c ├── cn-create.c ├── cn-encoder.c ├── cn-error.c └── cn-get.c └── test ├── CMakeLists.txt ├── cases.cbor ├── cbor_test.c ├── ctest.h ├── expected.out └── test.c /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # Matches multiple files with brace expansion notation 12 | # Set default charset 13 | [*.{c,h}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cntest 2 | new.out 3 | *.o 4 | build 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - clang 4 | - gcc 5 | sudo: false 6 | addons: 7 | apt: 8 | sources: 9 | - george-edison55-precise-backports 10 | packages: 11 | - cmake 12 | - cmake-data 13 | script: 14 | - "./build.sh all test" 15 | notifications: 16 | slack: 17 | secure: WdgYxQrnFR5eu/eKygPuLjlFsuZxD9m2PLRWTLT85aj+18Gp2ooPjnI9UFdb1xY87+4InhWk6PvQU35j4bG0etPQtX+0H4T4Zdk/aD6KxgJBHIYGqtfZUMmdFfVpUH9cCPx99Jjw81mhKrxM+6rXiZdiWXuNhvbJOApRT6uxE2k= 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # top level build file for cn-cbor 4 | 5 | ## prepare CMAKE 6 | cmake_minimum_required ( VERSION 3.0.0 ) 7 | 8 | set ( VERSION_MAJOR 0 CACHE STRING "Project major version number") 9 | set ( VERSION_MINOR "1" CACHE STRING "Project minor version number" ) 10 | set ( VERSION_PATCH "0" CACHE STRING "Project patch version number" ) 11 | set ( CN_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" ) 12 | mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH CN_VERSION) 13 | 14 | project ( "cn-cbor" VERSION "${CN_VERSION}") 15 | 16 | find_package(Doxygen) 17 | 18 | ## setup options 19 | option ( use_context "Use context pointer for CBOR functions" OFF ) 20 | option ( verbose "Produce verbose makefile output" OFF ) 21 | option ( optimize "Optimize for size" OFF ) 22 | option ( fatal_warnings "Treat build warnings as errors" ON ) 23 | option ( coveralls "Generate coveralls data" ON ) 24 | option ( coveralls_send "Send data to coveralls site" OFF ) 25 | option ( build_docs "Create docs using Doxygen" ${DOXYGEN_FOUND} ) 26 | option ( no_floats "Build without floating point support" OFF ) 27 | option ( align_reads "Use memcpy in ntoh*p()" OFF ) 28 | 29 | set ( dist_dir ${CMAKE_BINARY_DIR}/dist ) 30 | set ( prefix ${CMAKE_INSTALL_PREFIX} ) 31 | set ( exec_prefix ${CMAKE_INSTALL_PREFIX}/bin ) 32 | set ( libdir ${CMAKE_INSTALL_PREFIX}/lib ) 33 | set ( includedir ${CMAKE_INSTALL_PREFIX}/include ) 34 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cn-cbor.pc.in 35 | ${CMAKE_CURRENT_BINARY_DIR}/cn-cbor.pc @ONLY) 36 | install (FILES ${CMAKE_CURRENT_BINARY_DIR}/cn-cbor.pc DESTINATION lib/pkgconfig ) 37 | 38 | set ( package_prefix "${CMAKE_PACKAGE_NAME}-${CMAKE_SYSTEM_NAME}" ) 39 | 40 | set ( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dist_dir}/bin ) 41 | set ( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${dist_dir}/lib ) 42 | set ( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${dist_dir}/lib ) 43 | 44 | if (NOT CMAKE_BUILD_TYPE) 45 | if ( optimize ) 46 | set ( CMAKE_BUILD_TYPE MinSizeRel ) 47 | set ( coveralls OFF ) 48 | set ( coveralls_send OFF ) 49 | else () 50 | set ( CMAKE_BUILD_TYPE Debug ) 51 | endif () 52 | endif() 53 | 54 | message ( "Build type: ${CMAKE_BUILD_TYPE}" ) 55 | 56 | if ( CMAKE_C_COMPILER_ID STREQUAL "GNU" OR 57 | CMAKE_C_COMPILER_ID MATCHES "Clang" ) 58 | message ( STATUS "adding GCC/Clang options ") 59 | add_definitions ( -std=gnu99 -Wall -Wextra -pedantic ) 60 | if ( fatal_warnings ) 61 | add_definitions ( -Werror ) 62 | endif () 63 | if ( optimize ) 64 | add_definitions ( -Os ) 65 | endif () 66 | elseif ( MSVC ) 67 | add_definitions ( /W3 ) 68 | if ( fatal_warnings ) 69 | add_definitions ( /WX ) 70 | endif () 71 | else () 72 | message ( FATAL_ERROR "unhandled compiler id: ${CMAKE_C_COMPILER_ID}" ) 73 | endif () 74 | 75 | if ( no_floats ) 76 | add_definitions(-DCBOR_NO_FLOAT) 77 | endif() 78 | 79 | if ( verbose ) 80 | set ( CMAKE_VERBOSE_MAKEFILE ON ) 81 | endif () 82 | 83 | ## include the parts 84 | add_subdirectory ( include ) 85 | add_subdirectory ( src ) 86 | add_subdirectory ( test ) 87 | 88 | install (FILES LICENSE README.md DESTINATION .) 89 | 90 | ## setup packaging 91 | set ( CPACK_GENERATOR "TGZ" ) 92 | set ( CPACK_PACKAGE_VERSION "${PROJECT_VERSION}" ) 93 | set ( CPACK_SOURCE_GENERATOR "TGZ" ) 94 | set ( CPACK_SOURCE_IGNORE_FILES "/\\\\.git/" ) 95 | file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/.gitignore igs) 96 | foreach (ig IN ITEMS ${igs}) 97 | # remove comments 98 | string ( REGEX REPLACE "^\\s*#.*" "" ig "${ig}") 99 | # remove any other whitespace 100 | string ( STRIP "${ig}" ig) 101 | # anything left? 102 | if (ig) 103 | # dots are literal 104 | string ( REPLACE "." "\\\\." ig "${ig}" ) 105 | # stars are on thars 106 | string ( REPLACE "*" ".*" ig "${ig}" ) 107 | list ( APPEND CPACK_SOURCE_IGNORE_FILES "/${ig}/" ) 108 | endif() 109 | endforeach() 110 | 111 | set ( CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/README.md ) 112 | set ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" ) 113 | 114 | include ( CPack ) 115 | include ( CTest ) 116 | 117 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) 118 | include ( LCov ) 119 | 120 | if (build_docs) 121 | if(NOT DOXYGEN_FOUND) 122 | message(FATAL_ERROR "Doxygen is needed to build the documentation.") 123 | endif() 124 | 125 | set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) 126 | set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 127 | 128 | configure_file(${doxyfile_in} ${doxyfile} @ONLY) 129 | 130 | add_custom_target(doc 131 | COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} 132 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 133 | COMMENT "Generating API documentation with Doxygen" 134 | VERBATIM) 135 | 136 | install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc) 137 | endif() 138 | -------------------------------------------------------------------------------- /Doxyfile.in: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = "@CMAKE_PROJECT_NAME@" 2 | PROJECT_NUMBER = @CN_VERSION@ 3 | STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ \ 4 | @PROJECT_BINARY_DIR@ 5 | INPUT = @PROJECT_SOURCE_DIR@/README.md \ 6 | @PROJECT_SOURCE_DIR@/include 7 | FILE_PATTERNS = *.h 8 | RECURSIVE = YES 9 | USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Carsten Bormann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/cabo/cn-cbor.png?branch=master)](https://travis-ci.org/cabo/cn-cbor) 2 | 3 | # cn-cbor: A constrained node implementation of CBOR in C 4 | 5 | Below is the original README for cn-cbor, a Proof of Concept 6 | implementation of [CBOR](https://cbor.io) that I wrote in 2013. 7 | People have been sending me fixes and updates to make this 8 | implementation more useful for actual use, and for a while I have 9 | occasionally integrated them. Jim Schaad now is so far ahead of this 10 | repo that you are most likely better off using [his 11 | repo](https://github.com/jimsch/cn-cbor) as your upstream. 12 | 13 | If you *are* here looking for experimental CBOR implementations for 14 | constrained nodes, there are many, some of which are listed at 15 | [cbor.io](https://cbor.io/impls.html). One more: I have recently 16 | [extracted](https://github.com/cabo/bm_cbor) the implementation from 17 | ARM-mbed's [SUIT manifest 18 | generator](https://github.com/ARMmbed/suit-manifest-generator) (Apache 19 | 2.0). SUIT implementers report (CBOR-specific) code sizes in the low 20 | hundreds of bytes. Thank you, [ARM](https://github.com/bremoran). 21 | 22 | # cn-cbor: A constrained node implementation of CBOR in C 23 | 24 | This is a constrained node implementation of [CBOR](http://cbor.io) in 25 | C that I threw together in 2013, before the publication of 26 | [RFC 7049](http://tools.ietf.org/html/rfc7049), to validate certain 27 | implementability considerations. 28 | 29 | Its API model was inspired by 30 | [nxjson](https://bitbucket.org/yarosla/nxjson). It turns out that 31 | this API model actually works even better with the advantages of the 32 | CBOR format. 33 | 34 | This code has been used in a number of research implementations on 35 | constrained nodes, with resulting code sizes appreciably under 1 KiB 36 | on ARM platforms. 37 | 38 | I always meant to improve the interface some more with certain API 39 | changes, in order to get even closer to 0.5 KiB, but I ran out of 40 | time. So here it is. If I do get around to making these changes, the 41 | API will indeed change a bit, so please be forewarned. 42 | 43 | ## Building 44 | 45 | There is a `Simple-Makefile` for playing around, as well as a complete 46 | [`cmake`](http://www.cmake.org)-based build environment. 47 | (You can choose what fits your needs better.) 48 | 49 | Building with `cmake`: 50 | 51 | ./build.sh 52 | 53 | Building including testing: 54 | 55 | ./build.sh all test 56 | 57 | Generating a test coverage report (requires lcov[^1]; result in `build/lcov/index.html`): 58 | 59 | ./build.sh all coveralls coverage_report 60 | 61 | License: MIT 62 | 63 | [^1]: Installation with homebrew: `brew install lcov` 64 | -------------------------------------------------------------------------------- /Simple-Makefile: -------------------------------------------------------------------------------- 1 | # enable this for armv7 builds, lazily using iPhone SDK 2 | #CFLAGS = -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include -arch armv7 -Os 3 | CFLAGS = -Os -Wall -Wextra -Wno-unknown-pragmas -Werror-implicit-function-declaration -Werror -Wno-unused-parameter -Wdeclaration-after-statement -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Iinclude 4 | 5 | all: cntest 6 | 7 | test: cntest 8 | (cd test; env MallocStackLogging=true ../cntest) >new.out 9 | -diff new.out test/expected.out 10 | 11 | cntest: src/cbor.h include/cn-cbor/cn-cbor.h src/cn-cbor.c src/cn-error.c src/cn-get.c test/test.c 12 | clang $(CFLAGS) src/cn-cbor.c src/cn-error.c src/cn-get.c test/test.c -o cntest 13 | 14 | size: cn-cbor.o 15 | size cn-cbor.o 16 | size -m cn-cbor.o 17 | 18 | cn-cbor.o: src/cn-cbor.c include/cn-cbor/cn-cbor.h src/cbor.h 19 | clang $(CFLAGS) -c src/cn-cbor.c 20 | 21 | cn-cbor-play.zip: Makefile src/cbor.h src/cn-cbor.c include/cn-cbor/cn-cbor.h test/expected.out test/test.c 22 | zip $@ $^ 23 | 24 | clean: 25 | $(RM) cntest *.o new.out cn-cbor-play.zip 26 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -d "build" ]; then 3 | mkdir build 4 | fi 5 | cd build && cmake .. && make $* 6 | -------------------------------------------------------------------------------- /cmake/Coveralls.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Permission is hereby granted, free of charge, to any person obtaining a copy 3 | # of this software and associated documentation files (the "Software"), to deal 4 | # in the Software without restriction, including without limitation the rights 5 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 6 | # copies of the Software, and to permit persons to whom the Software is 7 | # furnished to do so, subject to the following conditions: 8 | # 9 | # The above copyright notice and this permission notice shall be included in all 10 | # copies or substantial portions of the Software. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | # SOFTWARE. 19 | # 20 | # Copyright (C) 2014 Joakim Söderberg 21 | # 22 | 23 | 24 | # 25 | # Param _COVERAGE_SRCS A list of source files that coverage should be collected for. 26 | # Param _COVERALLS_UPLOAD Upload the result to coveralls? 27 | # 28 | function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD) 29 | 30 | if (ARGC GREATER 2) 31 | set(_CMAKE_SCRIPT_PATH ${ARGN}) 32 | message("Coveralls: Using alternate CMake script dir: ${_CMAKE_SCRIPT_PATH}") 33 | else() 34 | set(_CMAKE_SCRIPT_PATH ${PROJECT_SOURCE_DIR}/cmake) 35 | endif() 36 | 37 | if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake") 38 | message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake") 39 | endif() 40 | 41 | if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake") 42 | message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake") 43 | endif() 44 | 45 | # When passing a CMake list to an external process, the list 46 | # will be converted from the format "1;2;3" to "1 2 3". 47 | # This means the script we're calling won't see it as a list 48 | # of sources, but rather just one long path. We remedy this 49 | # by replacing ";" with "*" and then reversing that in the script 50 | # that we're calling. 51 | # http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html 52 | set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS}) 53 | set(COVERAGE_SRCS "") 54 | foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP}) 55 | set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}") 56 | endforeach() 57 | 58 | #message("Coverage sources: ${COVERAGE_SRCS}") 59 | set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json) 60 | 61 | add_custom_target(coveralls_generate 62 | 63 | # Zero the coverage counters. 64 | COMMAND ${CMAKE_COMMAND} 65 | -P "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake" 66 | 67 | # Run regress tests. 68 | COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure 69 | 70 | # Generate Gcov and translate it into coveralls JSON. 71 | # We do this by executing an external CMake script. 72 | # (We don't want this to run at CMake generation time, but after compilation and everything has run). 73 | COMMAND ${CMAKE_COMMAND} 74 | -DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c" 75 | -DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}" 76 | -DCOV_PATH="${PROJECT_BINARY_DIR}" 77 | -DPROJECT_ROOT="${PROJECT_SOURCE_DIR}" 78 | -P "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake" 79 | 80 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 81 | COMMENT "Generating coveralls output..." 82 | ) 83 | 84 | if (_COVERALLS_UPLOAD) 85 | message("COVERALLS UPLOAD: ON") 86 | 87 | find_program(CURL_EXECUTABLE curl) 88 | 89 | if (NOT CURL_EXECUTABLE) 90 | message(FATAL_ERROR "Coveralls: curl not found! Aborting") 91 | endif() 92 | 93 | add_custom_target(coveralls_upload 94 | # Upload the JSON to coveralls. 95 | COMMAND ${CURL_EXECUTABLE} 96 | -S -F json_file=@${COVERALLS_FILE} 97 | https://coveralls.io/api/v1/jobs 98 | 99 | DEPENDS coveralls_generate 100 | 101 | WORKING_DIRECTORY ${PROJECT_BINARY_DIR} 102 | COMMENT "Uploading coveralls output...") 103 | 104 | add_custom_target(coveralls DEPENDS coveralls_upload) 105 | else() 106 | message("COVERALLS UPLOAD: OFF") 107 | add_custom_target(coveralls DEPENDS coveralls_generate) 108 | endif() 109 | 110 | endfunction() 111 | 112 | macro(coveralls_turn_on_coverage) 113 | if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") 114 | message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug") 115 | endif() 116 | 117 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") 118 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") 119 | endmacro() 120 | -------------------------------------------------------------------------------- /cmake/CoverallsClear.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Permission is hereby granted, free of charge, to any person obtaining a copy 3 | # of this software and associated documentation files (the "Software"), to deal 4 | # in the Software without restriction, including without limitation the rights 5 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 6 | # copies of the Software, and to permit persons to whom the Software is 7 | # furnished to do so, subject to the following conditions: 8 | # 9 | # The above copyright notice and this permission notice shall be included in all 10 | # copies or substantial portions of the Software. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | # SOFTWARE. 19 | # 20 | # Copyright (C) 2014 Joakim Söderberg 21 | # 22 | 23 | message ( "Clearing coverage data" ) 24 | file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda) 25 | file(REMOVE ${PROJECT_BINARY_DIR}/*.gcov) 26 | file(REMOVE ${PROJECT_BINARY_DIR}/*.gcov_tmp) 27 | -------------------------------------------------------------------------------- /cmake/CoverallsGenerateGcov.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Permission is hereby granted, free of charge, to any person obtaining a copy 3 | # of this software and associated documentation files (the "Software"), to deal 4 | # in the Software without restriction, including without limitation the rights 5 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 6 | # copies of the Software, and to permit persons to whom the Software is 7 | # furnished to do so, subject to the following conditions: 8 | # 9 | # The above copyright notice and this permission notice shall be included in all 10 | # copies or substantial portions of the Software. 11 | # 12 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | # SOFTWARE. 19 | # 20 | # Copyright (C) 2014 Joakim Söderberg 21 | # 22 | # This is intended to be run by a custom target in a CMake project like this. 23 | # 0. Compile program with coverage support. 24 | # 1. Clear coverage data. (Recursively delete *.gcda in build dir) 25 | # 2. Run the unit tests. 26 | # 3. Run this script specifying which source files the coverage should be performed on. 27 | # 28 | # This script will then use gcov to generate .gcov files in the directory specified 29 | # via the COV_PATH var. This should probably be the same as your cmake build dir. 30 | # 31 | # It then parses the .gcov files to convert them into the Coveralls JSON format: 32 | # https://coveralls.io/docs/api 33 | # 34 | # Example for running as standalone CMake script from the command line: 35 | # (Note it is important the -P is at the end...) 36 | # $ cmake -DCOV_PATH=$(pwd) 37 | # -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c" 38 | # -P ../cmake/CoverallsGcovUpload.cmake 39 | # 40 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 41 | 42 | 43 | # 44 | # Make sure we have the needed arguments. 45 | # 46 | if (NOT COVERALLS_OUTPUT_FILE) 47 | message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE") 48 | endif() 49 | 50 | if (NOT COV_PATH) 51 | message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH") 52 | endif() 53 | 54 | if (NOT COVERAGE_SRCS) 55 | message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS") 56 | endif() 57 | 58 | if (NOT PROJECT_ROOT) 59 | message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.") 60 | endif() 61 | 62 | # Since it's not possible to pass a CMake list properly in the 63 | # "1;2;3" format to an external process, we have replaced the 64 | # ";" with "*", so reverse that here so we get it back into the 65 | # CMake list format. 66 | string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS}) 67 | 68 | find_program(GCOV_EXECUTABLE gcov) 69 | 70 | if (NOT GCOV_EXECUTABLE) 71 | message(FATAL_ERROR "gcov not found! Aborting...") 72 | endif() 73 | 74 | find_package(Git) 75 | 76 | # TODO: Add these git things to the coveralls json. 77 | if (GIT_FOUND) 78 | # Branch. 79 | execute_process( 80 | COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD 81 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 82 | OUTPUT_VARIABLE GIT_BRANCH 83 | OUTPUT_STRIP_TRAILING_WHITESPACE 84 | ) 85 | 86 | macro (git_log_format FORMAT_CHARS VAR_NAME) 87 | execute_process( 88 | COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS} 89 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 90 | OUTPUT_VARIABLE ${VAR_NAME} 91 | OUTPUT_STRIP_TRAILING_WHITESPACE 92 | ) 93 | endmacro() 94 | 95 | git_log_format(an GIT_AUTHOR_EMAIL) 96 | git_log_format(ae GIT_AUTHOR_EMAIL) 97 | git_log_format(cn GIT_COMMITTER_NAME) 98 | git_log_format(ce GIT_COMMITTER_EMAIL) 99 | git_log_format(B GIT_COMMIT_MESSAGE) 100 | 101 | message("Git exe: ${GIT_EXECUTABLE}") 102 | message("Git branch: ${GIT_BRANCH}") 103 | message("Git author: ${GIT_AUTHOR_NAME}") 104 | message("Git e-mail: ${GIT_AUTHOR_EMAIL}") 105 | message("Git commiter name: ${GIT_COMMITTER_NAME}") 106 | message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}") 107 | message("Git commit message: ${GIT_COMMIT_MESSAGE}") 108 | 109 | endif() 110 | 111 | ############################# Macros ######################################### 112 | 113 | # 114 | # This macro converts from the full path format gcov outputs: 115 | # 116 | # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov 117 | # 118 | # to the original source file path the .gcov is for: 119 | # 120 | # /path/to/project/root/subdir/the_file.c 121 | # 122 | macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME) 123 | 124 | # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov 125 | # -> 126 | # #path#to#project#root#subdir#the_file.c.gcov 127 | get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME) 128 | 129 | # #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c 130 | string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT}) 131 | string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP}) 132 | set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}") 133 | endmacro() 134 | 135 | ############################################################################## 136 | 137 | # Get the coverage data. 138 | file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda") 139 | message("GCDA files:") 140 | 141 | # Get a list of all the object directories needed by gcov 142 | # (The directories the .gcda files and .o files are found in) 143 | # and run gcov on those. 144 | foreach(GCDA ${GCDA_FILES}) 145 | message("Process: ${GCDA}") 146 | message("------------------------------------------------------------------------------") 147 | get_filename_component(GCDA_DIR ${GCDA} PATH) 148 | 149 | # 150 | # The -p below refers to "Preserve path components", 151 | # This means that the generated gcov filename of a source file will 152 | # keep the original files entire filepath, but / is replaced with #. 153 | # Example: 154 | # 155 | # /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda 156 | # ------------------------------------------------------------------------------ 157 | # File '/path/to/project/root/subdir/the_file.c' 158 | # Lines executed:68.34% of 199 159 | # /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov' 160 | # 161 | # If -p is not specified then the file is named only "the_file.c.gcov" 162 | # 163 | execute_process( 164 | COMMAND ${GCOV_EXECUTABLE} -c -p -o ${GCDA_DIR} ${GCDA} 165 | WORKING_DIRECTORY ${COV_PATH} 166 | ) 167 | endforeach() 168 | 169 | # TODO: Make these be absolute path 170 | file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov) 171 | 172 | # Get only the filenames to use for filtering. 173 | #set(COVERAGE_SRCS_NAMES "") 174 | #foreach (COVSRC ${COVERAGE_SRCS}) 175 | # get_filename_component(COVSRC_NAME ${COVSRC} NAME) 176 | # message("${COVSRC} -> ${COVSRC_NAME}") 177 | # list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}") 178 | #endforeach() 179 | 180 | # 181 | # Filter out all but the gcov files we want. 182 | # 183 | # We do this by comparing the list of COVERAGE_SRCS filepaths that the 184 | # user wants the coverage data for with the paths of the generated .gcov files, 185 | # so that we only keep the relevant gcov files. 186 | # 187 | # Example: 188 | # COVERAGE_SRCS = 189 | # /path/to/project/root/subdir/the_file.c 190 | # 191 | # ALL_GCOV_FILES = 192 | # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov 193 | # /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov 194 | # 195 | # Result should be: 196 | # GCOV_FILES = 197 | # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov 198 | # 199 | set(GCOV_FILES "") 200 | #message("Look in coverage sources: ${COVERAGE_SRCS}") 201 | message("\nFilter out unwanted GCOV files:") 202 | message("===============================") 203 | 204 | set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS}) 205 | 206 | foreach (GCOV_FILE ${ALL_GCOV_FILES}) 207 | 208 | # 209 | # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov 210 | # -> 211 | # /path/to/project/root/subdir/the_file.c 212 | get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE}) 213 | 214 | # Is this in the list of source files? 215 | # TODO: We want to match against relative path filenames from the source file root... 216 | list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND) 217 | 218 | if (NOT WAS_FOUND EQUAL -1) 219 | message("YES: ${GCOV_FILE}") 220 | list(APPEND GCOV_FILES ${GCOV_FILE}) 221 | 222 | # We remove it from the list, so we don't bother searching for it again. 223 | # Also files left in COVERAGE_SRCS_REMAINING after this loop ends should 224 | # have coverage data generated from them (no lines are covered). 225 | list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH}) 226 | else() 227 | message("NO: ${GCOV_FILE}") 228 | endif() 229 | endforeach() 230 | 231 | # TODO: Enable setting these 232 | set(JSON_SERVICE_NAME "travis-ci") 233 | set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID}) 234 | 235 | set(JSON_TEMPLATE 236 | "{ 237 | \"service_name\": \"\@JSON_SERVICE_NAME\@\", 238 | \"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\", 239 | \"source_files\": \@JSON_GCOV_FILES\@ 240 | }" 241 | ) 242 | 243 | set(SRC_FILE_TEMPLATE 244 | "{ 245 | \"name\": \"\@GCOV_SRC_REL_PATH\@\", 246 | \"source_digest\": \"\@GCOV_CONTENTS_MD5\@\", 247 | \"coverage\": \@GCOV_FILE_COVERAGE\@ 248 | }" 249 | ) 250 | 251 | message("\nGenerate JSON for files:") 252 | message("=========================") 253 | 254 | set(JSON_GCOV_FILES "[") 255 | 256 | # Read the GCOV files line by line and get the coverage data. 257 | foreach (GCOV_FILE ${GCOV_FILES}) 258 | 259 | get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE}) 260 | file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}") 261 | 262 | # The new coveralls API doesn't need the entire source (Yay!) 263 | # However, still keeping that part for now. Will cleanup in the future. 264 | file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5) 265 | message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}") 266 | 267 | # Loads the gcov file as a list of lines. 268 | # (We first open the file and replace all occurences of [] with _ 269 | # because CMake will fail to parse a line containing unmatched brackets... 270 | # also the \ to escaped \n in macros screws up things.) 271 | # https://public.kitware.com/Bug/view.php?id=15369 272 | file(READ ${GCOV_FILE} GCOV_CONTENTS) 273 | string(REPLACE "[" "_" GCOV_CONTENTS "${GCOV_CONTENTS}") 274 | string(REPLACE "]" "_" GCOV_CONTENTS "${GCOV_CONTENTS}") 275 | string(REPLACE "\\" "_" GCOV_CONTENTS "${GCOV_CONTENTS}") 276 | file(WRITE ${GCOV_FILE}_tmp "${GCOV_CONTENTS}") 277 | 278 | file(STRINGS ${GCOV_FILE}_tmp GCOV_LINES) 279 | list(LENGTH GCOV_LINES LINE_COUNT) 280 | 281 | # Instead of trying to parse the source from the 282 | # gcov file, simply read the file contents from the source file. 283 | # (Parsing it from the gcov is hard because C-code uses ; in many places 284 | # which also happens to be the same as the CMake list delimeter). 285 | file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE) 286 | 287 | string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 288 | string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 289 | string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 290 | string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 291 | string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 292 | # According to http://json.org/ these should be escaped as well. 293 | # Don't know how to do that in CMake however... 294 | #string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 295 | #string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 296 | #string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}") 297 | 298 | # We want a json array of coverage data as a single string 299 | # start building them from the contents of the .gcov 300 | set(GCOV_FILE_COVERAGE "[") 301 | 302 | set(GCOV_LINE_COUNT 1) # Line number for the .gcov. 303 | set(DO_SKIP 0) 304 | foreach (GCOV_LINE ${GCOV_LINES}) 305 | #message("${GCOV_LINE}") 306 | # Example of what we're parsing: 307 | # Hitcount |Line | Source 308 | # " 8: 26: if (!allowed || (strlen(allowed) == 0))" 309 | string(REGEX REPLACE 310 | "^([^:]*):([^:]*):(.*)$" 311 | "\\1;\\2;\\3" 312 | RES 313 | "${GCOV_LINE}") 314 | 315 | # Check if we should exclude lines using the Lcov syntax. 316 | string(REGEX MATCH "LCOV_EXCL_START" START_SKIP "${GCOV_LINE}") 317 | string(REGEX MATCH "LCOV_EXCL_END" END_SKIP "${GCOV_LINE}") 318 | string(REGEX MATCH "LCOV_EXCL_LINE" LINE_SKIP "${GCOV_LINE}") 319 | 320 | set(RESET_SKIP 0) 321 | if (LINE_SKIP AND NOT DO_SKIP) 322 | set(DO_SKIP 1) 323 | set(RESET_SKIP 1) 324 | endif() 325 | 326 | if (START_SKIP) 327 | set(DO_SKIP 1) 328 | message("${GCOV_LINE_COUNT}: Start skip") 329 | endif() 330 | 331 | if (END_SKIP) 332 | set(DO_SKIP 0) 333 | endif() 334 | 335 | list(LENGTH RES RES_COUNT) 336 | 337 | if (RES_COUNT GREATER 2) 338 | list(GET RES 0 HITCOUNT) 339 | list(GET RES 1 LINE) 340 | list(GET RES 2 SOURCE) 341 | 342 | string(STRIP ${HITCOUNT} HITCOUNT) 343 | string(STRIP ${LINE} LINE) 344 | 345 | # Lines with 0 line numbers are metadata and can be ignored. 346 | if (NOT ${LINE} EQUAL 0) 347 | 348 | if (DO_SKIP) 349 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ") 350 | else() 351 | # Translate the hitcount into valid JSON values. 352 | if (${HITCOUNT} STREQUAL "#####") 353 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ") 354 | elseif (${HITCOUNT} STREQUAL "-") 355 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ") 356 | else() 357 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ") 358 | endif() 359 | endif() 360 | endif() 361 | else() 362 | message(WARNING "Failed to properly parse line (RES_COUNT = ${RES_COUNT}) ${GCOV_FILE}:${GCOV_LINE_COUNT}\n-->${GCOV_LINE}") 363 | endif() 364 | 365 | if (RESET_SKIP) 366 | set(DO_SKIP 0) 367 | endif() 368 | math(EXPR GCOV_LINE_COUNT "${GCOV_LINE_COUNT}+1") 369 | endforeach() 370 | 371 | message("${GCOV_LINE_COUNT} of ${LINE_COUNT} lines read!") 372 | 373 | # Advanced way of removing the trailing comma in the JSON array. 374 | # "[1, 2, 3, " -> "[1, 2, 3" 375 | string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE}) 376 | 377 | # Append the trailing ] to complete the JSON array. 378 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]") 379 | 380 | # Generate the final JSON for this file. 381 | message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...") 382 | string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON) 383 | 384 | set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ") 385 | endforeach() 386 | 387 | # Loop through all files we couldn't find any coverage for 388 | # as well, and generate JSON for those as well with 0% coverage. 389 | foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING}) 390 | 391 | # Loads the source file as a list of lines. 392 | file(STRINGS ${NOT_COVERED_SRC} SRC_LINES) 393 | 394 | set(GCOV_FILE_COVERAGE "[") 395 | set(GCOV_FILE_SOURCE "") 396 | 397 | foreach (SOURCE ${SRC_LINES}) 398 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ") 399 | 400 | string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}") 401 | string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}") 402 | string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}") 403 | string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}") 404 | set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n") 405 | endforeach() 406 | 407 | # Remove trailing comma, and complete JSON array with ] 408 | string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE}) 409 | set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]") 410 | 411 | # Generate the final JSON for this file. 412 | message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...") 413 | string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON) 414 | set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ") 415 | endforeach() 416 | 417 | # Get rid of trailing comma. 418 | string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES}) 419 | set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]") 420 | 421 | # Generate the final complete JSON! 422 | message("Generate final JSON...") 423 | string(CONFIGURE ${JSON_TEMPLATE} JSON) 424 | 425 | file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}") 426 | message("###########################################################################") 427 | message("Generated coveralls JSON containing coverage data:") 428 | message("${COVERALLS_OUTPUT_FILE}") 429 | message("###########################################################################") 430 | -------------------------------------------------------------------------------- /cmake/LCov.cmake: -------------------------------------------------------------------------------- 1 | FIND_PROGRAM( LCOV_PATH lcov ) 2 | FIND_PROGRAM( GENHTML_PATH genhtml ) 3 | 4 | if (LCOV_PATH) 5 | # message ( "lcov: ${LCOV_PATH}" ) 6 | 7 | add_custom_target(coverage_report 8 | COMMAND "${LCOV_PATH}" --rc lcov_branch_coverage=1 --no-checksum --base-directory "${CMAKE_CURRENT_SOURCE_DIR}" --directory src/CMakeFiles/${PROJECT_NAME}.dir --no-external --capture --output-file ${PROJECT_NAME}.info 9 | COMMAND "${GENHTML_PATH}" --rc genhtml_branch_coverage=1 --output-directory lcov ${PROJECT_NAME}.info 10 | COMMAND echo "Coverage report in: file://${CMAKE_BINARY_DIR}/lcov/index.html" 11 | ) 12 | endif() 13 | -------------------------------------------------------------------------------- /cn-cbor.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: cn-cbor 7 | Description: A constrained node implementation of CBOR in C 8 | URL: https://github.com/cabo/cn-cbor 9 | Version: @CN_VERSION@ 10 | Libs: -L${libdir} -lcn-cbor 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install ( DIRECTORY ../include DESTINATION . 2 | PATTERN CMakeLists.txt EXCLUDE ) 3 | -------------------------------------------------------------------------------- /include/cn-cbor/cn-cbor.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * \file 4 | * \brief 5 | * CBOR parsing 6 | */ 7 | 8 | #ifndef CN_CBOR_H 9 | #define CN_CBOR_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | #ifdef EMACS_INDENTATION_HELPER 15 | } /* Duh. */ 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | /** 23 | * All of the different kinds of CBOR values. 24 | */ 25 | typedef enum cn_cbor_type { 26 | /** false */ 27 | CN_CBOR_FALSE, 28 | /** true */ 29 | CN_CBOR_TRUE, 30 | /** null */ 31 | CN_CBOR_NULL, 32 | /** undefined */ 33 | CN_CBOR_UNDEF, 34 | /** Positive integers */ 35 | CN_CBOR_UINT, 36 | /** Negative integers */ 37 | CN_CBOR_INT, 38 | /** Byte string */ 39 | CN_CBOR_BYTES, 40 | /** UTF-8 string */ 41 | CN_CBOR_TEXT, 42 | /** Byte string, in chunks. Each chunk is a child. */ 43 | CN_CBOR_BYTES_CHUNKED, 44 | /** UTF-8 string, in chunks. Each chunk is a child */ 45 | CN_CBOR_TEXT_CHUNKED, 46 | /** Array of CBOR values. Each array element is a child, in order */ 47 | CN_CBOR_ARRAY, 48 | /** Map of key/value pairs. Each key and value is a child, alternating. */ 49 | CN_CBOR_MAP, 50 | /** Tag describing the next value. The next value is the single child. */ 51 | CN_CBOR_TAG, 52 | /** Simple value, other than the defined ones */ 53 | CN_CBOR_SIMPLE, 54 | /** Doubles, floats, and half-floats */ 55 | CN_CBOR_DOUBLE, 56 | /** Floats, and half-floats */ 57 | CN_CBOR_FLOAT, 58 | /** An error has occurred */ 59 | CN_CBOR_INVALID 60 | } cn_cbor_type; 61 | 62 | /** 63 | * Flags used during parsing. Not useful for consumers of the 64 | * `cn_cbor` structure. 65 | */ 66 | typedef enum cn_cbor_flags { 67 | /** The count field will be used for parsing */ 68 | CN_CBOR_FL_COUNT = 1, 69 | /** An indefinite number of children */ 70 | CN_CBOR_FL_INDEF = 2, 71 | /** Not used yet; the structure must free the v.str pointer when the 72 | structure is freed */ 73 | CN_CBOR_FL_OWNER = 0x80, /* of str */ 74 | } cn_cbor_flags; 75 | 76 | /** 77 | * A CBOR value 78 | */ 79 | typedef struct cn_cbor { 80 | /** The type of value */ 81 | cn_cbor_type type; 82 | /** Flags used at parse time */ 83 | cn_cbor_flags flags; 84 | /** Data associated with the value; different branches of the union are 85 | used depending on the `type` field. */ 86 | union { 87 | /** CN_CBOR_BYTES */ 88 | const uint8_t* bytes; 89 | /** CN_CBOR_TEXT */ 90 | const char* str; 91 | /** CN_CBOR_INT */ 92 | long sint; 93 | /** CN_CBOR_UINT */ 94 | unsigned long uint; 95 | /** CN_CBOR_DOUBLE */ 96 | double dbl; 97 | /** CN_CBOR_FLOAT */ 98 | float f; 99 | /** for use during parsing */ 100 | unsigned long count; 101 | } v; /* TBD: optimize immediate */ 102 | /** Number of children. 103 | * @note: for maps, this is 2x the number of entries */ 104 | int length; 105 | /** The first child value */ 106 | struct cn_cbor* first_child; 107 | /** The last child value */ 108 | struct cn_cbor* last_child; 109 | /** The sibling after this one, or NULL if this is the last */ 110 | struct cn_cbor* next; 111 | /** The parent of this value, or NULL if this is the root */ 112 | struct cn_cbor* parent; 113 | } cn_cbor; 114 | 115 | /** 116 | * All of the different kinds of errors 117 | */ 118 | typedef enum cn_cbor_error { 119 | /** No error has occurred */ 120 | CN_CBOR_NO_ERROR, 121 | /** More data was expected while parsing */ 122 | CN_CBOR_ERR_OUT_OF_DATA, 123 | /** Some extra data was left over at the end of parsing */ 124 | CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED, 125 | /** A map should be alternating keys and values. A break was found 126 | when a value was expected */ 127 | CN_CBOR_ERR_ODD_SIZE_INDEF_MAP, 128 | /** A break was found where it wasn't expected */ 129 | CN_CBOR_ERR_BREAK_OUTSIDE_INDEF, 130 | /** Indefinite encoding works for bstrs, strings, arrays, and maps. 131 | A different major type tried to use it. */ 132 | CN_CBOR_ERR_MT_UNDEF_FOR_INDEF, 133 | /** Additional Information values 28-30 are reserved */ 134 | CN_CBOR_ERR_RESERVED_AI, 135 | /** A chunked encoding was used for a string or bstr, and one of the elements 136 | wasn't the expected (string/bstr) type */ 137 | CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING, 138 | /** An invalid parameter was passed to a function */ 139 | CN_CBOR_ERR_INVALID_PARAMETER, 140 | /** Allocation failed */ 141 | CN_CBOR_ERR_OUT_OF_MEMORY, 142 | /** A float was encountered during parse but the library was built without 143 | support for float types. */ 144 | CN_CBOR_ERR_FLOAT_NOT_SUPPORTED 145 | } cn_cbor_error; 146 | 147 | /** 148 | * Strings matching the `cn_cbor_error` conditions. 149 | * 150 | * @todo: turn into a function to make the type safety more clear? 151 | */ 152 | extern const char *cn_cbor_error_str[]; 153 | 154 | /** 155 | * Errors 156 | */ 157 | typedef struct cn_cbor_errback { 158 | /** The position in the input where the erorr happened */ 159 | int pos; 160 | /** The error, or CN_CBOR_NO_ERROR if none */ 161 | cn_cbor_error err; 162 | } cn_cbor_errback; 163 | 164 | #ifdef USE_CBOR_CONTEXT 165 | 166 | /** 167 | * Allocate and zero out memory. `count` elements of `size` are required, 168 | * as for `calloc(3)`. The `context` is the `cn_cbor_context` passed in 169 | * earlier to the CBOR routine. 170 | * 171 | * @param[in] count The number of items to allocate 172 | * @param[in] size The size of each item 173 | * @param[in] context The allocation context 174 | */ 175 | typedef void* (*cn_calloc_func)(size_t count, size_t size, void *context); 176 | 177 | /** 178 | * Free memory previously allocated with a context. If using a pool allocator, 179 | * this function will often be a no-op, but it must be supplied in order to 180 | * prevent the CBOR library from calling `free(3)`. 181 | * 182 | * @note: it may be that this is never needed; if so, it will be removed for 183 | * clarity and speed. 184 | * 185 | * @param context [description] 186 | * @return [description] 187 | */ 188 | typedef void (*cn_free_func)(void *ptr, void *context); 189 | 190 | /** 191 | * The allocation context. 192 | */ 193 | typedef struct cn_cbor_context { 194 | /** The pool `calloc` routine. Must allocate and zero. */ 195 | cn_calloc_func calloc_func; 196 | /** The pool `free` routine. Often a no-op, but required. */ 197 | cn_free_func free_func; 198 | /** Typically, the pool object, to be used when calling `calloc_func` 199 | * and `free_func` */ 200 | void *context; 201 | } cn_cbor_context; 202 | 203 | /** When USE_CBOR_CONTEXT is defined, many functions take an extra `context` 204 | * parameter */ 205 | #define CBOR_CONTEXT , cn_cbor_context *context 206 | /** When USE_CBOR_CONTEXT is defined, some functions take an extra `context` 207 | * parameter at the beginning */ 208 | #define CBOR_CONTEXT_COMMA cn_cbor_context *context, 209 | 210 | #else 211 | 212 | #define CBOR_CONTEXT 213 | #define CBOR_CONTEXT_COMMA 214 | 215 | #endif 216 | 217 | /** 218 | * Decode an array of CBOR bytes into structures. 219 | * 220 | * @param[in] buf The array of bytes to parse 221 | * @param[in] len The number of bytes in the array 222 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 223 | * @param[out] errp Error, if NULL is returned 224 | * @return The parsed CBOR structure, or NULL on error 225 | */ 226 | cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp); 227 | 228 | /** 229 | * Get a value from a CBOR map that has the given string as a key. 230 | * 231 | * @param[in] cb The CBOR map 232 | * @param[in] key The string to look up in the map 233 | * @return The matching value, or NULL if the key is not found 234 | */ 235 | cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key); 236 | 237 | /** 238 | * Get a value from a CBOR map that has the given integer as a key. 239 | * 240 | * @param[in] cb The CBOR map 241 | * @param[in] key The int to look up in the map 242 | * @return The matching value, or NULL if the key is not found 243 | */ 244 | cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key); 245 | 246 | /** 247 | * Get the item with the given index from a CBOR array. 248 | * 249 | * @param[in] cb The CBOR map 250 | * @param[in] idx The array index 251 | * @return The matching value, or NULL if the index is invalid 252 | */ 253 | cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx); 254 | 255 | /** 256 | * Free the given CBOR structure. 257 | * You MUST NOT try to free a cn_cbor structure with a parent (i.e., one 258 | * that is not a root in the tree). 259 | * 260 | * @param[in] cb The CBOR value to free. May be NULL, or a root object. 261 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 262 | */ 263 | void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT); 264 | 265 | /** 266 | * Write a CBOR value and all of the child values. 267 | * 268 | * @param[in] buf The buffer into which to write 269 | * @param[in] buf_offset The offset (in bytes) from the beginning of the buffer 270 | * to start writing at 271 | * @param[in] buf_size The total length (in bytes) of the buffer 272 | * @param[in] cb [description] 273 | * @return -1 on fail, or number of bytes written 274 | */ 275 | ssize_t cn_cbor_encoder_write(uint8_t *buf, 276 | size_t buf_offset, 277 | size_t buf_size, 278 | const cn_cbor *cb); 279 | 280 | /** 281 | * Create a CBOR map. 282 | * 283 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 284 | * @param[out] errp Error, if NULL is returned 285 | * @return The created map, or NULL on error 286 | */ 287 | cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp); 288 | 289 | /** 290 | * Create a CBOR byte string. The data in the byte string is *not* owned 291 | * by the CBOR object, so it is not freed automatically. 292 | * 293 | * @param[in] data The data 294 | * @param[in] len The number of bytes of data 295 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 296 | * @param[out] errp Error, if NULL is returned 297 | * @return The created object, or NULL on error 298 | */ 299 | cn_cbor* cn_cbor_data_create(const uint8_t* data, int len 300 | CBOR_CONTEXT, 301 | cn_cbor_errback *errp); 302 | 303 | /** 304 | * Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness. 305 | * The data being stored in the string is *not* owned the CBOR object, so it is 306 | * not freed automatically. 307 | * 308 | * @note: Do NOT use this function with untrusted data. It calls strlen, and 309 | * relies on proper NULL-termination. 310 | * 311 | * @param[in] data NULL-terminated UTF-8 string 312 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 313 | * @param[out] errp Error, if NULL is returned 314 | * @return The created object, or NULL on error 315 | */ 316 | cn_cbor* cn_cbor_string_create(const char* data 317 | CBOR_CONTEXT, 318 | cn_cbor_errback *errp); 319 | 320 | /** 321 | * Create a CBOR integer (either positive or negative). 322 | * 323 | * @param[in] value the value of the integer 324 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 325 | * @param[out] errp Error, if NULL is returned 326 | * @return The created object, or NULL on error 327 | */ 328 | cn_cbor* cn_cbor_int_create(int64_t value 329 | CBOR_CONTEXT, 330 | cn_cbor_errback *errp); 331 | 332 | #ifndef CBOR_NO_FLOAT 333 | /** 334 | * Create a CBOR float. 335 | * 336 | * @param[in] value the value of the float 337 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 338 | * @param[out] errp Error, if NULL is returned 339 | * @return The created object, or NULL on error 340 | */ 341 | cn_cbor* cn_cbor_float_create(float value 342 | CBOR_CONTEXT, 343 | cn_cbor_errback *errp); 344 | 345 | /** 346 | * Create a CBOR double. 347 | * 348 | * @param[in] value the value of the double 349 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 350 | * @param[out] errp Error, if NULL is returned 351 | * @return The created object, or NULL on error 352 | */ 353 | cn_cbor* cn_cbor_double_create(double value 354 | CBOR_CONTEXT, 355 | cn_cbor_errback *errp); 356 | #endif /* CBOR_NO_FLOAT */ 357 | 358 | /** 359 | * Put a CBOR object into a map with a CBOR object key. Duplicate checks are NOT 360 | * currently performed. 361 | * 362 | * @param[in] cb_map The map to insert into 363 | * @param[in] key The key 364 | * @param[in] cb_value The value 365 | * @param[out] errp Error 366 | * @return True on success 367 | */ 368 | bool cn_cbor_map_put(cn_cbor* cb_map, 369 | cn_cbor *cb_key, cn_cbor *cb_value, 370 | cn_cbor_errback *errp); 371 | 372 | /** 373 | * Put a CBOR object into a map with an integer key. Duplicate checks are NOT 374 | * currently performed. 375 | * 376 | * @param[in] cb_map The map to insert into 377 | * @param[in] key The integer key 378 | * @param[in] cb_value The value 379 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 380 | * @param[out] errp Error 381 | * @return True on success 382 | */ 383 | bool cn_cbor_mapput_int(cn_cbor* cb_map, 384 | int64_t key, cn_cbor* cb_value 385 | CBOR_CONTEXT, 386 | cn_cbor_errback *errp); 387 | 388 | /** 389 | * Put a CBOR object into a map with a string key. Duplicate checks are NOT 390 | * currently performed. 391 | * 392 | * @note: do not call this routine with untrusted string data. It calls 393 | * strlen, and requires a properly NULL-terminated key. 394 | * 395 | * @param[in] cb_map The map to insert into 396 | * @param[in] key The string key 397 | * @param[in] cb_value The value 398 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 399 | * @param[out] errp Error 400 | * @return True on success 401 | */ 402 | bool cn_cbor_mapput_string(cn_cbor* cb_map, 403 | const char* key, cn_cbor* cb_value 404 | CBOR_CONTEXT, 405 | cn_cbor_errback *errp); 406 | 407 | /** 408 | * Create a CBOR array 409 | * 410 | * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) 411 | * @param[out] errp Error, if NULL is returned 412 | * @return The created object, or NULL on error 413 | */ 414 | cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp); 415 | 416 | /** 417 | * Append an item to the end of a CBOR array. 418 | * 419 | * @param[in] cb_array The array into which to insert 420 | * @param[in] cb_value The value to insert 421 | * @param[out] errp Error 422 | * @return True on success 423 | */ 424 | bool cn_cbor_array_append(cn_cbor* cb_array, 425 | cn_cbor* cb_value, 426 | cn_cbor_errback *errp); 427 | 428 | #ifdef __cplusplus 429 | } 430 | #endif 431 | 432 | #endif /* CN_CBOR_H */ 433 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # compiling/installing sources for cn-cbor 4 | 5 | set ( cbor_srcs 6 | cn-cbor.c 7 | cn-create.c 8 | cn-encoder.c 9 | cn-error.c 10 | cn-get.c 11 | ) 12 | 13 | if (align_reads) 14 | add_definitions(-DCBOR_ALIGN_READS) 15 | endif() 16 | if (use_context) 17 | add_definitions(-DUSE_CBOR_CONTEXT) 18 | endif() 19 | add_library ( cn-cbor SHARED ${cbor_srcs} ) 20 | target_include_directories ( cn-cbor PUBLIC ../include ) 21 | target_include_directories ( cn-cbor PRIVATE ../src ) 22 | 23 | install ( TARGETS cn-cbor 24 | LIBRARY DESTINATION lib 25 | ARCHIVE DESTINATION lib 26 | RUNTIME DESTINATION bin) 27 | 28 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) 29 | if (coveralls) 30 | include(Coveralls) 31 | coveralls_turn_on_coverage() 32 | 33 | set(COVERAGE_SRCS "") 34 | foreach (S ${cbor_srcs}) 35 | get_filename_component(S_ABS ${S} ABSOLUTE) 36 | list (APPEND COVERAGE_SRCS ${S_ABS}) 37 | endforeach() 38 | 39 | # Create the coveralls target. 40 | coveralls_setup( 41 | "${COVERAGE_SRCS}" 42 | ${coveralls_send} # If we should upload. 43 | ) 44 | 45 | #add_dependencies(coveralls, all) 46 | endif() 47 | 48 | add_custom_target(size 49 | COMMAND echo "${CMAKE_BINARY_DIR}/src/CMakeFiles/cn-cbor.dir/cn-cbor.c.o" 50 | COMMAND size "${CMAKE_BINARY_DIR}/src/CMakeFiles/cn-cbor.dir/cn-cbor.c.o" 51 | COMMAND size -m "${CMAKE_BINARY_DIR}/src/CMakeFiles/cn-cbor.dir/cn-cbor.c.o" 52 | DEPENDS cn-cbor 53 | COMMENT "Output the size of the parse routine") 54 | -------------------------------------------------------------------------------- /src/cbor.h: -------------------------------------------------------------------------------- 1 | #ifndef CBOR_PROTOCOL_H__ 2 | #define CBOR_PROTOCOL_H__ 3 | 4 | /* The 8 major types */ 5 | #define MT_UNSIGNED 0 6 | #define MT_NEGATIVE 1 7 | #define MT_BYTES 2 8 | #define MT_TEXT 3 9 | #define MT_ARRAY 4 10 | #define MT_MAP 5 11 | #define MT_TAG 6 12 | #define MT_PRIM 7 13 | 14 | /* The initial bytes resulting from those */ 15 | #define IB_UNSIGNED (MT_UNSIGNED << 5) 16 | #define IB_NEGATIVE (MT_NEGATIVE << 5) 17 | #define IB_BYTES (MT_BYTES << 5) 18 | #define IB_TEXT (MT_TEXT << 5) 19 | #define IB_ARRAY (MT_ARRAY << 5) 20 | #define IB_MAP (MT_MAP << 5) 21 | #define IB_TAG (MT_TAG << 5) 22 | #define IB_PRIM (MT_PRIM << 5) 23 | 24 | #define IB_NEGFLAG (IB_NEGATIVE - IB_UNSIGNED) 25 | #define IB_NEGFLAG_AS_BIT(ib) ((ib) >> 5) 26 | #define IB_TEXTFLAG (IB_TEXT - IB_BYTES) 27 | 28 | #define IB_AI(ib) ((ib) & 0x1F) 29 | #define IB_MT(ib) ((ib) >> 5) 30 | 31 | /* Tag numbers handled by this implementation */ 32 | #define TAG_TIME_EPOCH 1 33 | #define TAG_BIGNUM 2 34 | #define TAG_BIGNUM_NEG 3 35 | #define TAG_URI 32 36 | #define TAG_RE 35 37 | 38 | /* Initial bytes of those tag numbers */ 39 | #define IB_TIME_EPOCH (IB_TAG | TAG_TIME_EPOCH) 40 | #define IB_BIGNUM (IB_TAG | TAG_BIGNUM) 41 | #define IB_BIGNUM_NEG (IB_TAG | TAG_BIGNUM_NEG) 42 | /* TAG_URI and TAG_RE are non-immediate tags */ 43 | 44 | /* Simple values handled by this implementation */ 45 | #define VAL_FALSE 20 46 | #define VAL_TRUE 21 47 | #define VAL_NIL 22 48 | #define VAL_UNDEF 23 49 | 50 | /* Initial bytes of those simple values */ 51 | #define IB_FALSE (IB_PRIM | VAL_FALSE) 52 | #define IB_TRUE (IB_PRIM | VAL_TRUE) 53 | #define IB_NIL (IB_PRIM | VAL_NIL) 54 | #define IB_UNDEF (IB_PRIM | VAL_UNDEF) 55 | 56 | /* AI values with more data in head */ 57 | #define AI_1 24 58 | #define AI_2 25 59 | #define AI_4 26 60 | #define AI_8 27 61 | #define AI_INDEF 31 62 | #define IB_BREAK (IB_PRIM | AI_INDEF) 63 | /* For */ 64 | #define IB_UNUSED (IB_TAG | AI_INDEF) 65 | 66 | /* Floating point initial bytes */ 67 | #define IB_FLOAT2 (IB_PRIM | AI_2) 68 | #define IB_FLOAT4 (IB_PRIM | AI_4) 69 | #define IB_FLOAT8 (IB_PRIM | AI_8) 70 | 71 | // These definitions are here because they aren't required for the public 72 | // interface, and they were quite confusing in cn-cbor.h 73 | 74 | #ifdef USE_CBOR_CONTEXT 75 | /** 76 | * Allocate enough space for 1 `cn_cbor` structure. 77 | * 78 | * @param[in] ctx The allocation context, or NULL for calloc. 79 | * @return A pointer to a `cn_cbor` or NULL on failure 80 | */ 81 | #define CN_CALLOC(ctx) ((ctx) && (ctx)->calloc_func) ? \ 82 | (ctx)->calloc_func(1, sizeof(cn_cbor), (ctx)->context) : \ 83 | calloc(1, sizeof(cn_cbor)); 84 | 85 | /** 86 | * Free a 87 | * @param free_func [description] 88 | * @return [description] 89 | */ 90 | #define CN_FREE(ptr, ctx) ((ctx) && (ctx)->free_func) ? \ 91 | (ctx)->free_func((ptr), (ctx)->context) : \ 92 | free((ptr)); 93 | 94 | #define CBOR_CONTEXT_PARAM , context 95 | #define CN_CALLOC_CONTEXT() CN_CALLOC(context) 96 | #define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p, context) 97 | 98 | #else 99 | 100 | #define CBOR_CONTEXT_PARAM 101 | #define CN_CALLOC_CONTEXT() CN_CALLOC 102 | #define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p) 103 | 104 | #ifndef CN_CALLOC 105 | #define CN_CALLOC calloc(1, sizeof(cn_cbor)) 106 | #endif 107 | 108 | #ifndef CN_FREE 109 | #define CN_FREE free 110 | #endif 111 | 112 | #endif // USE_CBOR_CONTEXT 113 | 114 | #ifndef UNUSED_PARAM 115 | #define UNUSED_PARAM(p) ((void)&(p)) 116 | #endif 117 | 118 | #endif // CBOR_PROTOCOL_H__ 119 | -------------------------------------------------------------------------------- /src/cn-cbor.c: -------------------------------------------------------------------------------- 1 | #ifndef CN_CBOR_C 2 | #define CN_CBOR_C 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | #ifdef EMACS_INDENTATION_HELPER 8 | } /* Duh. */ 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include // needed for ntohl (e.g.) on Linux 17 | 18 | #include "cn-cbor/cn-cbor.h" 19 | #include "cbor.h" 20 | 21 | #define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0) 22 | 23 | void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) { 24 | cn_cbor* p = cb; 25 | assert(!p || !p->parent); 26 | while (p) { 27 | cn_cbor* p1; 28 | while ((p1 = p->first_child)) { /* go down */ 29 | p = p1; 30 | } 31 | if (!(p1 = p->next)) { /* go up next */ 32 | if ((p1 = p->parent)) 33 | p1->first_child = 0; 34 | } 35 | CN_CBOR_FREE_CONTEXT(p); 36 | p = p1; 37 | } 38 | } 39 | 40 | #ifndef CBOR_NO_FLOAT 41 | static double decode_half(int half) { 42 | int exp = (half >> 10) & 0x1f; 43 | int mant = half & 0x3ff; 44 | double val; 45 | if (exp == 0) val = ldexp(mant, -24); 46 | else if (exp != 31) val = ldexp(mant + 1024, exp - 25); 47 | else val = mant == 0 ? INFINITY : NAN; 48 | return half & 0x8000 ? -val : val; 49 | } 50 | #endif /* CBOR_NO_FLOAT */ 51 | 52 | #define ntoh8p(p) (*(unsigned char*)(p)) 53 | 54 | #ifndef CBOR_ALIGN_READS 55 | #define ntoh16p(p) (ntohs(*(unsigned short*)(p))) 56 | #define ntoh32p(p) (ntohl(*(unsigned long*)(p))) 57 | #else 58 | static uint16_t ntoh16p(unsigned char *p) { 59 | uint16_t tmp; 60 | memcpy(&tmp, p, sizeof(tmp)); 61 | return ntohs(tmp); 62 | } 63 | 64 | static uint32_t ntoh32p(unsigned char *p) { 65 | uint32_t tmp; 66 | memcpy(&tmp, p, sizeof(tmp)); 67 | return ntohl(tmp); 68 | } 69 | #endif /* CBOR_ALIGN_READS */ 70 | 71 | static uint64_t ntoh64p(unsigned char *p) { 72 | uint64_t ret = ntoh32p(p); 73 | ret <<= 32; 74 | ret += ntoh32p(p+4); 75 | return ret; 76 | } 77 | 78 | static cn_cbor_type mt_trans[] = { 79 | CN_CBOR_UINT, CN_CBOR_INT, 80 | CN_CBOR_BYTES, CN_CBOR_TEXT, 81 | CN_CBOR_ARRAY, CN_CBOR_MAP, 82 | CN_CBOR_TAG, CN_CBOR_SIMPLE, 83 | }; 84 | 85 | struct parse_buf { 86 | unsigned char *buf; 87 | unsigned char *ebuf; 88 | cn_cbor_error err; 89 | }; 90 | 91 | #define TAKE(pos, ebuf, n, stmt) \ 92 | if (n > (size_t)(ebuf - pos)) \ 93 | CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \ 94 | stmt; \ 95 | pos += n; 96 | 97 | static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) { 98 | unsigned char *pos = pb->buf; 99 | unsigned char *ebuf = pb->ebuf; 100 | cn_cbor* parent = top_parent; 101 | int ib; 102 | unsigned int mt; 103 | int ai; 104 | uint64_t val; 105 | cn_cbor* cb = NULL; 106 | #ifndef CBOR_NO_FLOAT 107 | union { 108 | float f; 109 | uint32_t u; 110 | } u32; 111 | union { 112 | double d; 113 | uint64_t u; 114 | } u64; 115 | #endif /* CBOR_NO_FLOAT */ 116 | 117 | again: 118 | TAKE(pos, ebuf, 1, ib = ntoh8p(pos) ); 119 | if (ib == IB_BREAK) { 120 | if (!(parent->flags & CN_CBOR_FL_INDEF)) 121 | CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF); 122 | switch (parent->type) { 123 | case CN_CBOR_BYTES: case CN_CBOR_TEXT: 124 | parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */ 125 | break; 126 | case CN_CBOR_MAP: 127 | if (parent->length & 1) 128 | CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP); 129 | default:; 130 | } 131 | goto complete; 132 | } 133 | mt = ib >> 5; 134 | ai = ib & 0x1f; 135 | val = ai; 136 | 137 | cb = CN_CALLOC_CONTEXT(); 138 | if (!cb) 139 | CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY); 140 | 141 | cb->type = mt_trans[mt]; 142 | 143 | cb->parent = parent; 144 | if (parent->last_child) { 145 | parent->last_child->next = cb; 146 | } else { 147 | parent->first_child = cb; 148 | } 149 | parent->last_child = cb; 150 | parent->length++; 151 | 152 | switch (ai) { 153 | case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos)) ; break; 154 | case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; break; 155 | case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; break; 156 | case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; break; 157 | case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI); 158 | case AI_INDEF: 159 | if ((mt - MT_BYTES) <= MT_MAP) { 160 | cb->flags |= CN_CBOR_FL_INDEF; 161 | goto push; 162 | } else { 163 | CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF); 164 | } 165 | } 166 | // process content 167 | switch (mt) { 168 | case MT_UNSIGNED: 169 | cb->v.uint = val; /* to do: Overflow check */ 170 | break; 171 | case MT_NEGATIVE: 172 | cb->v.sint = ~val; /* to do: Overflow check */ 173 | break; 174 | case MT_BYTES: case MT_TEXT: 175 | cb->v.str = (char *) pos; 176 | cb->length = val; 177 | TAKE(pos, ebuf, val, ;); 178 | break; 179 | case MT_MAP: 180 | val <<= 1; 181 | /* fall through */ 182 | case MT_ARRAY: 183 | if ((cb->v.count = val)) { 184 | cb->flags |= CN_CBOR_FL_COUNT; 185 | goto push; 186 | } 187 | break; 188 | case MT_TAG: 189 | cb->v.uint = val; 190 | goto push; 191 | case MT_PRIM: 192 | switch (ai) { 193 | case VAL_FALSE: cb->type = CN_CBOR_FALSE; break; 194 | case VAL_TRUE: cb->type = CN_CBOR_TRUE; break; 195 | case VAL_NIL: cb->type = CN_CBOR_NULL; break; 196 | case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break; 197 | case AI_2: 198 | #ifndef CBOR_NO_FLOAT 199 | cb->type = CN_CBOR_DOUBLE; 200 | cb->v.dbl = decode_half(val); 201 | #else /* CBOR_NO_FLOAT */ 202 | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); 203 | #endif /* CBOR_NO_FLOAT */ 204 | break; 205 | case AI_4: 206 | #ifndef CBOR_NO_FLOAT 207 | cb->type = CN_CBOR_DOUBLE; 208 | u32.u = val; 209 | cb->v.dbl = u32.f; 210 | #else /* CBOR_NO_FLOAT */ 211 | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); 212 | #endif /* CBOR_NO_FLOAT */ 213 | break; 214 | case AI_8: 215 | #ifndef CBOR_NO_FLOAT 216 | cb->type = CN_CBOR_DOUBLE; 217 | u64.u = val; 218 | cb->v.dbl = u64.d; 219 | #else /* CBOR_NO_FLOAT */ 220 | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); 221 | #endif /* CBOR_NO_FLOAT */ 222 | break; 223 | default: cb->v.uint = val; 224 | } 225 | } 226 | fill: /* emulate loops */ 227 | if (parent->flags & CN_CBOR_FL_INDEF) { 228 | if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT) 229 | if (cb->type != parent->type) 230 | CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); 231 | goto again; 232 | } 233 | if (parent->flags & CN_CBOR_FL_COUNT) { 234 | if (--parent->v.count) 235 | goto again; 236 | } 237 | /* so we are done filling parent. */ 238 | complete: /* emulate return from call */ 239 | if (parent == top_parent) { 240 | if (pos != ebuf) /* XXX do this outside */ 241 | CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED); 242 | pb->buf = pos; 243 | return cb; 244 | } 245 | cb = parent; 246 | parent = parent->parent; 247 | goto fill; 248 | push: /* emulate recursive call */ 249 | parent = cb; 250 | goto again; 251 | fail: 252 | pb->buf = pos; 253 | return 0; 254 | } 255 | 256 | cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) { 257 | cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL}; 258 | struct parse_buf pb; 259 | cn_cbor* ret; 260 | 261 | pb.buf = (unsigned char *)buf; 262 | pb.ebuf = (unsigned char *)buf+len; 263 | pb.err = CN_CBOR_NO_ERROR; 264 | ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher); 265 | if (ret != NULL) { 266 | /* mark as top node */ 267 | ret->parent = NULL; 268 | } else { 269 | if (catcher.first_child) { 270 | catcher.first_child->parent = 0; 271 | cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM); 272 | } 273 | //fail: 274 | if (errp) { 275 | errp->err = pb.err; 276 | errp->pos = pb.buf - (unsigned char *)buf; 277 | } 278 | return NULL; 279 | } 280 | return ret; 281 | } 282 | 283 | #ifdef __cplusplus 284 | } 285 | #endif 286 | 287 | #endif /* CN_CBOR_C */ 288 | -------------------------------------------------------------------------------- /src/cn-create.c: -------------------------------------------------------------------------------- 1 | #ifndef CN_CREATE_C 2 | #define CN_CREATE_C 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #include "cn-cbor/cn-cbor.h" 12 | #include "cbor.h" 13 | 14 | #define INIT_CB(v) \ 15 | if (errp) {errp->err = CN_CBOR_NO_ERROR;} \ 16 | (v) = CN_CALLOC_CONTEXT(); \ 17 | if (!(v)) { if (errp) {errp->err = CN_CBOR_ERR_OUT_OF_MEMORY;} return NULL; } 18 | 19 | cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp) 20 | { 21 | cn_cbor* ret; 22 | INIT_CB(ret); 23 | 24 | ret->type = CN_CBOR_MAP; 25 | ret->flags |= CN_CBOR_FL_COUNT; 26 | 27 | return ret; 28 | } 29 | 30 | cn_cbor* cn_cbor_data_create(const uint8_t* data, int len 31 | CBOR_CONTEXT, 32 | cn_cbor_errback *errp) 33 | { 34 | cn_cbor* ret; 35 | INIT_CB(ret); 36 | 37 | ret->type = CN_CBOR_BYTES; 38 | ret->length = len; 39 | ret->v.str = (const char*) data; // TODO: add v.ustr to the union? 40 | 41 | return ret; 42 | } 43 | 44 | cn_cbor* cn_cbor_string_create(const char* data 45 | CBOR_CONTEXT, 46 | cn_cbor_errback *errp) 47 | { 48 | cn_cbor* ret; 49 | INIT_CB(ret); 50 | 51 | ret->type = CN_CBOR_TEXT; 52 | ret->length = strlen(data); 53 | ret->v.str = data; 54 | 55 | return ret; 56 | } 57 | 58 | cn_cbor* cn_cbor_int_create(int64_t value 59 | CBOR_CONTEXT, 60 | cn_cbor_errback *errp) 61 | { 62 | cn_cbor* ret; 63 | INIT_CB(ret); 64 | 65 | if (value<0) { 66 | ret->type = CN_CBOR_INT; 67 | ret->v.sint = value; 68 | } else { 69 | ret->type = CN_CBOR_UINT; 70 | ret->v.uint = value; 71 | } 72 | 73 | return ret; 74 | } 75 | 76 | #ifndef CBOR_NO_FLOAT 77 | cn_cbor* cn_cbor_float_create(float value 78 | CBOR_CONTEXT, 79 | cn_cbor_errback *errp) 80 | { 81 | cn_cbor* ret; 82 | INIT_CB(ret); 83 | 84 | ret->type = CN_CBOR_FLOAT; 85 | ret->v.f = value; 86 | 87 | return ret; 88 | } 89 | 90 | cn_cbor* cn_cbor_double_create(double value 91 | CBOR_CONTEXT, 92 | cn_cbor_errback *errp) 93 | { 94 | cn_cbor* ret; 95 | INIT_CB(ret); 96 | 97 | ret->type = CN_CBOR_DOUBLE; 98 | ret->v.dbl = value; 99 | 100 | return ret; 101 | } 102 | #endif /* CBOR_NO_FLOAT */ 103 | 104 | static bool _append_kv(cn_cbor *cb_map, cn_cbor *key, cn_cbor *val) 105 | { 106 | //Connect key and value and insert them into the map. 107 | key->parent = cb_map; 108 | key->next = val; 109 | val->parent = cb_map; 110 | val->next = NULL; 111 | 112 | if(cb_map->last_child) { 113 | cb_map->last_child->next = key; 114 | } else { 115 | cb_map->first_child = key; 116 | } 117 | cb_map->last_child = val; 118 | cb_map->length += 2; 119 | return true; 120 | } 121 | 122 | bool cn_cbor_map_put(cn_cbor* cb_map, 123 | cn_cbor *cb_key, cn_cbor *cb_value, 124 | cn_cbor_errback *errp) 125 | { 126 | //Make sure input is a map. Otherwise 127 | if(!cb_map || !cb_key || !cb_value || cb_map->type != CN_CBOR_MAP) 128 | { 129 | if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} 130 | return false; 131 | } 132 | 133 | return _append_kv(cb_map, cb_key, cb_value); 134 | } 135 | 136 | bool cn_cbor_mapput_int(cn_cbor* cb_map, 137 | int64_t key, cn_cbor* cb_value 138 | CBOR_CONTEXT, 139 | cn_cbor_errback *errp) 140 | { 141 | cn_cbor* cb_key; 142 | 143 | //Make sure input is a map. Otherwise 144 | if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) 145 | { 146 | if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} 147 | return false; 148 | } 149 | 150 | cb_key = cn_cbor_int_create(key CBOR_CONTEXT_PARAM, errp); 151 | if (!cb_key) { return false; } 152 | return _append_kv(cb_map, cb_key, cb_value); 153 | } 154 | 155 | bool cn_cbor_mapput_string(cn_cbor* cb_map, 156 | const char* key, cn_cbor* cb_value 157 | CBOR_CONTEXT, 158 | cn_cbor_errback *errp) 159 | { 160 | cn_cbor* cb_key; 161 | 162 | //Make sure input is a map. Otherwise 163 | if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) 164 | { 165 | if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} 166 | return false; 167 | } 168 | 169 | cb_key = cn_cbor_string_create(key CBOR_CONTEXT_PARAM, errp); 170 | if (!cb_key) { return false; } 171 | return _append_kv(cb_map, cb_key, cb_value); 172 | } 173 | 174 | cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp) 175 | { 176 | cn_cbor* ret; 177 | INIT_CB(ret); 178 | 179 | ret->type = CN_CBOR_ARRAY; 180 | ret->flags |= CN_CBOR_FL_COUNT; 181 | 182 | return ret; 183 | } 184 | 185 | bool cn_cbor_array_append(cn_cbor* cb_array, 186 | cn_cbor* cb_value, 187 | cn_cbor_errback *errp) 188 | { 189 | //Make sure input is an array. 190 | if(!cb_array || !cb_value || cb_array->type != CN_CBOR_ARRAY) 191 | { 192 | if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} 193 | return false; 194 | } 195 | 196 | cb_value->parent = cb_array; 197 | cb_value->next = NULL; 198 | if(cb_array->last_child) { 199 | cb_array->last_child->next = cb_value; 200 | } else { 201 | cb_array->first_child = cb_value; 202 | } 203 | cb_array->last_child = cb_value; 204 | cb_array->length++; 205 | return true; 206 | } 207 | 208 | #ifdef __cplusplus 209 | } 210 | #endif 211 | 212 | #endif /* CN_CBOR_C */ 213 | -------------------------------------------------------------------------------- /src/cn-encoder.c: -------------------------------------------------------------------------------- 1 | #ifndef CN_ENCODER_C 2 | #define CN_ENCODER_C 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | #ifdef EMACS_INDENTATION_HELPER 8 | } /* Duh. */ 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "cn-cbor/cn-cbor.h" 18 | #include "cbor.h" 19 | 20 | #define hton8p(p) (*(uint8_t*)(p)) 21 | #define hton16p(p) (htons(*(uint16_t*)(p))) 22 | #define hton32p(p) (htonl(*(uint32_t*)(p))) 23 | static uint64_t hton64p(const uint8_t *p) { 24 | /* TODO: does this work on both BE and LE systems? */ 25 | uint64_t ret = hton32p(p); 26 | ret <<= 32; 27 | ret |= hton32p(p+4); 28 | return ret; 29 | } 30 | 31 | typedef struct _write_state 32 | { 33 | uint8_t *buf; 34 | ssize_t offset; 35 | ssize_t size; 36 | } cn_write_state; 37 | 38 | #define ensure_writable(sz) if ((ws->offset<0) || (ws->offset + (sz) >= ws->size)) { \ 39 | ws->offset = -1; \ 40 | return; \ 41 | } 42 | 43 | #define write_byte_and_data(b, data, sz) \ 44 | ws->buf[ws->offset++] = (b); \ 45 | memcpy(ws->buf+ws->offset, (data), (sz)); \ 46 | ws->offset += sz; 47 | 48 | #define write_byte(b) \ 49 | ws->buf[ws->offset++] = (b); \ 50 | 51 | #define write_byte_ensured(b) \ 52 | ensure_writable(1); \ 53 | write_byte(b); \ 54 | 55 | static uint8_t _xlate[] = { 56 | IB_FALSE, /* CN_CBOR_FALSE */ 57 | IB_TRUE, /* CN_CBOR_TRUE */ 58 | IB_NIL, /* CN_CBOR_NULL */ 59 | IB_UNDEF, /* CN_CBOR_UNDEF */ 60 | IB_UNSIGNED, /* CN_CBOR_UINT */ 61 | IB_NEGATIVE, /* CN_CBOR_INT */ 62 | IB_BYTES, /* CN_CBOR_BYTES */ 63 | IB_TEXT, /* CN_CBOR_TEXT */ 64 | IB_BYTES, /* CN_CBOR_BYTES_CHUNKED */ 65 | IB_TEXT, /* CN_CBOR_TEXT_CHUNKED */ 66 | IB_ARRAY, /* CN_CBOR_ARRAY */ 67 | IB_MAP, /* CN_CBOR_MAP */ 68 | IB_TAG, /* CN_CBOR_TAG */ 69 | IB_PRIM, /* CN_CBOR_SIMPLE */ 70 | 0xFF, /* CN_CBOR_DOUBLE */ 71 | 0xFF /* CN_CBOR_INVALID */ 72 | }; 73 | 74 | static inline bool is_indefinite(const cn_cbor *cb) 75 | { 76 | return (cb->flags & CN_CBOR_FL_INDEF) != 0; 77 | } 78 | 79 | static void _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val) { 80 | uint8_t ib; 81 | 82 | assert((size_t)typ < sizeof(_xlate)); 83 | 84 | ib = _xlate[typ]; 85 | if (ib == 0xFF) { 86 | ws->offset = -1; 87 | return; 88 | } 89 | 90 | if (val < 24) { 91 | ensure_writable(1); 92 | write_byte(ib | val); 93 | } else if (val < 256) { 94 | ensure_writable(2); 95 | write_byte(ib | 24); 96 | write_byte((uint8_t)val); 97 | } else if (val < 65536) { 98 | uint16_t be16 = (uint16_t)val; 99 | ensure_writable(3); 100 | be16 = hton16p(&be16); 101 | write_byte_and_data(ib | 25, (const void*)&be16, 2); 102 | } else if (val < 0x100000000L) { 103 | uint32_t be32 = (uint32_t)val; 104 | ensure_writable(5); 105 | be32 = hton32p(&be32); 106 | write_byte_and_data(ib | 26, (const void*)&be32, 4); 107 | } else { 108 | uint64_t be64; 109 | ensure_writable(9); 110 | be64 = hton64p((const uint8_t*)&val); 111 | write_byte_and_data(ib | 27, (const void*)&be64, 8); 112 | } 113 | } 114 | 115 | #ifndef CBOR_NO_FLOAT 116 | static void _write_double(cn_write_state *ws, double val) 117 | { 118 | float float_val = val; 119 | if (float_val == val) { /* 32 bits is enough and we aren't NaN */ 120 | uint32_t be32; 121 | uint16_t be16, u16; 122 | union { 123 | float f; 124 | uint32_t u; 125 | } u32; 126 | u32.f = float_val; 127 | if ((u32.u & 0x1FFF) == 0) { /* worth trying half */ 128 | int s16 = (u32.u >> 16) & 0x8000; 129 | int exp = (u32.u >> 23) & 0xff; 130 | int mant = u32.u & 0x7fffff; 131 | if (exp == 0 && mant == 0) 132 | ; /* 0.0, -0.0 */ 133 | else if (exp >= 113 && exp <= 142) /* normalized */ 134 | s16 += ((exp - 112) << 10) + (mant >> 13); 135 | else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */ 136 | if (mant & ((1 << (126 - exp)) - 1)) 137 | goto float32; /* loss of precision */ 138 | s16 += ((mant + 0x800000) >> (126 - exp)); 139 | } else if (exp == 255 && mant == 0) { /* Inf */ 140 | s16 += 0x7c00; 141 | } else 142 | goto float32; /* loss of range */ 143 | 144 | ensure_writable(3); 145 | u16 = s16; 146 | be16 = hton16p((const uint8_t*)&u16); 147 | 148 | write_byte_and_data(IB_PRIM | 25, (const void*)&be16, 2); 149 | return; 150 | } 151 | float32: 152 | ensure_writable(5); 153 | be32 = hton32p((const uint8_t*)&u32.u); 154 | 155 | write_byte_and_data(IB_PRIM | 26, (const void*)&be32, 4); 156 | 157 | } else if (val != val) { /* NaN -- we always write a half NaN*/ 158 | ensure_writable(3); 159 | write_byte_and_data(IB_PRIM | 25, (const void*)"\x7e\x00", 2); 160 | } else { 161 | uint64_t be64; 162 | /* Copy the same problematic implementation from the decoder. */ 163 | union { 164 | double d; 165 | uint64_t u; 166 | } u64; 167 | 168 | u64.d = val; 169 | 170 | ensure_writable(9); 171 | be64 = hton64p((const uint8_t*)&u64.u); 172 | 173 | write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8); 174 | 175 | } 176 | } 177 | #endif /* CBOR_NO_FLOAT */ 178 | 179 | // TODO: make public? 180 | typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context); 181 | static void _visit(const cn_cbor *cb, 182 | cn_visit_func visitor, 183 | cn_visit_func breaker, 184 | void *context) 185 | { 186 | const cn_cbor *p = cb; 187 | int depth = 0; 188 | while (p) 189 | { 190 | visit: 191 | visitor(p, depth, context); 192 | if (p->first_child) { 193 | p = p->first_child; 194 | depth++; 195 | } else{ 196 | // Empty indefinite 197 | if (is_indefinite(p)) { 198 | breaker(p->parent, depth, context); 199 | } 200 | if (p->next) { 201 | p = p->next; 202 | } else { 203 | while (p->parent) { 204 | depth--; 205 | if (is_indefinite(p->parent)) { 206 | breaker(p->parent, depth, context); 207 | } 208 | if (p->parent->next) { 209 | p = p->parent->next; 210 | goto visit; 211 | } 212 | p = p->parent; 213 | } 214 | return; 215 | } 216 | } 217 | } 218 | } 219 | 220 | #define CHECK(st) (st); \ 221 | if (ws->offset < 0) { return; } 222 | 223 | void _encoder_visitor(const cn_cbor *cb, int depth, void *context) 224 | { 225 | cn_write_state *ws = context; 226 | UNUSED_PARAM(depth); 227 | 228 | switch (cb->type) { 229 | case CN_CBOR_ARRAY: 230 | if (is_indefinite(cb)) { 231 | write_byte_ensured(IB_ARRAY | AI_INDEF); 232 | } else { 233 | CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length)); 234 | } 235 | break; 236 | case CN_CBOR_MAP: 237 | if (is_indefinite(cb)) { 238 | write_byte_ensured(IB_MAP | AI_INDEF); 239 | } else { 240 | CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length/2)); 241 | } 242 | break; 243 | case CN_CBOR_BYTES_CHUNKED: 244 | case CN_CBOR_TEXT_CHUNKED: 245 | write_byte_ensured(_xlate[cb->type] | AI_INDEF); 246 | break; 247 | 248 | case CN_CBOR_TEXT: 249 | case CN_CBOR_BYTES: 250 | CHECK(_write_positive(ws, cb->type, cb->length)); 251 | ensure_writable(cb->length); 252 | memcpy(ws->buf+ws->offset, cb->v.str, cb->length); 253 | ws->offset += cb->length; 254 | break; 255 | 256 | case CN_CBOR_FALSE: 257 | case CN_CBOR_TRUE: 258 | case CN_CBOR_NULL: 259 | case CN_CBOR_UNDEF: 260 | write_byte_ensured(_xlate[cb->type]); 261 | break; 262 | 263 | case CN_CBOR_TAG: 264 | case CN_CBOR_UINT: 265 | case CN_CBOR_SIMPLE: 266 | CHECK(_write_positive(ws, cb->type, cb->v.uint)); 267 | break; 268 | 269 | case CN_CBOR_INT: 270 | assert(cb->v.sint < 0); 271 | CHECK(_write_positive(ws, CN_CBOR_INT, ~(cb->v.sint))); 272 | break; 273 | 274 | case CN_CBOR_DOUBLE: 275 | #ifndef CBOR_NO_FLOAT 276 | CHECK(_write_double(ws, cb->v.dbl)); 277 | #endif /* CBOR_NO_FLOAT */ 278 | break; 279 | case CN_CBOR_FLOAT: 280 | #ifndef CBOR_NO_FLOAT 281 | CHECK(_write_double(ws, cb->v.f)); 282 | #endif /* CBOR_NO_FLOAT */ 283 | break; 284 | 285 | case CN_CBOR_INVALID: 286 | ws->offset = -1; 287 | break; 288 | } 289 | } 290 | 291 | void _encoder_breaker(const cn_cbor *cb, int depth, void *context) 292 | { 293 | cn_write_state *ws = context; 294 | UNUSED_PARAM(cb); 295 | UNUSED_PARAM(depth); 296 | write_byte_ensured(IB_BREAK); 297 | } 298 | 299 | ssize_t cn_cbor_encoder_write(uint8_t *buf, 300 | size_t buf_offset, 301 | size_t buf_size, 302 | const cn_cbor *cb) 303 | { 304 | cn_write_state ws = { buf, buf_offset, buf_size }; 305 | _visit(cb, _encoder_visitor, _encoder_breaker, &ws); 306 | if (ws.offset < 0) { return -1; } 307 | return ws.offset - buf_offset; 308 | } 309 | 310 | #ifdef __cplusplus 311 | } 312 | #endif 313 | 314 | #endif /* CN_CBOR_C */ 315 | -------------------------------------------------------------------------------- /src/cn-error.c: -------------------------------------------------------------------------------- 1 | const char *cn_cbor_error_str[] = { 2 | "CN_CBOR_NO_ERROR", 3 | "CN_CBOR_ERR_OUT_OF_DATA", 4 | "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED", 5 | "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP", 6 | "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF", 7 | "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF", 8 | "CN_CBOR_ERR_RESERVED_AI", 9 | "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING", 10 | "CN_CBOR_ERR_INVALID_PARAMETER", 11 | "CN_CBOR_ERR_OUT_OF_MEMORY", 12 | "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED" 13 | }; 14 | -------------------------------------------------------------------------------- /src/cn-get.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cn-cbor/cn-cbor.h" 6 | 7 | cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key) { 8 | cn_cbor* cp; 9 | assert(cb); 10 | for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) { 11 | switch(cp->type) { 12 | case CN_CBOR_UINT: 13 | if (cp->v.uint == (unsigned long)key) { 14 | return cp->next; 15 | } 16 | break; 17 | case CN_CBOR_INT: 18 | if (cp->v.sint == (long)key) { 19 | return cp->next; 20 | } 21 | break; 22 | default: 23 | ; // skip non-integer keys 24 | } 25 | } 26 | return NULL; 27 | } 28 | 29 | cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key) { 30 | cn_cbor *cp; 31 | int keylen; 32 | assert(cb); 33 | assert(key); 34 | keylen = strlen(key); 35 | for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) { 36 | switch(cp->type) { 37 | case CN_CBOR_TEXT: // fall-through 38 | case CN_CBOR_BYTES: 39 | if (keylen != cp->length) { 40 | continue; 41 | } 42 | if (memcmp(key, cp->v.str, keylen) == 0) { 43 | return cp->next; 44 | } 45 | default: 46 | ; // skip non-string keys 47 | } 48 | } 49 | return NULL; 50 | } 51 | 52 | cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx) { 53 | cn_cbor *cp; 54 | unsigned int i = 0; 55 | assert(cb); 56 | for (cp = cb->first_child; cp; cp = cp->next) { 57 | if (i == idx) { 58 | return cp; 59 | } 60 | i++; 61 | } 62 | return NULL; 63 | } 64 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Compiling/running tests 4 | 5 | if (use_context) 6 | add_definitions(-DUSE_CBOR_CONTEXT) 7 | endif() 8 | 9 | set ( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dist_dir}/test ) 10 | 11 | function (create_test name) 12 | add_executable ( ${name}_test ${name}_test.c ) 13 | target_link_libraries ( ${name}_test PRIVATE cn-cbor ) 14 | target_include_directories ( ${name}_test PRIVATE ../include ) 15 | add_test ( NAME ${name} COMMAND ${name}_test ) 16 | endfunction() 17 | 18 | create_test ( cbor ) 19 | include ( CTest ) 20 | 21 | if (APPLE) 22 | # difftest uses Apple-specific memory tests 23 | add_executable (cn-test test.c ) 24 | target_include_directories ( cn-test PRIVATE ../include ) 25 | target_link_libraries ( cn-test PRIVATE cn-cbor ) 26 | 27 | configure_file(cases.cbor cases.cbor COPYONLY) 28 | configure_file(expected.out expected.out COPYONLY) 29 | 30 | add_custom_target(difftest 31 | COMMAND env MallocStackLogging=true ./cn-test >new.out 32 | COMMAND diff new.out expected.out 33 | DEPENDS cn-test 34 | WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} 35 | COMMENT "generate differences between actual and expected output") 36 | 37 | endif() 38 | -------------------------------------------------------------------------------- /test/cases.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabo/cn-cbor/c2e53733a895587c2910d2b2a1312d4224e5a474/test/cases.cbor -------------------------------------------------------------------------------- /test/cbor_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 SPUDlib authors. See LICENSE file. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "cn-cbor/cn-cbor.h" 12 | 13 | #define CTEST_MAIN 14 | #include "ctest.h" 15 | 16 | int main(int argc, const char *argv[]) 17 | { 18 | return ctest_main(argc, argv); 19 | } 20 | 21 | #ifdef USE_CBOR_CONTEXT 22 | #define CONTEXT_NULL , NULL 23 | #define CONTEXT_NULL_COMMA NULL, 24 | #else 25 | #define CONTEXT_NULL 26 | #define CONTEXT_NULL_COMMA 27 | #endif 28 | 29 | typedef struct _buffer { 30 | size_t sz; 31 | unsigned char *ptr; 32 | } buffer; 33 | 34 | static bool parse_hex(char *inp, buffer *b) 35 | { 36 | int len = strlen(inp); 37 | size_t i; 38 | if (len%2 != 0) { 39 | b->sz = -1; 40 | b->ptr = NULL; 41 | return false; 42 | } 43 | b->sz = len / 2; 44 | b->ptr = malloc(b->sz); 45 | for (i=0; isz; i++) { 46 | sscanf(inp+(2*i), "%02hhx", &b->ptr[i]); 47 | } 48 | return true; 49 | } 50 | 51 | CTEST(cbor, error) 52 | { 53 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_NO_ERROR], "CN_CBOR_NO_ERROR"); 54 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_OUT_OF_DATA], "CN_CBOR_ERR_OUT_OF_DATA"); 55 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED], "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED"); 56 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_ODD_SIZE_INDEF_MAP], "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP"); 57 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_BREAK_OUTSIDE_INDEF], "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF"); 58 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_MT_UNDEF_FOR_INDEF], "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF"); 59 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_RESERVED_AI], "CN_CBOR_ERR_RESERVED_AI"); 60 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING], "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING"); 61 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_INVALID_PARAMETER], "CN_CBOR_ERR_INVALID_PARAMETER"); 62 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_OUT_OF_MEMORY], "CN_CBOR_ERR_OUT_OF_MEMORY"); 63 | ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_FLOAT_NOT_SUPPORTED], "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED"); 64 | } 65 | 66 | CTEST(cbor, parse) 67 | { 68 | cn_cbor_errback err; 69 | char *tests[] = { 70 | "00", // 0 71 | "01", // 1 72 | "17", // 23 73 | "1818", // 24 74 | "190100", // 256 75 | "1a00010000", // 65536 76 | "1b0000000100000000", // 4294967296 77 | "20", // -1 78 | "37", // -24 79 | "3818", // -25 80 | "390100", // -257 81 | "3a00010000", // -65537 82 | "3b0000000100000000", // -4294967297 83 | "4161", // h"a" 84 | "6161", // "a" 85 | "80", // [] 86 | "8100", // [0] 87 | "820102", // [1,2] 88 | "818100", // [[0]] 89 | "a1616100", // {"a":0} 90 | "d8184100", // tag 91 | "f4", // false 92 | "f5", // true 93 | "f6", // null 94 | "f7", // undefined 95 | "f8ff", // simple(255) 96 | #ifndef CBOR_NO_FLOAT 97 | "f93c00", // 1.0 98 | "f9bc00", // -1.0 99 | "f903ff", // 6.097555160522461e-05 100 | "f90400", // 6.103515625e-05 101 | "f907ff", // 0.00012201070785522461 102 | "f90800", // 0.0001220703125 103 | "fa47800000", // 65536.0 104 | "fb3ff199999999999a", // 1.1 105 | "f97e00", // NaN 106 | #endif /* CBOR_NO_FLOAT */ 107 | "5f42010243030405ff", // (_ h'0102', h'030405') 108 | "7f61616161ff", // (_ "a", "a") 109 | "9fff", // [_ ] 110 | "9f9f9fffffff", // [_ [_ [_ ]]] 111 | "9f009f00ff00ff", // [_ 0, [_ 0], 0] 112 | "bf61610161629f0203ffff", // {_ "a": 1, "b": [_ 2, 3]} 113 | }; 114 | cn_cbor *cb; 115 | buffer b; 116 | size_t i; 117 | unsigned char encoded[1024]; 118 | ssize_t enc_sz; 119 | 120 | for (i=0; ilength == 2); 354 | 355 | cn_cbor_mapput_int(cb_map, -7, cb_data CONTEXT_NULL, &err); 356 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 357 | ASSERT_TRUE(cb_map->length == 4); 358 | 359 | cn_cbor_mapput_string(cb_map, "foo", 360 | cn_cbor_string_create(data CONTEXT_NULL, &err) 361 | CONTEXT_NULL, &err); 362 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 363 | ASSERT_TRUE(cb_map->length == 6); 364 | 365 | cn_cbor_map_put(cb_map, 366 | cn_cbor_string_create("bar" CONTEXT_NULL, &err), 367 | cn_cbor_string_create("qux" CONTEXT_NULL, &err), 368 | &err); 369 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 370 | ASSERT_TRUE(cb_map->length == 8); 371 | 372 | #ifndef CBOR_NO_FLOAT 373 | cn_cbor_mapput_int(cb_map, 42, cb_dbl CONTEXT_NULL, &err); 374 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 375 | ASSERT_TRUE(cb_map->length == 10); 376 | #endif 377 | 378 | val = cn_cbor_mapget_int(cb_map, 5); 379 | ASSERT_NOT_NULL(val); 380 | ASSERT_TRUE(val->v.sint == 256); 381 | 382 | val = cn_cbor_mapget_int(cb_map, -7); 383 | ASSERT_NOT_NULL(val); 384 | ASSERT_STR(val->v.str, "abc"); 385 | 386 | #ifndef CBOR_NO_FLOAT 387 | val = cn_cbor_mapget_int(cb_map, 42); 388 | ASSERT_NOT_NULL(val); 389 | ASSERT_TRUE(val->v.dbl > 3.14 && val->v.dbl < 3.15); 390 | #endif 391 | 392 | cn_cbor_free(cb_map CONTEXT_NULL); 393 | } 394 | 395 | CTEST(cbor, map_errors) 396 | { 397 | cn_cbor_errback err; 398 | cn_cbor *ci; 399 | ci = cn_cbor_int_create(65536, CONTEXT_NULL_COMMA &err); 400 | cn_cbor_mapput_int(ci, -5, NULL, CONTEXT_NULL_COMMA &err); 401 | ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER); 402 | cn_cbor_mapput_string(ci, "foo", NULL, CONTEXT_NULL_COMMA &err); 403 | ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER); 404 | cn_cbor_map_put(ci, NULL, NULL, &err); 405 | } 406 | 407 | CTEST(cbor, array) 408 | { 409 | cn_cbor_errback err; 410 | cn_cbor *a = cn_cbor_array_create(CONTEXT_NULL_COMMA &err); 411 | ASSERT_NOT_NULL(a); 412 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 413 | ASSERT_EQUAL(a->length, 0); 414 | 415 | cn_cbor_array_append(a, cn_cbor_int_create(256, CONTEXT_NULL_COMMA &err), &err); 416 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 417 | ASSERT_EQUAL(a->length, 1); 418 | 419 | cn_cbor_array_append(a, cn_cbor_string_create("five", CONTEXT_NULL_COMMA &err), &err); 420 | ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR); 421 | ASSERT_EQUAL(a->length, 2); 422 | } 423 | 424 | CTEST(cbor, array_errors) 425 | { 426 | cn_cbor_errback err; 427 | cn_cbor *ci = cn_cbor_int_create(12, CONTEXT_NULL_COMMA &err); 428 | cn_cbor_array_append(NULL, ci, &err); 429 | ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER); 430 | cn_cbor_array_append(ci, NULL, &err); 431 | ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER); 432 | } 433 | 434 | CTEST(cbor, create_encode) 435 | { 436 | cn_cbor *map; 437 | cn_cbor *cdata; 438 | char data[] = "data"; 439 | unsigned char encoded[1024]; 440 | ssize_t enc_sz; 441 | 442 | map = cn_cbor_map_create(CONTEXT_NULL_COMMA NULL); 443 | ASSERT_NOT_NULL(map); 444 | 445 | cdata = cn_cbor_data_create((uint8_t*)data, sizeof(data)-1, CONTEXT_NULL_COMMA NULL); 446 | ASSERT_NOT_NULL(cdata); 447 | 448 | ASSERT_TRUE(cn_cbor_mapput_int(map, 0, cdata, CONTEXT_NULL_COMMA NULL)); 449 | enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), map); 450 | ASSERT_EQUAL(7, enc_sz); 451 | } 452 | -------------------------------------------------------------------------------- /test/ctest.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2011,2012 Bas van den Berg 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef CTEST_H 17 | #define CTEST_H 18 | 19 | #ifndef UNUSED_PARAM 20 | /** 21 | * \def UNUSED_PARAM(p); 22 | * 23 | * A macro for quelling compiler warnings about unused variables. 24 | */ 25 | # define UNUSED_PARAM(p) ((void)&(p)) 26 | #endif /* UNUSED_PARM */ 27 | 28 | typedef void (*SetupFunc)(void*); 29 | typedef void (*TearDownFunc)(void*); 30 | 31 | struct ctest { 32 | const char* ssname; // suite name 33 | const char* ttname; // test name 34 | void (*run)(); 35 | int skip; 36 | 37 | void* data; 38 | SetupFunc setup; 39 | TearDownFunc teardown; 40 | 41 | unsigned int magic; 42 | }; 43 | 44 | #define __FNAME(sname, tname) __ctest_##sname##_##tname##_run 45 | #define __TNAME(sname, tname) __ctest_##sname##_##tname 46 | 47 | #define __CTEST_MAGIC (0xdeadbeef) 48 | #ifdef __APPLE__ 49 | #define __Test_Section __attribute__ ((unused,section ("__DATA, .ctest"))) 50 | #else 51 | #define __Test_Section __attribute__ ((unused,section (".ctest"))) 52 | #endif 53 | 54 | #define __CTEST_STRUCT(sname, tname, _skip, __data, __setup, __teardown) \ 55 | struct ctest __TNAME(sname, tname) __Test_Section = { \ 56 | .ssname=#sname, \ 57 | .ttname=#tname, \ 58 | .run = __FNAME(sname, tname), \ 59 | .skip = _skip, \ 60 | .data = __data, \ 61 | .setup = (SetupFunc)__setup, \ 62 | .teardown = (TearDownFunc)__teardown, \ 63 | .magic = __CTEST_MAGIC }; 64 | 65 | #define CTEST_DATA(sname) struct sname##_data 66 | 67 | #define CTEST_SETUP(sname) \ 68 | void __attribute__ ((weak)) sname##_setup(struct sname##_data* data) 69 | 70 | #define CTEST_TEARDOWN(sname) \ 71 | void __attribute__ ((weak)) sname##_teardown(struct sname##_data* data) 72 | 73 | #define __CTEST_INTERNAL(sname, tname, _skip) \ 74 | void __FNAME(sname, tname)(); \ 75 | __CTEST_STRUCT(sname, tname, _skip, NULL, NULL, NULL) \ 76 | void __FNAME(sname, tname)() 77 | 78 | #ifdef __APPLE__ 79 | #define SETUP_FNAME(sname) NULL 80 | #define TEARDOWN_FNAME(sname) NULL 81 | #else 82 | #define SETUP_FNAME(sname) sname##_setup 83 | #define TEARDOWN_FNAME(sname) sname##_teardown 84 | #endif 85 | 86 | #define __CTEST2_INTERNAL(sname, tname, _skip) \ 87 | static struct sname##_data __ctest_##sname##_data; \ 88 | CTEST_SETUP(sname); \ 89 | CTEST_TEARDOWN(sname); \ 90 | void __FNAME(sname, tname)(struct sname##_data* data); \ 91 | __CTEST_STRUCT(sname, tname, _skip, &__ctest_##sname##_data, SETUP_FNAME(sname), TEARDOWN_FNAME(sname)) \ 92 | void __FNAME(sname, tname)(struct sname##_data* data) 93 | 94 | 95 | void CTEST_LOG(char *fmt, ...); 96 | void CTEST_ERR(char *fmt, ...); // doesn't return 97 | 98 | #define CTEST(sname, tname) __CTEST_INTERNAL(sname, tname, 0) 99 | #define CTEST_SKIP(sname, tname) __CTEST_INTERNAL(sname, tname, 1) 100 | 101 | #define CTEST2(sname, tname) __CTEST2_INTERNAL(sname, tname, 0) 102 | #define CTEST2_SKIP(sname, tname) __CTEST2_INTERNAL(sname, tname, 1) 103 | 104 | 105 | void assert_str(const char* exp, const char* real, const char* caller, int line); 106 | #define ASSERT_STR(exp, real) assert_str(exp, real, __FILE__, __LINE__) 107 | 108 | void assert_data(const unsigned char* exp, int expsize, 109 | const unsigned char* real, int realsize, 110 | const char* caller, int line); 111 | #define ASSERT_DATA(exp, expsize, real, realsize) \ 112 | assert_data(exp, expsize, real, realsize, __FILE__, __LINE__) 113 | 114 | void assert_equal(long exp, long real, const char* caller, int line); 115 | #define ASSERT_EQUAL(exp, real) assert_equal(exp, real, __FILE__, __LINE__) 116 | 117 | void assert_not_equal(long exp, long real, const char* caller, int line); 118 | #define ASSERT_NOT_EQUAL(exp, real) assert_not_equal(exp, real, __FILE__, __LINE__) 119 | 120 | void assert_null(void* real, const char* caller, int line); 121 | #define ASSERT_NULL(real) assert_null((void*)real, __FILE__, __LINE__) 122 | 123 | void assert_not_null(const void* real, const char* caller, int line); 124 | #define ASSERT_NOT_NULL(real) assert_not_null(real, __FILE__, __LINE__) 125 | 126 | void assert_true(int real, const char* caller, int line); 127 | #define ASSERT_TRUE(real) assert_true(real, __FILE__, __LINE__) 128 | 129 | void assert_false(int real, const char* caller, int line); 130 | #define ASSERT_FALSE(real) assert_false(real, __FILE__, __LINE__) 131 | 132 | void assert_fail(const char* caller, int line); 133 | #define ASSERT_FAIL() assert_fail(__FILE__, __LINE__) 134 | 135 | #ifdef CTEST_MAIN 136 | 137 | #include 138 | #include 139 | #include 140 | #include 141 | #include 142 | #include 143 | #include 144 | #include 145 | #include 146 | 147 | #ifdef __APPLE__ 148 | #include 149 | #endif 150 | 151 | //#define COLOR_OK 152 | 153 | static size_t ctest_errorsize; 154 | static char* ctest_errormsg; 155 | #define MSG_SIZE 4096 156 | static char ctest_errorbuffer[MSG_SIZE]; 157 | static jmp_buf ctest_err; 158 | static int color_output = 1; 159 | static const char* suite_name; 160 | 161 | typedef int (*filter_func)(struct ctest*); 162 | 163 | #define ANSI_BLACK "\033[0;30m" 164 | #define ANSI_RED "\033[0;31m" 165 | #define ANSI_GREEN "\033[0;32m" 166 | #define ANSI_YELLOW "\033[0;33m" 167 | #define ANSI_BLUE "\033[0;34m" 168 | #define ANSI_MAGENTA "\033[0;35m" 169 | #define ANSI_CYAN "\033[0;36m" 170 | #define ANSI_GREY "\033[0;37m" 171 | #define ANSI_DARKGREY "\033[01;30m" 172 | #define ANSI_BRED "\033[01;31m" 173 | #define ANSI_BGREEN "\033[01;32m" 174 | #define ANSI_BYELLOW "\033[01;33m" 175 | #define ANSI_BBLUE "\033[01;34m" 176 | #define ANSI_BMAGENTA "\033[01;35m" 177 | #define ANSI_BCYAN "\033[01;36m" 178 | #define ANSI_WHITE "\033[01;37m" 179 | #define ANSI_NORMAL "\033[0m" 180 | 181 | static CTEST(suite, test) { } 182 | 183 | static void msg_start(const char* color, const char* title) { 184 | int size; 185 | if (color_output) { 186 | size = snprintf(ctest_errormsg, ctest_errorsize, "%s", color); 187 | ctest_errorsize -= size; 188 | ctest_errormsg += size; 189 | } 190 | size = snprintf(ctest_errormsg, ctest_errorsize, " %s: ", title); 191 | ctest_errorsize -= size; 192 | ctest_errormsg += size; 193 | } 194 | 195 | static void msg_end() { 196 | int size; 197 | if (color_output) { 198 | size = snprintf(ctest_errormsg, ctest_errorsize, ANSI_NORMAL); 199 | ctest_errorsize -= size; 200 | ctest_errormsg += size; 201 | } 202 | size = snprintf(ctest_errormsg, ctest_errorsize, "\n"); 203 | ctest_errorsize -= size; 204 | ctest_errormsg += size; 205 | } 206 | 207 | void CTEST_LOG(char *fmt, ...) 208 | { 209 | va_list argp; 210 | msg_start(ANSI_BLUE, "LOG"); 211 | 212 | va_start(argp, fmt); 213 | int size = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, argp); 214 | ctest_errorsize -= size; 215 | ctest_errormsg += size; 216 | va_end(argp); 217 | 218 | msg_end(); 219 | } 220 | 221 | void CTEST_ERR(char *fmt, ...) 222 | { 223 | va_list argp; 224 | msg_start(ANSI_YELLOW, "ERR"); 225 | 226 | va_start(argp, fmt); 227 | int size = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, argp); 228 | ctest_errorsize -= size; 229 | ctest_errormsg += size; 230 | va_end(argp); 231 | 232 | msg_end(); 233 | longjmp(ctest_err, 1); 234 | } 235 | 236 | void assert_str(const char* exp, const char* real, const char* caller, int line) { 237 | if ((exp == NULL && real != NULL) || 238 | (exp != NULL && real == NULL) || 239 | (exp && real && strcmp(exp, real) != 0)) { 240 | CTEST_ERR("%s:%d expected '%s', got '%s'", caller, line, exp, real); 241 | } 242 | } 243 | 244 | void assert_data(const unsigned char* exp, int expsize, 245 | const unsigned char* real, int realsize, 246 | const char* caller, int line) { 247 | int i; 248 | if (expsize != realsize) { 249 | CTEST_ERR("%s:%d expected %d bytes, got %d", caller, line, expsize, realsize); 250 | } 251 | for (i=0; issname, strlen(suite_name)) == 0; 307 | } 308 | 309 | static uint64_t getCurrentTime() { 310 | struct timeval now; 311 | gettimeofday(&now, NULL); 312 | uint64_t now64 = now.tv_sec; 313 | now64 *= 1000000; 314 | now64 += (now.tv_usec); 315 | return now64; 316 | } 317 | 318 | static void color_print(const char* color, const char* text) { 319 | if (color_output) 320 | printf("%s%s"ANSI_NORMAL"\n", color, text); 321 | else 322 | printf("%s\n", text); 323 | } 324 | 325 | #ifdef __APPLE__ 326 | static void *find_symbol(struct ctest *test, const char *fname) 327 | { 328 | size_t len = strlen(test->ssname) + 1 + strlen(fname); 329 | char *symbol_name = (char *) malloc(len + 1); 330 | memset(symbol_name, 0, len + 1); 331 | snprintf(symbol_name, len + 1, "%s_%s", test->ssname, fname); 332 | 333 | //fprintf(stderr, ">>>> dlsym: loading %s\n", symbol_name); 334 | void *symbol = dlsym(RTLD_DEFAULT, symbol_name); 335 | if (!symbol) { 336 | //fprintf(stderr, ">>>> ERROR: %s\n", dlerror()); 337 | } 338 | // returns NULL on error 339 | 340 | free(symbol_name); 341 | return symbol; 342 | } 343 | #endif 344 | 345 | #ifdef CTEST_SEGFAULT 346 | #include 347 | static void sighandler(int signum) 348 | { 349 | char msg[128]; 350 | sprintf(msg, "[SIGNAL %d: %s]", signum, sys_siglist[signum]); 351 | color_print(ANSI_BRED, msg); 352 | fflush(stdout); 353 | 354 | /* "Unregister" the signal handler and send the signal back to the process 355 | * so it can terminate as expected */ 356 | signal(signum, SIG_DFL); 357 | kill(getpid(), signum); 358 | } 359 | #endif 360 | 361 | int ctest_main(int argc, const char *argv[]) 362 | { 363 | static int total = 0; 364 | static int num_ok = 0; 365 | static int num_fail = 0; 366 | static int num_skip = 0; 367 | static int index = 1; 368 | static filter_func filter = suite_all; 369 | 370 | #ifdef CTEST_SEGFAULT 371 | signal(SIGSEGV, sighandler); 372 | #endif 373 | 374 | if (argc == 2) { 375 | suite_name = argv[1]; 376 | filter = suite_filter; 377 | } 378 | 379 | color_output = isatty(1); 380 | uint64_t t1 = getCurrentTime(); 381 | 382 | struct ctest* ctest_begin = &__TNAME(suite, test); 383 | struct ctest* ctest_end = &__TNAME(suite, test); 384 | // find begin and end of section by comparing magics 385 | while (1) { 386 | struct ctest* t = ctest_begin-1; 387 | if (t->magic != __CTEST_MAGIC) break; 388 | ctest_begin--; 389 | } 390 | while (1) { 391 | struct ctest* t = ctest_end+1; 392 | if (t->magic != __CTEST_MAGIC) break; 393 | ctest_end++; 394 | } 395 | ctest_end++; // end after last one 396 | 397 | static struct ctest* test; 398 | for (test = ctest_begin; test != ctest_end; test++) { 399 | if (test == &__ctest_suite_test) continue; 400 | if (filter(test)) total++; 401 | } 402 | 403 | for (test = ctest_begin; test != ctest_end; test++) { 404 | if (test == &__ctest_suite_test) continue; 405 | if (filter(test)) { 406 | ctest_errorbuffer[0] = 0; 407 | ctest_errorsize = MSG_SIZE-1; 408 | ctest_errormsg = ctest_errorbuffer; 409 | printf("TEST %d/%d %s:%s ", index, total, test->ssname, test->ttname); 410 | fflush(stdout); 411 | if (test->skip) { 412 | color_print(ANSI_BYELLOW, "[SKIPPED]"); 413 | num_skip++; 414 | } else { 415 | int result = setjmp(ctest_err); 416 | if (result == 0) { 417 | #ifdef __APPLE__ 418 | if (!test->setup) { 419 | test->setup = (SetupFunc)find_symbol(test, "setup"); 420 | } 421 | if (!test->teardown) { 422 | test->teardown = (SetupFunc)find_symbol(test, "teardown"); 423 | } 424 | #endif 425 | 426 | if (test->setup) test->setup(test->data); 427 | if (test->data) 428 | test->run(test->data); 429 | else 430 | test->run(); 431 | if (test->teardown) test->teardown(test->data); 432 | // if we got here it's ok 433 | #ifdef COLOR_OK 434 | color_print(ANSI_BGREEN, "[OK]"); 435 | #else 436 | printf("[OK]\n"); 437 | #endif 438 | num_ok++; 439 | } else { 440 | color_print(ANSI_BRED, "[FAIL]"); 441 | num_fail++; 442 | } 443 | if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer); 444 | } 445 | index++; 446 | } 447 | } 448 | uint64_t t2 = getCurrentTime(); 449 | 450 | const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN; 451 | char results[80]; 452 | sprintf(results, "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %"PRIu64" ms", total, num_ok, num_fail, num_skip, (t2 - t1)/1000); 453 | color_print(color, results); 454 | return num_fail; 455 | } 456 | 457 | #endif 458 | 459 | #endif 460 | -------------------------------------------------------------------------------- /test/expected.out: -------------------------------------------------------------------------------- 1 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2 | 511 3 | [ 4 | 0 5 | 1 6 | 10 7 | 23 8 | 24 9 | 25 10 | 100 11 | 1000 12 | 1000000 13 | 1000000000000 14 | 18446744073709551615 15 | 2( 16 | h'010000000000000000' 17 | ) 18 | OVERFLOW 19 | 3( 20 | h'010000000000000000' 21 | ) 22 | -1 23 | -10 24 | -100 25 | -1000 26 | 0.000000e+00 27 | -0.000000e+00 28 | 1.000000e+00 29 | 1.100000e+00 30 | 1.500000e+00 31 | 6.550400e+04 32 | 1.000000e+05 33 | 3.402823e+38 34 | 1.000000e+300 35 | 5.960464e-08 36 | 6.103516e-05 37 | -4.000000e+00 38 | -4.100000e+00 39 | inf 40 | nan 41 | -inf 42 | inf 43 | nan 44 | -inf 45 | inf 46 | nan 47 | -inf 48 | false 49 | true 50 | null 51 | simple(23) 52 | simple(16) 53 | simple(24) 54 | simple(255) 55 | 0( 56 | "2013-03-21T20:04:00Z" 57 | ) 58 | 1( 59 | 1363896240 60 | ) 61 | 1( 62 | 1.363896e+09 63 | ) 64 | 23( 65 | h'01020304' 66 | ) 67 | 24( 68 | h'6449455446' 69 | ) 70 | 32( 71 | "http://www.example.com" 72 | ) 73 | h'' 74 | h'01020304' 75 | "" 76 | "a" 77 | "IETF" 78 | ""\" 79 | "ü" 80 | "水" 81 | "𐅑" 82 | [ 83 | ] 84 | [ 85 | 1 86 | 2 87 | 3 88 | ] 89 | [ 90 | 1 91 | [ 92 | 2 93 | 3 94 | ] 95 | [ 96 | 4 97 | 5 98 | ] 99 | ] 100 | [ 101 | 1 102 | 2 103 | 3 104 | 4 105 | 5 106 | 6 107 | 7 108 | 8 109 | 9 110 | 10 111 | 11 112 | 12 113 | 13 114 | 14 115 | 15 116 | 16 117 | 17 118 | 18 119 | 19 120 | 20 121 | 21 122 | 22 123 | 23 124 | 24 125 | 25 126 | ] 127 | { 128 | } 129 | { 130 | 1 131 | 2 132 | 3 133 | 4 134 | } 135 | { 136 | "a" 137 | 1 138 | "b" 139 | [ 140 | 2 141 | 3 142 | ] 143 | } 144 | [ 145 | "a" 146 | { 147 | "b" 148 | "c" 149 | } 150 | ] 151 | { 152 | "a" 153 | "A" 154 | "b" 155 | "B" 156 | "c" 157 | "C" 158 | "d" 159 | "D" 160 | "e" 161 | "E" 162 | } 163 | (_ 164 | 165 | h'0102' 166 | h'030405' 167 | ) 168 | (_ 169 | "strea" 170 | "ming" 171 | ) 172 | [ 173 | ] 174 | [ 175 | 1 176 | [ 177 | 2 178 | 3 179 | ] 180 | [ 181 | 4 182 | 5 183 | ] 184 | ] 185 | [ 186 | 1 187 | [ 188 | 2 189 | 3 190 | ] 191 | [ 192 | 4 193 | 5 194 | ] 195 | ] 196 | [ 197 | 1 198 | [ 199 | 2 200 | 3 201 | ] 202 | [ 203 | 4 204 | 5 205 | ] 206 | ] 207 | [ 208 | 1 209 | [ 210 | 2 211 | 3 212 | ] 213 | [ 214 | 4 215 | 5 216 | ] 217 | ] 218 | [ 219 | 1 220 | 2 221 | 3 222 | 4 223 | 5 224 | 6 225 | 7 226 | 8 227 | 9 228 | 10 229 | 11 230 | 12 231 | 13 232 | 14 233 | 15 234 | 16 235 | 17 236 | 18 237 | 19 238 | 20 239 | 21 240 | 22 241 | 23 242 | 24 243 | 25 244 | ] 245 | { 246 | "a" 247 | 1 248 | "b" 249 | [ 250 | 2 251 | 3 252 | ] 253 | } 254 | [ 255 | "a" 256 | { 257 | "b" 258 | "c" 259 | } 260 | ] 261 | { 262 | "Fun" 263 | true 264 | "Amt" 265 | -2 266 | } 267 | ] 268 | 269 | CN_CBOR_ERR_BREAK_OUTSIDE_INDEF at 1 270 | CN_CBOR_ERR_MT_UNDEF_FOR_INDEF at 1 271 | CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED at 1 272 | CN_CBOR_ERR_OUT_OF_DATA at 1 273 | CN_CBOR_ERR_RESERVED_AI at 1 274 | CN_CBOR_ERR_ODD_SIZE_INDEF_MAP at 3 275 | CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING at 2 276 | 277 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 278 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cn-cbor/cn-cbor.h" 9 | 10 | #ifdef USE_CBOR_CONTEXT 11 | #define CBOR_CONTEXT_PARAM , NULL 12 | #else 13 | #define CBOR_CONTEXT_PARAM 14 | #endif 15 | 16 | #define ERROR(msg, p) fprintf(stderr, "ERROR: " msg " %s\n", (p)); 17 | 18 | static unsigned char* load_file(const char* filepath, unsigned char **end) { 19 | struct stat st; 20 | if (stat(filepath, &st)==-1) { 21 | ERROR("can't find file", filepath); 22 | return 0; 23 | } 24 | int fd=open(filepath, O_RDONLY); 25 | if (fd==-1) { 26 | ERROR("can't open file", filepath); 27 | return 0; 28 | } 29 | unsigned char* text=malloc(st.st_size+1); // this is not going to be freed 30 | if (st.st_size!=read(fd, text, st.st_size)) { 31 | ERROR("can't read file", filepath); 32 | close(fd); 33 | return 0; 34 | } 35 | close(fd); 36 | text[st.st_size]='\0'; 37 | *end = text + st.st_size; 38 | return text; 39 | } 40 | 41 | static void dump(const cn_cbor* cb, char* out, char** end, int indent) { 42 | if (!cb) 43 | goto done; 44 | int i; 45 | cn_cbor* cp; 46 | char finchar = ')'; /* most likely */ 47 | 48 | #define CPY(s, l) memcpy(out, s, l); out += l; 49 | #define OUT(s) CPY(s, sizeof(s)-1) 50 | #define PRF(f, a) out += sprintf(out, f, a) 51 | 52 | for (i = 0; i < indent; i++) *out++ = ' '; 53 | switch (cb->type) { 54 | case CN_CBOR_TEXT_CHUNKED: OUT("(_\n"); goto sequence; 55 | case CN_CBOR_BYTES_CHUNKED: OUT("(_\n\n"); goto sequence; 56 | case CN_CBOR_TAG: PRF("%ld(\n", cb->v.sint); goto sequence; 57 | case CN_CBOR_ARRAY: finchar = ']'; OUT("[\n"); goto sequence; 58 | case CN_CBOR_MAP: finchar = '}'; OUT("{\n"); goto sequence; 59 | sequence: 60 | for (cp = cb->first_child; cp; cp = cp->next) { 61 | dump(cp, out, &out, indent+2); 62 | } 63 | for (i=0; ilength; i++) 68 | PRF("%02x", cb->v.str[i] & 0xff); 69 | *out++ = '\''; 70 | break; 71 | case CN_CBOR_TEXT: *out++ = '"'; 72 | CPY(cb->v.str, cb->length); /* should escape stuff */ 73 | *out++ = '"'; 74 | break; 75 | case CN_CBOR_NULL: OUT("null"); break; 76 | case CN_CBOR_TRUE: OUT("true"); break; 77 | case CN_CBOR_FALSE: OUT("false"); break; 78 | case CN_CBOR_UNDEF: OUT("simple(23)"); break; 79 | case CN_CBOR_INT: PRF("%ld", cb->v.sint); break; 80 | case CN_CBOR_UINT: PRF("%lu", cb->v.uint); break; 81 | case CN_CBOR_DOUBLE: PRF("%e", cb->v.dbl); break; 82 | case CN_CBOR_SIMPLE: PRF("simple(%ld)", cb->v.sint); break; 83 | default: PRF("???%d???", cb->type); break; 84 | } 85 | *out++ = '\n'; 86 | done: 87 | *end = out; 88 | } 89 | 90 | 91 | const char *err_name[] = { 92 | "CN_CBOR_NO_ERROR", 93 | "CN_CBOR_ERR_OUT_OF_DATA", 94 | "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED", 95 | "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP", 96 | "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF", 97 | "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF", 98 | "CN_CBOR_ERR_RESERVED_AI", 99 | "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING", 100 | "CN_CBOR_ERR_OUT_OF_MEMORY", 101 | "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED", 102 | }; 103 | 104 | static void cn_cbor_decode_test(const unsigned char *buf, int len) { 105 | struct cn_cbor_errback back; 106 | const cn_cbor *ret = cn_cbor_decode(buf, len CBOR_CONTEXT_PARAM, &back); 107 | if (ret) 108 | printf("oops 1"); 109 | printf("%s at %d\n", err_name[back.err], back.pos); 110 | } 111 | 112 | int main(void) { 113 | char buf[100000]; 114 | unsigned char *end; 115 | char *bufend; 116 | unsigned char *s = load_file("cases.cbor", &end); 117 | printf("%zd\n", end-s); 118 | cn_cbor *cb = cn_cbor_decode(s, end-s CBOR_CONTEXT_PARAM, 0); 119 | if (cb) { 120 | dump(cb, buf, &bufend, 0); 121 | *bufend = 0; 122 | printf("%s\n", buf); 123 | cn_cbor_free(cb CBOR_CONTEXT_PARAM); 124 | cb = 0; /* for leaks testing */ 125 | } 126 | cn_cbor_decode_test((const unsigned char*)"\xff", 1); /* break outside indef */ 127 | cn_cbor_decode_test((const unsigned char*)"\x1f", 1); /* mt undef for indef */ 128 | cn_cbor_decode_test((const unsigned char*)"\x00\x00", 2); /* not all data consumed */ 129 | cn_cbor_decode_test((const unsigned char*)"\x81", 1); /* out of data */ 130 | cn_cbor_decode_test((const unsigned char*)"\x1c", 1); /* reserved ai */ 131 | cn_cbor_decode_test((const unsigned char*)"\xbf\x00\xff", 3); /* odd size indef map */ 132 | cn_cbor_decode_test((const unsigned char*)"\x7f\x40\xff", 3); /* wrong nesting in indef string */ 133 | system("leaks test"); 134 | } 135 | 136 | /* cn-cbor.c:112: CN_CBOR_FAIL("out of memory"); */ 137 | --------------------------------------------------------------------------------