From c9938ea8389cd723357f6d7a7e5216775e6a5b92 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Sun, 7 Jan 2018 22:09:12 +0100
Subject: [PATCH] :construction: started UBJSON implementation

---
 src/json.hpp                                  |  405 ++++-
 test/Makefile                                 |    1 +
 .../CouchDB4k.formatted.json                  |  275 +++
 .../universal-binary-json-java/CouchDB4k.ubj  |  Bin 0 -> 2724 bytes
 test/data/universal-binary-json-java/LICENSE  |  202 +++
 .../MediaContent.formatted.json               |   34 +
 .../MediaContent.ubj                          |  Bin 0 -> 442 bytes
 test/data/universal-binary-json-java/README   |  357 ++++
 .../TwitterTimeline.formatted.json            |   80 +
 .../TwitterTimeline.ubj                       |  Bin 0 -> 1792 bytes
 test/data/universal-binary-json-java/url.txt  |    1 +
 test/src/unit-ubjson.cpp                      | 1593 +++++++++++++++++
 12 files changed, 2929 insertions(+), 19 deletions(-)
 create mode 100644 test/data/universal-binary-json-java/CouchDB4k.formatted.json
 create mode 100644 test/data/universal-binary-json-java/CouchDB4k.ubj
 create mode 100644 test/data/universal-binary-json-java/LICENSE
 create mode 100644 test/data/universal-binary-json-java/MediaContent.formatted.json
 create mode 100644 test/data/universal-binary-json-java/MediaContent.ubj
 create mode 100644 test/data/universal-binary-json-java/README
 create mode 100644 test/data/universal-binary-json-java/TwitterTimeline.formatted.json
 create mode 100644 test/data/universal-binary-json-java/TwitterTimeline.ubj
 create mode 100644 test/data/universal-binary-json-java/url.txt
 create mode 100644 test/src/unit-ubjson.cpp

diff --git a/src/json.hpp b/src/json.hpp
index a12fcbe3..a356bdef 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -4534,6 +4534,27 @@ class binary_reader
         return res;
     }
 
