From c9938ea8389cd723357f6d7a7e5216775e6a5b92 Mon Sep 17 00:00:00 2001 From: Niels Lohmann 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::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(); + case 'i': + return get_number(); + case 'I': + return get_number(); + case 'l': + return get_number(); + case 'L': + return get_number(); + case 'd': + return get_number(); + case 'D': + return get_number(); + + case 'C': // char + { + get(); + check_eof(); + return std::string(1, static_cast(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()); + case 'i': + return get_string(get_number()); + case 'I': + return get_string(get_number()); + case 'l': + return get_string(get_number()); + case 'L': + return get_string(get_number()); + 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(0x60 + N)); } - else if (N <= 0xFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x78)); write_number(static_cast(N)); } - else if (N <= 0xFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x79)); write_number(static_cast(N)); } - else if (N <= 0xFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x7A)); write_number(static_cast(N)); } // LCOV_EXCL_START - else if (N <= 0xFFFFFFFFFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x7B)); write_number(static_cast(N)); @@ -5716,23 +5873,23 @@ class binary_writer { write_number(static_cast(0x80 + N)); } - else if (N <= 0xFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x98)); write_number(static_cast(N)); } - else if (N <= 0xFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x99)); write_number(static_cast(N)); } - else if (N <= 0xFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x9A)); write_number(static_cast(N)); } // LCOV_EXCL_START - else if (N <= 0xFFFFFFFFFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0x9B)); write_number(static_cast(N)); @@ -5755,23 +5912,23 @@ class binary_writer { write_number(static_cast(0xA0 + N)); } - else if (N <= 0xFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xB8)); write_number(static_cast(N)); } - else if (N <= 0xFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xB9)); write_number(static_cast(N)); } - else if (N <= 0xFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xBA)); write_number(static_cast(N)); } // LCOV_EXCL_START - else if (N <= 0xFFFFFFFFFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { oa->write_character(static_cast(0xBB)); write_number(static_cast(N)); @@ -5939,19 +6096,19 @@ class binary_writer // fixstr write_number(static_cast(0xA0 | N)); } - else if (N <= 255) + else if (N <= (std::numeric_limits::max)()) { // str 8 oa->write_character(static_cast(0xD9)); write_number(static_cast(N)); } - else if (N <= 65535) + else if (N <= (std::numeric_limits::max)()) { // str 16 oa->write_character(static_cast(0xDA)); write_number(static_cast(N)); } - else if (N <= 4294967295) + else if (N <= (std::numeric_limits::max)()) { // str 32 oa->write_character(static_cast(0xDB)); @@ -5974,13 +6131,13 @@ class binary_writer // fixarray write_number(static_cast(0x90 | N)); } - else if (N <= 0xFFFF) + else if (N <= (std::numeric_limits::max)()) { // array 16 oa->write_character(static_cast(0xDC)); write_number(static_cast(N)); } - else if (N <= 0xFFFFFFFF) + else if (N <= (std::numeric_limits::max)()) { // array 32 oa->write_character(static_cast(0xDD)); @@ -6004,13 +6161,13 @@ class binary_writer // fixmap write_number(static_cast(0x80 | (N & 0xF))); } - else if (N <= 65535) + else if (N <= (std::numeric_limits::max)()) { // map 16 oa->write_character(static_cast(0xDE)); write_number(static_cast(N)); } - else if (N <= 4294967295) + else if (N <= (std::numeric_limits::max)()) { // map 32 oa->write_character(static_cast(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('Z')); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? static_cast('T') + : static_cast('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('S')); + write_number_with_ubjson_prefix(j.m_value.string->size()); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + oa->write_character(static_cast('[')); + + if (use_count) + { + oa->write_character(static_cast('#')); + 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(']')); + } + + break; + } + + case value_t::object: + { + oa->write_character(static_cast('{')); + + if (use_count) + { + oa->write_character(static_cast('#')); + 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(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count); + } + + if (not use_count) + { + oa->write_character(static_cast('}')); + } + + 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 + void write_number_with_ubjson_prefix(const NumberType n) + { + if (std::is_floating_point::value) + { + oa->write_character(static_cast('D')); // float64 + write_number(n); + } + else if (std::is_unsigned::value) + { + if (n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('i')); // uint8 + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('U')); // uint8 + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('I')); // int16 + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('l')); // int32 + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('L')); // int64 + write_number(static_cast(n)); + } + else + { + // TODO: replace by exception + assert(false); + } + } + else + { + if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('i')); // int8 + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('U')); // uint8 + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('I')); // int16 + write_number(static_cast(n)); + } + else if (-(std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('l')); // int32 + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n and n <= (std::numeric_limits::max)()) + { + oa->write_character(static_cast('L')); // int64 + write_number(static_cast(n)); + } + else + { + // TODO: replace by exception + assert(false); + } + } + } + private: /// whether we can assume little endianess const bool is_little_endian = binary_reader::little_endianess(); @@ -13511,6 +13848,23 @@ class basic_json binary_writer(o).write_msgpack(j); } + static std::vector to_ubjson(const basic_json& j) + { + std::vector result; + to_ubjson(j, result); + return result; + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_ubjson(j); + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o) + { + binary_writer(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), std::forward(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::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), std::forward(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(JHh+svO#X1O!TnIw~ClDU(~ zB#OA;LVr|(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`uC~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&@y$$~L&^y*Ewq7xFM%oWMemL?KkV26?4=?kT1wWHV!C)O+WoVHbbJmD!?Yu?c8 zq(EcB*BTCEW#dqA1|AUxmL|{kE+%+tMk}I)XI4k@MswY#Lq#i%6teDVWGhz~6RsXBezd4To7NK6Z{ssdMVYb& zRVQg!wSr}*Ni`g^AU3&iB1{!(Y~ppj^m4>>T zbSanTlfKcY<*VAL-gafSTB^w-T`=Pwl`y@gj|meSRFg<*p*kf|N9HA|Q!B8Aroqk( z&0s^4M0u?CkltqLl59rWti&s>{Z<8L)wwz6QQvUY#IZHv(ZSwQ7aB&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|BYai5pB1*@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~v94=c8hA&jM8tr6*PZEWcWGk<-lbe6kmM^C zPB#~p#~jC&kC5+RH9z9S?Mf^6U~dGvj~`Rem}nfuTAM^DIyv_7kU%*Gj9dKbVw`#B zvGx2@&u^RsK7GCKh_~-G{2N>)swUkos=9B6gKbdC(aH)mkhxpkd zs+jN*V8}CR*@mAG?`}O0@!`T^z~jr2A=M@e7^LZTaEc|#)d+VFo%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@b%JoUUn*C&ovI70NP(^uENJZ9VEGCR zd6@UbqLMAVbB#Bz. +Copyright (c) 2013-2017 Niels Lohmann . + +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 + +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 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 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 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 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 expected; + expected.push_back(static_cast('L')); + expected.push_back(static_cast((i >> 56) & 0xff)); + expected.push_back(static_cast((i >> 48) & 0xff)); + expected.push_back(static_cast((i >> 40) & 0xff)); + expected.push_back(static_cast((i >> 32) & 0xff)); + expected.push_back(static_cast((i >> 24) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(result[1]) << 070) + + (static_cast(result[2]) << 060) + + (static_cast(result[3]) << 050) + + (static_cast(result[4]) << 040) + + (static_cast(result[5]) << 030) + + (static_cast(result[6]) << 020) + + (static_cast(result[7]) << 010) + + static_cast(result[8]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_ubjson(result) == j); + } + } + + SECTION("-2147483648..-32769 (int32)") + { + std::vector 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 expected; + expected.push_back(static_cast('l')); + expected.push_back(static_cast((i >> 24) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(result[1]) << 030) + + (static_cast(result[2]) << 020) + + (static_cast(result[3]) << 010) + + static_cast(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 expected; + expected.push_back(static_cast('I')); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(((result[1] << 8) + result[2])); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_ubjson(result) == j); + } + } + + SECTION("-9263 (int16)") + { + json j = -9263; + std::vector 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(((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 expected; + expected.push_back('i'); + expected.push_back(static_cast(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(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() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('i')); + expected.push_back(static_cast(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() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('U')); + expected.push_back(static_cast(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() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back(static_cast('I')); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(static_cast(result[1]) * 256 + static_cast(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() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back('l'); + expected.push_back(static_cast((i >> 24) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(result[1]) << 030) + + (static_cast(result[2]) << 020) + + (static_cast(result[3]) << 010) + + static_cast(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() = static_cast(i); + + // check type + CHECK(j.is_number_integer()); + + // create expected byte vector + std::vector expected; + expected.push_back('L'); + expected.push_back(static_cast((i >> 070) & 0xff)); + expected.push_back(static_cast((i >> 060) & 0xff)); + expected.push_back(static_cast((i >> 050) & 0xff)); + expected.push_back(static_cast((i >> 040) & 0xff)); + expected.push_back(static_cast((i >> 030) & 0xff)); + expected.push_back(static_cast((i >> 020) & 0xff)); + expected.push_back(static_cast((i >> 010) & 0xff)); + expected.push_back(static_cast(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(result[1]) << 070) + + (static_cast(result[2]) << 060) + + (static_cast(result[3]) << 050) + + (static_cast(result[4]) << 040) + + (static_cast(result[5]) << 030) + + (static_cast(result[6]) << 020) + + (static_cast(result[7]) << 010) + + static_cast(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 expected; + expected.push_back('i'); + expected.push_back(static_cast(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(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 expected; + expected.push_back('U'); + expected.push_back(static_cast(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(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 expected; + expected.push_back('I'); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(static_cast(result[1]) * 256 + static_cast(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 expected; + expected.push_back('l'); + expected.push_back(static_cast((i >> 24) & 0xff)); + expected.push_back(static_cast((i >> 16) & 0xff)); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(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(result[1]) << 030) + + (static_cast(result[2]) << 020) + + (static_cast(result[3]) << 010) + + static_cast(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 expected; + expected.push_back('L'); + expected.push_back(static_cast((i >> 070) & 0xff)); + expected.push_back(static_cast((i >> 060) & 0xff)); + expected.push_back(static_cast((i >> 050) & 0xff)); + expected.push_back(static_cast((i >> 040) & 0xff)); + expected.push_back(static_cast((i >> 030) & 0xff)); + expected.push_back(static_cast((i >> 020) & 0xff)); + expected.push_back(static_cast((i >> 010) & 0xff)); + expected.push_back(static_cast(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(result[1]) << 070) + + (static_cast(result[2]) << 060) + + (static_cast(result[3]) << 050) + + (static_cast(result[4]) << 040) + + (static_cast(result[5]) << 030) + + (static_cast(result[6]) << 020) + + (static_cast(result[7]) << 010) + + static_cast(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 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 expected; + expected.push_back('S'); + expected.push_back('i'); + expected.push_back(static_cast(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 expected; + expected.push_back('S'); + expected.push_back('U'); + expected.push_back(static_cast(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 expected(N, 'x'); + // reverse order of commands, because we insert at begin() + expected.insert(expected.begin(), static_cast(N & 0xff)); + expected.insert(expected.begin(), static_cast((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 expected(N, 'x'); + // reverse order of commands, because we insert at begin() + expected.insert(expected.begin(), static_cast(N & 0xff)); + expected.insert(expected.begin(), static_cast((N >> 8) & 0xff)); + expected.insert(expected.begin(), static_cast((N >> 16) & 0xff)); + expected.insert(expected.begin(), static_cast((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 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 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 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 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 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 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 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 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 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()), json::parse_error&); + CHECK_THROWS_WITH(json::from_ubjson(std::vector()), + "[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({0x1c})), json::parse_error&); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0x1c})), + "[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1C"); + CHECK_THROWS_AS(json::from_cbor(std::vector({0xf8})), json::parse_error&); + CHECK_THROWS_WITH(json::from_cbor(std::vector({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({static_cast(byte)})), json::parse_error&); + } + } + } + + SECTION("invalid string in map") + { + CHECK_THROWS_AS(json::from_cbor(std::vector({0xa1, 0xff, 0x01})), json::parse_error&); + CHECK_THROWS_WITH(json::from_cbor(std::vector({0xa1, 0xff, 0x01})), + "[json.exception.parse_error.113] parse error at 2: expected a CBOR string; last byte: 0xFF"); + } + + SECTION("strict mode") + { + std::vector 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 packed((std::istreambuf_iterator(f_cbor)), + std::istreambuf_iterator()); + 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") + { + // parse CBOR file + std::ifstream f_cbor(filename + ".cbor", std::ios::binary); + std::vector packed( + (std::istreambuf_iterator(f_cbor)), + std::istreambuf_iterator()); + 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 packed( + (std::istreambuf_iterator(f_cbor)), + std::istreambuf_iterator()); + 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 packed( + (std::istreambuf_iterator(f_cbor)), + std::istreambuf_iterator()); + + SECTION("std::vector") + { + std::vector 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") + { + // parse UBJSON file + std::ifstream f_ubjson(prefix + ".ubj", std::ios::binary); + std::vector packed( + (std::istreambuf_iterator(f_ubjson)), + std::istreambuf_iterator()); + 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 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(i); + + try + { + json::from_cbor(std::vector(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({0x00})); + CHECK(json::parse("0") == json::from_cbor(std::vector({0x00}))); + + CHECK(json::to_cbor(json::parse("1")) == std::vector({0x01})); + CHECK(json::parse("1") == json::from_cbor(std::vector({0x01}))); + + CHECK(json::to_cbor(json::parse("10")) == std::vector({0x0a})); + CHECK(json::parse("10") == json::from_cbor(std::vector({0x0a}))); + + CHECK(json::to_cbor(json::parse("23")) == std::vector({0x17})); + CHECK(json::parse("23") == json::from_cbor(std::vector({0x17}))); + + CHECK(json::to_cbor(json::parse("24")) == std::vector({0x18, 0x18})); + CHECK(json::parse("24") == json::from_cbor(std::vector({0x18, 0x18}))); + + CHECK(json::to_cbor(json::parse("25")) == std::vector({0x18, 0x19})); + CHECK(json::parse("25") == json::from_cbor(std::vector({0x18, 0x19}))); + + CHECK(json::to_cbor(json::parse("100")) == std::vector({0x18, 0x64})); + CHECK(json::parse("100") == json::from_cbor(std::vector({0x18, 0x64}))); + + CHECK(json::to_cbor(json::parse("1000")) == std::vector({0x19, 0x03, 0xe8})); + CHECK(json::parse("1000") == json::from_cbor(std::vector({0x19, 0x03, 0xe8}))); + + CHECK(json::to_cbor(json::parse("1000000")) == std::vector({0x1a, 0x00, 0x0f, 0x42, 0x40})); + CHECK(json::parse("1000000") == json::from_cbor(std::vector({0x1a, 0x00, 0x0f, 0x42, 0x40}))); + + CHECK(json::to_cbor(json::parse("1000000000000")) == std::vector({0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00})); + CHECK(json::parse("1000000000000") == json::from_cbor(std::vector({0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00}))); + + CHECK(json::to_cbor(json::parse("18446744073709551615")) == std::vector({0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})); + CHECK(json::parse("18446744073709551615") == json::from_cbor(std::vector({0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}))); + + // positive bignum is not supported + //CHECK(json::to_cbor(json::parse("18446744073709551616")) == std::vector({0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + //CHECK(json::parse("18446744073709551616") == json::from_cbor(std::vector({0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))); + + //CHECK(json::to_cbor(json::parse("-18446744073709551616")) == std::vector({0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})); + //CHECK(json::parse("-18446744073709551616") == json::from_cbor(std::vector({0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}))); + + // negative bignum is not supported + //CHECK(json::to_cbor(json::parse("-18446744073709551617")) == std::vector({0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})); + //CHECK(json::parse("-18446744073709551617") == json::from_cbor(std::vector({0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}))); + + CHECK(json::to_cbor(json::parse("-1")) == std::vector({0x20})); + CHECK(json::parse("-1") == json::from_cbor(std::vector({0x20}))); + + CHECK(json::to_cbor(json::parse("-10")) == std::vector({0x29})); + CHECK(json::parse("-10") == json::from_cbor(std::vector({0x29}))); + + CHECK(json::to_cbor(json::parse("-100")) == std::vector({0x38, 0x63})); + CHECK(json::parse("-100") == json::from_cbor(std::vector({0x38, 0x63}))); + + CHECK(json::to_cbor(json::parse("-1000")) == std::vector({0x39, 0x03, 0xe7})); + CHECK(json::parse("-1000") == json::from_cbor(std::vector({0x39, 0x03, 0xe7}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("0.0")) == std::vector({0xf9, 0x00, 0x00})); + CHECK(json::parse("0.0") == json::from_cbor(std::vector({0xf9, 0x00, 0x00}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("-0.0")) == std::vector({0xf9, 0x80, 0x00})); + CHECK(json::parse("-0.0") == json::from_cbor(std::vector({0xf9, 0x80, 0x00}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("1.0")) == std::vector({0xf9, 0x3c, 0x00})); + CHECK(json::parse("1.0") == json::from_cbor(std::vector({0xf9, 0x3c, 0x00}))); + + CHECK(json::to_cbor(json::parse("1.1")) == std::vector({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a})); + CHECK(json::parse("1.1") == json::from_cbor(std::vector({0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("1.5")) == std::vector({0xf9, 0x3e, 0x00})); + CHECK(json::parse("1.5") == json::from_cbor(std::vector({0xf9, 0x3e, 0x00}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("65504.0")) == std::vector({0xf9, 0x7b, 0xff})); + CHECK(json::parse("65504.0") == json::from_cbor(std::vector({0xf9, 0x7b, 0xff}))); + + //CHECK(json::to_cbor(json::parse("100000.0")) == std::vector({0xfa, 0x47, 0xc3, 0x50, 0x00})); + CHECK(json::parse("100000.0") == json::from_cbor(std::vector({0xfa, 0x47, 0xc3, 0x50, 0x00}))); + + //CHECK(json::to_cbor(json::parse("3.4028234663852886e+38")) == std::vector({0xfa, 0x7f, 0x7f, 0xff, 0xff})); + CHECK(json::parse("3.4028234663852886e+38") == json::from_cbor(std::vector({0xfa, 0x7f, 0x7f, 0xff, 0xff}))); + + CHECK(json::to_cbor(json::parse("1.0e+300")) == std::vector({0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c})); + CHECK(json::parse("1.0e+300") == json::from_cbor(std::vector({0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("5.960464477539063e-8")) == std::vector({0xf9, 0x00, 0x01})); + CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("0.00006103515625")) == std::vector({0xf9, 0x04, 0x00})); + CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); + + // half-precision float + //CHECK(json::to_cbor(json::parse("-4.0")) == std::vector({0xf9, 0xc4, 0x00})); + CHECK(json::parse("-4.0") == json::from_cbor(std::vector({0xf9, 0xc4, 0x00}))); + + CHECK(json::to_cbor(json::parse("-4.1")) == std::vector({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})); + CHECK(json::parse("-4.1") == json::from_cbor(std::vector({0xfb, 0xc0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}))); + } + + SECTION("simple values") + { + CHECK(json::to_cbor(json::parse("false")) == std::vector({0xf4})); + CHECK(json::parse("false") == json::from_cbor(std::vector({0xf4}))); + + CHECK(json::to_cbor(json::parse("true")) == std::vector({0xf5})); + CHECK(json::parse("true") == json::from_cbor(std::vector({0xf5}))); + + CHECK(json::to_cbor(json::parse("true")) == std::vector({0xf5})); + CHECK(json::parse("true") == json::from_cbor(std::vector({0xf5}))); + } + + SECTION("strings") + { + CHECK(json::to_cbor(json::parse("\"\"")) == std::vector({0x60})); + CHECK(json::parse("\"\"") == json::from_cbor(std::vector({0x60}))); + + CHECK(json::to_cbor(json::parse("\"a\"")) == std::vector({0x61, 0x61})); + CHECK(json::parse("\"a\"") == json::from_cbor(std::vector({0x61, 0x61}))); + + CHECK(json::to_cbor(json::parse("\"IETF\"")) == std::vector({0x64, 0x49, 0x45, 0x54, 0x46})); + CHECK(json::parse("\"IETF\"") == json::from_cbor(std::vector({0x64, 0x49, 0x45, 0x54, 0x46}))); + + CHECK(json::to_cbor(json::parse("\"\\u00fc\"")) == std::vector({0x62, 0xc3, 0xbc})); + CHECK(json::parse("\"\\u00fc\"") == json::from_cbor(std::vector({0x62, 0xc3, 0xbc}))); + + CHECK(json::to_cbor(json::parse("\"\\u6c34\"")) == std::vector({0x63, 0xe6, 0xb0, 0xb4})); + CHECK(json::parse("\"\\u6c34\"") == json::from_cbor(std::vector({0x63, 0xe6, 0xb0, 0xb4}))); + + CHECK(json::to_cbor(json::parse("\"\\ud800\\udd51\"")) == std::vector({0x64, 0xf0, 0x90, 0x85, 0x91})); + CHECK(json::parse("\"\\ud800\\udd51\"") == json::from_cbor(std::vector({0x64, 0xf0, 0x90, 0x85, 0x91}))); + + // indefinite length strings + CHECK(json::parse("\"streaming\"") == json::from_cbor(std::vector({0x7f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0xff}))); + } + + SECTION("arrays") + { + CHECK(json::to_cbor(json::parse("[]")) == std::vector({0x80})); + CHECK(json::parse("[]") == json::from_cbor(std::vector({0x80}))); + + CHECK(json::to_cbor(json::parse("[1, 2, 3]")) == std::vector({0x83, 0x01, 0x02, 0x03})); + CHECK(json::parse("[1, 2, 3]") == json::from_cbor(std::vector({0x83, 0x01, 0x02, 0x03}))); + + CHECK(json::to_cbor(json::parse("[1, [2, 3], [4, 5]]")) == std::vector({0x83, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05})); + CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector({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({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({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({0x9f, 0xff}))); + CHECK(json::parse("[1, [2, 3], [4, 5]] ") == json::from_cbor(std::vector({0x9f, 0x01, 0x82, 0x02, 0x03, 0x9f, 0x04, 0x05, 0xff, 0xff}))); + CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector({0x9f, 0x01, 0x82, 0x02, 0x03, 0x82, 0x04, 0x05, 0xff}))); + CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector({0x83, 0x01, 0x82, 0x02, 0x03, 0x9f, 0x04, 0x05, 0xff}))); + CHECK(json::parse("[1, [2, 3], [4, 5]]") == json::from_cbor(std::vector({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({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({0xa0})); + CHECK(json::parse("{}") == json::from_cbor(std::vector({0xa0}))); + + CHECK(json::to_cbor(json::parse("{\"a\": 1, \"b\": [2, 3]}")) == std::vector({0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03})); + CHECK(json::parse("{\"a\": 1, \"b\": [2, 3]}") == json::from_cbor(std::vector({0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03}))); + + CHECK(json::to_cbor(json::parse("[\"a\", {\"b\": \"c\"}]")) == std::vector({0x82, 0x61, 0x61, 0xa1, 0x61, 0x62, 0x61, 0x63})); + CHECK(json::parse("[\"a\", {\"b\": \"c\"}]") == json::from_cbor(std::vector({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({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({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({0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff}))); + CHECK(json::parse("[\"a\", {\"b\": \"c\"}]") == json::from_cbor(std::vector({0x82, 0x61, 0x61, 0xbf, 0x61, 0x62, 0x61, 0x63, 0xff}))); + CHECK(json::parse("{\"Fun\": true, \"Amt\": -2}") == json::from_cbor(std::vector({0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff}))); + } +} +*/