Merge upstream for the first time am: 3798baae63
am: 8a9d358531

Change-Id: I4140da4f2effcdd77979049fa6645f93aa7ad47e
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 <[email protected]>
+
+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 <[email protected]>
+#
+
+
+#
+# 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 <[email protected]>
+#
+
+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 <[email protected]>
+#
+# 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"); */