+    /*!
+    @brief create a JSON value from UBJSON input
+
+    @param[in] strict  whether to expect the input to be consumed completed
+    @return JSON value created from UBJSON input
+
+    @throw parse_error.110 if input ended unexpectedly or the end of file was
+                           not reached when @a strict was set to true
+    @throw parse_error.112 if unsupported byte was read
+    */
+    BasicJsonType parse_ubjson(const bool strict)
+    {
+        const auto res = parse_ubjson_internal();
+        if (strict)
+        {
+            get();
+            check_eof(true);
+        }
+        return res;
+    }
+
     /*!
     @brief determine system byte order
 
@@ -5195,6 +5216,68 @@ class binary_reader
         }
     }
 
+    /*!
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true, default) or whether the last read
+                         character should be considered instead
+    */
+    BasicJsonType parse_ubjson_internal(const bool get_char = true)
+    {
+        switch (get_char ? get() : current)
+        {
+            case std::char_traits<char>::eof():  // EOF
+                JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+            case 'T':  // true
+                return true;
+            case 'F':  // false
+                return false;
+
+            case 'Z':  // null
+                return nullptr;
+
+            case 'N':  // no-op
+                return parse_ubjson_internal();  // read next byte
+
+            case 'U':
+                return get_number<uint8_t>();
+            case 'i':
+                return get_number<int8_t>();
+            case 'I':
+                return get_number<int16_t>();
+            case 'l':
+                return get_number<int32_t>();
+            case 'L':
+                return get_number<int64_t>();
+            case 'd':
+                return get_number<float>();
+            case 'D':
+                return get_number<double>();
+
+            case 'C':  // char
+            {
+                get();
+                check_eof();
+                return std::string(1, static_cast<char>(current));
+            }
+
+            case 'S':  // string
+                return get_ubjson_string();
+
+            case '[':  // array
+                return get_ubjson_array();
+
+            case '{':  // object
+                return get_ubjson_object();
+
+            default: // anything else
+                std::stringstream ss;
+                ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+                JSON_THROW(parse_error::create(112, chars_read,
+                                               "error reading UBJSON; last byte: 0x" + ss.str()));
+        }
+    }
+
     /*!
     @brief get next character from the input
 
@@ -5495,6 +5578,80 @@ class binary_reader
         return result;
     }
 
+    /*!
+    @brief reads a UBJSON string
+
+    This function first reads starting bytes to determine the expected
+    string length and then copies this number of bytes into a string.
+
+    @param[in] get_char  whether a new character should be retrieved from the
+                         input (true, default) or whether the last read
+                         character should be considered instead
+
+    @return string
+
+    @throw parse_error.110 if input ended
+    @throw parse_error.113 if an unexpected byte is read
+    */
+    std::string get_ubjson_string(const bool get_char = true)
+    {
+        if (get_char)
+        {
+            get();
+        }
+
+        check_eof();
+
+        switch (current)
+        {
+            case 'U':
+                return get_string(get_number<uint8_t>());
+            case 'i':
+                return get_string(get_number<int8_t>());
+            case 'I':
+                return get_string(get_number<int16_t>());
+            case 'l':
+                return get_string(get_number<int32_t>());
+            case 'L':
+                return get_string(get_number<int64_t>());
+            default:
+                std::stringstream ss;
+                ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+                JSON_THROW(parse_error::create(113, chars_read,
+                                               "expected a UBJSON string; last byte: 0x" + ss.str()));
+        }
+    }
+
+    BasicJsonType get_ubjson_array()
+    {
+        BasicJsonType result = value_t::array;
+
+        while (get() != ']')
+        {
+            // skip no-op
+            if (current == 'N')
+            {
+                continue;
+            }
+            result.push_back(parse_ubjson_internal(false));
+        }
+
+        return result;
+    }
+
+    BasicJsonType get_ubjson_object()
+    {
+        BasicJsonType result = value_t::object;
+
+        while (get() != '}')
+        {
+            auto key = get_ubjson_string(false);
+            result[std::move(key)] = parse_ubjson_internal();
+        }
+
+        return result;
+    }
+
     /*!
     @brief check if input ended
     @throw parse_error.110 if input ended
@@ -5678,23 +5835,23 @@ class binary_writer
                 {
                     write_number(static_cast<uint8_t>(0x60 + N));
                 }
-                else if (N <= 0xFF)
+                else if (N <= (std::numeric_limits<uint8_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x78));
                     write_number(static_cast<uint8_t>(N));
                 }
-                else if (N <= 0xFFFF)
+                else if (N <= (std::numeric_limits<uint16_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x79));
                     write_number(static_cast<uint16_t>(N));
                 }
-                else if (N <= 0xFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x7A));
                     write_number(static_cast<uint32_t>(N));
                 }
                 // LCOV_EXCL_START
-                else if (N <= 0xFFFFFFFFFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint64_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x7B));
                     write_number(static_cast<uint64_t>(N));
@@ -5716,23 +5873,23 @@ class binary_writer
                 {
                     write_number(static_cast<uint8_t>(0x80 + N));
                 }
-                else if (N <= 0xFF)
+                else if (N <= (std::numeric_limits<uint8_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x98));
                     write_number(static_cast<uint8_t>(N));
                 }
-                else if (N <= 0xFFFF)
+                else if (N <= (std::numeric_limits<uint16_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x99));
                     write_number(static_cast<uint16_t>(N));
                 }
-                else if (N <= 0xFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x9A));
                     write_number(static_cast<uint32_t>(N));
                 }
                 // LCOV_EXCL_START
-                else if (N <= 0xFFFFFFFFFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint64_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0x9B));
                     write_number(static_cast<uint64_t>(N));
@@ -5755,23 +5912,23 @@ class binary_writer
                 {
                     write_number(static_cast<uint8_t>(0xA0 + N));
                 }
-                else if (N <= 0xFF)
+                else if (N <= (std::numeric_limits<uint8_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0xB8));
                     write_number(static_cast<uint8_t>(N));
                 }
-                else if (N <= 0xFFFF)
+                else if (N <= (std::numeric_limits<uint16_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0xB9));
                     write_number(static_cast<uint16_t>(N));
                 }
-                else if (N <= 0xFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0xBA));
                     write_number(static_cast<uint32_t>(N));
                 }
                 // LCOV_EXCL_START
-                else if (N <= 0xFFFFFFFFFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     oa->write_character(static_cast<CharType>(0xBB));
                     write_number(static_cast<uint64_t>(N));
@@ -5939,19 +6096,19 @@ class binary_writer
                     // fixstr
                     write_number(static_cast<uint8_t>(0xA0 | N));
                 }
-                else if (N <= 255)
+                else if (N <= (std::numeric_limits<uint8_t>::max)())
                 {
                     // str 8
                     oa->write_character(static_cast<CharType>(0xD9));
                     write_number(static_cast<uint8_t>(N));
                 }
-                else if (N <= 65535)
+                else if (N <= (std::numeric_limits<uint16_t>::max)())
                 {
                     // str 16
                     oa->write_character(static_cast<CharType>(0xDA));
                     write_number(static_cast<uint16_t>(N));
                 }
-                else if (N <= 4294967295)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     // str 32
                     oa->write_character(static_cast<CharType>(0xDB));
@@ -5974,13 +6131,13 @@ class binary_writer
                     // fixarray
                     write_number(static_cast<uint8_t>(0x90 | N));
                 }
-                else if (N <= 0xFFFF)
+                else if (N <= (std::numeric_limits<uint16_t>::max)())
                 {
                     // array 16
                     oa->write_character(static_cast<CharType>(0xDC));
                     write_number(static_cast<uint16_t>(N));
                 }
-                else if (N <= 0xFFFFFFFF)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     // array 32
                     oa->write_character(static_cast<CharType>(0xDD));
@@ -6004,13 +6161,13 @@ class binary_writer
                     // fixmap
                     write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
                 }
-                else if (N <= 65535)
+                else if (N <= (std::numeric_limits<uint16_t>::max)())
                 {
                     // map 16
                     oa->write_character(static_cast<CharType>(0xDE));
                     write_number(static_cast<uint16_t>(N));
                 }
-                else if (N <= 4294967295)
+                else if (N <= (std::numeric_limits<uint32_t>::max)())
                 {
                     // map 32
                     oa->write_character(static_cast<CharType>(0xDF));
@@ -6031,6 +6188,110 @@ class binary_writer
         }
     }
 
+    /*!
+    @brief[in] j  JSON value to serialize
+    */
+    void write_ubjson(const BasicJsonType& j, const bool use_count = false)
+    {
+        switch (j.type())
+        {
+            case value_t::null:
+            {
+                oa->write_character(static_cast<CharType>('Z'));
+                break;
+            }
+
+            case value_t::boolean:
+            {
+                oa->write_character(j.m_value.boolean
+                                    ? static_cast<CharType>('T')
+                                    : static_cast<CharType>('F'));
+                break;
+            }
+
+            case value_t::number_integer:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_integer);
+                break;
+            }
+
+            case value_t::number_unsigned:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_unsigned);
+                break;
+            }
+
+            case value_t::number_float:
+            {
+                write_number_with_ubjson_prefix(j.m_value.number_float);
+                break;
+            }
+
+            case value_t::string:
+            {
+                oa->write_character(static_cast<CharType>('S'));
+                write_number_with_ubjson_prefix(j.m_value.string->size());
+                oa->write_characters(
+                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+                    j.m_value.string->size());
+                break;
+            }
+
+            case value_t::array:
+            {
+                oa->write_character(static_cast<CharType>('['));
+
+                if (use_count)
+                {
+                    oa->write_character(static_cast<CharType>('#'));
+                    write_number_with_ubjson_prefix(j.m_value.array->size());
+                }
+
+                for (const auto& el : *j.m_value.array)
+                {
+                    write_ubjson(el, use_count);
+                }
+
+                if (not use_count)
+                {
+                    oa->write_character(static_cast<CharType>(']'));
+                }
+
+                break;
+            }
+
+            case value_t::object:
+            {
+                oa->write_character(static_cast<CharType>('{'));
+
+                if (use_count)
+                {
+                    oa->write_character(static_cast<CharType>('#'));
+                    write_number_with_ubjson_prefix(j.m_value.object->size());
+                }
+
+                for (const auto& el : *j.m_value.object)
+                {
+                    write_number_with_ubjson_prefix(el.first.size());
+                    oa->write_characters(
+                        reinterpret_cast<const CharType*>(el.first.c_str()),
+                        el.first.size());
+                    write_ubjson(el.second, use_count);
+                }
+
+                if (not use_count)
+                {
+                    oa->write_character(static_cast<CharType>('}'));
+                }
+
+                break;
+            }
+
+            default:
+                break;
+        }
+    }
+
   private:
     /*
     @brief write a number to output input
@@ -6058,6 +6319,82 @@ class binary_writer
         oa->write_characters(vec.data(), sizeof(NumberType));
     }
 
+    template<typename NumberType>
+    void write_number_with_ubjson_prefix(const NumberType n)
+    {
+        if (std::is_floating_point<NumberType>::value)
+        {
+            oa->write_character(static_cast<CharType>('D'));  // float64
+            write_number(n);
+        }
+        else if (std::is_unsigned<NumberType>::value)
+        {
+            if (n <= (std::numeric_limits<int8_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('i'));  // uint8
+                write_number(static_cast<uint8_t>(n));
+            }
+            else if (n <= (std::numeric_limits<uint8_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('U'));  // uint8
+                write_number(static_cast<uint8_t>(n));
+            }
+            else if (n <= (std::numeric_limits<int16_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('I'));  // int16
+                write_number(static_cast<int16_t>(n));
+            }
+            else if (n <= (std::numeric_limits<int32_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('l'));  // int32
+                write_number(static_cast<int32_t>(n));
+            }
+            else if (n <= (std::numeric_limits<int64_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('L'));  // int64
+                write_number(static_cast<int64_t>(n));
+            }
+            else
+            {
+                // TODO: replace by exception
+                assert(false);
+            }
+        }
+        else
+        {
+            if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('i'));  // int8
+                write_number(static_cast<int8_t>(n));
+            }
+            else if ((std::numeric_limits<uint8_t>::min)() <= n and n <= (std::numeric_limits<uint8_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('U'));  // uint8
+                write_number(static_cast<uint8_t>(n));
+            }
+            else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('I'));  // int16
+                write_number(static_cast<int16_t>(n));
+            }
+            else if (-(std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('l'));  // int32
+                write_number(static_cast<int32_t>(n));
+            }
+            else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())
+            {
+                oa->write_character(static_cast<CharType>('L'));  // int64
+                write_number(static_cast<int64_t>(n));
+            }
+            else
+            {
+                // TODO: replace by exception
+                assert(false);
+            }
+        }
+    }
+
   private:
     /// whether we can assume little endianess
     const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
@@ -13511,6 +13848,23 @@ class basic_json
         binary_writer<char>(o).write_msgpack(j);
     }
 
+    static std::vector<uint8_t> to_ubjson(const basic_json& j)
+    {
+        std::vector<uint8_t> result;
+        to_ubjson(j, result);
+        return result;
+    }
+
+    static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o)
+    {
+        binary_writer<uint8_t>(o).write_ubjson(j);
+    }
+
+    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o)
+    {
+        binary_writer<char>(o).write_ubjson(j);
+    }
+
     /*!
     @brief create a JSON value from an input in CBOR format
 
@@ -13705,6 +14059,19 @@ class basic_json
         return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict);
     }
 
+    static basic_json from_ubjson(detail::input_adapter i,
+                                  const bool strict = true)
+    {
+        return binary_reader(i).parse_ubjson(strict);
+    }
+
+    template<typename A1, typename A2,
+             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
+    static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true)
+    {
+        return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_ubjson(strict);
+    }
+
     /// @}
 
     //////////////////////////
diff --git a/test/Makefile b/test/Makefile
index a170acb9..d13fa1e8 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -39,6 +39,7 @@ SOURCES = src/unit.cpp \
           src/unit-regression.cpp \
           src/unit-serialization.cpp \
           src/unit-testsuites.cpp \
+          src/unit-ubjson.cpp \
           src/unit-unicode.cpp
 
 OBJECTS = $(SOURCES:.cpp=.o)
diff --git a/test/data/universal-binary-json-java/CouchDB4k.formatted.json b/test/data/universal-binary-json-java/CouchDB4k.formatted.json
new file mode 100644
index 00000000..d12387da
--- /dev/null
+++ b/test/data/universal-binary-json-java/CouchDB4k.formatted.json
@@ -0,0 +1,275 @@
+{
+   "data3":"ColreUHAtuYoUOx1N4ZloouQt2o6ugnUT6eYtS10gu7niM8i0vEiNufpk1RlMQXaHXlIwQBDsMFDFUQcFeg2vW5eD259Xm",
+   "data4":"zCxriJhL726WNNTdJJzurgSA8vKT6rHA0cFCb9koZcLUMXg4rmoXVPqIHWYaCV0ovl2t6xm7I1Hm36jXpLlXEb8fRfbwBeTW2V0OAsVqYH8eAT",
+   "data0":"9EVqHm5ARqcEB5jq2D14U2bCJPyBY0JWDr1Tjh8gTB0sWUNjqYiWDxFzlx6S",
+   "data7":"Bi1ujcgEvfADfBeyZudE7nwxc3Ik8qpYjsJIfKmwOMEbV2L3Bi0x2tcRpGuf4fiyvIbypDvJN1PPdQtfQW1Gv6zccXHwwZwKzUq6",
+   "data5":{
+      "integers":[
+         756509,
+         116117,
+         776378,
+         275045,
+         703447,
+         50156,
+         685803,
+         147958,
+         941747,
+         905651,
+         57367,
+         530248,
+         312888,
+         740951,
+         988947,
+         450154
+      ],
+      "float1":76.572,
+      "float2":83.5299,
+      "nested1":{
+         "integers":[
+            756509,
+            116117,
+            776378,
+            275045,
+            703447,
+            50156,
+            685803,
+            147958,
+            941747,
+            905651,
+            57367,
+            530248,
+            312888,
+            740951,
+            988947,
+            450154
+         ],
+         "floats":[
+            43121609.5543,
+            99454976.3019,
+            32945584.756,
+            18122905.9212,
+            45893183.44,
+            63052200.6225,
+            69032152.6897,
+            3748217.6946,
+            75449850.474,
+            37111527.415,
+            84852536.859,
+            32906366.487,
+            27027600.417,
+            63874310.5614,
+            39440408.51,
+            97176857.1716,
+            97438252.1171,
+            49728043.5056,
+            40818570.245,
+            41415831.8949,
+            24796297.4251,
+            2819085.3449,
+            84263963.4848,
+            74503228.6878,
+            67925677.403,
+            4758851.9417,
+            75227407.9214,
+            76946667.8403,
+            72518275.9469,
+            94167085.9588,
+            75883067.8321,
+            27389831.6101,
+            57987075.5053,
+            1298995.2674,
+            14590614.6939,
+            45292214.2242,
+            3332166.364,
+            53784167.729,
+            25193846.1867,
+            81456965.477,
+            68532032.39,
+            73820009.7952,
+            57736110.5717,
+            37304166.7363,
+            20054244.864,
+            29746392.7397,
+            86467624.6,
+            45192685.8793,
+            44008816.5186,
+            1861872.8736,
+            14595859.467,
+            87795257.6703,
+            57768720.8303,
+            18290154.3126,
+            45893183.44,
+            63052200.6225,
+            69032152.6897,
+            3748217.6946,
+            75449850.474,
+            37111527.415,
+            84852536.859,
+            32906366.487,
+            27027600.417,
+            63874310.5614,
+            39440408.51,
+            97176857.1716,
+            97438252.1171,
+            49728043.5056,
+            40818570.245,
+            41415831.8949,
+            24796297.4251,
+            2819085.3449,
+            84263963.4848,
+            74503228.6878,
+            67925677.403,
+            4758851.9417,
+            75227407.9214,
+            76946667.8403,
+            72518275.9469,
+            94167085.9588,
+            75883067.8321,
+            27389831.6101,
+            57987075.5053,
+            1298995.2674,
+            80858801.2712,
+            98262252.4656,
+            51612877.944,
+            33397812.7835,
+            36089655.3049,
+            50164685.8153,
+            16852105.5192,
+            61171929.752,
+            86376339.7175,
+            73009014.5521,
+            7397302.331,
+            34345128.9589,
+            98343269.4418,
+            95039116.9058,
+            44833102.5752,
+            51052997.8873,
+            22719195.6783,
+            64883244.8699
+         ]
+      },
+      "nested2":{
+         "integers":[
+            756509,
+            116117,
+            776378,
+            275045,
+            703447,
+            50156,
+            685803,
+            147958,
+            941747,
+            905651,
+            57367,
+            530248,
+            312888,
+            740951,
+            988947,
+            450154
+         ],
+         "float1":76.572,
+         "float2":83.5299
+      }
+   },
+   "strings":[
+      "edx5XzRkPVeEW2MBQzQMcUSuMI4FntjhlJ9VGhQaBHKPEazAaT",
+      "2fQUbzRUax4A",
+      "jURcBZ0vrJcmf2roZUMzZJQoTsKZDIdj7KhO7itskKvM80jBU9",
+      "8jKLmo3N2zYdKyTyfTczfr2x6bPaarorlnTNJ7r8lIkiZyBvrP",
+      "jbUeAVOdBSPzYmYhH0sabUHUH39O5e",
+      "I8yAQKZsyZhMfpzWjArQU9pQ6PfU6b14q2eWvQjtCUdgAUxFjg",
+      "97N8ZmGcxRZO4ZabzRRcY4KVHqxJwQ8qY",
+      "0DtY1aWXmUfJENt9rYW9",
+      "DtpBUEppPwMnWexi8eIIxlXRO3GUpPgeNFG9ONpWJYvk8xBkVj",
+      "YsX8V2xOrTw6LhNIMMhO4F4VXFyXUXFr66L3sTkLWgFA9NZuBV",
+      "fKYYthv8iFvaYoFoYZyB",
+      "zGuLsPXoJqMbO4PcePteZfDMYFXdWtvNF8WvaplXypsd6"
+   ],
+   "data1":"9EVqHm5ARqcEB5jq21v2g0jVcG9CXB0Abk7uAF4NHYyTzeF3TnHhpZBECD14U2bCJPyBY0JWDr1Tjh8gTB0sWUNjqYiWDxFzlx6S",
+   "integers":[
+      756509,
+      116117,
+      776378,
+      275045,
+      703447,
+      50156,
+      685803,
+      147958,
+      941747,
+      905651,
+      57367,
+      530248,
+      312888,
+      740951,
+      988947,
+      450154
+   ],
+   "more_nested":{
+      "integers":[
+         756509,
+         116117,
+         776378,
+         275045,
+         703447,
+         50156,
+         685803,
+         147958,
+         941747,
+         905651,
+         57367,
+         530248,
+         312888,
+         740951,
+         988947,
+         450154
+      ],
+      "float1":76.572,
+      "float2":83.5299,
+      "nested1":{
+         "integers":[
+            756509,
+            116117,
+            776378,
+            275045,
+            703447,
+            50156,
+            685803,
+            147958,
+            941747,
+            905651,
+            57367,
+            530248,
+            312888,
+            740951,
+            988947,
+            450154
+         ]
+      },
+      "nested2":{
+         "strings":[
+            "2fQUbzRUax4A",
+            "jURcBZ0vrJcmf2roZUMzZJQoTsKZDIdj7KhO7itskKvM80jBU9",
+            "8jKLmo3N2zYdKyTyfTczfr2x6bPaarorlnTNJ7r8lIkiZyBvrP",
+            "jbUeAVOdBSPzYmYhH0sabUHUH39O5e",
+            "I8yAQKZsyZhMfpzWjArQU9pQ6PfU6b14q2eWvQjtCUdgAUxFjg",
+            "97N8ZmGcxRZO4ZabzRRcY4KVHqxJwQ8qY",
+            "0DtY1aWXmUfJENt9rYW9",
+            "DtpBUEppPwMnWexi8eIIxlXRO3GUpPgeNFG9ONpWJYvk8xBkVj",
+            "YsX8V2xOrTw6LhNIMMhO4F4VXFyXUXFr66L3sTkLWgFA9NZuBV",
+            "fKYYthv8iFvaYoFoYZyB",
+            "zGuLsPXoJqMbO4PcePteZfDMYFXdWtvNF8WvaplXypsd6"
+         ],
+         "integers":[
+            756509,
+            116117,
+            776378,
+            57367,
+            530248,
+            312888,
+            740951,
+            988947,
+            450154
+         ]
+      }
+   }
+}
\ No newline at end of file
diff --git a/test/data/universal-binary-json-java/CouchDB4k.ubj b/test/data/universal-binary-json-java/CouchDB4k.ubj
new file mode 100644
index 0000000000000000000000000000000000000000..75a6465bc74dcbec4c856640935b331b9e6fa0c3
GIT binary patch
literal 2724
zcmeHJS;!kz6i!{Lty(J<ixmY$6s$`!$v>Hh+svO#X1O!T<e$kbw1vr>nIw~ClDU(~
zB#OA;LVr<P5ouj;0};^&#kEQWw-+hugH}PY_Cdvppr{X`IM@~+6u}2K@a1yu<t+Ey
zd%p9XU==<Z?8wgJEsH@vbTy8SCT5`3lAK(y`av*JA|fCs?V+ZUt{E-mvh7K6*p;Ym
zHm-E#i8t!y8huGIY|iNOQ-vvGi7m65;;?R;i1h`xOe~a)!ND*EJe)5kVOQv&B0=i1
ztb!oSCt-VureaYgLypcm>|(3b3oHj|lF=@NgTQFk$2_N-_F^*|#C;+nlR=Tsaf3YR
z8za;=DlN)uc&#bpsyflk)@a-un;hlR>cOyCd`YR&9CL#Ox-oVt%z{5A%DI9@v=)W>
zj4`u<UJi4r-=W$nlf}9w`(v}KmlJm0PspW%PKp?qZf@c`?Mm#?Wsh-Z)&wfW;WTmb
ze2*HBOdkupCl01HsnTi^D9?1W2@yGsQFY=KyzVUKTeDF)7UW#L4oc)HdafFibH_2b
z>C~Evb8So>47Ly)jgRdPBe(5_*j~$Lj$1yR&#ZXu&Q#j_0-rgGaQV#YUGMRk%qs^{
zDgJ`b96^7_XVz?aiqD+zcDiIfUe9NaUC*V;uT!a+tn--@*PqO1R(|B;m0mxvqZ}|h
z&i?Swh5+V~6KnXGp^GCIa={UQ!v(PI0XqBYuK~UEWCp|^ti2#^ElYqdI_Y*mqZ@rd
z*FSa|2=LmcfNuM2H=q-?y#VNe_dZRP*IW+bo&pC5Kgs|)C0YsSdGn!kd7lMz>66QV
zPJ4PkpwoXm2gH{ytblO-+66%OKX*N#TQ}Sc=-OM~1!3F8sdU>_X93#&%DsTLDs@0l
z-IKby{lzMXZ+-tNpv{F9z~8rRCx}}fybI9zuRj5TXrBs5+L)fbOzr}7_vX(5UH8mq
zfNpv7OF)}0I{;{n^Z|%FG9ieceBB51@PQjrTlY#p=kD7C{Ql}qslR7W2J}euMryI;
zRS=iAe)})Y9PKQ<3ussGK0xLDc@TfvvjNbm$MyiaeA71|UbDRdsB4jFmb=rxT)yHf
z5ccgJ06Oxh^HQ@rE&%j&<sCp9SE_)Rm&YJJt_=ZAuUkG`ZbWe8-%xJ9TkkjuyTdlN
zkH>@y$$~L&^y*Ewq7xFM%oWMemL?KkV26?4=?kT1wWHV!C)O+WoVHbbJmD!?Yu?c8
zq(EcB*BTCEW#dqA1|AUxmL|<DK?zhWT4f&mqS&bwyAke*u|#EkMk`^0@<lWV@-i_u
zL7b^GPj%*ANF-#dZrfoH_J^u06ho@d_qx`MiNiWR!*6LW-K+t#RG*szv%_Vv-O@OX
z%a>{kE+%+tMk}I)XI4k@MswY#Lq#i%6teDVWGhz~6RsXBezd4To7NK6Z{ssdMVYb&
zRVQg!wSr}*Ni`g^AU3&iB1{!(Y~pp<a%ARg-56+|P?4ijXzFPK<!HocmC>j^m4>>T
zbSanTlfKcY<*VAL-gafSTB^w-T`=Pwl`y@gj|meSRFg<*p*kf|N9HA|Q!B8Aroqk(
z&0s^4M0u?CkltqLl59<wCSK=>rWti&s>{Z<8L)wwz6QQvUY#IZHv(ZSwQ7aB<JKeB
z^2(CQ8lXq9%u;%6kNU=JgyB$k&0+WrALL@9o%NeewX|q3S-RCLPH46ubLLE)yKG(^
za-EUIR2KgXZ>&1p!HK`1g|8k2p)3E=tp9nkSn-#l_Yfx$DJLCs7_P_t554~w^nRUZ
MR{dt4`J-XwXD4K^8UO$Q

literal 0
HcmV?d00001

diff --git a/test/data/universal-binary-json-java/LICENSE b/test/data/universal-binary-json-java/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/test/data/universal-binary-json-java/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/test/data/universal-binary-json-java/MediaContent.formatted.json b/test/data/universal-binary-json-java/MediaContent.formatted.json
new file mode 100644
index 00000000..fe547871
--- /dev/null
+++ b/test/data/universal-binary-json-java/MediaContent.formatted.json
@@ -0,0 +1,34 @@
+{
+   "Media":{
+      "uri":"http://javaone.com/keynote.mpg",
+      "title":"Javaone Keynote",
+      "width":640,
+      "height":480,
+      "format":"video/mpg4",
+      "duration":18000000,
+      "size":58982400,
+      "bitrate":262144,
+      "persons":[
+         "Bill Gates",
+         "Steve Jobs"
+      ],
+      "player":"JAVA",
+      "copyright":null
+   },
+   "Images":[
+      {
+         "uri":"http://javaone.com/keynote_large.jpg",
+         "title":"Javaone Keynote",
+         "width":1024,
+         "height":768,
+         "size":"LARGE"
+      },
+      {
+         "uri":"http://javaone.com/keynote_small.jpg",
+         "title":"Javaone Keynote",
+         "width":320,
+         "height":240,
+         "size":"SMALL"
+      }
+   ]
+}
\ No newline at end of file
diff --git a/test/data/universal-binary-json-java/MediaContent.ubj b/test/data/universal-binary-json-java/MediaContent.ubj
new file mode 100644
index 0000000000000000000000000000000000000000..616531ce4938f44ec1dfac12194504e76cea6ca0
GIT binary patch
literal 442
zcma)%Jx;?w5QW#jfGLnT0CGWXH&j#@2}L$elSmXaXcG_ihPBt)w=ptR3J$}uI08pt
zBMT`FQp%?{Gv9p5MKGsIaCHuEs|BtaWBS`P-SB~{j*_)%(>EGBWhiO;8i5f;5-y6V
zj~}NF!MmtTlQZV-5H=+0#@OI|BYai5<p$9pDpIMvaf8!ptGN-X%bm-w9ejun%Dqp<
zaIzA{R+KY;pL)_z9k`3=UPu|wYz0J1Lj%QyS^;4%`Dmy7B74ekwpRV9_wZ+gdCP0E
yyDFH}uK!nikzCi5Y>pB1*@2+PrtcAy*<$u^3<@om@;H=pcYtC)f2id=E6ZO%@s+y(

literal 0
HcmV?d00001

diff --git a/test/data/universal-binary-json-java/README b/test/data/universal-binary-json-java/README
new file mode 100644
index 00000000..b4fc4059
--- /dev/null
+++ b/test/data/universal-binary-json-java/README
@@ -0,0 +1,357 @@
+Universal Binary JSON Java Library
+http://ubjson.org
+
+
+About this project...
+---------------------
+This code base is actively under development and implements the latest 
+specification of Universal Binary JSON (Draft 8).
+
+I/O is handled through the following core classes:
+
+	* UBJOutputStream
+	* UBJInputStream
+	* UBJInputStreamParser
+	
+Additionally, if you are working with Java's NIO and need byte[]-based
+results, you can wrap any of the above I/O classes around one of the highly
+optimized custom byte[]-stream impls:
+
+	* ByteArrayInputStream	(optimized for reuse, not from JDK)
+	* ByteArrayOutputStream (optimized for reuse, not from JDK)
+	
+If you are working with NIO and want maximum performance by using (and reusing)
+direct ByteBuffers along with the UBJSON stream impls, take a look at the:
+
+	* ByteBufferInputStream
+	* ByteBufferOutputStream
+	
+classes. You can wrap any ByteBuffer source or destination with this stream type,
+then wrap that stream type with a UBJSON stream impl and utilize the full
+potential of Java's NIO with Universal Binary JSON without giving yourself an
+ulcer.
+
+This allows you to re-use the streams over and over again in a pool of reusable
+streams for high-performance I/O with no object creation and garbage collection
+overhead; a perfect match for high frequency NIO-based communication.
+
+All of the core I/O classes have been stable for a while, with tweaks to make the
+performance tighter and the error messages more informative over the last few
+months.
+
+More Java-convenient reflection-based I/O classes are available in the
+org.ubjson.io.reflect package, but they are under active development.
+
+There are other efforts (like utilities) in other sub portions of the source
+tree. This project intends to eventually contain a multitude of UBJSON 
+abstraction layers, I/O methods and utilities.
+
+
+Changelog
+---------
+02-10-12
+	* Added ByteBuffer input and output stream impls as compliments to the
+	re-usable byte[] stream impls.
+	
+	Provides a fast translation layer between standard Java stream-IO and the
+	new Buffer-based I/O in NIO (including transparent support for using
+	ultra-fast direct ByteBuffers).
+	
+	* Optimized some of the read/write methods by removing unnecessary bounds
+	checks that are done by subsequent Input or Output stream impls themselves.
+
+02-09-12
+	* Fixed bug with readHugeAsBigInteger returning an invalid value and not
+	treating the underlying bytes as a string-encoded value.
+	
+	* Removed implicit buffer.flip() at the end of StreamDecoder; there is no 
+	way	to know what the caller had planned for the buffer before reading all 
+	the data back out. Also the flip was in the wrong place and in the case of 
+	an empty decode request (length=0) the flip would not have been performed, 
+	providing the caller with a "full" buffer of nothing.
+	
+	* Rewrote all readHugeXXX method impls; now huge's can be read in as a 
+	simple Number (BigInteger or BigDecimal) as well as raw bytes and even
+	decoded chars. Additionally the methods can even accept and use existing
+	buffers to write into to allow for tighter optimizations.
+	
+	* Rewrote all readStringXXX methods using the same optimizations and
+	flexibility that readHuge methods now use.
+
+02-07-12
+	More Memory and CPU optimizations across all the I/O impls.
+	
+	* StreamDecoder was rewritten to no longer create a ByteBuffer on every
+	invocation and instead re-use the same one to decode from on every single call.
+	
+	* StreamDecoder now requires the call to pass in a CharBuffer instance to hold
+	the result of the decode operation. This avoids the creation of a CharBuffer
+	and allows for large-scale optimization by re-using existing buffers between
+	calls.
+	
+	* StreamEncoder was rewritten to no longer create a ByteBuffer on every
+	invocation either and now re-uses the same single instance over and over
+	again.
+	
+	* UBJOutputStream writeHuge and writeString series of methods were all
+	rewritten to accept a CharBuffer in the rawest form (no longer char[]) to stop
+	hiding the fact that the underlying encode operation required one.
+	
+	This gives the caller an opportunity to cache and re-use CharBuffers over 
+	and over again if they can; otherwise this just pushes the CharBuffer.wrap() 
+	call up to the caller instead of hiding it secretly in the method impl under 
+	the guise of accepting a raw char[] (that it couldn't use directly).
+	
+	For callers that can re-use buffers, this will lead to big performance gains
+	now that were previously impossible.
+	
+	* UBJInputStream added readHuge and readString methods that accept an existing
+	CharBuffer argument to make use of the optimizations made in the Stream encoder
+	and decoder impls.
+
+01-15-12
+	Huge performance boost for deserialization!
+	
+	StreamDecoder previously used separate read and write buffers for decoding 
+	bytes to chars including the resulting char[] that was returned to the caller. 
+	This design required at least 1 full array copy before returning a result in 
+	the best case and 2x full array copies before returning the result in the 
+	worst case.
+	
+	The rewrite removed the need for a write buffer entire as well as ALL array
+	copies; in the best OR worse case they never occur anymore.
+	
+	Raw performance boost of roughly 25% in all UBJ I/O classes as a result. 
+
+12-01-11 through 01-24-12
+	A large amount of work has continued on the core I/O classes (stream impls)
+	to help make them not only faster and more robust, but also more helpful.
+	When errors are encountered in the streams, they are reported along with the
+	stream positions. This is critical for debugging problems with corrupt 
+	formats.
+	
+	Also provided ByteArray I/O stream classes that have the potential to provide
+	HUGE performance boosts for high frequency systems.
+	
+	Both these classes (ByteArrayInputStream and ByteArrayOutputStream) are
+	reusable and when wrapped by a UBJInputStream or UBJOutputStream, the top
+	level UBJ streams implicitly become reusable as well.
+	
+	Reusing the streams not only saves on object creation/GC cleanup but also
+	allows the caller to re-use the temporary byte[] used to translate to and
+	from the UBJ format, avoiding object churn entirely!
+	
+	This optimized design was chosen to be intentionally performant when combined
+	with NIO implementations as the ByteBuffer's can be used to wrap() existing
+	outbound buffers (avoiding the most expensive part of a buffer) or use
+	array() to get access to the underlying buffer that needs to be written to
+	the stream.
+	
+	In the case of direct ByteBuffers, there is no additional overhead added
+	because the calls to get or put are required anyway to pull or push the 
+	values from the native memory location.
+	
+	This approach allows the fastest implementation of Universal Binary JSON
+	I/O possible in the JVM whether you are using the standard IO (stream) 
+	classes or the NIO (ByteBuffer) classes in the JDK.
+	
+	Some ancillary work on UBJ-based command line utilities (viewers, converters,
+	etc.) has begun as well.
+
+11-28-11
+	* Fixed UBJInputStreamParser implementation; nextType correctly implements
+	logic to skip existing element (if called back to back) as well as validate
+	the marker type it encounters before returning it to the caller.
+	
+	* Modified IObjectReader contract; a Parser implementation is required to 
+	make traversing the UBJ stream possible without knowing what type of element
+	is next.
+	
+11-27-11
+	* Streamlined ByteArrayOutputStream implementation to ensure the capacity
+	of the underlying byte[] is never grown unless absolutely necessary.
+	
+	* Rewrote class Javadoc for ByteArrayOutputStream to include a code snippet
+	on how to use it.
+
+11-26-11
+	* Fixed major bug in how 16, 32 and 64-bit integers are re-assembled when
+	read back from binary representations.
+	
+	* Added a numeric test to specifically catch this error if it ever pops up
+	again.
+	
+	* Optimized reading and writing of numeric values in Input and Output
+	stream implementations.
+	
+	* Optimized ObjectWriter implementation by streamlining the numeric read/write
+	logic and removing the sub-optimal force-compression of on-disk storage.
+	
+	* Fixed ObjectWriter to match exactly with the output written by 
+	UBJOutputStream.
+	
+	* Normalized all testing between I/O classes so they use the same classes
+	to ensure parity.
+
+11-10-11
+	* DRAFT 8 Spec Support Added.
+
+	* Added support for the END marker (readEnd) to the InputStreams which is
+	required for proper unbounded-container support.
+	
+	* Added support for the END marker (writeEnd) to UBJOutputStream.
+	
+	UBJInputStreamParser must be used for properly support unbounded-containers
+	because you never know when the 'E' will be encountered marking the end;
+	so the caller needs to pay attention to the type marker that nextType()
+	returns and respond accordingly.
+	
+	* Added readHugeAsBytes to InputStream implementations, allowing the bytes
+	used to represent a HUGE to be read in their raw form with no decoding.
+	
+	This can save on processing as BigInteger and BigDecimal do their own decoding
+	of byte[] directly.
+	
+	* Added readHugeAsChars to InputStream implementations, allowing a HUGE 
+	value to be read in as a raw char[] without trying to decode it to a 
+	BigInteger or BigDecimal.
+	
+	* Added writeHuge(char[]) to support writing out HUGE values directly from
+	their raw char[] form without trying to decode from a BigInteger or
+	BigDecimal.
+	
+	* readArrayLength and readObjectLenght were modified to return -1 when an 
+	unbounded container length is encountered (255).
+
+	* Fixed UBJInputStreamParser.nextType to correctly skip past any NOOP
+	markers found in the underlying stream before returning a valid next type.
+	
+	* Normalized all reading of "next byte" to the singular
+	UBJInputStream.nextMarker method -- correctly skips over NOOP until end of 
+	stream OR until the next valid marker byte, then returns it.
+
+	* Modified readNull behavior to have no return type. It is already designed
+	to throw an exception when 'NULL' isn't found, no need for the additional
+	return type.
+	
+	* Began work on a simple abstract representation of the UBJ data types as
+	objects that can be assembled into maps and lists and written/read easily
+	using the IO package.
+	
+	This is intended to be a higher level of abstraction than the IO streams,
+	but lower level (and faster) than the reflection-based work that inspects
+	user-provided classes.
+	
+	* Refactored StreamDecoder and StreamEncoder into the core IO package, 
+	because they are part of core IO.
+	
+	* Refactored StreamParser into the io.parser package to more clearly denote
+	its relationship to the core IO classes. It is a slightly higher level
+	abstraction ontop of the core IO, having it along side the core IO classes
+	while .reflect was a subpackage was confusing and suggested that 
+	StreamParser was somehow intended as a swapable replacement for 
+	UBJInputStream which is not how it is intended to be used.
+	
+	* Refactored org.ubjson.reflect to org.ubjson.io.reflect to more correctly
+	communicate the relationship -- the reflection-based classes are built on
+	the core IO classes and are just a higher abstraction to interact with UBJSON
+	with.
+	
+	* Renamed IDataType to IMarkerType to follow the naming convention for the
+	marker bytes set forth by the spec doc.
+
+
+10-14-11
+	* ObjectWriter rewritten and works correctly. Tested with the example test
+	data and wrote out the compressed and uncompressed formats files correctly
+	from their original object representation.
+	
+	* Added support for reading and writing huge values as BigInteger as well
+	as BigDecimal.
+	
+	* Added automatic numeric storage compression support to ObjectWriter - based
+	on the numeric value (regardless of type) the value will be stored as the
+	smallest possible representation in the UBJ format if requested.
+	
+	* Added mapping support for BigDecimal, BigInteger, AtomicInteger and 
+	AtomicLong to ObjectWriter.
+	
+	* Added readNull and readBoolean to the UBJInputStream and 
+	UBJInputStreamParser implementations to make the API feel complete and feel
+	more natural to use.
+
+10-10-11
+	* com.ubjson.io AND com.ubjson.io.charset are finalized against the 
+	Draft 8 specification.
+	
+	* The lowest level UBJInput/OuputStream classes were tightened up to run as 
+	fast as possible showing an 800ns-per-op improvement in speed.
+	
+	* Profiled core UBJInput/OuputStream classes using HPROF for a few million
+	iterations and found no memory leaks and no performance traps; everything at
+	that low level is as tight as it can be.
+	
+	* Stream-parsing facilities were moved out of the overloaded UBJInputStream
+	class and into their own subclass called UBJInputStreamParser which operates
+	exactly like a pull-parsing scenario (calling nextType then switching on the
+	value and pulling the appropriate value out of the stream).
+	
+	* More example testing/benchmarking data checked into /test/java/com/ubjson/data
+	
+	Will begin reworking the Reflection based Object mapping in the 
+	org.ubjson.reflect package now that the core IO classes are finalized.
+	
+	* Removed all old scratch test files from the org.ubjson package, this was
+	confusing.
+
+09-27-11
+	* Initial check-in of core IO classes to read/write spec.
+
+
+Status
+------
+Using the standard UBJInputStream, UBJInputStreamParser and UBJOutputStream
+implementations to manually read/write UBJ objects is stable and tuned for
+optimal performance.
+
+Automatic mapping of objects to/from UBJ format via the reflection-based 
+implementation is not tuned yet. Writing is implemented, but not tuned for
+optimal performance and reading still has to be written.
+
+	* org.ubjson.io - STABLE
+	* org.ubjson.io.parser - STABLE
+	* org.ubjson.io.reflect - ALPHA
+	* org.ubjson.model - BETA
+
+
+License
+-------
+This library is released under the Apache 2 License. See LICENSE.
+
+
+Description
+-----------
+This project represents (the official?) Java implementations of the 
+Universal Binary JSON specification: http://ubjson.org
+
+
+Example
+-------
+Comming soon...
+
+
+Performance
+-----------
+Comming soon...
+
+
+Reference
+---------
+Universal Binary JSON Specification - http://ubjson.org
+JSON Specification - http://json.org
+
+
+Contact
+-------
+If you have questions, comments or bug reports for this software please contact
+us at: software@thebuzzmedia.com
\ No newline at end of file
diff --git a/test/data/universal-binary-json-java/TwitterTimeline.formatted.json b/test/data/universal-binary-json-java/TwitterTimeline.formatted.json
new file mode 100644
index 00000000..f56dd9b2
--- /dev/null
+++ b/test/data/universal-binary-json-java/TwitterTimeline.formatted.json
@@ -0,0 +1,80 @@
+{
+   "id_str":"121769183821312000",
+   "retweet_count":0,
+   "in_reply_to_screen_name":null,
+   "in_reply_to_user_id":null,
+   "truncated":false,
+   "retweeted":false,
+   "possibly_sensitive":false,
+   "in_reply_to_status_id_str":null,
+   "entities":{
+      "urls":[
+         {
+            "url":"http:\/\/t.co\/wtioKkFS",
+            "display_url":"dlvr.it\/pWQy2",
+            "indices":[
+               33,
+               53
+            ],
+            "expanded_url":"http:\/\/dlvr.it\/pWQy2"
+         }
+      ],
+      "hashtags":[
+
+      ],
+      "user_mentions":[
+
+      ]
+   },
+   "geo":null,
+   "place":null,
+   "coordinates":null,
+   "created_at":"Thu Oct 06 02:10:10 +0000 2011",
+   "in_reply_to_user_id_str":null,
+   "user":{
+      "id_str":"77029015",
+      "profile_link_color":"009999",
+      "protected":false,
+      "url":"http:\/\/www.techday.co.nz\/",
+      "screen_name":"techdaynz",
+      "statuses_count":5144,
+      "profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1479058408\/techday_48_normal.jpg",
+      "name":"TechDay",
+      "default_profile_image":false,
+      "default_profile":false,
+      "profile_background_color":"131516",
+      "lang":"en",
+      "profile_background_tile":false,
+      "utc_offset":43200,
+      "description":"",
+      "is_translator":false,
+      "show_all_inline_media":false,
+      "contributors_enabled":false,
+      "profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/75893948\/Techday_Background.jpg",
+      "created_at":"Thu Sep 24 20:02:01 +0000 2009",
+      "profile_sidebar_fill_color":"efefef",
+      "follow_request_sent":false,
+      "friends_count":3215,
+      "followers_count":3149,
+      "time_zone":"Auckland",
+      "favourites_count":0,
+      "profile_sidebar_border_color":"eeeeee",
+      "profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1479058408\/techday_48_normal.jpg",
+      "following":false,
+      "geo_enabled":false,
+      "notifications":false,
+      "profile_use_background_image":true,
+      "listed_count":151,
+      "verified":false,
+      "profile_text_color":"333333",
+      "location":"Ponsonby, Auckland, NZ",
+      "id":77029015,
+      "profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/75893948\/Techday_Background.jpg"
+   },
+   "contributors":null,
+   "source":"\u003Ca href=\"http:\/\/dlvr.it\" rel=\"nofollow\"\u003Edlvr.it\u003C\/a\u003E",
+   "in_reply_to_status_id":null,
+   "favorited":false,
+   "id":121769183821312000,
+   "text":"Apple CEO's message to employees http:\/\/t.co\/wtioKkFS"
+}
\ No newline at end of file
diff --git a/test/data/universal-binary-json-java/TwitterTimeline.ubj b/test/data/universal-binary-json-java/TwitterTimeline.ubj
new file mode 100644
index 0000000000000000000000000000000000000000..19702d4bb147d330ce7bfbc9d819e065c83cafad
GIT binary patch
literal 1792
zcmai#-)|d55XVDeQYWz~v<YnyLWdvVfy%n`kJygdN^R*$6_kJ)3CTLKTHlTBCF|W|
zXD_bvPw<!*{s12N6Zq4ZJ)eIPwQK9nzMY-f{mggf^a<{ZgrPNfFK7gv(_YYRb{j!6
zX!yR5RReYkU|Fm)Wnb^mzz0Gx1F4)bs~N@yKrzK9(8v4hGZ}(mBI)CSHJOUJg(Sp-
z!k_znFV%=*Kz<Mu3M;N4#7FBc&~lq0IdS{zV;PhsGeEtErOZgwCERoO<FU2rSrplN
ztfQ$F`iIN#EgmHTQ^{w{nN$;bW$MC4>94=c8hA&jM8tr6*PZEWcWGk<-lbe6kmM^C
zPB#~p#~jC&kC5+RH9z9S?Mf^6U~dGvj~`Rem}nfuTAM^DIyv_7kU%*Gj9dKbVw`#B
zvGx2@&u^RsK7GCKh_~-G{2<sQTyUHtD7k<7n`O?*osQq=`9Yg#8a)&em=x-g(j~RQ
zec$iVz(bm|5L0x*o)ddi5Svb?b($F`d`9f*>N>)swUkos=9B6gKbdC(aH)mkhxpkd
zs+jN*<lJ5suDM^gQ!yEl-$}G#hEdS!^!#?W<#(eZ0Bd!b(q_VC{r7Z)B_}Anb3xI+
z=QBJ>V8}CR*@mAG?`}O0@!`T^z~jr2A=M@e7^LZTaEc|#)d+V<au05?wS+Ll!_3A^
z4~Ga&^MC(KS^~l%(md0Yd4bFtu26ETO^7Er)>Fo%WI_>tU=v6L5AitG${I1q$O;)K
zK9ElNwOax&0W!xA@jSngs8QSoa_xBeN{l+~Zm-#Ebt4Cj^2uH-3FJusC&0ImdW{w-
z?u<0(2TK9^J^W&^K@<rLxM8F$x!?#x_rd!^EhU}Wz#kc)bwzALtPYI;m8?Wk`4jKX
zErAd$+DPRMNlQ$CU26qcexAjbl$HcP9r7!k89^npw568w<@SCCR9Vzs3YkMbR{7j!
z`Re^F3xFRK<-z<MLXAQ^qEcTaw5qffLqSc>b%JoUUn*C&ovI70NP(^uENJZ9VEGCR
zd6@UbqLMAVbB#Bz<YqpwEOim&^Pee_R)g7NZ$a;|_fsEtMe@3I^V`j858LtcDt+_5
zD&1c3VR2Q~h2F<~q$E;%Z9d`N*ud~<tq4~%)0$^MKCLM|Ct7<}Jbx1LXLxe!D}df3
zP8&`dR6%sgA3yr%uZel#?_kM+!uIntmEgU6b^a}S)I5+Hl4rFClT_*%0D8q9Hr^8d
E0ob35-v9sr

literal 0
HcmV?d00001

diff --git a/test/data/universal-binary-json-java/url.txt b/test/data/universal-binary-json-java/url.txt
new file mode 100644
index 00000000..9991ce6c
--- /dev/null
+++ b/test/data/universal-binary-json-java/url.txt
@@ -0,0 +1 @@
+https://github.com/ubjson/universal-binary-json-java/tree/master/src/test/resources/org/ubjson
\ No newline at end of file
diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp
new file mode 100644
index 00000000..ce1b3c38
--- /dev/null
+++ b/test/src/unit-ubjson.cpp
@@ -0,0 +1,1593 @@
+/*
+    __ _____ _____ _____
+ __|  |   __|     |   | |  JSON for Modern C++ (test suite)
+|  |  |__   |  |  | | | |  version 3.0.1
+|_____|_____|_____|_|___|  https://github.com/nlohmann/json
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
+
+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.
+*/
+
+#include "catch.hpp"
+
+#include "json.hpp"
+using nlohmann::json;
+
+#include <fstream>
+
+TEST_CASE("UBJSON")
+{
+    SECTION("individual values")
+    {
+        SECTION("discarded")
+        {
+            // discarded values are not serialized
+            json j = json::value_t::discarded;
+            const auto result = json::to_ubjson(j);
+            CHECK(result.empty());
+        }
+
+        SECTION("null")
+        {
+            json j = nullptr;
+            std::vector<uint8_t> expected = {'Z'};
+            const auto result = json::to_ubjson(j);
+            CHECK(result == expected);
+
+            // roundtrip
+            CHECK(json::from_ubjson(result) == j);
+        }
+
+        SECTION("boolean")
+        {
+            SECTION("true")
+            {
+                json j = true;
+                std::vector<uint8_t> expected = {'T'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("false")
+            {
+                json j = false;
+                std::vector<uint8_t> expected = {'F'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+        }
+
+        SECTION("number")
+        {
+            SECTION("signed")
+            {
+                SECTION("-9223372036854775808..-2147483649 (int64)")
+                {
+                    std::vector<int64_t> numbers;
+                    numbers.push_back(INT64_MIN);
+                    numbers.push_back(-1000000000000000000);
+                    numbers.push_back(-100000000000000000);
+                    numbers.push_back(-10000000000000000);
+                    numbers.push_back(-1000000000000000);
+                    numbers.push_back(-100000000000000);
+                    numbers.push_back(-10000000000000);
+                    numbers.push_back(-1000000000000);
+                    numbers.push_back(-100000000000);
+                    numbers.push_back(-10000000000);
+                    numbers.push_back(-2147483649);
+                    for (auto i : numbers)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back(static_cast<uint8_t>('L'));
+                        expected.push_back(static_cast<uint8_t>((i >> 56) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 48) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 40) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 32) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 24) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 16) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 9);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'L');
+                        uint64_t restored = (static_cast<uint64_t>(result[1]) << 070) +
+                                            (static_cast<uint64_t>(result[2]) << 060) +
+                                            (static_cast<uint64_t>(result[3]) << 050) +
+                                            (static_cast<uint64_t>(result[4]) << 040) +
+                                            (static_cast<uint64_t>(result[5]) << 030) +
+                                            (static_cast<uint64_t>(result[6]) << 020) +
+                                            (static_cast<uint64_t>(result[7]) << 010) +
+                                            static_cast<uint64_t>(result[8]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("-2147483648..-32769 (int32)")
+                {
+                    std::vector<int64_t> numbers;
+                    numbers.push_back(-32769);
+                    numbers.push_back(-100000);
+                    numbers.push_back(-1000000);
+                    numbers.push_back(-10000000);
+                    numbers.push_back(-100000000);
+                    numbers.push_back(-1000000000);
+                    numbers.push_back(-2147483648);
+                    for (auto i : numbers)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back(static_cast<uint8_t>('l'));
+                        expected.push_back(static_cast<uint8_t>((i >> 24) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 16) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 5);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'l');
+                        int32_t restored = (static_cast<int32_t>(result[1]) << 030) +
+                                           (static_cast<int32_t>(result[2]) << 020) +
+                                           (static_cast<int32_t>(result[3]) << 010) +
+                                           static_cast<int32_t>(result[4]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("-32768..-129 (int16)")
+                {
+                    for (int32_t i = -32768; i <= -129; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back(static_cast<uint8_t>('I'));
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 3);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'I');
+                        int16_t restored = static_cast<int16_t>(((result[1] << 8) + result[2]));
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("-9263 (int16)")
+                {
+                    json j = -9263;
+                    std::vector<uint8_t> expected = {'I', 0xdb, 0xd1};
+
+                    // compare result + size
+                    const auto result = json::to_ubjson(j);
+                    CHECK(result == expected);
+                    CHECK(result.size() == 3);
+
+                    // check individual bytes
+                    CHECK(result[0] == 'I');
+                    int16_t restored = static_cast<int16_t>(((result[1] << 8) + result[2]));
+                    CHECK(restored == -9263);
+
+                    // roundtrip
+                    CHECK(json::from_ubjson(result) == j);
+                }
+
+                SECTION("-128..-1 (int8)")
+                {
+                    for (auto i = -128; i <= -1; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('i');
+                        expected.push_back(static_cast<uint8_t>(i));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 2);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'i');
+                        CHECK(static_cast<int8_t>(result[1]) == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("0..127 (int8)")
+                {
+                    for (size_t i = 0; i <= 127; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = -1;
+                        j.get_ref<json::number_integer_t&>() = static_cast<json::number_integer_t>(i);
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back(static_cast<uint8_t>('i'));
+                        expected.push_back(static_cast<uint8_t>(i));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 2);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'i');
+                        CHECK(result[1] == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("128..255 (uint8)")
+                {
+                    for (size_t i = 128; i <= 255; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = -1;
+                        j.get_ref<json::number_integer_t&>() = static_cast<json::number_integer_t>(i);
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back(static_cast<uint8_t>('U'));
+                        expected.push_back(static_cast<uint8_t>(i));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 2);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'U');
+                        CHECK(result[1] == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("256..32767 (int16)")
+                {
+                    for (size_t i = 256; i <= 32767; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = -1;
+                        j.get_ref<json::number_integer_t&>() = static_cast<json::number_integer_t>(i);
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back(static_cast<uint8_t>('I'));
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 3);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'I');
+                        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("65536..2147483647 (int32)")
+                {
+                    for (uint32_t i :
+                            {
+                                65536u, 77777u, 1048576u
+                            })
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = -1;
+                        j.get_ref<json::number_integer_t&>() = static_cast<json::number_integer_t>(i);
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('l');
+                        expected.push_back(static_cast<uint8_t>((i >> 24) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 16) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 5);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'l');
+                        uint32_t restored = (static_cast<uint32_t>(result[1]) << 030) +
+                                            (static_cast<uint32_t>(result[2]) << 020) +
+                                            (static_cast<uint32_t>(result[3]) << 010) +
+                                            static_cast<uint32_t>(result[4]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("2147483648..9223372036854775807 (int64)")
+                {
+                    for (uint64_t i :
+                            {
+                                2147483648ul, 9223372036854775807ul
+                            })
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = -1;
+                        j.get_ref<json::number_integer_t&>() = static_cast<json::number_integer_t>(i);
+
+                        // check type
+                        CHECK(j.is_number_integer());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('L');
+                        expected.push_back(static_cast<uint8_t>((i >> 070) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 060) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 050) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 040) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 030) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 020) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 010) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 9);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'L');
+                        uint64_t restored = (static_cast<uint64_t>(result[1]) << 070) +
+                                            (static_cast<uint64_t>(result[2]) << 060) +
+                                            (static_cast<uint64_t>(result[3]) << 050) +
+                                            (static_cast<uint64_t>(result[4]) << 040) +
+                                            (static_cast<uint64_t>(result[5]) << 030) +
+                                            (static_cast<uint64_t>(result[6]) << 020) +
+                                            (static_cast<uint64_t>(result[7]) << 010) +
+                                            static_cast<uint64_t>(result[8]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+            }
+
+            SECTION("unsigned")
+            {
+                SECTION("0..127 (int8)")
+                {
+                    for (size_t i = 0; i <= 127; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with unsigned integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_unsigned());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('i');
+                        expected.push_back(static_cast<uint8_t>(i));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 2);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'i');
+                        uint8_t restored = static_cast<uint8_t>(result[1]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("128..255 (uint8)")
+                {
+                    for (size_t i = 128; i <= 255; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with unsigned integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_unsigned());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('U');
+                        expected.push_back(static_cast<uint8_t>(i));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 2);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'U');
+                        uint8_t restored = static_cast<uint8_t>(result[1]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("256..32767 (int16)")
+                {
+                    for (size_t i = 256; i <= 32767; ++i)
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with unsigned integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_unsigned());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('I');
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 3);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'I');
+                        uint16_t restored = static_cast<uint16_t>(static_cast<uint8_t>(result[1]) * 256 + static_cast<uint8_t>(result[2]));
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("65536..2147483647 (int32)")
+                {
+                    for (uint32_t i :
+                            {
+                                65536u, 77777u, 1048576u
+                            })
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with unsigned integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_unsigned());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('l');
+                        expected.push_back(static_cast<uint8_t>((i >> 24) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 16) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 8) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 5);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'l');
+                        uint32_t restored = (static_cast<uint32_t>(result[1]) << 030) +
+                                            (static_cast<uint32_t>(result[2]) << 020) +
+                                            (static_cast<uint32_t>(result[3]) << 010) +
+                                            static_cast<uint32_t>(result[4]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+
+                SECTION("2147483648..9223372036854775807 (int64)")
+                {
+                    for (uint64_t i :
+                            {
+                                2147483648ul, 9223372036854775807ul
+                            })
+                    {
+                        CAPTURE(i);
+
+                        // create JSON value with integer number
+                        json j = i;
+
+                        // check type
+                        CHECK(j.is_number_unsigned());
+
+                        // create expected byte vector
+                        std::vector<uint8_t> expected;
+                        expected.push_back('L');
+                        expected.push_back(static_cast<uint8_t>((i >> 070) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 060) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 050) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 040) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 030) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 020) & 0xff));
+                        expected.push_back(static_cast<uint8_t>((i >> 010) & 0xff));
+                        expected.push_back(static_cast<uint8_t>(i & 0xff));
+
+                        // compare result + size
+                        const auto result = json::to_ubjson(j);
+                        CHECK(result == expected);
+                        CHECK(result.size() == 9);
+
+                        // check individual bytes
+                        CHECK(result[0] == 'L');
+                        uint64_t restored = (static_cast<uint64_t>(result[1]) << 070) +
+                                            (static_cast<uint64_t>(result[2]) << 060) +
+                                            (static_cast<uint64_t>(result[3]) << 050) +
+                                            (static_cast<uint64_t>(result[4]) << 040) +
+                                            (static_cast<uint64_t>(result[5]) << 030) +
+                                            (static_cast<uint64_t>(result[6]) << 020) +
+                                            (static_cast<uint64_t>(result[7]) << 010) +
+                                            static_cast<uint64_t>(result[8]);
+                        CHECK(restored == i);
+
+                        // roundtrip
+                        CHECK(json::from_ubjson(result) == j);
+                    }
+                }
+            }
+
+            SECTION("float64")
+            {
+                SECTION("3.1415925")
+                {
+                    double v = 3.1415925;
+                    json j = v;
+                    std::vector<uint8_t> expected =
+                    {
+                        'D', 0x40, 0x09, 0x21, 0xfb, 0x3f, 0xa6, 0xde, 0xfc
+                    };
+                    const auto result = json::to_ubjson(j);
+                    CHECK(result == expected);
+
+                    // roundtrip
+                    CHECK(json::from_ubjson(result) == j);
+                    CHECK(json::from_ubjson(result) == v);
+                }
+            }
+        }
+
+        SECTION("string")
+        {
+            SECTION("N = 0..127")
+            {
+                for (size_t N = 0; N <= 127; ++N)
+                {
+                    CAPTURE(N);
+
+                    // create JSON value with string containing of N * 'x'
+                    const auto s = std::string(N, 'x');
+                    json j = s;
+
+                    // create expected byte vector
+                    std::vector<uint8_t> expected;
+                    expected.push_back('S');
+                    expected.push_back('i');
+                    expected.push_back(static_cast<uint8_t>(N));
+                    for (size_t i = 0; i < N; ++i)
+                    {
+                        expected.push_back('x');
+                    }
+
+                    // compare result + size
+                    const auto result = json::to_ubjson(j);
+                    CHECK(result == expected);
+                    CHECK(result.size() == N + 3);
+                    // check that no null byte is appended
+                    if (N > 0)
+                    {
+                        CHECK(result.back() != '\x00');
+                    }
+
+                    // roundtrip
+                    CHECK(json::from_ubjson(result) == j);
+                }
+            }
+
+            SECTION("N = 128..255")
+            {
+                for (size_t N = 128; N <= 255; ++N)
+                {
+                    CAPTURE(N);
+
+                    // create JSON value with string containing of N * 'x'
+                    const auto s = std::string(N, 'x');
+                    json j = s;
+
+                    // create expected byte vector
+                    std::vector<uint8_t> expected;
+                    expected.push_back('S');
+                    expected.push_back('U');
+                    expected.push_back(static_cast<uint8_t>(N));
+                    for (size_t i = 0; i < N; ++i)
+                    {
+                        expected.push_back('x');
+                    }
+
+                    // compare result + size
+                    const auto result = json::to_ubjson(j);
+                    CHECK(result == expected);
+                    CHECK(result.size() == N + 3);
+                    // check that no null byte is appended
+                    CHECK(result.back() != '\x00');
+
+                    // roundtrip
+                    CHECK(json::from_ubjson(result) == j);
+                }
+            }
+
+            SECTION("N = 256..32767")
+            {
+                for (size_t N :
+                        {
+                            256u, 999u, 1025u, 3333u, 2048u, 32767u
+                        })
+                {
+                    CAPTURE(N);
+
+                    // create JSON value with string containing of N * 'x'
+                    const auto s = std::string(N, 'x');
+                    json j = s;
+
+                    // create expected byte vector (hack: create string first)
+                    std::vector<uint8_t> expected(N, 'x');
+                    // reverse order of commands, because we insert at begin()
+                    expected.insert(expected.begin(), static_cast<uint8_t>(N & 0xff));
+                    expected.insert(expected.begin(), static_cast<uint8_t>((N >> 8) & 0xff));
+                    expected.insert(expected.begin(), 'I');
+                    expected.insert(expected.begin(), 'S');
+
+                    // compare result + size
+                    const auto result = json::to_ubjson(j);
+                    CHECK(result == expected);
+                    CHECK(result.size() == N + 4);
+                    // check that no null byte is appended
+                    CHECK(result.back() != '\x00');
+
+                    // roundtrip
+                    CHECK(json::from_ubjson(result) == j);
+                }
+            }
+
+            SECTION("N = 65536..2147483647")
+            {
+                for (size_t N :
+                        {
+                            65536u, 77777u, 1048576u
+                        })
+                {
+                    CAPTURE(N);
+
+                    // create JSON value with string containing of N * 'x'
+                    const auto s = std::string(N, 'x');
+                    json j = s;
+
+                    // create expected byte vector (hack: create string first)
+                    std::vector<uint8_t> expected(N, 'x');
+                    // reverse order of commands, because we insert at begin()
+                    expected.insert(expected.begin(), static_cast<uint8_t>(N & 0xff));
+                    expected.insert(expected.begin(), static_cast<uint8_t>((N >> 8) & 0xff));
+                    expected.insert(expected.begin(), static_cast<uint8_t>((N >> 16) & 0xff));
+                    expected.insert(expected.begin(), static_cast<uint8_t>((N >> 24) & 0xff));
+                    expected.insert(expected.begin(), 'l');
+                    expected.insert(expected.begin(), 'S');
+
+                    // compare result + size
+                    const auto result = json::to_ubjson(j);
+                    CHECK(result == expected);
+                    CHECK(result.size() == N + 6);
+                    // check that no null byte is appended
+                    CHECK(result.back() != '\x00');
+
+                    // roundtrip
+                    CHECK(json::from_ubjson(result) == j);
+                }
+            }
+        }
+
+        SECTION("array")
+        {
+            SECTION("empty")
+            {
+                json j = json::array();
+                std::vector<uint8_t> expected = {'[', ']'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("[null]")
+            {
+                json j = {nullptr};
+                std::vector<uint8_t> expected = {'[', 'Z', ']'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("[1,2,3,4,5]")
+            {
+                json j = json::parse("[1,2,3,4,5]");
+                std::vector<uint8_t> expected = {'[', 'i', 1, 'i', 2, 'i', 3, 'i', 4, 'i', 5, ']'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("[[[[]]]]")
+            {
+                json j = json::parse("[[[[]]]]");
+                std::vector<uint8_t> expected = {'[', '[', '[', '[', ']', ']', ']', ']'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("array with uint16_t elements")
+            {
+                json j(257, nullptr);
+                std::vector<uint8_t> expected(j.size() + 2, 'Z'); // all null
+                expected[0] = '['; // opening array
+                expected[258] = ']'; // closing array
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("array with uint32_t elements")
+            {
+                json j(65793, nullptr);
+                std::vector<uint8_t> expected(j.size() + 2, 'Z'); // all null
+                expected[0] = '['; // opening array
+                expected[65794] = ']'; // closing array
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+        }
+
+        SECTION("object")
+        {
+            SECTION("empty")
+            {
+                json j = json::object();
+                std::vector<uint8_t> expected = {'{', '}'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("{\"\":null}")
+            {
+                json j = {{"", nullptr}};
+                std::vector<uint8_t> expected = {'{', 'i', 0, 'Z', '}'};
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+
+            SECTION("{\"a\": {\"b\": {\"c\": {}}}}")
+            {
+                json j = json::parse("{\"a\": {\"b\": {\"c\": {}}}}");
+                std::vector<uint8_t> expected =
+                {
+                    '{', 'i', 1, 'a', '{', 'i', 1, 'b', '{', 'i', 1, 'c', '{', '}', '}', '}', '}'
+                };
+                const auto result = json::to_ubjson(j);
+                CHECK(result == expected);
+
+                // roundtrip
+                CHECK(json::from_ubjson(result) == j);
+            }
+        }
+    }
+
+    SECTION("errors")
+    {
+        SECTION("empty byte vector")
+        {
+            CHECK_THROWS_AS(json::from_ubjson(std::vector<uint8_t>()), json::parse_error&);
+            CHECK_THROWS_WITH(json::from_ubjson(std::vector<uint8_t>()),
+                              "[json.exception.parse_error.110] parse error at 1: unexpected end of input");
+        }
+
+        SECTION("too short byte vector")
+        {
+        }
+
+        SECTION("unsupported bytes")
+        {
+            SECTION("concrete examples")
+            {
+                CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1c})), json::parse_error&);
+                CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1c})),
+                                  "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1C");
+                CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xf8})), json::parse_error&);
+                CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xf8})),
+                                  "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0xF8");
+            }
+
+            SECTION("all unsupported bytes")
+            {
+                for (auto byte :
+                        {
+                            // ?
+                            0x1c, 0x1d, 0x1e, 0x1f,
+                            // ?
+                            0x3c, 0x3d, 0x3e, 0x3f,
+                            // byte strings
+                            0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+                            // byte strings
+                            0x58, 0x59, 0x5a, 0x5b,
+                            // ?
+                            0x5c, 0x5d, 0x5e,
+                            // byte string
+                            0x5f,
+                            // ?
+                            0x7c, 0x7d, 0x7e,
+                            // ?
+                            0x9c, 0x9d, 0x9e,
+                            // ?
+                            0xbc, 0xbd, 0xbe,
+                            // date/time
+                            0xc0, 0xc1,
+                            // bignum
+                            0xc2, 0xc3,
+                            // fraction
+                            0xc4,
+                            // bigfloat
+                            0xc5,
+                            // tagged item
+                            0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
+                            // expected conversion
+                            0xd5, 0xd6, 0xd7,
+                            // more tagged items
+                            0xd8, 0xd9, 0xda, 0xdb,
+                            // ?
+                            0xdc, 0xdd, 0xde, 0xdf,
+                            // (simple value)
+                            0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
+                            // undefined
+                            0xf7,
+                            // simple value
+                            0xf8
+                        })
+                {
+                    CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error&);
+                }
+            }
+        }
+
+        SECTION("invalid string in map")
+        {
+            CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})), json::parse_error&);
+            CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})),
+                              "[json.exception.parse_error.113] parse error at 2: expected a CBOR string; last byte: 0xFF");
+        }
+
+        SECTION("strict mode")
+        {
+            std::vector<uint8_t> vec = {0xf6, 0xf6};
+            SECTION("non-strict mode")
+            {
+                const auto result = json::from_cbor(vec, false);
+                CHECK(result == json());
+            }
+
+            SECTION("strict mode")
+            {
+                CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error&);
+                CHECK_THROWS_WITH(json::from_cbor(vec),
+                                  "[json.exception.parse_error.110] parse error at 2: expected end of input");
+            }
+        }
+    }
+}
+
+// use this testcase outside [hide] to run it with Valgrind
+TEST_CASE("single CBOR roundtrip")
+{
+    SECTION("sample.json")
+    {
+        std::string filename = "test/data/json_testsuite/sample.json";
+
+        // parse JSON file
+        std::ifstream f_json(filename);
+        json j1 = json::parse(f_json);
+
+        // parse MessagePack file
+        std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+        std::vector<uint8_t> packed((std::istreambuf_iterator<char>(f_cbor)),
+                                    std::istreambuf_iterator<char>());
+        json j2;
+        CHECK_NOTHROW(j2 = json::from_cbor(packed));
+
+        // compare parsed JSON values
+        CHECK(j1 == j2);
+
+        SECTION("roundtrips")
+        {
+            SECTION("std::ostringstream")
+            {
+                std::ostringstream ss;
+                json::to_cbor(j1, ss);
+                json j3 = json::from_cbor(ss.str());
+                CHECK(j1 == j3);
+            }
+
+            SECTION("std::string")
+            {
+                std::string s;
+                json::to_cbor(j1, s);
+                json j3 = json::from_cbor(s);
+                CHECK(j1 == j3);
+            }
+        }
+
+        // check with different start index
+        packed.insert(packed.begin(), 5, 0xff);
+        CHECK(j1 == json::from_cbor(packed.begin() + 5, packed.end()));
+    }
+}
+
+TEST_CASE("CBOR roundtrips", "[hide]")
+{
+    SECTION("input from flynn")
+    {
+        for (std::string filename :
+                {
+                    "test/data/json_nlohmann_tests/all_unicode.json",
+                    "test/data/json.org/1.json",
+                    "test/data/json.org/2.json",
+                    "test/data/json.org/3.json",
+                    "test/data/json.org/4.json",
+                    "test/data/json.org/5.json",
+                    "test/data/json_roundtrip/roundtrip01.json",
+                    "test/data/json_roundtrip/roundtrip02.json",
+                    "test/data/json_roundtrip/roundtrip03.json",
+                    "test/data/json_roundtrip/roundtrip04.json",
+                    "test/data/json_roundtrip/roundtrip05.json",
+                    "test/data/json_roundtrip/roundtrip06.json",
+                    "test/data/json_roundtrip/roundtrip07.json",
+                    "test/data/json_roundtrip/roundtrip08.json",
+                    "test/data/json_roundtrip/roundtrip09.json",
+                    "test/data/json_roundtrip/roundtrip10.json",
+                    "test/data/json_roundtrip/roundtrip11.json",
+                    "test/data/json_roundtrip/roundtrip12.json",
+                    "test/data/json_roundtrip/roundtrip13.json",
+                    "test/data/json_roundtrip/roundtrip14.json",
+                    "test/data/json_roundtrip/roundtrip15.json",
+                    "test/data/json_roundtrip/roundtrip16.json",
+                    "test/data/json_roundtrip/roundtrip17.json",
+                    "test/data/json_roundtrip/roundtrip18.json",
+                    "test/data/json_roundtrip/roundtrip19.json",
+                    "test/data/json_roundtrip/roundtrip20.json",
+                    "test/data/json_roundtrip/roundtrip21.json",
+                    "test/data/json_roundtrip/roundtrip22.json",
+                    "test/data/json_roundtrip/roundtrip23.json",
+                    "test/data/json_roundtrip/roundtrip24.json",
+                    "test/data/json_roundtrip/roundtrip25.json",
+                    "test/data/json_roundtrip/roundtrip26.json",
+                    "test/data/json_roundtrip/roundtrip27.json",
+                    "test/data/json_roundtrip/roundtrip28.json",
+                    "test/data/json_roundtrip/roundtrip29.json",
+                    "test/data/json_roundtrip/roundtrip30.json",
+                    "test/data/json_roundtrip/roundtrip31.json",
+                    "test/data/json_roundtrip/roundtrip32.json",
+                    "test/data/json_testsuite/sample.json", // kills AppVeyor
+                    "test/data/json_tests/pass1.json",
+                    "test/data/json_tests/pass2.json",
+                    "test/data/json_tests/pass3.json",
+                    "test/data/regression/floats.json",
+                    "test/data/regression/signed_ints.json",
+                    "test/data/regression/unsigned_ints.json",
+                    "test/data/regression/working_file.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_arraysWithSpaces.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_empty-string.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_empty.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_ending_with_newline.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_false.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_heterogeneous.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_null.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_with_1_and_newline.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_with_leading_space.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_with_several_null.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_array_with_trailing_space.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_0e+1.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_0e1.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_after_space.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_double_close_to_zero.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_double_huge_neg_exp.json",
+                    //"test/data/nst_json_testsuite/test_parsing/y_number_huge_exp.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_int_with_exp.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_minus_zero.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_negative_int.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_negative_one.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_negative_zero.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_neg_exp.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_capital_e_pos_exp.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_exponent.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_fraction_exponent.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_neg_exp.json",
+                    //"test/data/nst_json_testsuite/test_parsing/y_number_real_neg_overflow.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_pos_exponent.json",
+                    //"test/data/nst_json_testsuite/test_parsing/y_number_real_pos_overflow.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_real_underflow.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_simple_int.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_number_simple_real.json",
+                    //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_neg_int.json",
+                    //"test/data/nst_json_testsuite/test_parsing/y_number_too_big_pos_int.json",
+                    //"test/data/nst_json_testsuite/test_parsing/y_number_very_big_negative_int.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_basic.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_duplicated_key_and_value.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_empty.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_empty_key.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_escaped_null_in_key.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_extreme_numbers.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_long_strings.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_simple.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_string_unicode.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_object_with_newlines.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_UTF-16_Surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pair.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_accepted_surrogate_pairs.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_allowed_escapes.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_backslash_and_u_escaped_zero.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_backslash_doublequotes.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_comments.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_a.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_double_escape_n.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_escaped_control_character.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_escaped_noncharacter.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_in_array.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_in_array_with_leading_space.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_last_surrogates_1_and_2.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_newline_uescaped.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+1FFFF.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_null_escape.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_one-byte-utf-8.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_pi.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_simple_ascii.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_space.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_three-byte-utf-8.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_two-byte-utf-8.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_u+2028_line_sep.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_u+2029_par_sep.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_uEscape.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unescaped_char_delete.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unicode.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unicodeEscapedBackslash.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_2.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_U+2064_invisible_plus.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_unicode_escaped_double_quote.json",
+                    // "test/data/nst_json_testsuite/test_parsing/y_string_utf16.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_utf8.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_string_with_del_character.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_false.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_int.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_negative_real.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_null.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_string.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_lonely_true.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_string_empty.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_trailing_newline.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_true_in_array.json",
+                    "test/data/nst_json_testsuite/test_parsing/y_structure_whitespace_array.json"
+                })
+        {
+            CAPTURE(filename);
+
+            // parse JSON file
+            std::ifstream f_json(filename);
+            json j1 = json::parse(f_json);
+
+            SECTION("std::vector<uint8_t>")
+            {
+                // parse CBOR file
+                std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+                std::vector<uint8_t> packed(
+                    (std::istreambuf_iterator<char>(f_cbor)),
+                    std::istreambuf_iterator<char>());
+                json j2;
+                CHECK_NOTHROW(j2 = json::from_cbor(packed));
+
+                // compare parsed JSON values
+                CHECK(j1 == j2);
+            }
+
+            SECTION("std::ifstream")
+            {
+                // parse CBOR file
+                std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+                json j2;
+                CHECK_NOTHROW(j2 = json::from_cbor(f_cbor));
+
+                // compare parsed JSON values
+                CHECK(j1 == j2);
+            }
+
+            SECTION("uint8_t* and size")
+            {
+                // parse CBOR file
+                std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+                std::vector<uint8_t> packed(
+                    (std::istreambuf_iterator<char>(f_cbor)),
+                    std::istreambuf_iterator<char>());
+                json j2;
+                CHECK_NOTHROW(j2 = json::from_cbor({packed.data(), packed.size()}));
+
+                // compare parsed JSON values
+                CHECK(j1 == j2);
+            }
+
+            SECTION("output to output adapters")
+            {
+                // parse CBOR file
+                std::ifstream f_cbor(filename + ".cbor", std::ios::binary);
+                std::vector<uint8_t> packed(
+                    (std::istreambuf_iterator<char>(f_cbor)),
+                    std::istreambuf_iterator<char>());
+
+                SECTION("std::vector<uint8_t>")
+                {
+                    std::vector<uint8_t> vec;
+                    json::to_cbor(j1, vec);
+                    CHECK(vec == packed);
+                }
+            }
+        }
+    }
+}
+
+/*
+TEST_CASE("Universal Binary JSON Specification Examples")
+{
+    for (std::string prefix :
+            {
+                "test/data/universal-binary-json-java/CouchDB4k",
+                "test/data/universal-binary-json-java/MediaContent",
+                "test/data/universal-binary-json-java/TwitterTimeline"
+            })
+    {
+        CAPTURE(prefix);
+
+        // parse JSON file
+        std::ifstream f_json(prefix + ".formatted.json");
+        json j1 = json::parse(f_json);
+
+        SECTION("std::vector<uint8_t>")
+        {
+            // parse UBJSON file
+            std::ifstream f_ubjson(prefix + ".ubj", std::ios::binary);
+            std::vector<uint8_t> packed(
+                (std::istreambuf_iterator<char>(f_ubjson)),
+                std::istreambuf_iterator<char>());
+            json j2;
+            CHECK_NOTHROW(j2 = json::from_ubjson(packed));
+
+            // compare parsed JSON values
+            CHECK(j1 == j2);
+        }
+    }
+}
+*/
+
+
+/*
+TEST_CASE("all first bytes", "[!throws]")
+{
+    // these bytes will fail immediately with exception parse_error.112
+    std::set<uint8_t> unsupported =
+    {
+        //// types not supported by this library
+
+        // byte strings
+        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+        0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+        0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+        // byte strings
+        0x58, 0x59, 0x5a, 0x5b, 0x5f,
+        // date/time
+        0xc0, 0xc1,
+        // bignum
+        0xc2, 0xc3,
+        // decimal fracion
+        0xc4,
+        // bigfloat
+        0xc5,
+        // tagged item
+        0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd,
+        0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd8,
+        0xd9, 0xda, 0xdb,
+        // expected conversion
+        0xd5, 0xd6, 0xd7,
+        // simple value
+        0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+        0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0,
+        0xf1, 0xf2, 0xf3,
+        0xf8,
+        // undefined
+        0xf7,
+
+        //// bytes not specified by CBOR
+
+        0x1c, 0x1d, 0x1e, 0x1f,
+        0x3c, 0x3d, 0x3e, 0x3f,
+        0x5c, 0x5d, 0x5e,
+        0x7c, 0x7d, 0x7e,
+        0x9c, 0x9d, 0x9e,
+        0xbc, 0xbd, 0xbe,
+        0xdc, 0xdd, 0xde, 0xdf,
+        0xee,
+        0xfc, 0xfe, 0xfd,
+
+        /// break cannot be the first byte
+
+        0xff
+    };
+
+    for (auto i = 0; i < 256; ++i)
+    {
+        const auto byte = static_cast<uint8_t>(i);
+
+        try
+        {
+            json::from_cbor(std::vector<uint8_t>(1, byte));
+        }
+        catch (const json::parse_error& e)
+        {
+            // check that parse_error.112 is only thrown if the
+            // first byte is in the unsupported set
+            CAPTURE(e.what());
+            if (std::find(unsupported.begin(), unsupported.end(), byte) != unsupported.end())
+            {
+                CHECK(e.id == 112);
+            }
+            else
+            {
+                CHECK(e.id != 112);
+            }
+        }
+    }
+}
+*/
+
+/*
+TEST_CASE("examples from RFC 7049 Appendix A")
+{
+    SECTION("numbers")
+    {
+        CHECK(json::to_cbor(json::parse("0")) == std::vector<uint8_t>({0x00}));
+        CHECK(json::parse("0") == json::from_cbor(std::vector<uint8_t>({0x00})));
+
+        CHECK(json::to_cbor(json::parse("1")) == std::vector<uint8_t>({0x01}));
+        CHECK(json::parse("1") == json::from_cbor(std::vector<uint8_t>({0x01})));
+
+        CHECK(json::to_cbor(json::parse("10")) == std::vector<uint8_t>({0x0a}));
+        CHECK(json::parse("10") == json::from_cbor(std::vector<uint8_t>({0x0a})));
+
+        CHECK(json::to_cbor(json::parse("23")) == std::vector<uint8_t>({0x17}));
+        CHECK(json::parse("23") == json::from_cbor(std::vector<uint8_t>({0x17})));
+
+        CHECK(json::to_cbor(json::parse("24")) == std::vector<uint8_t>({0x18, 0x18}));
+        CHECK(json::parse("24") == json::from_cbor(std::vector<uint8_t>({0x18, 0x18})));
+
+        CHECK(json::to_cbor(json::parse("25")) == std::vector<uint8_t>({0x18, 0x19}));
+        CHECK(json::parse("25") == json::from_cbor(std::vector<uint8_t>({0x18, 0x19})));
+
+        CHECK(json::to_cbor(json::parse("100")) == std::vector<uint8_t>({0x18, 0x64}));
+        CHECK(json::parse("100") == json::from_cbor(std::vector<uint8_t>({0x18, 0x64})));
+
+        CHECK(json::to_cbor(json::parse("1000")) == std::vector<uint8_t>({0x19, 0x03, 0xe8}));
+        CHECK(json::parse("1000") == json::from_cbor(std::vector<uint8_t>({0x19, 0x03, 0xe8})));
+
+        CHECK(json::to_cbor(json::parse("1000000")) == std::vector<uint8_t>({0x1a, 0x00, 0x0f, 0x42, 0x40}));
+        CHECK(json::parse("1000000") == json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x0f, 0x42, 0x40})));
+
+        CHECK(json::to_cbor(json::parse("1000000000000")) == std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00}));
+        CHECK(json::parse("1000000000000") == json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00})));
+
+        CHECK(json::to_cbor(json::parse("18446744073709551615")) == std::vector<uint8_t>({0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}));
+        CHECK(json::parse("18446744073709551615") == json::from_cbor(std::vector<uint8_t>({0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})));
+
+        // positive bignum is not supported
+        //CHECK(json::to_cbor(json::parse("18446744073709551616")) == std::vector<uint8_t>({0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
+        //CHECK(json::parse("18446744073709551616") == json::from_cbor(std::vector<uint8_t>({0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})));
+
+        //CHECK(json::to_cbor(json::parse("-18446744073709551616")) == std::vector<uint8_t>({0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}));
+        //CHECK(json::parse("-18446744073709551616") == json::from_cbor(std::vector<uint8_t>({0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})));
+
+        // negative bignum is not supported
+        //CHECK(json::to_cbor(json::parse("-18446744073709551617")) == std::vector<uint8_t>({0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
+        //CHECK(json::parse("-18446744073709551617") == json::from_cbor(std::vector<uint8_t>({0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})));
+
+        CHECK(json::to_cbor(json::parse("-1")) == std::vector<uint8_t>({0x20}));
+        CHECK(json::parse("-1") == json::from_cbor(std::vector<uint8_t>({0x20})));
+
+        CHECK(json::to_cbor(json::parse("-10")) == std::vector<uint8_t>({0x29}));
+        CHECK(json::parse("-10") == json::from_cbor(std::vector<uint8_t>({0x29})));
+
+        CHECK(json::to_cbor(json::parse("-100")) == std::vector<uint8_t>({0x38, 0x63}));
+        CHECK(json::parse("-100") == json::from_cbor(std::vector<uint8_t>({0x38, 0x63})));
+
+        CHECK(json::to_cbor(json::parse("-1000")) == std::vector<uint8_t>({0x39, 0x03, 0xe7}));
+        CHECK(json::parse("-1000") == json::from_cbor(std::vector<uint8_t>({0x39, 0x03, 0xe7})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("0.0")) == std::vector<uint8_t>({0xf9, 0x00, 0x00}));
+        CHECK(json::parse("0.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x00})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("-0.0")) == std::vector<uint8_t>({0xf9, 0x80, 0x00}));
+        CHECK(json::parse("-0.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x80, 0x00})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("1.0")) == std::vector<uint8_t>({0xf9, 0x3c, 0x00}));
+        CHECK(json::parse("1.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x3c, 0x00})));
+
+        CHECK(json::to_cbor(json::parse("1.1")) == std::vector<uint8_t>({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}));
+        CHECK(json::parse("1.1") == json::from_cbor(std::vector<uint8_t>({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("1.5")) == std::vector<uint8_t>({0xf9, 0x3e, 0x00}));
+        CHECK(json::parse("1.5") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x3e, 0x00})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("65504.0")) == std::vector<uint8_t>({0xf9, 0x7b, 0xff}));
+        CHECK(json::parse("65504.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0x7b, 0xff})));
+
+        //CHECK(json::to_cbor(json::parse("100000.0")) == std::vector<uint8_t>({0xfa, 0x47, 0xc3, 0x50, 0x00}));
+        CHECK(json::parse("100000.0") == json::from_cbor(std::vector<uint8_t>({0xfa, 0x47, 0xc3, 0x50, 0x00})));
+
+        //CHECK(json::to_cbor(json::parse("3.4028234663852886e+38")) == std::vector<uint8_t>({0xfa, 0x7f, 0x7f, 0xff, 0xff}));
+        CHECK(json::parse("3.4028234663852886e+38") == json::from_cbor(std::vector<uint8_t>({0xfa, 0x7f, 0x7f, 0xff, 0xff})));
+
+        CHECK(json::to_cbor(json::parse("1.0e+300")) == std::vector<uint8_t>({0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c}));
+        CHECK(json::parse("1.0e+300") == json::from_cbor(std::vector<uint8_t>({0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("5.960464477539063e-8")) == std::vector<uint8_t>({0xf9, 0x00, 0x01}));
+        CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("0.00006103515625")) == std::vector<uint8_t>({0xf9, 0x04, 0x00}));
+        CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00})));
+
+        // half-precision float
+        //CHECK(json::to_cbor(json::parse("-4.0")) == std::vector<uint8_t>({0xf9, 0xc4, 0x00}));
+        CHECK(json::parse("-4.0") == json::from_cbor(std::vector<uint8_t>({0xf9, 0xc4, 0x00})));
+
+        CHECK(json::to_cbor(json::parse("-4.1")) == std::vector<uint8_t>({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}));
+        CHECK(json::parse("-4.1") == json::from_cbor(std::vector<uint8_t>({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})));
+    }
+
+    SECTION("simple values")
+    {
+        CHECK(json::to_cbor(json::parse("false")) == std::vector<uint8_t>({0xf4}));
+        CHECK(json::parse("false") == json::from_cbor(std::vector<uint8_t>({0xf4})));
+
+        CHECK(json::to_cbor(json::parse("true")) == std::vector<uint8_t>({0xf5}));
+        CHECK(json::parse("true") == json::from_cbor(std::vector<uint8_t>({0xf5})));
+
+        CHECK(json::to_cbor(json::parse("true")) == std::vector<uint8_t>({0xf5}));
+        CHECK(json::parse("true") == json::from_cbor(std::vector<uint8_t>({0xf5})));
+    }
+
+    SECTION("strings")
+    {
+        CHECK(json::to_cbor(json::parse("\"\"")) == std::vector<uint8_t>({0x60}));
+        CHECK(json::parse("\"\"") == json::from_cbor(std::vector<uint8_t>({0x60})));
+
+        CHECK(json::to_cbor(json::parse("\"a\"")) == std::vector<uint8_t>({0x61, 0x61}));
+        CHECK(json::parse("\"a\"") == json::from_cbor(std::vector<uint8_t>({0x61, 0x61})));
+
+        CHECK(json::to_cbor(json::parse("\"IETF\"")) == std::vector<uint8_t>({0x64, 0x49, 0x45, 0x54, 0x46}));
+        CHECK(json::parse("\"IETF\"") == json::from_cbor(std::vector<uint8_t>({0x64, 0x49, 0x45, 0x54, 0x46})));
+
+        CHECK(json::to_cbor(json::parse("\"\\u00fc\"")) == std::vector<uint8_t>({0x62, 0xc3, 0xbc}));
+        CHECK(json::parse("\"\\u00fc\"") == json::from_cbor(std::vector<uint8_t>({0x62, 0xc3, 0xbc})));
+
+        CHECK(json::to_cbor(json::parse("\"\\u6c34\"")) == std::vector<uint8_t>({0x63, 0xe6, 0xb0, 0xb4}));
+        CHECK(json::parse("\"\\u6c34\"") == json::from_cbor(std::vector<uint8_t>({0x63, 0xe6, 0xb0, 0xb4})));
+
+        CHECK(json::to_cbor(json::parse("\"\\ud800\\udd51\"")) == std::vector<uint8_t>({0x64, 0xf0, 0x90, 0x85, 0x91}));
+        CHECK(json::parse("\"\\ud800\\udd51\"") == json::from_cbor(std::vector<uint8_t>({0x64, 0xf0, 0x90, 0x85, 0x91})));
+
+        // indefinite length strings
+        CHECK(json::parse("\"streaming\"") == json::from_cbor(std::vector<uint8_t>({0x7f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0xff})));
+    }
+
+    SECTION("arrays")
+    {
+        CHECK(json::to_cbor(json::parse("[]")) == std::vector<uint8_t>({0x80}));
+        CHECK(json::parse("[]") == json::from_cbor(std::vector<uint8_t>({0x80})));
+
+        CHECK(json::to_cbor(json::parse("[1, 2, 3]")) == std::vector<uint8_t>({0x83, 0x01, 0x02, 0x03}));
+        CHECK(json::parse("[1, 2, 3]") == json::from_cbor(std::vector<uint8_t>({0x83, 0x01, 0x02, 0x03})));
+
+        CHECK(json::to_cbor(json::parse("[1, [2, 3], [4, 5]]")) == std::vector<uint8_t>({0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05}));
+        CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector<uint8_t>({0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05})));
+
+        CHECK(json::to_cbor(json::parse("[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]")) == std::vector<uint8_t>({0x98, 0x19, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19}));
+        CHECK(json::parse("[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]") == json::from_cbor(std::vector<uint8_t>({0x98, 0x19, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19})));
+
+        // indefinite length arrays
+        CHECK(json::parse("[]") == json::from_cbor(std::vector<uint8_t>({0x9f, 0xff})));
+        CHECK(json::parse("[1, [2, 3], [4, 5]] ") == json::from_cbor(std::vector<uint8_t>({0x9f, 0x01, 0x82, 0x02, 0x03, 0x9f, 0x04, 0x05, 0xff, 0xff})));
+        CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector<uint8_t>({0x9f, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05, 0xff})));
+        CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector<uint8_t>({0x83, 0x01, 0x82, 0x02, 0x03, 0x9f, 0x04, 0x05, 0xff})));
+        CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector<uint8_t>({0x83, 0x01, 0x9f, 0x02, 0x03, 0xff, 0x82, 0x04, 0x05})));
+        CHECK(json::parse("[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]") == json::from_cbor(std::vector<uint8_t>({0x9f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19, 0xff})));
+    }
+
+    SECTION("objects")
+    {
+        CHECK(json::to_cbor(json::parse("{}")) == std::vector<uint8_t>({0xa0}));
+        CHECK(json::parse("{}") == json::from_cbor(std::vector<uint8_t>({0xa0})));
+
+        CHECK(json::to_cbor(json::parse("{\"a\": 1, \"b\": [2, 3]}")) == std::vector<uint8_t>({0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03}));
+        CHECK(json::parse("{\"a\": 1, \"b\": [2, 3]}") == json::from_cbor(std::vector<uint8_t>({0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03})));
+
+        CHECK(json::to_cbor(json::parse("[\"a\", {\"b\": \"c\"}]")) == std::vector<uint8_t>({0x82, 0x61, 0x61, 0xa1, 0x61, 0x62, 0x61, 0x63}));
+        CHECK(json::parse("[\"a\", {\"b\": \"c\"}]") == json::from_cbor(std::vector<uint8_t>({0x82, 0x61, 0x61, 0xa1, 0x61, 0x62, 0x61, 0x63})));
+
+        CHECK(json::to_cbor(json::parse("{\"a\": \"A\", \"b\": \"B\", \"c\": \"C\", \"d\": \"D\", \"e\": \"E\"}")) == std::vector<uint8_t>({0xa5, 0x61, 0x61, 0x61, 0x41, 0x61, 0x62, 0x61, 0x42, 0x61, 0x63, 0x61, 0x43, 0x61, 0x64, 0x61, 0x44, 0x61, 0x65, 0x61, 0x45}));
+        CHECK(json::parse("{\"a\": \"A\", \"b\": \"B\", \"c\": \"C\", \"d\": \"D\", \"e\": \"E\"}") == json::from_cbor(std::vector<uint8_t>({0xa5, 0x61, 0x61, 0x61, 0x41, 0x61, 0x62, 0x61, 0x42, 0x61, 0x63, 0x61, 0x43, 0x61, 0x64, 0x61, 0x44, 0x61, 0x65, 0x61, 0x45})));
+
+        // indefinite length objects
+        CHECK(json::parse("{\"a\": 1, \"b\": [2, 3]}") == json::from_cbor(std::vector<uint8_t>({0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff})));
+        CHECK(json::parse("[\"a\", {\"b\": \"c\"}]") == json::from_cbor(std::vector<uint8_t>({0x82, 0x61, 0x61, 0xbf, 0x61, 0x62, 0x61, 0x63, 0xff})));
+        CHECK(json::parse("{\"Fun\": true, \"Amt\": -2}") == json::from_cbor(std::vector<uint8_t>({0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff})));
+    }
+}
+*/