aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJanis Danisevskis <jdanis@google.com>2018-01-18 18:17:59 +0000
committerandroid-build-merger <android-build-merger@google.com>2018-01-18 18:17:59 +0000
commit8a9d358531e6ec95340d95b0a42ee84310e3701e (patch)
treef31948b9ce23f6cce9d3a9450ca3c90b4ac73355
parentfd4e34b1b3a898829f1fedaafbaea001193f2536 (diff)
parent3798baae63cca5d67e550ee3ef449bc56f80958b (diff)
downloadcn-cbor-8a9d358531e6ec95340d95b0a42ee84310e3701e.tar.gz
Merge upstream for the first time
am: 3798baae63 Change-Id: I88dff63df6e103fe8a285218eec83160db49ef7a
-rw-r--r--.editorconfig15
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml17
-rw-r--r--CMakeLists.txt136
-rw-r--r--Doxyfile.in9
-rw-r--r--LICENSE21
-rw-r--r--README.md44
-rw-r--r--Simple-Makefile25
-rwxr-xr-xbuild.sh5
-rw-r--r--cmake/Coveralls.cmake119
-rw-r--r--cmake/CoverallsClear.cmake26
-rw-r--r--cmake/CoverallsGenerateGcov.cmake429
-rw-r--r--cmake/LCov.cmake12
-rw-r--r--cn-cbor.pc.in11
-rw-r--r--include/CMakeLists.txt2
-rw-r--r--include/cn-cbor/cn-cbor.h401
-rw-r--r--src/CMakeLists.txt50
-rw-r--r--src/cbor.h118
-rw-r--r--src/cn-cbor.c272
-rw-r--r--src/cn-create.c184
-rw-r--r--src/cn-encoder.c309
-rw-r--r--src/cn-error.c13
-rw-r--r--src/cn-get.c63
-rw-r--r--test/CMakeLists.txt37
-rw-r--r--test/cases.cborbin0 -> 511 bytes
-rw-r--r--test/cbor_test.c430
-rw-r--r--test/ctest.h459
-rw-r--r--test/expected.out277
-rw-r--r--test/test.c136
29 files changed, 3624 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..91dbe7f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,15 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# Matches multiple files with brace expansion notation
+# Set default charset
+[*.{c,h}]
+indent_style = space
+indent_size = 2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2eef09e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+cntest
+new.out
+*.o
+build
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1d16cc7
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: c
+compiler:
+- clang
+- gcc
+sudo: false
+addons:
+ apt:
+ sources:
+ - george-edison55-precise-backports
+ packages:
+ - cmake
+ - cmake-data
+script:
+- "./build.sh all test"
+notifications:
+ slack:
+ secure: WdgYxQrnFR5eu/eKygPuLjlFsuZxD9m2PLRWTLT85aj+18Gp2ooPjnI9UFdb1xY87+4InhWk6PvQU35j4bG0etPQtX+0H4T4Zdk/aD6KxgJBHIYGqtfZUMmdFfVpUH9cCPx99Jjw81mhKrxM+6rXiZdiWXuNhvbJOApRT6uxE2k=
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..195c780
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,136 @@
+#
+#
+# top level build file for cn-cbor
+
+## prepare CMAKE
+cmake_minimum_required ( VERSION 3.0.0 )
+
+set ( VERSION_MAJOR 0 CACHE STRING "Project major version number")
+set ( VERSION_MINOR "1" CACHE STRING "Project minor version number" )
+set ( VERSION_PATCH "0" CACHE STRING "Project patch version number" )
+set ( CN_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" )
+mark_as_advanced(VERSION_MAJOR VERSION_MINOR VERSION_PATCH CN_VERSION)
+
+project ( "cn-cbor" VERSION "${CN_VERSION}")
+
+find_package(Doxygen)
+
+## setup options
+option ( use_context "Use context pointer for CBOR functions" OFF )
+option ( verbose "Produce verbose makefile output" OFF )
+option ( optimize "Optimize for size" OFF )
+option ( fatal_warnings "Treat build warnings as errors" ON )
+option ( coveralls "Generate coveralls data" ON )
+option ( coveralls_send "Send data to coveralls site" OFF )
+option ( build_docs "Create docs using Doxygen" ${DOXYGEN_FOUND} )
+option ( no_floats "Build without floating point support" OFF )
+
+set ( dist_dir ${CMAKE_BINARY_DIR}/dist )
+set ( prefix ${CMAKE_INSTALL_PREFIX} )
+set ( exec_prefix ${CMAKE_INSTALL_PREFIX}/bin )
+set ( libdir ${CMAKE_INSTALL_PREFIX}/lib )
+set ( includedir ${CMAKE_INSTALL_PREFIX}/include )
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cn-cbor.pc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/cn-cbor.pc @ONLY)
+install (FILES ${CMAKE_CURRENT_BINARY_DIR}/cn-cbor.pc DESTINATION lib/pkgconfig )
+
+set ( package_prefix "${CMAKE_PACKAGE_NAME}-${CMAKE_SYSTEM_NAME}" )
+
+set ( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dist_dir}/bin )
+set ( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${dist_dir}/lib )
+set ( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${dist_dir}/lib )
+
+if (NOT CMAKE_BUILD_TYPE)
+ if ( optimize )
+ set ( CMAKE_BUILD_TYPE MinSizeRel )
+ set ( coveralls OFF )
+ set ( coveralls_send OFF )
+ else ()
+ set ( CMAKE_BUILD_TYPE Debug )
+ endif ()
+endif()
+
+message ( "Build type: ${CMAKE_BUILD_TYPE}" )
+
+if ( CMAKE_C_COMPILER_ID STREQUAL "GNU" OR
+ CMAKE_C_COMPILER_ID MATCHES "Clang" )
+ message ( STATUS "adding GCC/Clang options ")
+ add_definitions ( -std=gnu99 -Wall -Wextra -pedantic )
+ if ( fatal_warnings )
+ add_definitions ( -Werror )
+ endif ()
+ if ( optimize )
+ add_definitions ( -Os )
+ endif ()
+elseif ( MSVC )
+ add_definitions ( /W3 )
+ if ( fatal_warnings )
+ add_definitions ( /WX )
+ endif ()
+else ()
+ message ( FATAL_ERROR "unhandled compiler id: ${CMAKE_C_COMPILER_ID}" )
+endif ()
+
+if ( no_floats )
+ add_definitions(-DCBOR_NO_FLOAT)
+endif()
+
+if ( verbose )
+ set ( CMAKE_VERBOSE_MAKEFILE ON )
+endif ()
+
+## include the parts
+add_subdirectory ( include )
+add_subdirectory ( src )
+add_subdirectory ( test )
+
+install (FILES LICENSE README.md DESTINATION .)
+
+## setup packaging
+set ( CPACK_GENERATOR "TGZ" )
+set ( CPACK_PACKAGE_VERSION "${PROJECT_VERSION}" )
+set ( CPACK_SOURCE_GENERATOR "TGZ" )
+set ( CPACK_SOURCE_IGNORE_FILES "/\\\\.git/" )
+file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/.gitignore igs)
+foreach (ig IN ITEMS ${igs})
+ # remove comments
+ string ( REGEX REPLACE "^\\s*#.*" "" ig "${ig}")
+ # remove any other whitespace
+ string ( STRIP "${ig}" ig)
+ # anything left?
+ if (ig)
+ # dots are literal
+ string ( REPLACE "." "\\\\." ig "${ig}" )
+ # stars are on thars
+ string ( REPLACE "*" ".*" ig "${ig}" )
+ list ( APPEND CPACK_SOURCE_IGNORE_FILES "/${ig}/" )
+ endif()
+endforeach()
+
+set ( CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/README.md )
+set ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" )
+
+include ( CPack )
+include ( CTest )
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)
+include ( LCov )
+
+if (build_docs)
+ if(NOT DOXYGEN_FOUND)
+ message(FATAL_ERROR "Doxygen is needed to build the documentation.")
+ endif()
+
+ set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
+ set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+
+ configure_file(${doxyfile_in} ${doxyfile} @ONLY)
+
+ add_custom_target(doc
+ COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Generating API documentation with Doxygen"
+ VERBATIM)
+
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc)
+endif()
diff --git a/Doxyfile.in b/Doxyfile.in
new file mode 100644
index 0000000..7339517
--- /dev/null
+++ b/Doxyfile.in
@@ -0,0 +1,9 @@
+PROJECT_NAME = "@CMAKE_PROJECT_NAME@"
+PROJECT_NUMBER = @CN_VERSION@
+STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ \
+ @PROJECT_BINARY_DIR@
+INPUT = @PROJECT_SOURCE_DIR@/README.md \
+ @PROJECT_SOURCE_DIR@/include
+FILE_PATTERNS = *.h
+RECURSIVE = YES
+USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..df377b1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Carsten Bormann <cabo@tzi.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5820858
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+[![Build Status](https://travis-ci.org/cabo/cn-cbor.png?branch=master)](https://travis-ci.org/cabo/cn-cbor)
+
+# cn-cbor: A constrained node implementation of CBOR in C
+
+This is a constrained node implementation of [CBOR](http://cbor.io) in
+C that I threw together in 2013, before the publication of
+[RFC 7049](http://tools.ietf.org/html/rfc7049), to validate certain
+implementability considerations.
+
+Its API model was inspired by
+[nxjson](https://bitbucket.org/yarosla/nxjson). It turns out that
+this API model actually works even better with the advantages of the
+CBOR format.
+
+This code has been used in a number of research implementations on
+constrained nodes, with resulting code sizes appreciably under 1 KiB
+on ARM platforms.
+
+I always meant to improve the interface some more with certain API
+changes, in order to get even closer to 0.5 KiB, but I ran out of
+time. So here it is. If I do get around to making these changes, the
+API will indeed change a bit, so please be forewarned.
+
+## Building
+
+There is a `Simple-Makefile` for playing around, as well as a complete
+[`cmake`](http://www.cmake.org)-based build environment.
+(You can choose what fits your needs better.)
+
+Building with `cmake`:
+
+ ./build.sh
+
+Building including testing:
+
+ ./build.sh all test
+
+Generating a test coverage report (requires lcov[^1]; result in `build/lcov/index.html`):
+
+ ./build.sh all coveralls coverage_report
+
+License: MIT
+
+[^1]: Installation with homebrew: `brew install lcov`
diff --git a/Simple-Makefile b/Simple-Makefile
new file mode 100644
index 0000000..51e877d
--- /dev/null
+++ b/Simple-Makefile
@@ -0,0 +1,25 @@
+# enable this for armv7 builds, lazily using iPhone SDK
+#CFLAGS = -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include -arch armv7 -Os
+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
+
+all: cntest
+
+test: cntest
+ (cd test; env MallocStackLogging=true ../cntest) >new.out
+ -diff new.out test/expected.out
+
+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
+ clang $(CFLAGS) src/cn-cbor.c src/cn-error.c src/cn-get.c test/test.c -o cntest
+
+size: cn-cbor.o
+ size cn-cbor.o
+ size -m cn-cbor.o
+
+cn-cbor.o: src/cn-cbor.c include/cn-cbor/cn-cbor.h src/cbor.h
+ clang $(CFLAGS) -c src/cn-cbor.c
+
+cn-cbor-play.zip: Makefile src/cbor.h src/cn-cbor.c include/cn-cbor/cn-cbor.h test/expected.out test/test.c
+ zip $@ $^
+
+clean:
+ $(RM) cntest *.o new.out cn-cbor-play.zip
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..69dd2e9
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+if [ ! -d "build" ]; then
+ mkdir build
+fi
+cd build && cmake .. && make $*
diff --git a/cmake/Coveralls.cmake b/cmake/Coveralls.cmake
new file mode 100644
index 0000000..d60adba
--- /dev/null
+++ b/cmake/Coveralls.cmake
@@ -0,0 +1,119 @@
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
+#
+
+
+#
+# Param _COVERAGE_SRCS A list of source files that coverage should be collected for.
+# Param _COVERALLS_UPLOAD Upload the result to coveralls?
+#
+function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD)
+
+ if (ARGC GREATER 2)
+ set(_CMAKE_SCRIPT_PATH ${ARGN})
+ message("Coveralls: Using alternate CMake script dir: ${_CMAKE_SCRIPT_PATH}")
+ else()
+ set(_CMAKE_SCRIPT_PATH ${PROJECT_SOURCE_DIR}/cmake)
+ endif()
+
+ if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
+ message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
+ endif()
+
+ if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
+ message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
+ endif()
+
+ # When passing a CMake list to an external process, the list
+ # will be converted from the format "1;2;3" to "1 2 3".
+ # This means the script we're calling won't see it as a list
+ # of sources, but rather just one long path. We remedy this
+ # by replacing ";" with "*" and then reversing that in the script
+ # that we're calling.
+ # http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html
+ set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS})
+ set(COVERAGE_SRCS "")
+ foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP})
+ set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}")
+ endforeach()
+
+ #message("Coverage sources: ${COVERAGE_SRCS}")
+ set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json)
+
+ add_custom_target(coveralls_generate
+
+ # Zero the coverage counters.
+ COMMAND ${CMAKE_COMMAND}
+ -P "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake"
+
+ # Run regress tests.
+ COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
+
+ # Generate Gcov and translate it into coveralls JSON.
+ # We do this by executing an external CMake script.
+ # (We don't want this to run at CMake generation time, but after compilation and everything has run).
+ COMMAND ${CMAKE_COMMAND}
+ -DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c"
+ -DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}"
+ -DCOV_PATH="${PROJECT_BINARY_DIR}"
+ -DPROJECT_ROOT="${PROJECT_SOURCE_DIR}"
+ -P "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake"
+
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ COMMENT "Generating coveralls output..."
+ )
+
+ if (_COVERALLS_UPLOAD)
+ message("COVERALLS UPLOAD: ON")
+
+ find_program(CURL_EXECUTABLE curl)
+
+ if (NOT CURL_EXECUTABLE)
+ message(FATAL_ERROR "Coveralls: curl not found! Aborting")
+ endif()
+
+ add_custom_target(coveralls_upload
+ # Upload the JSON to coveralls.
+ COMMAND ${CURL_EXECUTABLE}
+ -S -F json_file=@${COVERALLS_FILE}
+ https://coveralls.io/api/v1/jobs
+
+ DEPENDS coveralls_generate
+
+ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+ COMMENT "Uploading coveralls output...")
+
+ add_custom_target(coveralls DEPENDS coveralls_upload)
+ else()
+ message("COVERALLS UPLOAD: OFF")
+ add_custom_target(coveralls DEPENDS coveralls_generate)
+ endif()
+
+endfunction()
+
+macro(coveralls_turn_on_coverage)
+ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug")
+ endif()
+
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
+endmacro()
diff --git a/cmake/CoverallsClear.cmake b/cmake/CoverallsClear.cmake
new file mode 100644
index 0000000..f6b0ace
--- /dev/null
+++ b/cmake/CoverallsClear.cmake
@@ -0,0 +1,26 @@
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
+#
+
+message ( "Clearing coverage data" )
+file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/*.gcda)
+file(REMOVE ${PROJECT_BINARY_DIR}/*.gcov)
+file(REMOVE ${PROJECT_BINARY_DIR}/*.gcov_tmp)
diff --git a/cmake/CoverallsGenerateGcov.cmake b/cmake/CoverallsGenerateGcov.cmake
new file mode 100644
index 0000000..2c26bd9
--- /dev/null
+++ b/cmake/CoverallsGenerateGcov.cmake
@@ -0,0 +1,429 @@
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
+#
+# This is intended to be run by a custom target in a CMake project like this.
+# 0. Compile program with coverage support.
+# 1. Clear coverage data. (Recursively delete *.gcda in build dir)
+# 2. Run the unit tests.
+# 3. Run this script specifying which source files the coverage should be performed on.
+#
+# This script will then use gcov to generate .gcov files in the directory specified
+# via the COV_PATH var. This should probably be the same as your cmake build dir.
+#
+# It then parses the .gcov files to convert them into the Coveralls JSON format:
+# https://coveralls.io/docs/api
+#
+# Example for running as standalone CMake script from the command line:
+# (Note it is important the -P is at the end...)
+# $ cmake -DCOV_PATH=$(pwd)
+# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
+# -P ../cmake/CoverallsGcovUpload.cmake
+#
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+
+#
+# Make sure we have the needed arguments.
+#
+if (NOT COVERALLS_OUTPUT_FILE)
+ message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
+endif()
+
+if (NOT COV_PATH)
+ message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
+endif()
+
+if (NOT COVERAGE_SRCS)
+ message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
+endif()
+
+if (NOT PROJECT_ROOT)
+ message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
+endif()
+
+# Since it's not possible to pass a CMake list properly in the
+# "1;2;3" format to an external process, we have replaced the
+# ";" with "*", so reverse that here so we get it back into the
+# CMake list format.
+string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
+
+find_program(GCOV_EXECUTABLE gcov)
+
+if (NOT GCOV_EXECUTABLE)
+ message(FATAL_ERROR "gcov not found! Aborting...")
+endif()
+
+find_package(Git)
+
+# TODO: Add these git things to the coveralls json.
+if (GIT_FOUND)
+ # Branch.
+ execute_process(
+ COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE GIT_BRANCH
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+ macro (git_log_format FORMAT_CHARS VAR_NAME)
+ execute_process(
+ COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE ${VAR_NAME}
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ endmacro()
+
+ git_log_format(an GIT_AUTHOR_EMAIL)
+ git_log_format(ae GIT_AUTHOR_EMAIL)
+ git_log_format(cn GIT_COMMITTER_NAME)
+ git_log_format(ce GIT_COMMITTER_EMAIL)
+ git_log_format(B GIT_COMMIT_MESSAGE)
+
+ message("Git exe: ${GIT_EXECUTABLE}")
+ message("Git branch: ${GIT_BRANCH}")
+ message("Git author: ${GIT_AUTHOR_NAME}")
+ message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
+ message("Git commiter name: ${GIT_COMMITTER_NAME}")
+ message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
+ message("Git commit message: ${GIT_COMMIT_MESSAGE}")
+
+endif()
+
+############################# Macros #########################################
+
+#
+# This macro converts from the full path format gcov outputs:
+#
+# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+#
+# to the original source file path the .gcov is for:
+#
+# /path/to/project/root/subdir/the_file.c
+#
+macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
+
+ # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+ # ->
+ # #path#to#project#root#subdir#the_file.c.gcov
+ get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
+
+ # #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
+ string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
+ string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
+ set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
+endmacro()
+
+##############################################################################
+
+# Get the coverage data.
+file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
+message("GCDA files:")
+
+# Get a list of all the object directories needed by gcov
+# (The directories the .gcda files and .o files are found in)
+# and run gcov on those.
+foreach(GCDA ${GCDA_FILES})
+ message("Process: ${GCDA}")
+ message("------------------------------------------------------------------------------")
+ get_filename_component(GCDA_DIR ${GCDA} PATH)
+
+ #
+ # The -p below refers to "Preserve path components",
+ # This means that the generated gcov filename of a source file will
+ # keep the original files entire filepath, but / is replaced with #.
+ # Example:
+ #
+ # /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
+ # ------------------------------------------------------------------------------
+ # File '/path/to/project/root/subdir/the_file.c'
+ # Lines executed:68.34% of 199
+ # /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
+ #
+ # If -p is not specified then the file is named only "the_file.c.gcov"
+ #
+ execute_process(
+ COMMAND ${GCOV_EXECUTABLE} -c -p -o ${GCDA_DIR} ${GCDA}
+ WORKING_DIRECTORY ${COV_PATH}
+ )
+endforeach()
+
+# TODO: Make these be absolute path
+file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
+
+# Get only the filenames to use for filtering.
+#set(COVERAGE_SRCS_NAMES "")
+#foreach (COVSRC ${COVERAGE_SRCS})
+# get_filename_component(COVSRC_NAME ${COVSRC} NAME)
+# message("${COVSRC} -> ${COVSRC_NAME}")
+# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
+#endforeach()
+
+#
+# Filter out all but the gcov files we want.
+#
+# We do this by comparing the list of COVERAGE_SRCS filepaths that the
+# user wants the coverage data for with the paths of the generated .gcov files,
+# so that we only keep the relevant gcov files.
+#
+# Example:
+# COVERAGE_SRCS =
+# /path/to/project/root/subdir/the_file.c
+#
+# ALL_GCOV_FILES =
+# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
+#
+# Result should be:
+# GCOV_FILES =
+# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+#
+set(GCOV_FILES "")
+#message("Look in coverage sources: ${COVERAGE_SRCS}")
+message("\nFilter out unwanted GCOV files:")
+message("===============================")
+
+set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
+
+foreach (GCOV_FILE ${ALL_GCOV_FILES})
+
+ #
+ # /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
+ # ->
+ # /path/to/project/root/subdir/the_file.c
+ get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
+
+ # Is this in the list of source files?
+ # TODO: We want to match against relative path filenames from the source file root...
+ list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
+
+ if (NOT WAS_FOUND EQUAL -1)
+ message("YES: ${GCOV_FILE}")
+ list(APPEND GCOV_FILES ${GCOV_FILE})
+
+ # We remove it from the list, so we don't bother searching for it again.
+ # Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
+ # have coverage data generated from them (no lines are covered).
+ list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
+ else()
+ message("NO: ${GCOV_FILE}")
+ endif()
+endforeach()
+
+# TODO: Enable setting these
+set(JSON_SERVICE_NAME "travis-ci")
+set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
+
+set(JSON_TEMPLATE
+"{
+ \"service_name\": \"\@JSON_SERVICE_NAME\@\",
+ \"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
+ \"source_files\": \@JSON_GCOV_FILES\@
+}"
+)
+
+set(SRC_FILE_TEMPLATE
+"{
+ \"name\": \"\@GCOV_SRC_REL_PATH\@\",
+ \"source_digest\": \"\@GCOV_CONTENTS_MD5\@\",
+ \"coverage\": \@GCOV_FILE_COVERAGE\@
+ }"
+)
+
+message("\nGenerate JSON for files:")
+message("=========================")
+
+set(JSON_GCOV_FILES "[")
+
+# Read the GCOV files line by line and get the coverage data.
+foreach (GCOV_FILE ${GCOV_FILES})
+
+ get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
+ file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
+
+ # The new coveralls API doesn't need the entire source (Yay!)
+ # However, still keeping that part for now. Will cleanup in the future.
+ file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
+ message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}")
+
+ # Loads the gcov file as a list of lines.
+ # (We first open the file and replace all occurences of [] with _
+ # because CMake will fail to parse a line containing unmatched brackets...
+ # also the \ to escaped \n in macros screws up things.)
+ # https://public.kitware.com/Bug/view.php?id=15369
+ file(READ ${GCOV_FILE} GCOV_CONTENTS)
+ string(REPLACE "[" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+ string(REPLACE "]" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+ string(REPLACE "\\" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
+ file(WRITE ${GCOV_FILE}_tmp "${GCOV_CONTENTS}")
+
+ file(STRINGS ${GCOV_FILE}_tmp GCOV_LINES)
+ list(LENGTH GCOV_LINES LINE_COUNT)
+
+ # Instead of trying to parse the source from the
+ # gcov file, simply read the file contents from the source file.
+ # (Parsing it from the gcov is hard because C-code uses ; in many places
+ # which also happens to be the same as the CMake list delimeter).
+ file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
+
+ string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ # According to http://json.org/ these should be escaped as well.
+ # Don't know how to do that in CMake however...
+ #string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ #string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+ #string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
+
+ # We want a json array of coverage data as a single string
+ # start building them from the contents of the .gcov
+ set(GCOV_FILE_COVERAGE "[")
+
+ set(GCOV_LINE_COUNT 1) # Line number for the .gcov.
+ set(DO_SKIP 0)
+ foreach (GCOV_LINE ${GCOV_LINES})
+ #message("${GCOV_LINE}")
+ # Example of what we're parsing:
+ # Hitcount |Line | Source
+ # " 8: 26: if (!allowed || (strlen(allowed) == 0))"
+ string(REGEX REPLACE
+ "^([^:]*):([^:]*):(.*)$"
+ "\\1;\\2;\\3"
+ RES
+ "${GCOV_LINE}")
+
+ # Check if we should exclude lines using the Lcov syntax.
+ string(REGEX MATCH "LCOV_EXCL_START" START_SKIP "${GCOV_LINE}")
+ string(REGEX MATCH "LCOV_EXCL_END" END_SKIP "${GCOV_LINE}")
+ string(REGEX MATCH "LCOV_EXCL_LINE" LINE_SKIP "${GCOV_LINE}")
+
+ set(RESET_SKIP 0)
+ if (LINE_SKIP AND NOT DO_SKIP)
+ set(DO_SKIP 1)
+ set(RESET_SKIP 1)
+ endif()
+
+ if (START_SKIP)
+ set(DO_SKIP 1)
+ message("${GCOV_LINE_COUNT}: Start skip")
+ endif()
+
+ if (END_SKIP)
+ set(DO_SKIP 0)
+ endif()
+
+ list(LENGTH RES RES_COUNT)
+
+ if (RES_COUNT GREATER 2)
+ list(GET RES 0 HITCOUNT)
+ list(GET RES 1 LINE)
+ list(GET RES 2 SOURCE)
+
+ string(STRIP ${HITCOUNT} HITCOUNT)
+ string(STRIP ${LINE} LINE)
+
+ # Lines with 0 line numbers are metadata and can be ignored.
+ if (NOT ${LINE} EQUAL 0)
+
+ if (DO_SKIP)
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
+ else()
+ # Translate the hitcount into valid JSON values.
+ if (${HITCOUNT} STREQUAL "#####")
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
+ elseif (${HITCOUNT} STREQUAL "-")
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
+ else()
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
+ endif()
+ endif()
+ endif()
+ else()
+ message(WARNING "Failed to properly parse line (RES_COUNT = ${RES_COUNT}) ${GCOV_FILE}:${GCOV_LINE_COUNT}\n-->${GCOV_LINE}")
+ endif()
+
+ if (RESET_SKIP)
+ set(DO_SKIP 0)
+ endif()
+ math(EXPR GCOV_LINE_COUNT "${GCOV_LINE_COUNT}+1")
+ endforeach()
+
+ message("${GCOV_LINE_COUNT} of ${LINE_COUNT} lines read!")
+
+ # Advanced way of removing the trailing comma in the JSON array.
+ # "[1, 2, 3, " -> "[1, 2, 3"
+ string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
+
+ # Append the trailing ] to complete the JSON array.
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
+
+ # Generate the final JSON for this file.
+ message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
+ string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
+
+ set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
+endforeach()
+
+# Loop through all files we couldn't find any coverage for
+# as well, and generate JSON for those as well with 0% coverage.
+foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
+
+ # Loads the source file as a list of lines.
+ file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
+
+ set(GCOV_FILE_COVERAGE "[")
+ set(GCOV_FILE_SOURCE "")
+
+ foreach (SOURCE ${SRC_LINES})
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
+
+ string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
+ string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
+ string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
+ string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
+ set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
+ endforeach()
+
+ # Remove trailing comma, and complete JSON array with ]
+ string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
+ set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
+
+ # Generate the final JSON for this file.
+ message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
+ string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
+ set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
+endforeach()
+
+# Get rid of trailing comma.
+string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
+set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
+
+# Generate the final complete JSON!
+message("Generate final JSON...")
+string(CONFIGURE ${JSON_TEMPLATE} JSON)
+
+file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
+message("###########################################################################")
+message("Generated coveralls JSON containing coverage data:")
+message("${COVERALLS_OUTPUT_FILE}")
+message("###########################################################################")
diff --git a/cmake/LCov.cmake b/cmake/LCov.cmake
new file mode 100644
index 0000000..1ac9ec3
--- /dev/null
+++ b/cmake/LCov.cmake
@@ -0,0 +1,12 @@
+FIND_PROGRAM( LCOV_PATH lcov )
+FIND_PROGRAM( GENHTML_PATH genhtml )
+
+if (LCOV_PATH)
+ # message ( "lcov: ${LCOV_PATH}" )
+
+ add_custom_target(coverage_report
+ 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
+ COMMAND "${GENHTML_PATH}" --rc genhtml_branch_coverage=1 --output-directory lcov ${PROJECT_NAME}.info
+ COMMAND echo "Coverage report in: file://${CMAKE_BINARY_DIR}/lcov/index.html"
+ )
+endif()
diff --git a/cn-cbor.pc.in b/cn-cbor.pc.in
new file mode 100644
index 0000000..0ede53a
--- /dev/null
+++ b/cn-cbor.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: cn-cbor
+Description: A constrained node implementation of CBOR in C
+URL: https://github.com/cabo/cn-cbor
+Version: @CN_VERSION@
+Libs: -L${libdir} -lcn-cbor
+Cflags: -I${includedir}
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..898676a
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,2 @@
+install ( DIRECTORY ../include DESTINATION .
+ PATTERN CMakeLists.txt EXCLUDE )
diff --git a/include/cn-cbor/cn-cbor.h b/include/cn-cbor/cn-cbor.h
new file mode 100644
index 0000000..bf71af8
--- /dev/null
+++ b/include/cn-cbor/cn-cbor.h
@@ -0,0 +1,401 @@
+/**
+ * \file
+ * \brief
+ * CBOR parsing
+ */
+
+#ifndef CN_CBOR_H
+#define CN_CBOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+/**
+ * All of the different kinds of CBOR values.
+ */
+typedef enum cn_cbor_type {
+ /** false */
+ CN_CBOR_FALSE,
+ /** true */
+ CN_CBOR_TRUE,
+ /** null */
+ CN_CBOR_NULL,
+ /** undefined */
+ CN_CBOR_UNDEF,
+ /** Positive integers */
+ CN_CBOR_UINT,
+ /** Negative integers */
+ CN_CBOR_INT,
+ /** Byte string */
+ CN_CBOR_BYTES,
+ /** UTF-8 string */
+ CN_CBOR_TEXT,
+ /** Byte string, in chunks. Each chunk is a child. */
+ CN_CBOR_BYTES_CHUNKED,
+ /** UTF-8 string, in chunks. Each chunk is a child */
+ CN_CBOR_TEXT_CHUNKED,
+ /** Array of CBOR values. Each array element is a child, in order */
+ CN_CBOR_ARRAY,
+ /** Map of key/value pairs. Each key and value is a child, alternating. */
+ CN_CBOR_MAP,
+ /** Tag describing the next value. The next value is the single child. */
+ CN_CBOR_TAG,
+ /** Simple value, other than the defined ones */
+ CN_CBOR_SIMPLE,
+ /** Doubles, floats, and half-floats */
+ CN_CBOR_DOUBLE,
+ /** An error has occurred */
+ CN_CBOR_INVALID
+} cn_cbor_type;
+
+/**
+ * Flags used during parsing. Not useful for consumers of the
+ * `cn_cbor` structure.
+ */
+typedef enum cn_cbor_flags {
+ /** The count field will be used for parsing */
+ CN_CBOR_FL_COUNT = 1,
+ /** An indefinite number of children */
+ CN_CBOR_FL_INDEF = 2,
+ /** Not used yet; the structure must free the v.str pointer when the
+ structure is freed */
+ CN_CBOR_FL_OWNER = 0x80, /* of str */
+} cn_cbor_flags;
+
+/**
+ * A CBOR value
+ */
+typedef struct cn_cbor {
+ /** The type of value */
+ cn_cbor_type type;
+ /** Flags used at parse time */
+ cn_cbor_flags flags;
+ /** Data associated with the value; different branches of the union are
+ used depending on the `type` field. */
+ union {
+ /** CN_CBOR_BYTES */
+ const uint8_t* bytes;
+ /** CN_CBOR_TEXT */
+ const char* str;
+ /** CN_CBOR_INT */
+ long sint;
+ /** CN_CBOR_UINT */
+ unsigned long uint;
+ /** CN_CBOR_DOUBLE */
+ double dbl;
+ /** for use during parsing */
+ unsigned long count;
+ } v; /* TBD: optimize immediate */
+ /** Number of children.
+ * @note: for maps, this is 2x the number of entries */
+ int length;
+ /** The first child value */
+ struct cn_cbor* first_child;
+ /** The last child value */
+ struct cn_cbor* last_child;
+ /** The sibling after this one, or NULL if this is the last */
+ struct cn_cbor* next;
+ /** The parent of this value, or NULL if this is the root */
+ struct cn_cbor* parent;
+} cn_cbor;
+
+/**
+ * All of the different kinds of errors
+ */
+typedef enum cn_cbor_error {
+ /** No error has occurred */
+ CN_CBOR_NO_ERROR,
+ /** More data was expected while parsing */
+ CN_CBOR_ERR_OUT_OF_DATA,
+ /** Some extra data was left over at the end of parsing */
+ CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED,
+ /** A map should be alternating keys and values. A break was found
+ when a value was expected */
+ CN_CBOR_ERR_ODD_SIZE_INDEF_MAP,
+ /** A break was found where it wasn't expected */
+ CN_CBOR_ERR_BREAK_OUTSIDE_INDEF,
+ /** Indefinite encoding works for bstrs, strings, arrays, and maps.
+ A different major type tried to use it. */
+ CN_CBOR_ERR_MT_UNDEF_FOR_INDEF,
+ /** Additional Information values 28-30 are reserved */
+ CN_CBOR_ERR_RESERVED_AI,
+ /** A chunked encoding was used for a string or bstr, and one of the elements
+ wasn't the expected (string/bstr) type */
+ CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING,
+ /** An invalid parameter was passed to a function */
+ CN_CBOR_ERR_INVALID_PARAMETER,
+ /** Allocation failed */
+ CN_CBOR_ERR_OUT_OF_MEMORY,
+ /** A float was encountered during parse but the library was built without
+ support for float types. */
+ CN_CBOR_ERR_FLOAT_NOT_SUPPORTED
+} cn_cbor_error;
+
+/**
+ * Strings matching the `cn_cbor_error` conditions.
+ *
+ * @todo: turn into a function to make the type safety more clear?
+ */
+extern const char *cn_cbor_error_str[];
+
+/**
+ * Errors
+ */
+typedef struct cn_cbor_errback {
+ /** The position in the input where the erorr happened */
+ int pos;
+ /** The error, or CN_CBOR_NO_ERROR if none */
+ cn_cbor_error err;
+} cn_cbor_errback;
+
+#ifdef USE_CBOR_CONTEXT
+
+/**
+ * Allocate and zero out memory. `count` elements of `size` are required,
+ * as for `calloc(3)`. The `context` is the `cn_cbor_context` passed in
+ * earlier to the CBOR routine.
+ *
+ * @param[in] count The number of items to allocate
+ * @param[in] size The size of each item
+ * @param[in] context The allocation context
+ */
+typedef void* (*cn_calloc_func)(size_t count, size_t size, void *context);
+
+/**
+ * Free memory previously allocated with a context. If using a pool allocator,
+ * this function will often be a no-op, but it must be supplied in order to
+ * prevent the CBOR library from calling `free(3)`.
+ *
+ * @note: it may be that this is never needed; if so, it will be removed for
+ * clarity and speed.
+ *
+ * @param context [description]
+ * @return [description]
+ */
+typedef void (*cn_free_func)(void *ptr, void *context);
+
+/**
+ * The allocation context.
+ */
+typedef struct cn_cbor_context {
+ /** The pool `calloc` routine. Must allocate and zero. */
+ cn_calloc_func calloc_func;
+ /** The pool `free` routine. Often a no-op, but required. */
+ cn_free_func free_func;
+ /** Typically, the pool object, to be used when calling `calloc_func`
+ * and `free_func` */
+ void *context;
+} cn_cbor_context;
+
+/** When USE_CBOR_CONTEXT is defined, many functions take an extra `context`
+ * parameter */
+#define CBOR_CONTEXT , cn_cbor_context *context
+/** When USE_CBOR_CONTEXT is defined, some functions take an extra `context`
+ * parameter at the beginning */
+#define CBOR_CONTEXT_COMMA cn_cbor_context *context,
+
+#else
+
+#define CBOR_CONTEXT
+#define CBOR_CONTEXT_COMMA
+
+#endif
+
+/**
+ * Decode an array of CBOR bytes into structures.
+ *
+ * @param[in] buf The array of bytes to parse
+ * @param[in] len The number of bytes in the array
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The parsed CBOR structure, or NULL on error
+ */
+cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp);
+
+/**
+ * Get a value from a CBOR map that has the given string as a key.
+ *
+ * @param[in] cb The CBOR map
+ * @param[in] key The string to look up in the map
+ * @return The matching value, or NULL if the key is not found
+ */
+cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key);
+
+/**
+ * Get a value from a CBOR map that has the given integer as a key.
+ *
+ * @param[in] cb The CBOR map
+ * @param[in] key The int to look up in the map
+ * @return The matching value, or NULL if the key is not found
+ */
+cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key);
+
+/**
+ * Get the item with the given index from a CBOR array.
+ *
+ * @param[in] cb The CBOR map
+ * @param[in] idx The array index
+ * @return The matching value, or NULL if the index is invalid
+ */
+cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx);
+
+/**
+ * Free the given CBOR structure.
+ * You MUST NOT try to free a cn_cbor structure with a parent (i.e., one
+ * that is not a root in the tree).
+ *
+ * @param[in] cb The CBOR value to free. May be NULL, or a root object.
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ */
+void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT);
+
+/**
+ * Write a CBOR value and all of the child values.
+ *
+ * @param[in] buf The buffer into which to write
+ * @param[in] buf_offset The offset (in bytes) from the beginning of the buffer
+ * to start writing at
+ * @param[in] buf_size The total length (in bytes) of the buffer
+ * @param[in] cb [description]
+ * @return -1 on fail, or number of bytes written
+ */
+ssize_t cn_cbor_encoder_write(uint8_t *buf,
+ size_t buf_offset,
+ size_t buf_size,
+ const cn_cbor *cb);
+
+/**
+ * Create a CBOR map.
+ *
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created map, or NULL on error
+ */
+cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp);
+
+/**
+ * Create a CBOR byte string. The data in the byte string is *not* owned
+ * by the CBOR object, so it is not freed automatically.
+ *
+ * @param[in] data The data
+ * @param[in] len The number of bytes of data
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+cn_cbor* cn_cbor_data_create(const uint8_t* data, int len
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp);
+
+/**
+ * Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness.
+ * The data being stored in the string is *not* owned the CBOR object, so it is
+ * not freed automatically.
+ *
+ * @note: Do NOT use this function with untrusted data. It calls strlen, and
+ * relies on proper NULL-termination.
+ *
+ * @param[in] data NULL-terminated UTF-8 string
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+cn_cbor* cn_cbor_string_create(const char* data
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp);
+
+/**
+ * Create a CBOR integer (either positive or negative).
+ *
+ * @param[in] value the value of the integer
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+cn_cbor* cn_cbor_int_create(int64_t value
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp);
+
+/**
+ * Put a CBOR object into a map with a CBOR object key. Duplicate checks are NOT
+ * currently performed.
+ *
+ * @param[in] cb_map The map to insert into
+ * @param[in] key The key
+ * @param[in] cb_value The value
+ * @param[out] errp Error
+ * @return True on success
+ */
+bool cn_cbor_map_put(cn_cbor* cb_map,
+ cn_cbor *cb_key, cn_cbor *cb_value,
+ cn_cbor_errback *errp);
+
+/**
+ * Put a CBOR object into a map with an integer key. Duplicate checks are NOT
+ * currently performed.
+ *
+ * @param[in] cb_map The map to insert into
+ * @param[in] key The integer key
+ * @param[in] cb_value The value
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error
+ * @return True on success
+ */
+bool cn_cbor_mapput_int(cn_cbor* cb_map,
+ int64_t key, cn_cbor* cb_value
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp);
+
+/**
+ * Put a CBOR object into a map with a string key. Duplicate checks are NOT
+ * currently performed.
+ *
+ * @note: do not call this routine with untrusted string data. It calls
+ * strlen, and requires a properly NULL-terminated key.
+ *
+ * @param[in] cb_map The map to insert into
+ * @param[in] key The string key
+ * @param[in] cb_value The value
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error
+ * @return True on success
+ */
+bool cn_cbor_mapput_string(cn_cbor* cb_map,
+ const char* key, cn_cbor* cb_value
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp);
+
+/**
+ * Create a CBOR array
+ *
+ * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined)
+ * @param[out] errp Error, if NULL is returned
+ * @return The created object, or NULL on error
+ */
+cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp);
+
+/**
+ * Append an item to the end of a CBOR array.
+ *
+ * @param[in] cb_array The array into which to insert
+ * @param[in] cb_value The value to insert
+ * @param[out] errp Error
+ * @return True on success
+ */
+bool cn_cbor_array_append(cn_cbor* cb_array,
+ cn_cbor* cb_value,
+ cn_cbor_errback *errp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CN_CBOR_H */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..ceb0608
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,50 @@
+#
+#
+# compiling/installing sources for cn-cbor
+
+set ( cbor_srcs
+ cn-cbor.c
+ cn-create.c
+ cn-encoder.c
+ cn-error.c
+ cn-get.c
+)
+
+if (use_context)
+ add_definitions(-DUSE_CBOR_CONTEXT)
+endif()
+add_library ( cn-cbor SHARED ${cbor_srcs} )
+target_include_directories ( cn-cbor PUBLIC ../include )
+target_include_directories ( cn-cbor PRIVATE ../src )
+
+install ( TARGETS cn-cbor
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+ RUNTIME DESTINATION bin)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)
+if (coveralls)
+ include(Coveralls)
+ coveralls_turn_on_coverage()
+
+ set(COVERAGE_SRCS "")
+ foreach (S ${cbor_srcs})
+ get_filename_component(S_ABS ${S} ABSOLUTE)
+ list (APPEND COVERAGE_SRCS ${S_ABS})
+ endforeach()
+
+ # Create the coveralls target.
+ coveralls_setup(
+ "${COVERAGE_SRCS}"
+ ${coveralls_send} # If we should upload.
+ )
+
+ #add_dependencies(coveralls, all)
+endif()
+
+add_custom_target(size
+ COMMAND echo "${CMAKE_BINARY_DIR}/src/CMakeFiles/cn-cbor.dir/cn-cbor.c.o"
+ COMMAND size "${CMAKE_BINARY_DIR}/src/CMakeFiles/cn-cbor.dir/cn-cbor.c.o"
+ COMMAND size -m "${CMAKE_BINARY_DIR}/src/CMakeFiles/cn-cbor.dir/cn-cbor.c.o"
+ DEPENDS cn-cbor
+COMMENT "Output the size of the parse routine")
diff --git a/src/cbor.h b/src/cbor.h
new file mode 100644
index 0000000..1859f09
--- /dev/null
+++ b/src/cbor.h
@@ -0,0 +1,118 @@
+#ifndef CBOR_PROTOCOL_H__
+#define CBOR_PROTOCOL_H__
+
+/* The 8 major types */
+#define MT_UNSIGNED 0
+#define MT_NEGATIVE 1
+#define MT_BYTES 2
+#define MT_TEXT 3
+#define MT_ARRAY 4
+#define MT_MAP 5
+#define MT_TAG 6
+#define MT_PRIM 7
+
+/* The initial bytes resulting from those */
+#define IB_UNSIGNED (MT_UNSIGNED << 5)
+#define IB_NEGATIVE (MT_NEGATIVE << 5)
+#define IB_BYTES (MT_BYTES << 5)
+#define IB_TEXT (MT_TEXT << 5)
+#define IB_ARRAY (MT_ARRAY << 5)
+#define IB_MAP (MT_MAP << 5)
+#define IB_TAG (MT_TAG << 5)
+#define IB_PRIM (MT_PRIM << 5)
+
+#define IB_NEGFLAG (IB_NEGATIVE - IB_UNSIGNED)
+#define IB_NEGFLAG_AS_BIT(ib) ((ib) >> 5)
+#define IB_TEXTFLAG (IB_TEXT - IB_BYTES)
+
+#define IB_AI(ib) ((ib) & 0x1F)
+#define IB_MT(ib) ((ib) >> 5)
+
+/* Tag numbers handled by this implementation */
+#define TAG_TIME_EPOCH 1
+#define TAG_BIGNUM 2
+#define TAG_BIGNUM_NEG 3
+#define TAG_URI 32
+#define TAG_RE 35
+
+/* Initial bytes of those tag numbers */
+#define IB_TIME_EPOCH (IB_TAG | TAG_TIME_EPOCH)
+#define IB_BIGNUM (IB_TAG | TAG_BIGNUM)
+#define IB_BIGNUM_NEG (IB_TAG | TAG_BIGNUM_NEG)
+/* TAG_URI and TAG_RE are non-immediate tags */
+
+/* Simple values handled by this implementation */
+#define VAL_FALSE 20
+#define VAL_TRUE 21
+#define VAL_NIL 22
+#define VAL_UNDEF 23
+
+/* Initial bytes of those simple values */
+#define IB_FALSE (IB_PRIM | VAL_FALSE)
+#define IB_TRUE (IB_PRIM | VAL_TRUE)
+#define IB_NIL (IB_PRIM | VAL_NIL)
+#define IB_UNDEF (IB_PRIM | VAL_UNDEF)
+
+/* AI values with more data in head */
+#define AI_1 24
+#define AI_2 25
+#define AI_4 26
+#define AI_8 27
+#define AI_INDEF 31
+#define IB_BREAK (IB_PRIM | AI_INDEF)
+/* For */
+#define IB_UNUSED (IB_TAG | AI_INDEF)
+
+/* Floating point initial bytes */
+#define IB_FLOAT2 (IB_PRIM | AI_2)
+#define IB_FLOAT4 (IB_PRIM | AI_4)
+#define IB_FLOAT8 (IB_PRIM | AI_8)
+
+// These definitions are here because they aren't required for the public
+// interface, and they were quite confusing in cn-cbor.h
+
+#ifdef USE_CBOR_CONTEXT
+/**
+ * Allocate enough space for 1 `cn_cbor` structure.
+ *
+ * @param[in] ctx The allocation context, or NULL for calloc.
+ * @return A pointer to a `cn_cbor` or NULL on failure
+ */
+#define CN_CALLOC(ctx) ((ctx) && (ctx)->calloc_func) ? \
+ (ctx)->calloc_func(1, sizeof(cn_cbor), (ctx)->context) : \
+ calloc(1, sizeof(cn_cbor));
+
+/**
+ * Free a
+ * @param free_func [description]
+ * @return [description]
+ */
+#define CN_FREE(ptr, ctx) ((ctx) && (ctx)->free_func) ? \
+ (ctx)->free_func((ptr), (ctx)->context) : \
+ free((ptr));
+
+#define CBOR_CONTEXT_PARAM , context
+#define CN_CALLOC_CONTEXT() CN_CALLOC(context)
+#define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p, context)
+
+#else
+
+#define CBOR_CONTEXT_PARAM
+#define CN_CALLOC_CONTEXT() CN_CALLOC
+#define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p)
+
+#ifndef CN_CALLOC
+#define CN_CALLOC calloc(1, sizeof(cn_cbor))
+#endif
+
+#ifndef CN_FREE
+#define CN_FREE free
+#endif
+
+#endif // USE_CBOR_CONTEXT
+
+#ifndef UNUSED_PARAM
+#define UNUSED_PARAM(p) ((void)&(p))
+#endif
+
+#endif // CBOR_PROTOCOL_H__
diff --git a/src/cn-cbor.c b/src/cn-cbor.c
new file mode 100644
index 0000000..a7677ae
--- /dev/null
+++ b/src/cn-cbor.c
@@ -0,0 +1,272 @@
+#ifndef CN_CBOR_C
+#define CN_CBOR_C
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <arpa/inet.h> // needed for ntohl (e.g.) on Linux
+
+#include "cn-cbor/cn-cbor.h"
+#include "cbor.h"
+
+#define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0)
+
+void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) {
+ cn_cbor* p = cb;
+ assert(!p || !p->parent);
+ while (p) {
+ cn_cbor* p1;
+ while ((p1 = p->first_child)) { /* go down */
+ p = p1;
+ }
+ if (!(p1 = p->next)) { /* go up next */
+ if ((p1 = p->parent))
+ p1->first_child = 0;
+ }
+ CN_CBOR_FREE_CONTEXT(p);
+ p = p1;
+ }
+}
+
+#ifndef CBOR_NO_FLOAT
+static double decode_half(int half) {
+ int exp = (half >> 10) & 0x1f;
+ int mant = half & 0x3ff;
+ double val;
+ if (exp == 0) val = ldexp(mant, -24);
+ else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
+ else val = mant == 0 ? INFINITY : NAN;
+ return half & 0x8000 ? -val : val;
+}
+#endif /* CBOR_NO_FLOAT */
+
+/* Fix these if you can't do non-aligned reads */
+#define ntoh8p(p) (*(unsigned char*)(p))
+#define ntoh16p(p) (ntohs(*(unsigned short*)(p)))
+#define ntoh32p(p) (ntohl(*(unsigned long*)(p)))
+static uint64_t ntoh64p(unsigned char *p) {
+ uint64_t ret = ntoh32p(p);
+ ret <<= 32;
+ ret += ntoh32p(p+4);
+ return ret;
+}
+
+static cn_cbor_type mt_trans[] = {
+ CN_CBOR_UINT, CN_CBOR_INT,
+ CN_CBOR_BYTES, CN_CBOR_TEXT,
+ CN_CBOR_ARRAY, CN_CBOR_MAP,
+ CN_CBOR_TAG, CN_CBOR_SIMPLE,
+};
+
+struct parse_buf {
+ unsigned char *buf;
+ unsigned char *ebuf;
+ cn_cbor_error err;
+};
+
+#define TAKE(pos, ebuf, n, stmt) \
+ if (n > (size_t)(ebuf - pos)) \
+ CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \
+ stmt; \
+ pos += n;
+
+static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) {
+ unsigned char *pos = pb->buf;
+ unsigned char *ebuf = pb->ebuf;
+ cn_cbor* parent = top_parent;
+ int ib;
+ unsigned int mt;
+ int ai;
+ uint64_t val;
+ cn_cbor* cb = NULL;
+#ifndef CBOR_NO_FLOAT
+ union {
+ float f;
+ uint32_t u;
+ } u32;
+ union {
+ double d;
+ uint64_t u;
+ } u64;
+#endif /* CBOR_NO_FLOAT */
+
+again:
+ TAKE(pos, ebuf, 1, ib = ntoh8p(pos) );
+ if (ib == IB_BREAK) {
+ if (!(parent->flags & CN_CBOR_FL_INDEF))
+ CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF);
+ switch (parent->type) {
+ case CN_CBOR_BYTES: case CN_CBOR_TEXT:
+ parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */
+ break;
+ case CN_CBOR_MAP:
+ if (parent->length & 1)
+ CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP);
+ default:;
+ }
+ goto complete;
+ }
+ mt = ib >> 5;
+ ai = ib & 0x1f;
+ val = ai;
+
+ cb = CN_CALLOC_CONTEXT();
+ if (!cb)
+ CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY);
+
+ cb->type = mt_trans[mt];
+
+ cb->parent = parent;
+ if (parent->last_child) {
+ parent->last_child->next = cb;
+ } else {
+ parent->first_child = cb;
+ }
+ parent->last_child = cb;
+ parent->length++;
+
+ switch (ai) {
+ case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos)) ; break;
+ case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; break;
+ case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; break;
+ case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; break;
+ case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI);
+ case AI_INDEF:
+ if ((mt - MT_BYTES) <= MT_MAP) {
+ cb->flags |= CN_CBOR_FL_INDEF;
+ goto push;
+ } else {
+ CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF);
+ }
+ }
+ // process content
+ switch (mt) {
+ case MT_UNSIGNED:
+ cb->v.uint = val; /* to do: Overflow check */
+ break;
+ case MT_NEGATIVE:
+ cb->v.sint = ~val; /* to do: Overflow check */
+ break;
+ case MT_BYTES: case MT_TEXT:
+ cb->v.str = (char *) pos;
+ cb->length = val;
+ TAKE(pos, ebuf, val, ;);
+ break;
+ case MT_MAP:
+ val <<= 1;
+ /* fall through */
+ case MT_ARRAY:
+ if ((cb->v.count = val)) {
+ cb->flags |= CN_CBOR_FL_COUNT;
+ goto push;
+ }
+ break;
+ case MT_TAG:
+ cb->v.uint = val;
+ goto push;
+ case MT_PRIM:
+ switch (ai) {
+ case VAL_FALSE: cb->type = CN_CBOR_FALSE; break;
+ case VAL_TRUE: cb->type = CN_CBOR_TRUE; break;
+ case VAL_NIL: cb->type = CN_CBOR_NULL; break;
+ case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break;
+ case AI_2:
+#ifndef CBOR_NO_FLOAT
+ cb->type = CN_CBOR_DOUBLE;
+ cb->v.dbl = decode_half(val);
+#else /* CBOR_NO_FLOAT */
+ CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /* CBOR_NO_FLOAT */
+ break;
+ case AI_4:
+#ifndef CBOR_NO_FLOAT
+ cb->type = CN_CBOR_DOUBLE;
+ u32.u = val;
+ cb->v.dbl = u32.f;
+#else /* CBOR_NO_FLOAT */
+ CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /* CBOR_NO_FLOAT */
+ break;
+ case AI_8:
+#ifndef CBOR_NO_FLOAT
+ cb->type = CN_CBOR_DOUBLE;
+ u64.u = val;
+ cb->v.dbl = u64.d;
+#else /* CBOR_NO_FLOAT */
+ CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+#endif /* CBOR_NO_FLOAT */
+ break;
+ default: cb->v.uint = val;
+ }
+ }
+fill: /* emulate loops */
+ if (parent->flags & CN_CBOR_FL_INDEF) {
+ if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT)
+ if (cb->type != parent->type)
+ CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING);
+ goto again;
+ }
+ if (parent->flags & CN_CBOR_FL_COUNT) {
+ if (--parent->v.count)
+ goto again;
+ }
+ /* so we are done filling parent. */
+complete: /* emulate return from call */
+ if (parent == top_parent) {
+ if (pos != ebuf) /* XXX do this outside */
+ CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED);
+ pb->buf = pos;
+ return cb;
+ }
+ cb = parent;
+ parent = parent->parent;
+ goto fill;
+push: /* emulate recursive call */
+ parent = cb;
+ goto again;
+fail:
+ pb->buf = pos;
+ return 0;
+}
+
+cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) {
+ cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
+ struct parse_buf pb;
+ cn_cbor* ret;
+
+ pb.buf = (unsigned char *)buf;
+ pb.ebuf = (unsigned char *)buf+len;
+ pb.err = CN_CBOR_NO_ERROR;
+ ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher);
+ if (ret != NULL) {
+ /* mark as top node */
+ ret->parent = NULL;
+ } else {
+ if (catcher.first_child) {
+ catcher.first_child->parent = 0;
+ cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM);
+ }
+//fail:
+ if (errp) {
+ errp->err = pb.err;
+ errp->pos = pb.buf - (unsigned char *)buf;
+ }
+ return NULL;
+ }
+ return ret;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CN_CBOR_C */
diff --git a/src/cn-create.c b/src/cn-create.c
new file mode 100644
index 0000000..bc448e9
--- /dev/null
+++ b/src/cn-create.c
@@ -0,0 +1,184 @@
+#ifndef CN_CREATE_C
+#define CN_CREATE_C
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "cn-cbor/cn-cbor.h"
+#include "cbor.h"
+
+#define INIT_CB(v) \
+ if (errp) {errp->err = CN_CBOR_NO_ERROR;} \
+ (v) = CN_CALLOC_CONTEXT(); \
+ if (!(v)) { if (errp) {errp->err = CN_CBOR_ERR_OUT_OF_MEMORY;} return NULL; }
+
+cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp)
+{
+ cn_cbor* ret;
+ INIT_CB(ret);
+
+ ret->type = CN_CBOR_MAP;
+ ret->flags |= CN_CBOR_FL_COUNT;
+
+ return ret;
+}
+
+cn_cbor* cn_cbor_data_create(const uint8_t* data, int len
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp)
+{
+ cn_cbor* ret;
+ INIT_CB(ret);
+
+ ret->type = CN_CBOR_BYTES;
+ ret->length = len;
+ ret->v.str = (const char*) data; // TODO: add v.ustr to the union?
+
+ return ret;
+}
+
+cn_cbor* cn_cbor_string_create(const char* data
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp)
+{
+ cn_cbor* ret;
+ INIT_CB(ret);
+
+ ret->type = CN_CBOR_TEXT;
+ ret->length = strlen(data);
+ ret->v.str = data;
+
+ return ret;
+}
+
+cn_cbor* cn_cbor_int_create(int64_t value
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp)
+{
+ cn_cbor* ret;
+ INIT_CB(ret);
+
+ if (value<0) {
+ ret->type = CN_CBOR_INT;
+ ret->v.sint = value;
+ } else {
+ ret->type = CN_CBOR_UINT;
+ ret->v.uint = value;
+ }
+
+ return ret;
+}
+
+static bool _append_kv(cn_cbor *cb_map, cn_cbor *key, cn_cbor *val)
+{
+ //Connect key and value and insert them into the map.
+ key->parent = cb_map;
+ key->next = val;
+ val->parent = cb_map;
+ val->next = NULL;
+
+ if(cb_map->last_child) {
+ cb_map->last_child->next = key;
+ } else {
+ cb_map->first_child = key;
+ }
+ cb_map->last_child = val;
+ cb_map->length += 2;
+ return true;
+}
+
+bool cn_cbor_map_put(cn_cbor* cb_map,
+ cn_cbor *cb_key, cn_cbor *cb_value,
+ cn_cbor_errback *errp)
+{
+ //Make sure input is a map. Otherwise
+ if(!cb_map || !cb_key || !cb_value || cb_map->type != CN_CBOR_MAP)
+ {
+ if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;}
+ return false;
+ }
+
+ return _append_kv(cb_map, cb_key, cb_value);
+}
+
+bool cn_cbor_mapput_int(cn_cbor* cb_map,
+ int64_t key, cn_cbor* cb_value
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp)
+{
+ cn_cbor* cb_key;
+
+ //Make sure input is a map. Otherwise
+ if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP)
+ {
+ if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;}
+ return false;
+ }
+
+ cb_key = cn_cbor_int_create(key CBOR_CONTEXT_PARAM, errp);
+ if (!cb_key) { return false; }
+ return _append_kv(cb_map, cb_key, cb_value);
+}
+
+bool cn_cbor_mapput_string(cn_cbor* cb_map,
+ const char* key, cn_cbor* cb_value
+ CBOR_CONTEXT,
+ cn_cbor_errback *errp)
+{
+ cn_cbor* cb_key;
+
+ //Make sure input is a map. Otherwise
+ if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP)
+ {
+ if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;}
+ return false;
+ }
+
+ cb_key = cn_cbor_string_create(key CBOR_CONTEXT_PARAM, errp);
+ if (!cb_key) { return false; }
+ return _append_kv(cb_map, cb_key, cb_value);
+}
+
+cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp)
+{
+ cn_cbor* ret;
+ INIT_CB(ret);
+
+ ret->type = CN_CBOR_ARRAY;
+ ret->flags |= CN_CBOR_FL_COUNT;
+
+ return ret;
+}
+
+bool cn_cbor_array_append(cn_cbor* cb_array,
+ cn_cbor* cb_value,
+ cn_cbor_errback *errp)
+{
+ //Make sure input is an array.
+ if(!cb_array || !cb_value || cb_array->type != CN_CBOR_ARRAY)
+ {
+ if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;}
+ return false;
+ }
+
+ cb_value->parent = cb_array;
+ cb_value->next = NULL;
+ if(cb_array->last_child) {
+ cb_array->last_child->next = cb_value;
+ } else {
+ cb_array->first_child = cb_value;
+ }
+ cb_array->last_child = cb_value;
+ cb_array->length++;
+ return true;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CN_CBOR_C */
diff --git a/src/cn-encoder.c b/src/cn-encoder.c
new file mode 100644
index 0000000..8593b39
--- /dev/null
+++ b/src/cn-encoder.c
@@ -0,0 +1,309 @@
+#ifndef CN_ENCODER_C
+#define CN_ENCODER_C
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef EMACS_INDENTATION_HELPER
+} /* Duh. */
+#endif
+
+#include <arpa/inet.h>
+#include <string.h>
+#include <strings.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "cn-cbor/cn-cbor.h"
+#include "cbor.h"
+
+#define hton8p(p) (*(uint8_t*)(p))
+#define hton16p(p) (htons(*(uint16_t*)(p)))
+#define hton32p(p) (htonl(*(uint32_t*)(p)))
+static uint64_t hton64p(const uint8_t *p) {
+ /* TODO: does this work on both BE and LE systems? */
+ uint64_t ret = hton32p(p);
+ ret <<= 32;
+ ret |= hton32p(p+4);
+ return ret;
+}
+
+typedef struct _write_state
+{
+ uint8_t *buf;
+ ssize_t offset;
+ ssize_t size;
+} cn_write_state;
+
+#define ensure_writable(sz) if ((ws->offset<0) || (ws->offset + (sz) >= ws->size)) { \
+ ws->offset = -1; \
+ return; \
+}
+
+#define write_byte_and_data(b, data, sz) \
+ws->buf[ws->offset++] = (b); \
+memcpy(ws->buf+ws->offset, (data), (sz)); \
+ws->offset += sz;
+
+#define write_byte(b) \
+ws->buf[ws->offset++] = (b); \
+
+#define write_byte_ensured(b) \
+ensure_writable(1); \
+write_byte(b); \
+
+static uint8_t _xlate[] = {
+ IB_FALSE, /* CN_CBOR_FALSE */
+ IB_TRUE, /* CN_CBOR_TRUE */
+ IB_NIL, /* CN_CBOR_NULL */
+ IB_UNDEF, /* CN_CBOR_UNDEF */
+ IB_UNSIGNED, /* CN_CBOR_UINT */
+ IB_NEGATIVE, /* CN_CBOR_INT */
+ IB_BYTES, /* CN_CBOR_BYTES */
+ IB_TEXT, /* CN_CBOR_TEXT */
+ IB_BYTES, /* CN_CBOR_BYTES_CHUNKED */
+ IB_TEXT, /* CN_CBOR_TEXT_CHUNKED */
+ IB_ARRAY, /* CN_CBOR_ARRAY */
+ IB_MAP, /* CN_CBOR_MAP */
+ IB_TAG, /* CN_CBOR_TAG */
+ IB_PRIM, /* CN_CBOR_SIMPLE */
+ 0xFF, /* CN_CBOR_DOUBLE */
+ 0xFF /* CN_CBOR_INVALID */
+};
+
+static inline bool is_indefinite(const cn_cbor *cb)
+{
+ return (cb->flags & CN_CBOR_FL_INDEF) != 0;
+}
+
+static void _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val) {
+ uint8_t ib;
+
+ assert((size_t)typ < sizeof(_xlate));
+
+ ib = _xlate[typ];
+ if (ib == 0xFF) {
+ ws->offset = -1;
+ return;
+ }
+
+ if (val < 24) {
+ ensure_writable(1);
+ write_byte(ib | val);
+ } else if (val < 256) {
+ ensure_writable(2);
+ write_byte(ib | 24);
+ write_byte((uint8_t)val);
+ } else if (val < 65536) {
+ uint16_t be16 = (uint16_t)val;
+ ensure_writable(3);
+ be16 = hton16p(&be16);
+ write_byte_and_data(ib | 25, (const void*)&be16, 2);
+ } else if (val < 0x100000000L) {
+ uint32_t be32 = (uint32_t)val;
+ ensure_writable(5);
+ be32 = hton32p(&be32);
+ write_byte_and_data(ib | 26, (const void*)&be32, 4);
+ } else {
+ uint64_t be64;
+ ensure_writable(9);
+ be64 = hton64p((const uint8_t*)&val);
+ write_byte_and_data(ib | 27, (const void*)&be64, 8);
+ }
+}
+
+#ifndef CBOR_NO_FLOAT
+static void _write_double(cn_write_state *ws, double val)
+{
+ float float_val = val;
+ if (float_val == val) { /* 32 bits is enough and we aren't NaN */
+ uint32_t be32;
+ uint16_t be16, u16;
+ union {
+ float f;
+ uint32_t u;
+ } u32;
+ u32.f = float_val;
+ if ((u32.u & 0x1FFF) == 0) { /* worth trying half */
+ int s16 = (u32.u >> 16) & 0x8000;
+ int exp = (u32.u >> 23) & 0xff;
+ int mant = u32.u & 0x7fffff;
+ if (exp == 0 && mant == 0)
+ ; /* 0.0, -0.0 */
+ else if (exp >= 113 && exp <= 142) /* normalized */
+ s16 += ((exp - 112) << 10) + (mant >> 13);
+ else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */
+ if (mant & ((1 << (126 - exp)) - 1))
+ goto float32; /* loss of precision */
+ s16 += ((mant + 0x800000) >> (126 - exp));
+ } else if (exp == 255 && mant == 0) { /* Inf */
+ s16 += 0x7c00;
+ } else
+ goto float32; /* loss of range */
+
+ ensure_writable(3);
+ u16 = s16;
+ be16 = hton16p((const uint8_t*)&u16);
+
+ write_byte_and_data(IB_PRIM | 25, (const void*)&be16, 2);
+ return;
+ }
+ float32:
+ ensure_writable(5);
+ be32 = hton32p((const uint8_t*)&u32.u);
+
+ write_byte_and_data(IB_PRIM | 26, (const void*)&be32, 4);
+
+ } else if (val != val) { /* NaN -- we always write a half NaN*/
+ ensure_writable(3);
+ write_byte_and_data(IB_PRIM | 25, (const void*)"\x7e\x00", 2);
+ } else {
+ uint64_t be64;
+ /* Copy the same problematic implementation from the decoder. */
+ union {
+ double d;
+ uint64_t u;
+ } u64;
+
+ u64.d = val;
+
+ ensure_writable(9);
+ be64 = hton64p((const uint8_t*)&u64.u);
+
+ write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8);
+
+ }
+}
+#endif /* CBOR_NO_FLOAT */
+
+// TODO: make public?
+typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context);
+static void _visit(const cn_cbor *cb,
+ cn_visit_func visitor,
+ cn_visit_func breaker,
+ void *context)
+{
+ const cn_cbor *p = cb;
+ int depth = 0;
+ while (p)
+ {
+visit:
+ visitor(p, depth, context);
+ if (p->first_child) {
+ p = p->first_child;
+ depth++;
+ } else{
+ // Empty indefinite
+ if (is_indefinite(p)) {
+ breaker(p->parent, depth, context);
+ }
+ if (p->next) {
+ p = p->next;
+ } else {
+ while (p->parent) {
+ depth--;
+ if (is_indefinite(p->parent)) {
+ breaker(p->parent, depth, context);
+ }
+ if (p->parent->next) {
+ p = p->parent->next;
+ goto visit;
+ }
+ p = p->parent;
+ }
+ return;
+ }
+ }
+ }
+}
+
+#define CHECK(st) (st); \
+if (ws->offset < 0) { return; }
+
+void _encoder_visitor(const cn_cbor *cb, int depth, void *context)
+{
+ cn_write_state *ws = context;
+ UNUSED_PARAM(depth);
+
+ switch (cb->type) {
+ case CN_CBOR_ARRAY:
+ if (is_indefinite(cb)) {
+ write_byte_ensured(IB_ARRAY | AI_INDEF);
+ } else {
+ CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length));
+ }
+ break;
+ case CN_CBOR_MAP:
+ if (is_indefinite(cb)) {
+ write_byte_ensured(IB_MAP | AI_INDEF);
+ } else {
+ CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length/2));
+ }
+ break;
+ case CN_CBOR_BYTES_CHUNKED:
+ case CN_CBOR_TEXT_CHUNKED:
+ write_byte_ensured(_xlate[cb->type] | AI_INDEF);
+ break;
+
+ case CN_CBOR_TEXT:
+ case CN_CBOR_BYTES:
+ CHECK(_write_positive(ws, cb->type, cb->length));
+ ensure_writable(cb->length);
+ memcpy(ws->buf+ws->offset, cb->v.str, cb->length);
+ ws->offset += cb->length;
+ break;
+
+ case CN_CBOR_FALSE:
+ case CN_CBOR_TRUE:
+ case CN_CBOR_NULL:
+ case CN_CBOR_UNDEF:
+ write_byte_ensured(_xlate[cb->type]);
+ break;
+
+ case CN_CBOR_TAG:
+ case CN_CBOR_UINT:
+ case CN_CBOR_SIMPLE:
+ CHECK(_write_positive(ws, cb->type, cb->v.uint));
+ break;
+
+ case CN_CBOR_INT:
+ assert(cb->v.sint < 0);
+ CHECK(_write_positive(ws, CN_CBOR_INT, ~(cb->v.sint)));
+ break;
+
+ case CN_CBOR_DOUBLE:
+#ifndef CBOR_NO_FLOAT
+ CHECK(_write_double(ws, cb->v.dbl));
+#endif /* CBOR_NO_FLOAT */
+ break;
+
+ case CN_CBOR_INVALID:
+ ws->offset = -1;
+ break;
+ }
+}
+
+void _encoder_breaker(const cn_cbor *cb, int depth, void *context)
+{
+ cn_write_state *ws = context;
+ UNUSED_PARAM(cb);
+ UNUSED_PARAM(depth);
+ write_byte_ensured(IB_BREAK);
+}
+
+ssize_t cn_cbor_encoder_write(uint8_t *buf,
+ size_t buf_offset,
+ size_t buf_size,
+ const cn_cbor *cb)
+{
+ cn_write_state ws = { buf, buf_offset, buf_size };
+ _visit(cb, _encoder_visitor, _encoder_breaker, &ws);
+ if (ws.offset < 0) { return -1; }
+ return ws.offset - buf_offset;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CN_CBOR_C */
diff --git a/src/cn-error.c b/src/cn-error.c
new file mode 100644
index 0000000..4953cc9
--- /dev/null
+++ b/src/cn-error.c
@@ -0,0 +1,13 @@
+const char *cn_cbor_error_str[] = {
+ "CN_CBOR_NO_ERROR",
+ "CN_CBOR_ERR_OUT_OF_DATA",
+ "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED",
+ "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP",
+ "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF",
+ "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF",
+ "CN_CBOR_ERR_RESERVED_AI",
+ "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
+ "CN_CBOR_ERR_INVALID_PARAMETER",
+ "CN_CBOR_ERR_OUT_OF_MEMORY",
+ "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED"
+};
diff --git a/src/cn-get.c b/src/cn-get.c
new file mode 100644
index 0000000..79d3d72
--- /dev/null
+++ b/src/cn-get.c
@@ -0,0 +1,63 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "cn-cbor/cn-cbor.h"
+
+cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key) {
+ cn_cbor* cp;
+ assert(cb);
+ for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) {
+ switch(cp->type) {
+ case CN_CBOR_UINT:
+ if (cp->v.uint == (unsigned long)key) {
+ return cp->next;
+ }
+ break;
+ case CN_CBOR_INT:
+ if (cp->v.sint == (long)key) {
+ return cp->next;
+ }
+ break;
+ default:
+ ; // skip non-integer keys
+ }
+ }
+ return NULL;
+}
+
+cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key) {
+ cn_cbor *cp;
+ int keylen;
+ assert(cb);
+ assert(key);
+ keylen = strlen(key);
+ for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) {
+ switch(cp->type) {
+ case CN_CBOR_TEXT: // fall-through
+ case CN_CBOR_BYTES:
+ if (keylen != cp->length) {
+ continue;
+ }
+ if (memcmp(key, cp->v.str, keylen) == 0) {
+ return cp->next;
+ }
+ default:
+ ; // skip non-string keys
+ }
+ }
+ return NULL;
+}
+
+cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx) {
+ cn_cbor *cp;
+ unsigned int i = 0;
+ assert(cb);
+ for (cp = cb->first_child; cp; cp = cp->next) {
+ if (i == idx) {
+ return cp;
+ }
+ i++;
+ }
+ return NULL;
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..3181a8d
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+#
+# Compiling/running tests
+
+if (use_context)
+ add_definitions(-DUSE_CBOR_CONTEXT)
+endif()
+
+set ( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${dist_dir}/test )
+
+function (create_test name)
+ add_executable ( ${name}_test ${name}_test.c )
+ target_link_libraries ( ${name}_test PRIVATE cn-cbor )
+ target_include_directories ( ${name}_test PRIVATE ../include )
+ add_test ( NAME ${name} COMMAND ${name}_test )
+endfunction()
+
+create_test ( cbor )
+include ( CTest )
+
+if (APPLE)
+ # difftest uses Apple-specific memory tests
+ add_executable (cn-test test.c )
+ target_include_directories ( cn-test PRIVATE ../include )
+ target_link_libraries ( cn-test PRIVATE cn-cbor )
+
+ configure_file(cases.cbor cases.cbor COPYONLY)
+ configure_file(expected.out expected.out COPYONLY)
+
+ add_custom_target(difftest
+ COMMAND env MallocStackLogging=true ./cn-test >new.out
+ COMMAND diff new.out expected.out
+ DEPENDS cn-test
+ WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
+ COMMENT "generate differences between actual and expected output")
+
+endif()
diff --git a/test/cases.cbor b/test/cases.cbor
new file mode 100644
index 0000000..db6d126
--- /dev/null
+++ b/test/cases.cbor
Binary files differ
diff --git a/test/cbor_test.c b/test/cbor_test.c
new file mode 100644
index 0000000..3326497
--- /dev/null
+++ b/test/cbor_test.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2015 SPUDlib authors. See LICENSE file.
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "cn-cbor/cn-cbor.h"
+
+#define CTEST_MAIN
+#include "ctest.h"
+
+int main(int argc, const char *argv[])
+{
+ return ctest_main(argc, argv);
+}
+
+#ifdef USE_CBOR_CONTEXT
+#define CONTEXT_NULL , NULL
+#define CONTEXT_NULL_COMMA NULL,
+#else
+#define CONTEXT_NULL
+#define CONTEXT_NULL_COMMA
+#endif
+
+typedef struct _buffer {
+ size_t sz;
+ unsigned char *ptr;
+} buffer;
+
+static bool parse_hex(char *inp, buffer *b)
+{
+ int len = strlen(inp);
+ size_t i;
+ if (len%2 != 0) {
+ b->sz = -1;
+ b->ptr = NULL;
+ return false;
+ }
+ b->sz = len / 2;
+ b->ptr = malloc(b->sz);
+ for (i=0; i<b->sz; i++) {
+ sscanf(inp+(2*i), "%02hhx", &b->ptr[i]);
+ }
+ return true;
+}
+
+CTEST(cbor, error)
+{
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_NO_ERROR], "CN_CBOR_NO_ERROR");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_OUT_OF_DATA], "CN_CBOR_ERR_OUT_OF_DATA");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED], "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_ODD_SIZE_INDEF_MAP], "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_BREAK_OUTSIDE_INDEF], "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_MT_UNDEF_FOR_INDEF], "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_RESERVED_AI], "CN_CBOR_ERR_RESERVED_AI");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING], "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_INVALID_PARAMETER], "CN_CBOR_ERR_INVALID_PARAMETER");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_OUT_OF_MEMORY], "CN_CBOR_ERR_OUT_OF_MEMORY");
+ ASSERT_STR(cn_cbor_error_str[CN_CBOR_ERR_FLOAT_NOT_SUPPORTED], "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED");
+}
+
+CTEST(cbor, parse)
+{
+ cn_cbor_errback err;
+ char *tests[] = {
+ "00", // 0
+ "01", // 1
+ "17", // 23
+ "1818", // 24
+ "190100", // 256
+ "1a00010000", // 65536
+ "1b0000000100000000", // 4294967296
+ "20", // -1
+ "37", // -24
+ "3818", // -25
+ "390100", // -257
+ "3a00010000", // -65537
+ "3b0000000100000000", // -4294967297
+ "4161", // h"a"
+ "6161", // "a"
+ "80", // []
+ "8100", // [0]
+ "820102", // [1,2]
+ "818100", // [[0]]
+ "a1616100", // {"a":0}
+ "d8184100", // tag
+ "f4", // false
+ "f5", // true
+ "f6", // null
+ "f7", // undefined
+ "f8ff", // simple(255)
+#ifndef CBOR_NO_FLOAT
+ "f93c00", // 1.0
+ "f9bc00", // -1.0
+ "f903ff", // 6.097555160522461e-05
+ "f90400", // 6.103515625e-05
+ "f907ff", // 0.00012201070785522461
+ "f90800", // 0.0001220703125
+ "fa47800000", // 65536.0
+ "fb3ff199999999999a", // 1.1
+ "f97e00", // NaN
+#endif /* CBOR_NO_FLOAT */
+ "5f42010243030405ff", // (_ h'0102', h'030405')
+ "7f61616161ff", // (_ "a", "a")
+ "9fff", // [_ ]
+ "9f9f9fffffff", // [_ [_ [_ ]]]
+ "9f009f00ff00ff", // [_ 0, [_ 0], 0]
+ "bf61610161629f0203ffff", // {_ "a": 1, "b": [_ 2, 3]}
+ };
+ cn_cbor *cb;
+ buffer b;
+ size_t i;
+ unsigned char encoded[1024];
+ ssize_t enc_sz;
+
+ for (i=0; i<sizeof(tests)/sizeof(char*); i++) {
+ ASSERT_TRUE(parse_hex(tests[i], &b));
+ err.err = CN_CBOR_NO_ERROR;
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ //CTEST_LOG("%s: %s", tests[i], cn_cbor_error_str[err.err]);
+ ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
+ ASSERT_NOT_NULL(cb);
+
+ enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+ ASSERT_DATA(b.ptr, b.sz, encoded, enc_sz);
+ free(b.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+ }
+}
+
+
+CTEST(cbor, parse_normalize)
+{
+ cn_cbor_errback err;
+ char *basic_tests[] = {
+ "00", "00", // 0
+ "1800", "00",
+ "1818", "1818",
+ "190000", "00",
+ "190018", "1818",
+ "1a00000000", "00",
+ "1b0000000000000000", "00",
+ "20", "20", // -1
+ "3800", "20",
+ "c600", "c600", // 6(0) (undefined tag)
+ "d80600", "c600",
+ "d9000600", "c600",
+ };
+ char *float_tests[] = {
+ "fb3ff0000000000000", "f93c00", // 1.0
+ "fbbff0000000000000", "f9bc00", // -1.0
+ "fb40f86a0000000000", "fa47c35000", // 100000.0
+ "fb7ff8000000000000", "f97e00", // NaN
+ "fb3e70000000000000", "f90001", // 5.960464477539063e-08
+ "fb3e78000000000000", "fa33c00000", // 8.940696716308594e-08
+ "fb3e80000000000000", "f90002", // 1.1920928955078125e-07
+ };
+ cn_cbor *cb;
+ buffer b, b2;
+ size_t i;
+ unsigned char encoded[1024];
+ ssize_t enc_sz;
+
+ for (i=0; i<sizeof(basic_tests)/sizeof(char*); i+=2) {
+ ASSERT_TRUE(parse_hex(basic_tests[i], &b));
+ ASSERT_TRUE(parse_hex(basic_tests[i+1], &b2));
+ err.err = CN_CBOR_NO_ERROR;
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ CTEST_LOG("%s: %s", basic_tests[i], cn_cbor_error_str[err.err]);
+ ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
+ ASSERT_NOT_NULL(cb);
+
+ enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+ ASSERT_DATA(b2.ptr, b2.sz, encoded, enc_sz);
+ free(b.ptr);
+ free(b2.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+ }
+
+ for (i=0; i<sizeof(float_tests)/sizeof(char*); i+=2) {
+ ASSERT_TRUE(parse_hex(float_tests[i], &b));
+ ASSERT_TRUE(parse_hex(float_tests[i+1], &b2));
+ err.err = CN_CBOR_NO_ERROR;
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ CTEST_LOG("%s: %s", float_tests[i], cn_cbor_error_str[err.err]);
+#ifndef CBOR_NO_FLOAT
+ ASSERT_EQUAL(err.err, CN_CBOR_NO_ERROR);
+ ASSERT_NOT_NULL(cb);
+#else /* CBOR_NO_FLOAT */
+ ASSERT_EQUAL(err.err, CN_CBOR_ERR_FLOAT_NOT_SUPPORTED);
+ ASSERT_NULL(cb);
+#endif /* CBOR_NO_FLOAT */
+
+ /* enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb); */
+ /* ASSERT_DATA(b2.ptr, b2.sz, encoded, enc_sz); */
+ free(b.ptr);
+ free(b2.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+ }
+}
+
+typedef struct _cbor_failure
+{
+ char *hex;
+ cn_cbor_error err;
+} cbor_failure;
+
+CTEST(cbor, fail)
+{
+ cn_cbor_errback err;
+ cbor_failure tests[] = {
+ {"81", CN_CBOR_ERR_OUT_OF_DATA},
+ {"0000", CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED},
+ {"bf00ff", CN_CBOR_ERR_ODD_SIZE_INDEF_MAP},
+ {"ff", CN_CBOR_ERR_BREAK_OUTSIDE_INDEF},
+ {"1f", CN_CBOR_ERR_MT_UNDEF_FOR_INDEF},
+ {"1c", CN_CBOR_ERR_RESERVED_AI},
+ {"7f4100", CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING},
+ };
+ cn_cbor *cb;
+ buffer b;
+ size_t i;
+ uint8_t buf[10];
+ cn_cbor inv = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL};
+
+ ASSERT_EQUAL(-1, cn_cbor_encoder_write(buf, 0, sizeof(buf), &inv));
+
+ for (i=0; i<sizeof(tests)/sizeof(cbor_failure); i++) {
+ ASSERT_TRUE(parse_hex(tests[i].hex, &b));
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ ASSERT_NULL(cb);
+ ASSERT_EQUAL(err.err, tests[i].err);
+
+ free(b.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+ }
+}
+
+// Decoder loses float size information
+CTEST(cbor, float)
+{
+#ifndef CBOR_NO_FLOAT
+ cn_cbor_errback err;
+ char *tests[] = {
+ "f90001", // 5.960464477539063e-08
+ "f9c400", // -4.0
+ "fa47c35000", // 100000.0
+ "f97e00", // Half NaN, half beast
+ "f9fc00", // -Inf
+ "f97c00", // Inf
+ };
+ cn_cbor *cb;
+ buffer b;
+ size_t i;
+ unsigned char encoded[1024];
+ ssize_t enc_sz;
+
+ for (i=0; i<sizeof(tests)/sizeof(char*); i++) {
+ ASSERT_TRUE(parse_hex(tests[i], &b));
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ ASSERT_NOT_NULL(cb);
+
+ enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), cb);
+ ASSERT_DATA(b.ptr, b.sz, encoded, enc_sz);
+
+ free(b.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+ }
+#endif /* CBOR_NO_FLOAT */
+}
+
+CTEST(cbor, getset)
+{
+ buffer b;
+ cn_cbor *cb;
+ cn_cbor *val;
+ cn_cbor_errback err;
+
+ ASSERT_TRUE(parse_hex("a40000436363630262626201616100", &b));
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ ASSERT_NOT_NULL(cb);
+ val = cn_cbor_mapget_string(cb, "a");
+ ASSERT_NOT_NULL(val);
+ val = cn_cbor_mapget_string(cb, "bb");
+ ASSERT_NOT_NULL(val);
+ val = cn_cbor_mapget_string(cb, "ccc");
+ ASSERT_NOT_NULL(val);
+ val = cn_cbor_mapget_string(cb, "b");
+ ASSERT_NULL(val);
+ free(b.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+
+ ASSERT_TRUE(parse_hex("a3616100006161206162", &b));
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ ASSERT_NOT_NULL(cb);
+ val = cn_cbor_mapget_int(cb, 0);
+ ASSERT_NOT_NULL(val);
+ val = cn_cbor_mapget_int(cb, -1);
+ ASSERT_NOT_NULL(val);
+ val = cn_cbor_mapget_int(cb, 1);
+ ASSERT_NULL(val);
+ free(b.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+
+ ASSERT_TRUE(parse_hex("8100", &b));
+ cb = cn_cbor_decode(b.ptr, b.sz CONTEXT_NULL, &err);
+ ASSERT_NOT_NULL(cb);
+ val = cn_cbor_index(cb, 0);
+ ASSERT_NOT_NULL(val);
+ val = cn_cbor_index(cb, 1);
+ ASSERT_NULL(val);
+ val = cn_cbor_index(cb, -1);
+ ASSERT_NULL(val);
+ free(b.ptr);
+ cn_cbor_free(cb CONTEXT_NULL);
+}
+
+CTEST(cbor, create)
+{
+ cn_cbor_errback err;
+ const cn_cbor* val;
+ const char* data = "abc";
+ cn_cbor *cb_map = cn_cbor_map_create(CONTEXT_NULL_COMMA &err);
+ cn_cbor *cb_int;
+ cn_cbor *cb_data;
+
+ ASSERT_NOT_NULL(cb_map);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+
+ cb_int = cn_cbor_int_create(256 CONTEXT_NULL, &err);
+ ASSERT_NOT_NULL(cb_int);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+
+ cb_data = cn_cbor_data_create((const uint8_t*)data, 4 CONTEXT_NULL, &err);
+ ASSERT_NOT_NULL(cb_data);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+
+ cn_cbor_mapput_int(cb_map, 5, cb_int CONTEXT_NULL, &err);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_TRUE(cb_map->length == 2);
+
+ cn_cbor_mapput_int(cb_map, -7, cb_data CONTEXT_NULL, &err);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_TRUE(cb_map->length == 4);
+
+ cn_cbor_mapput_string(cb_map, "foo",
+ cn_cbor_string_create(data CONTEXT_NULL, &err)
+ CONTEXT_NULL, &err);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_TRUE(cb_map->length == 6);
+
+ cn_cbor_map_put(cb_map,
+ cn_cbor_string_create("bar" CONTEXT_NULL, &err),
+ cn_cbor_string_create("qux" CONTEXT_NULL, &err),
+ &err);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_TRUE(cb_map->length == 8);
+
+ val = cn_cbor_mapget_int(cb_map, 5);
+ ASSERT_NOT_NULL(val);
+ ASSERT_TRUE(val->v.sint == 256);
+
+ val = cn_cbor_mapget_int(cb_map, -7);
+ ASSERT_NOT_NULL(val);
+ ASSERT_STR(val->v.str, "abc");
+
+ cn_cbor_free(cb_map CONTEXT_NULL);
+}
+
+CTEST(cbor, map_errors)
+{
+ cn_cbor_errback err;
+ cn_cbor *ci;
+ ci = cn_cbor_int_create(65536, CONTEXT_NULL_COMMA &err);
+ cn_cbor_mapput_int(ci, -5, NULL, CONTEXT_NULL_COMMA &err);
+ ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER);
+ cn_cbor_mapput_string(ci, "foo", NULL, CONTEXT_NULL_COMMA &err);
+ ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER);
+ cn_cbor_map_put(ci, NULL, NULL, &err);
+}
+
+CTEST(cbor, array)
+{
+ cn_cbor_errback err;
+ cn_cbor *a = cn_cbor_array_create(CONTEXT_NULL_COMMA &err);
+ ASSERT_NOT_NULL(a);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_EQUAL(a->length, 0);
+
+ cn_cbor_array_append(a, cn_cbor_int_create(256, CONTEXT_NULL_COMMA &err), &err);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_EQUAL(a->length, 1);
+
+ cn_cbor_array_append(a, cn_cbor_string_create("five", CONTEXT_NULL_COMMA &err), &err);
+ ASSERT_TRUE(err.err == CN_CBOR_NO_ERROR);
+ ASSERT_EQUAL(a->length, 2);
+}
+
+CTEST(cbor, array_errors)
+{
+ cn_cbor_errback err;
+ cn_cbor *ci = cn_cbor_int_create(12, CONTEXT_NULL_COMMA &err);
+ cn_cbor_array_append(NULL, ci, &err);
+ ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER);
+ cn_cbor_array_append(ci, NULL, &err);
+ ASSERT_EQUAL(err.err, CN_CBOR_ERR_INVALID_PARAMETER);
+}
+
+CTEST(cbor, create_encode)
+{
+ cn_cbor *map;
+ cn_cbor *cdata;
+ char data[] = "data";
+ unsigned char encoded[1024];
+ ssize_t enc_sz;
+
+ map = cn_cbor_map_create(CONTEXT_NULL_COMMA NULL);
+ ASSERT_NOT_NULL(map);
+
+ cdata = cn_cbor_data_create((uint8_t*)data, sizeof(data)-1, CONTEXT_NULL_COMMA NULL);
+ ASSERT_NOT_NULL(cdata);
+
+ ASSERT_TRUE(cn_cbor_mapput_int(map, 0, cdata, CONTEXT_NULL_COMMA NULL));
+ enc_sz = cn_cbor_encoder_write(encoded, 0, sizeof(encoded), map);
+ ASSERT_EQUAL(7, enc_sz);
+}
diff --git a/test/ctest.h b/test/ctest.h
new file mode 100644
index 0000000..75fda66
--- /dev/null
+++ b/test/ctest.h
@@ -0,0 +1,459 @@
+/* Copyright 2011,2012 Bas van den Berg
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CTEST_H
+#define CTEST_H
+
+#ifndef UNUSED_PARAM
+ /**
+ * \def UNUSED_PARAM(p);
+ *
+ * A macro for quelling compiler warnings about unused variables.
+ */
+# define UNUSED_PARAM(p) ((void)&(p))
+#endif /* UNUSED_PARM */
+
+typedef void (*SetupFunc)(void*);
+typedef void (*TearDownFunc)(void*);
+
+struct ctest {
+ const char* ssname; // suite name
+ const char* ttname; // test name
+ void (*run)();
+ int skip;
+
+ void* data;
+ SetupFunc setup;
+ TearDownFunc teardown;
+
+ unsigned int magic;
+};
+
+#define __FNAME(sname, tname) __ctest_##sname##_##tname##_run
+#define __TNAME(sname, tname) __ctest_##sname##_##tname
+
+#define __CTEST_MAGIC (0xdeadbeef)
+#ifdef __APPLE__
+#define __Test_Section __attribute__ ((unused,section ("__DATA, .ctest")))
+#else
+#define __Test_Section __attribute__ ((unused,section (".ctest")))
+#endif
+
+#define __CTEST_STRUCT(sname, tname, _skip, __data, __setup, __teardown) \
+ struct ctest __TNAME(sname, tname) __Test_Section = { \
+ .ssname=#sname, \
+ .ttname=#tname, \
+ .run = __FNAME(sname, tname), \
+ .skip = _skip, \
+ .data = __data, \
+ .setup = (SetupFunc)__setup, \
+ .teardown = (TearDownFunc)__teardown, \
+ .magic = __CTEST_MAGIC };
+
+#define CTEST_DATA(sname) struct sname##_data
+
+#define CTEST_SETUP(sname) \
+ void __attribute__ ((weak)) sname##_setup(struct sname##_data* data)
+
+#define CTEST_TEARDOWN(sname) \
+ void __attribute__ ((weak)) sname##_teardown(struct sname##_data* data)
+
+#define __CTEST_INTERNAL(sname, tname, _skip) \
+ void __FNAME(sname, tname)(); \
+ __CTEST_STRUCT(sname, tname, _skip, NULL, NULL, NULL) \
+ void __FNAME(sname, tname)()
+
+#ifdef __APPLE__
+#define SETUP_FNAME(sname) NULL
+#define TEARDOWN_FNAME(sname) NULL
+#else
+#define SETUP_FNAME(sname) sname##_setup
+#define TEARDOWN_FNAME(sname) sname##_teardown
+#endif
+
+#define __CTEST2_INTERNAL(sname, tname, _skip) \
+ static struct sname##_data __ctest_##sname##_data; \
+ CTEST_SETUP(sname); \
+ CTEST_TEARDOWN(sname); \
+ void __FNAME(sname, tname)(struct sname##_data* data); \
+ __CTEST_STRUCT(sname, tname, _skip, &__ctest_##sname##_data, SETUP_FNAME(sname), TEARDOWN_FNAME(sname)) \
+ void __FNAME(sname, tname)(struct sname##_data* data)
+
+
+void CTEST_LOG(char *fmt, ...);
+void CTEST_ERR(char *fmt, ...); // doesn't return
+
+#define CTEST(sname, tname) __CTEST_INTERNAL(sname, tname, 0)
+#define CTEST_SKIP(sname, tname) __CTEST_INTERNAL(sname, tname, 1)
+
+#define CTEST2(sname, tname) __CTEST2_INTERNAL(sname, tname, 0)
+#define CTEST2_SKIP(sname, tname) __CTEST2_INTERNAL(sname, tname, 1)
+
+
+void assert_str(const char* exp, const char* real, const char* caller, int line);
+#define ASSERT_STR(exp, real) assert_str(exp, real, __FILE__, __LINE__)
+
+void assert_data(const unsigned char* exp, int expsize,
+ const unsigned char* real, int realsize,
+ const char* caller, int line);
+#define ASSERT_DATA(exp, expsize, real, realsize) \
+ assert_data(exp, expsize, real, realsize, __FILE__, __LINE__)
+
+void assert_equal(long exp, long real, const char* caller, int line);
+#define ASSERT_EQUAL(exp, real) assert_equal(exp, real, __FILE__, __LINE__)
+
+void assert_not_equal(long exp, long real, const char* caller, int line);
+#define ASSERT_NOT_EQUAL(exp, real) assert_not_equal(exp, real, __FILE__, __LINE__)
+
+void assert_null(void* real, const char* caller, int line);
+#define ASSERT_NULL(real) assert_null((void*)real, __FILE__, __LINE__)
+
+void assert_not_null(const void* real, const char* caller, int line);
+#define ASSERT_NOT_NULL(real) assert_not_null(real, __FILE__, __LINE__)
+
+void assert_true(int real, const char* caller, int line);
+#define ASSERT_TRUE(real) assert_true(real, __FILE__, __LINE__)
+
+void assert_false(int real, const char* caller, int line);
+#define ASSERT_FALSE(real) assert_false(real, __FILE__, __LINE__)
+
+void assert_fail(const char* caller, int line);
+#define ASSERT_FAIL() assert_fail(__FILE__, __LINE__)
+
+#ifdef CTEST_MAIN
+
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __APPLE__
+#include <dlfcn.h>
+#endif
+
+//#define COLOR_OK
+
+static size_t ctest_errorsize;
+static char* ctest_errormsg;
+#define MSG_SIZE 4096
+static char ctest_errorbuffer[MSG_SIZE];
+static jmp_buf ctest_err;
+static int color_output = 1;
+static const char* suite_name;
+
+typedef int (*filter_func)(struct ctest*);
+
+#define ANSI_BLACK "\033[0;30m"
+#define ANSI_RED "\033[0;31m"
+#define ANSI_GREEN "\033[0;32m"
+#define ANSI_YELLOW "\033[0;33m"
+#define ANSI_BLUE "\033[0;34m"
+#define ANSI_MAGENTA "\033[0;35m"
+#define ANSI_CYAN "\033[0;36m"
+#define ANSI_GREY "\033[0;37m"
+#define ANSI_DARKGREY "\033[01;30m"
+#define ANSI_BRED "\033[01;31m"
+#define ANSI_BGREEN "\033[01;32m"
+#define ANSI_BYELLOW "\033[01;33m"
+#define ANSI_BBLUE "\033[01;34m"
+#define ANSI_BMAGENTA "\033[01;35m"
+#define ANSI_BCYAN "\033[01;36m"
+#define ANSI_WHITE "\033[01;37m"
+#define ANSI_NORMAL "\033[0m"
+
+static CTEST(suite, test) { }
+
+static void msg_start(const char* color, const char* title) {
+ int size;
+ if (color_output) {
+ size = snprintf(ctest_errormsg, ctest_errorsize, "%s", color);
+ ctest_errorsize -= size;
+ ctest_errormsg += size;
+ }
+ size = snprintf(ctest_errormsg, ctest_errorsize, " %s: ", title);
+ ctest_errorsize -= size;
+ ctest_errormsg += size;
+}
+
+static void msg_end() {
+ int size;
+ if (color_output) {
+ size = snprintf(ctest_errormsg, ctest_errorsize, ANSI_NORMAL);
+ ctest_errorsize -= size;
+ ctest_errormsg += size;
+ }
+ size = snprintf(ctest_errormsg, ctest_errorsize, "\n");
+ ctest_errorsize -= size;
+ ctest_errormsg += size;
+}
+
+void CTEST_LOG(char *fmt, ...)
+{
+ va_list argp;
+ msg_start(ANSI_BLUE, "LOG");
+
+ va_start(argp, fmt);
+ int size = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, argp);
+ ctest_errorsize -= size;
+ ctest_errormsg += size;
+ va_end(argp);
+
+ msg_end();
+}
+
+void CTEST_ERR(char *fmt, ...)
+{
+ va_list argp;
+ msg_start(ANSI_YELLOW, "ERR");
+
+ va_start(argp, fmt);
+ int size = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, argp);
+ ctest_errorsize -= size;
+ ctest_errormsg += size;
+ va_end(argp);
+
+ msg_end();
+ longjmp(ctest_err, 1);
+}
+
+void assert_str(const char* exp, const char* real, const char* caller, int line) {
+ if ((exp == NULL && real != NULL) ||
+ (exp != NULL && real == NULL) ||
+ (exp && real && strcmp(exp, real) != 0)) {
+ CTEST_ERR("%s:%d expected '%s', got '%s'", caller, line, exp, real);
+ }
+}
+
+void assert_data(const unsigned char* exp, int expsize,
+ const unsigned char* real, int realsize,
+ const char* caller, int line) {
+ int i;
+ if (expsize != realsize) {
+ CTEST_ERR("%s:%d expected %d bytes, got %d", caller, line, expsize, realsize);
+ }
+ for (i=0; i<expsize; i++) {
+ if (exp[i] != real[i]) {
+ CTEST_ERR("%s:%d expected 0x%02x at offset %d got 0x%02x",
+ caller, line, exp[i], i, real[i]);
+ }
+ }
+}
+
+void assert_equal(long exp, long real, const char* caller, int line) {
+ if (exp != real) {
+ CTEST_ERR("%s:%d expected %ld, got %ld", caller, line, exp, real);
+ }
+}
+
+void assert_not_equal(long exp, long real, const char* caller, int line) {
+ if ((exp) == (real)) {
+ CTEST_ERR("%s:%d should not be %ld", caller, line, real);
+ }
+}
+
+void assert_null(void* real, const char* caller, int line) {
+ if ((real) != NULL) {
+ CTEST_ERR("%s:%d should be NULL", caller, line);
+ }
+}
+
+void assert_not_null(const void* real, const char* caller, int line) {
+ if (real == NULL) {
+ CTEST_ERR("%s:%d should not be NULL", caller, line);
+ }
+}
+
+void assert_true(int real, const char* caller, int line) {
+ if ((real) == 0) {
+ CTEST_ERR("%s:%d should be true", caller, line);
+ }
+}
+
+void assert_false(int real, const char* caller, int line) {
+ if ((real) != 0) {
+ CTEST_ERR("%s:%d should be false", caller, line);
+ }
+}
+
+void assert_fail(const char* caller, int line) {
+ CTEST_ERR("%s:%d shouldn't come here", caller, line);
+}
+
+
+static int suite_all(struct ctest* t) {
+ UNUSED_PARAM(t);
+ return 1;
+}
+
+static int suite_filter(struct ctest* t) {
+ return strncmp(suite_name, t->ssname, strlen(suite_name)) == 0;
+}
+
+static uint64_t getCurrentTime() {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ uint64_t now64 = now.tv_sec;
+ now64 *= 1000000;
+ now64 += (now.tv_usec);
+ return now64;
+}
+
+static void color_print(const char* color, const char* text) {
+ if (color_output)
+ printf("%s%s"ANSI_NORMAL"\n", color, text);
+ else
+ printf("%s\n", text);
+}
+
+#ifdef __APPLE__
+static void *find_symbol(struct ctest *test, const char *fname)
+{
+ size_t len = strlen(test->ssname) + 1 + strlen(fname);
+ char *symbol_name = (char *) malloc(len + 1);
+ memset(symbol_name, 0, len + 1);
+ snprintf(symbol_name, len + 1, "%s_%s", test->ssname, fname);
+
+ //fprintf(stderr, ">>>> dlsym: loading %s\n", symbol_name);
+ void *symbol = dlsym(RTLD_DEFAULT, symbol_name);
+ if (!symbol) {
+ //fprintf(stderr, ">>>> ERROR: %s\n", dlerror());
+ }
+ // returns NULL on error
+
+ free(symbol_name);
+ return symbol;
+}
+#endif
+
+#ifdef CTEST_SEGFAULT
+#include <signal.h>
+static void sighandler(int signum)
+{
+ char msg[128];
+ sprintf(msg, "[SIGNAL %d: %s]", signum, sys_siglist[signum]);
+ color_print(ANSI_BRED, msg);
+ fflush(stdout);
+
+ /* "Unregister" the signal handler and send the signal back to the process
+ * so it can terminate as expected */
+ signal(signum, SIG_DFL);
+ kill(getpid(), signum);
+}
+#endif
+
+int ctest_main(int argc, const char *argv[])
+{
+ static int total = 0;
+ static int num_ok = 0;
+ static int num_fail = 0;
+ static int num_skip = 0;
+ static int index = 1;
+ static filter_func filter = suite_all;
+
+#ifdef CTEST_SEGFAULT
+ signal(SIGSEGV, sighandler);
+#endif
+
+ if (argc == 2) {
+ suite_name = argv[1];
+ filter = suite_filter;
+ }
+
+ color_output = isatty(1);
+ uint64_t t1 = getCurrentTime();
+
+ struct ctest* ctest_begin = &__TNAME(suite, test);
+ struct ctest* ctest_end = &__TNAME(suite, test);
+ // find begin and end of section by comparing magics
+ while (1) {
+ struct ctest* t = ctest_begin-1;
+ if (t->magic != __CTEST_MAGIC) break;
+ ctest_begin--;
+ }
+ while (1) {
+ struct ctest* t = ctest_end+1;
+ if (t->magic != __CTEST_MAGIC) break;
+ ctest_end++;
+ }
+ ctest_end++; // end after last one
+
+ static struct ctest* test;
+ for (test = ctest_begin; test != ctest_end; test++) {
+ if (test == &__ctest_suite_test) continue;
+ if (filter(test)) total++;
+ }
+
+ for (test = ctest_begin; test != ctest_end; test++) {
+ if (test == &__ctest_suite_test) continue;
+ if (filter(test)) {
+ ctest_errorbuffer[0] = 0;
+ ctest_errorsize = MSG_SIZE-1;
+ ctest_errormsg = ctest_errorbuffer;
+ printf("TEST %d/%d %s:%s ", index, total, test->ssname, test->ttname);
+ fflush(stdout);
+ if (test->skip) {
+ color_print(ANSI_BYELLOW, "[SKIPPED]");
+ num_skip++;
+ } else {
+ int result = setjmp(ctest_err);
+ if (result == 0) {
+#ifdef __APPLE__
+ if (!test->setup) {
+ test->setup = (SetupFunc)find_symbol(test, "setup");
+ }
+ if (!test->teardown) {
+ test->teardown = (SetupFunc)find_symbol(test, "teardown");
+ }
+#endif
+
+ if (test->setup) test->setup(test->data);
+ if (test->data)
+ test->run(test->data);
+ else
+ test->run();
+ if (test->teardown) test->teardown(test->data);
+ // if we got here it's ok
+#ifdef COLOR_OK
+ color_print(ANSI_BGREEN, "[OK]");
+#else
+ printf("[OK]\n");
+#endif
+ num_ok++;
+ } else {
+ color_print(ANSI_BRED, "[FAIL]");
+ num_fail++;
+ }
+ if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer);
+ }
+ index++;
+ }
+ }
+ uint64_t t2 = getCurrentTime();
+
+ const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN;
+ char results[80];
+ 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);
+ color_print(color, results);
+ return num_fail;
+}
+
+#endif
+
+#endif
diff --git a/test/expected.out b/test/expected.out
new file mode 100644
index 0000000..3ed0d13
--- /dev/null
+++ b/test/expected.out
@@ -0,0 +1,277 @@
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+511
+[
+ 0
+ 1
+ 10
+ 23
+ 24
+ 25
+ 100
+ 1000
+ 1000000
+ 1000000000000
+ 18446744073709551615
+ 2(
+ h'010000000000000000'
+ )
+ OVERFLOW
+ 3(
+ h'010000000000000000'
+ )
+ -1
+ -10
+ -100
+ -1000
+ 0.000000e+00
+ -0.000000e+00
+ 1.000000e+00
+ 1.100000e+00
+ 1.500000e+00
+ 6.550400e+04
+ 1.000000e+05
+ 3.402823e+38
+ 1.000000e+300
+ 5.960464e-08
+ 6.103516e-05
+ -4.000000e+00
+ -4.100000e+00
+ inf
+ nan
+ -inf
+ inf
+ nan
+ -inf
+ inf
+ nan
+ -inf
+ false
+ true
+ null
+ simple(23)
+ simple(16)
+ simple(24)
+ simple(255)
+ 0(
+ "2013-03-21T20:04:00Z"
+ )
+ 1(
+ 1363896240
+ )
+ 1(
+ 1.363896e+09
+ )
+ 23(
+ h'01020304'
+ )
+ 24(
+ h'6449455446'
+ )
+ 32(
+ "http://www.example.com"
+ )
+ h''
+ h'01020304'
+ ""
+ "a"
+ "IETF"
+ ""\"
+ "ü"
+ "水"
+ "𐅑"
+ [
+ ]
+ [
+ 1
+ 2
+ 3
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ ]
+ {
+ }
+ {
+ 1
+ 2
+ 3
+ 4
+ }
+ {
+ "a"
+ 1
+ "b"
+ [
+ 2
+ 3
+ ]
+ }
+ [
+ "a"
+ {
+ "b"
+ "c"
+ }
+ ]
+ {
+ "a"
+ "A"
+ "b"
+ "B"
+ "c"
+ "C"
+ "d"
+ "D"
+ "e"
+ "E"
+ }
+ (_
+
+ h'0102'
+ h'030405'
+ )
+ (_
+ "strea"
+ "ming"
+ )
+ [
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ [
+ 2
+ 3
+ ]
+ [
+ 4
+ 5
+ ]
+ ]
+ [
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ ]
+ {
+ "a"
+ 1
+ "b"
+ [
+ 2
+ 3
+ ]
+ }
+ [
+ "a"
+ {
+ "b"
+ "c"
+ }
+ ]
+ {
+ "Fun"
+ true
+ "Amt"
+ -2
+ }
+]
+
+CN_CBOR_ERR_BREAK_OUTSIDE_INDEF at 1
+CN_CBOR_ERR_MT_UNDEF_FOR_INDEF at 1
+CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED at 1
+CN_CBOR_ERR_OUT_OF_DATA at 1
+CN_CBOR_ERR_RESERVED_AI at 1
+CN_CBOR_ERR_ODD_SIZE_INDEF_MAP at 3
+CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING at 2
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
diff --git a/test/test.c b/test/test.c
new file mode 100644
index 0000000..d24992f
--- /dev/null
+++ b/test/test.c
@@ -0,0 +1,136 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "cn-cbor/cn-cbor.h"
+
+#ifdef USE_CBOR_CONTEXT
+#define CBOR_CONTEXT_PARAM , NULL
+#else
+#define CBOR_CONTEXT_PARAM
+#endif
+
+#define ERROR(msg, p) fprintf(stderr, "ERROR: " msg " %s\n", (p));
+
+static unsigned char* load_file(const char* filepath, unsigned char **end) {
+ struct stat st;
+ if (stat(filepath, &st)==-1) {
+ ERROR("can't find file", filepath);
+ return 0;
+ }
+ int fd=open(filepath, O_RDONLY);
+ if (fd==-1) {
+ ERROR("can't open file", filepath);
+ return 0;
+ }
+ unsigned char* text=malloc(st.st_size+1); // this is not going to be freed
+ if (st.st_size!=read(fd, text, st.st_size)) {
+ ERROR("can't read file", filepath);
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ text[st.st_size]='\0';
+ *end = text + st.st_size;
+ return text;
+}
+
+static void dump(const cn_cbor* cb, char* out, char** end, int indent) {
+ if (!cb)
+ goto done;
+ int i;
+ cn_cbor* cp;
+ char finchar = ')'; /* most likely */
+
+#define CPY(s, l) memcpy(out, s, l); out += l;
+#define OUT(s) CPY(s, sizeof(s)-1)
+#define PRF(f, a) out += sprintf(out, f, a)
+
+ for (i = 0; i < indent; i++) *out++ = ' ';
+ switch (cb->type) {
+ case CN_CBOR_TEXT_CHUNKED: OUT("(_\n"); goto sequence;
+ case CN_CBOR_BYTES_CHUNKED: OUT("(_\n\n"); goto sequence;
+ case CN_CBOR_TAG: PRF("%ld(\n", cb->v.sint); goto sequence;
+ case CN_CBOR_ARRAY: finchar = ']'; OUT("[\n"); goto sequence;
+ case CN_CBOR_MAP: finchar = '}'; OUT("{\n"); goto sequence;
+ sequence:
+ for (cp = cb->first_child; cp; cp = cp->next) {
+ dump(cp, out, &out, indent+2);
+ }
+ for (i=0; i<indent; i++) *out++ = ' ';
+ *out++ = finchar;
+ break;
+ case CN_CBOR_BYTES: OUT("h'");
+ for (i=0; i<cb->length; i++)
+ PRF("%02x", cb->v.str[i] & 0xff);
+ *out++ = '\'';
+ break;
+ case CN_CBOR_TEXT: *out++ = '"';
+ CPY(cb->v.str, cb->length); /* should escape stuff */
+ *out++ = '"';
+ break;
+ case CN_CBOR_NULL: OUT("null"); break;
+ case CN_CBOR_TRUE: OUT("true"); break;
+ case CN_CBOR_FALSE: OUT("false"); break;
+ case CN_CBOR_UNDEF: OUT("simple(23)"); break;
+ case CN_CBOR_INT: PRF("%ld", cb->v.sint); break;
+ case CN_CBOR_UINT: PRF("%lu", cb->v.uint); break;
+ case CN_CBOR_DOUBLE: PRF("%e", cb->v.dbl); break;
+ case CN_CBOR_SIMPLE: PRF("simple(%ld)", cb->v.sint); break;
+ default: PRF("???%d???", cb->type); break;
+ }
+ *out++ = '\n';
+done:
+ *end = out;
+}
+
+
+const char *err_name[] = {
+ "CN_CBOR_NO_ERROR",
+ "CN_CBOR_ERR_OUT_OF_DATA",
+ "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED",
+ "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP",
+ "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF",
+ "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF",
+ "CN_CBOR_ERR_RESERVED_AI",
+ "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING",
+ "CN_CBOR_ERR_OUT_OF_MEMORY",
+ "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED",
+};
+
+static void cn_cbor_decode_test(const unsigned char *buf, int len) {
+ struct cn_cbor_errback back;
+ const cn_cbor *ret = cn_cbor_decode(buf, len CBOR_CONTEXT_PARAM, &back);
+ if (ret)
+ printf("oops 1");
+ printf("%s at %d\n", err_name[back.err], back.pos);
+}
+
+int main() {
+ char buf[100000];
+ unsigned char *end;
+ char *bufend;
+ unsigned char *s = load_file("cases.cbor", &end);
+ printf("%zd\n", end-s);
+ cn_cbor *cb = cn_cbor_decode(s, end-s CBOR_CONTEXT_PARAM, 0);
+ if (cb) {
+ dump(cb, buf, &bufend, 0);
+ *bufend = 0;
+ printf("%s\n", buf);
+ cn_cbor_free(cb CBOR_CONTEXT_PARAM);
+ cb = 0; /* for leaks testing */
+ }
+ cn_cbor_decode_test((const unsigned char*)"\xff", 1); /* break outside indef */
+ cn_cbor_decode_test((const unsigned char*)"\x1f", 1); /* mt undef for indef */
+ cn_cbor_decode_test((const unsigned char*)"\x00\x00", 2); /* not all data consumed */
+ cn_cbor_decode_test((const unsigned char*)"\x81", 1); /* out of data */
+ cn_cbor_decode_test((const unsigned char*)"\x1c", 1); /* reserved ai */
+ cn_cbor_decode_test((const unsigned char*)"\xbf\x00\xff", 3); /* odd size indef map */
+ cn_cbor_decode_test((const unsigned char*)"\x7f\x40\xff", 3); /* wrong nesting in indef string */
+ system("leaks test");
+}
+
+/* cn-cbor.c:112: CN_CBOR_FAIL("out of memory"); */