From acff23efb25b13de0e3b8d5d4e01d17dd550c3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= Date: Sun, 9 Jul 2017 17:31:25 +0200 Subject: [PATCH] move binary_{reader,writer} outside of basic_json --- src/json.hpp | 3603 +++++++++++++++++---------------- test/src/unit-convenience.cpp | 2 +- 2 files changed, 1825 insertions(+), 1780 deletions(-) diff --git a/src/json.hpp b/src/json.hpp index 7ecbe91d..d3f35710 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -4564,6 +4564,1820 @@ class json_reverse_iterator : public std::reverse_iterator return it.operator * (); } }; + +///////////////////// +// output adapters // +///////////////////// + +/// abstract output adapter interface +template class output_adapter +{ + public: + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, size_t length) = 0; + virtual ~output_adapter() {} +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter +{ + public: + output_vector_adapter(std::vector& vec) : v(vec) {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + void write_characters(const CharType* s, size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter +{ + public: + output_stream_adapter(std::basic_ostream& s) : stream(s) {} + + void write_character(CharType c) override + { + stream.put(c); + } + + void write_characters(const CharType* s, size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; + +/// output adapter for basic_string +template +class output_string_adapter : public output_adapter +{ + public: + output_string_adapter(std::string& s) : str(s) {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + void write_characters(const CharType* s, size_t length) override + { + str.append(s, length); + } + + private: + std::basic_string& str; +}; + +template struct output_adapter_factory +{ + static std::shared_ptr> + create(std::vector& vec) + { + return std::make_shared>(vec); + } + + static std::shared_ptr> create(std::ostream& s) + { + return std::make_shared>(s); + } + + static std::shared_ptr> create(std::string& s) + { + return std::make_shared>(s); + } +}; + +////////////////////////////// +// binary reader and writer // +////////////////////////////// + +/*! +@brief deserialization of CBOR and MessagePack values +*/ +template +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) + : ia(adapter), is_little_endian(little_endianess()) + { + assert(ia); + } + + /*! + @brief create a JSON value from CBOR input + + @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 JSON value created from CBOR input + + @throw parse_error.110 if input ended unexpectedly + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_cbor(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + { + JSON_THROW( + parse_error::create(110, chars_read, "unexpected end of input")); + } + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + return static_cast(current); + } + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + return get_number(); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + return get_number(); + } + + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + return get_number(); + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + return get_number(); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + { + return static_cast(0x20 - 1 - current); + } + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + // must be uint8_t ! + return static_cast(-1) - get_number(); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + { + return static_cast(-1) - + static_cast(get_number()); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7f: // UTF-8 string (indefinite length) + { + return get_cbor_string(); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + BasicJsonType result = value_t::array; + const auto len = static_cast(current & 0x1f); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x98: // array (one-byte uint8_t for n follows) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x9a: // array (four-byte uint32_t for n follow) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x9b: // array (eight-byte uint64_t for n follow) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } + + case 0x9f: // array (indefinite length) + { + BasicJsonType result = value_t::array; + while (get() != 0xff) + { + result.push_back(parse_cbor(false)); + } + return result; + } + + // map (0x00..0x17 pairs of data items follow) + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + { + BasicJsonType result = value_t::object; + const auto len = static_cast(current & 0x1f); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xb8: // map (one-byte uint8_t for n follows) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xb9: // map (two-byte uint16_t for n follow) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xba: // map (four-byte uint32_t for n follow) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xbb: // map (eight-byte uint64_t for n follow) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xbf: // map (indefinite length) + { + BasicJsonType result = value_t::object; + while (get() != 0xff) + { + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } + + case 0xf4: // false + { + return false; + } + + case 0xf5: // true + { + return true; + } + + case 0xf6: // null + { + return value_t::null; + } + + case 0xf9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1 = get(); + check_eof(); + const int byte2 = get(); + check_eof(); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const int half = (byte1 << 8) + byte2; + const int exp = (half >> 10) & 0x1f; + const int mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = (mant == 0) ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + } + return (half & 0x8000) != 0 ? -val : val; + } + + case 0xfa: // Single-Precision Float (four-byte IEEE 754) + { + return get_number(); + } + + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + return get_number(); + } + + default: // anything else (0xFF is handled inside the other types) + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); + } + } + } + + /*! + @brief create a JSON value from MessagePack input + + @return JSON value created from MessagePack input + + @throw parse_error.110 if input ended unexpectedly + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_msgpack() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + { + JSON_THROW( + parse_error::create(110, chars_read, "unexpected end of input")); + } + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + { + return static_cast(current); + } + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + { + BasicJsonType result = value_t::object; + const auto len = static_cast(current & 0x0f); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + { + BasicJsonType result = value_t::array; + const auto len = static_cast(current & 0x0f); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } + + // fixstr + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + { + return get_msgpack_string(); + } + + case 0xc0: // nil + { + return value_t::null; + } + + case 0xc2: // false + { + return false; + } + + case 0xc3: // true + { + return true; + } + + case 0xca: // float 32 + { + return get_number(); + } + + case 0xcb: // float 64 + { + return get_number(); + } + + case 0xcc: // uint 8 + { + return get_number(); + } + + case 0xcd: // uint 16 + { + return get_number(); + } + + case 0xce: // uint 32 + { + return get_number(); + } + + case 0xcf: // uint 64 + { + return get_number(); + } + + case 0xd0: // int 8 + { + return get_number(); + } + + case 0xd1: // int 16 + { + return get_number(); + } + + case 0xd2: // int 32 + { + return get_number(); + } + + case 0xd3: // int 64 + { + return get_number(); + } + + case 0xd9: // str 8 + case 0xda: // str 16 + case 0xdb: // str 32 + { + return get_msgpack_string(); + } + + case 0xdc: // array 16 + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } + + case 0xdd: // array 32 + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } + + case 0xde: // map 16 + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } + + case 0xdf: // map 32 + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } + + // positive fixint + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + case 0xe8: + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xed: + case 0xee: + case 0xef: + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + case 0xfd: + case 0xfe: + case 0xff: + { + return static_cast(current); + } + + default: // anything else + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 112, chars_read, + "error reading MessagePack; last byte: 0x" + ss.str())); + } + } + } + + /*! + @brief determine system byte order + + @return true iff system's byte order is little endian + + @note from http://stackoverflow.com/a/1001328/266378 + */ + static bool little_endianess() noexcept + { + int num = 1; + return (*reinterpret_cast(&num) == 1); + } + + private: + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + int get() + { + ++chars_read; + return (current = ia->get_character()); + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + + @return number of type @a NumberType + + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. + + @throw parse_error.110 if input has less than `sizeof(NumberType)` + bytes + */ + template NumberType get_number() + { + // step 1: read input into array with system's byte order + std::array vec; + for (size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + check_eof(); + + // reverse byte order prior to conversion if necessary + if (is_little_endian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + NumberType result; + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return result; + } + + /*! + @brief create a string by reading characters from the input + + @param[in] len number of bytes to read + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref check_eof() detects the end of + the input before we run out of string memory. + + @return string created by reading @a len bytes + + @throw parse_error.110 if input has less than @a len bytes + */ + std::string get_string(const size_t len) + { + std::string result; + for (size_t i = 0; i < len; ++i) + { + get(); + check_eof(); + result.append(1, static_cast(current)); + } + return result; + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @return string + + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + std::string get_cbor_string() + { + check_eof(); + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + const auto len = static_cast(current & 0x1f); + return get_string(len); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0x7f: // UTF-8 string (indefinite length) + { + std::string result; + while (get() != 0xff) + { + check_eof(); + result.append(1, static_cast(current)); + } + return result; + } + + default: + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @return string + + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + std::string get_msgpack_string() + { + check_eof(); + + switch (current) + { + // fixstr + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + { + const auto len = static_cast(current & 0x1f); + return get_string(len); + } + + case 0xd9: // str 8 + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0xda: // str 16 + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0xdb: // str 32 + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + default: + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 113, chars_read, + "expected a MessagePack string; last byte: 0x" + ss.str())); + } + } + } + + /*! + @brief check if input ended + @throw parse_error.110 if input ended + */ + void check_eof() const + { + if (JSON_UNLIKELY(current == std::char_traits::eof())) + { + JSON_THROW( + parse_error::create(110, chars_read, "unexpected end of input")); + } + } + + private: + /// input adapter + input_adapter_t ia = nullptr; + + /// the current character + int current = std::char_traits::eof(); + + /// the number of characters read + size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = true; +}; + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) + : is_little_endian(binary_reader::little_endianess()), oa(adapter) + { + assert(oa); + } + + /*! + @brief[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(0xf6); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + oa->write_character(0x18); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + oa->write_character(0x19); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + oa->write_character(0x1a); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(0x1b); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x38); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x39); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x3a); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(0x3b); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + oa->write_character(0x18); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + oa->write_character(0x19); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + oa->write_character(0x1a); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(0x1b); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + // Double-Precision Float + oa->write_character(0xfb); + write_number(j.m_value.number_float); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0x78); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0x79); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0x7a); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0x7b); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0x98); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0x99); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0x9a); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0x9b); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xa0 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0xb8); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0xb9); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0xba); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0xbb); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + default: + { + break; + } + } + } + + /*! + @brief[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + // nil + oa->write_character(0xc0); + break; + } + + case value_t::boolean: + { + // true and false + oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we + // used the code from the value_t::number_unsigned case + // here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(0xcc); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(0xcd); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(0xce); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(0xcf); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(0xd0); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(0xd1); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(0xd2); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(0xd3); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(0xcc); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(0xcd); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(0xce); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(0xcf); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + // float 64 + oa->write_character(0xcb); + write_number(j.m_value.number_float); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xa0 | N)); + } + else if (N <= 255) + { + // str 8 + oa->write_character(0xd9); + write_number(static_cast(N)); + } + else if (N <= 65535) + { + // str 16 + oa->write_character(0xda); + write_number(static_cast(N)); + } + else if (N <= 4294967295) + { + // str 32 + oa->write_character(0xdb); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= 0xffff) + { + // array 16 + oa->write_character(0xdc); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + // array 32 + oa->write_character(0xdd); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xf))); + } + else if (N <= 65535) + { + // map 16 + oa->write_character(0xde); + write_number(static_cast(N)); + } + else if (N <= 4294967295) + { + // map 32 + oa->write_character(0xdf); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + default: + { + break; + } + } + } + + private: + /* + @brief write a number to output input + + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template void write_number(NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + private: + /// whether we can assume little endianess + const bool is_little_endian = true; + + /// the output + output_adapter_t oa = nullptr; +}; } // namespace detail /// namespace to hold default `to_json` / `from_json` functions @@ -5053,6 +6867,12 @@ class basic_json using iteration_proxy = ::nlohmann::detail::iteration_proxy; template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + using binary_reader = ::nlohmann::detail::binary_reader; + using binary_writer = ::nlohmann::detail::binary_writer; + public: using value_t = detail::value_t; // forward declarations @@ -6621,7 +8441,7 @@ class basic_json string_t dump(const int indent = -1, const char indent_char = ' ') const { string_t result; - serializer s(output_adapter::create(result), indent_char); + serializer s(detail::output_adapter_factory::create(result), indent_char); if (indent >= 0) { @@ -10250,110 +12070,6 @@ class basic_json /// @} - private: - ///////////////////// - // output adapters // - ///////////////////// - - /// abstract output adapter interface - template - class output_adapter - { - public: - virtual void write_character(CharType c) = 0; - virtual void write_characters(const CharType* s, size_t length) = 0; - virtual ~output_adapter() {} - - static std::shared_ptr> create(std::vector& vec) - { - return std::make_shared>(vec); - } - - static std::shared_ptr> create(std::ostream& s) - { - return std::make_shared>(s); - } - - static std::shared_ptr> create(std::string& s) - { - return std::make_shared>(s); - } - }; - - /// a type to simplify interfaces - template - using output_adapter_t = std::shared_ptr>; - - /// output adapter for byte vectors - template - class output_vector_adapter : public output_adapter - { - public: - output_vector_adapter(std::vector& vec) - : v(vec) - {} - - void write_character(CharType c) override - { - v.push_back(c); - } - - void write_characters(const CharType* s, size_t length) override - { - std::copy(s, s + length, std::back_inserter(v)); - } - - private: - std::vector& v; - }; - - /// output adapter for output streams - template - class output_stream_adapter : public output_adapter - { - public: - output_stream_adapter(std::basic_ostream& s) - : stream(s) - {} - - void write_character(CharType c) override - { - stream.put(c); - } - - void write_characters(const CharType* s, size_t length) override - { - stream.write(s, static_cast(length)); - } - - private: - std::basic_ostream& stream; - }; - - /// output adapter for basic_string - template - class output_string_adapter : public output_adapter - { - public: - output_string_adapter(std::string& s) - : str(s) - {} - - void write_character(CharType c) override - { - str.push_back(c); - } - - void write_characters(const CharType* s, size_t length) override - { - str.append(s, length); - } - - private: - std::basic_string& str; - }; - - /////////////////// // serialization // /////////////////// @@ -10991,7 +12707,7 @@ class basic_json o.width(0); // do the actual serialization - serializer s(output_adapter::create(o), o.fill()); + serializer s(detail::output_adapter_factory::create(o), o.fill()); s.dump(j, pretty_print, static_cast(indentation)); return o; } @@ -11410,1677 +13126,6 @@ class basic_json /// @name binary serialization/deserialization support /// @{ - /*! - @brief deserialization of CBOR and MessagePack values - */ - class binary_reader - { - public: - /*! - @brief create a binary reader - - @param[in] adapter input adapter to read from - */ - explicit binary_reader(detail::input_adapter_t adapter) - : ia(adapter), is_little_endian(little_endianess()) - { - assert(ia); - } - - /*! - @brief create a JSON value from CBOR input - - @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 JSON value created from CBOR input - - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_cbor(const bool get_char = true) - { - switch (get_char ? get() : current) - { - // EOF - case std::char_traits::eof(): - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } - - // Integer 0x00..0x17 (0..23) - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - { - return static_cast(current); - } - - case 0x18: // Unsigned integer (one-byte uint8_t follows) - { - return get_number(); - } - - case 0x19: // Unsigned integer (two-byte uint16_t follows) - { - return get_number(); - } - - case 0x1a: // Unsigned integer (four-byte uint32_t follows) - { - return get_number(); - } - - case 0x1b: // Unsigned integer (eight-byte uint64_t follows) - { - return get_number(); - } - - // Negative integer -1-0x00..-1-0x17 (-1..-24) - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - { - return static_cast(0x20 - 1 - current); - } - - case 0x38: // Negative integer (one-byte uint8_t follows) - { - // must be uint8_t ! - return static_cast(-1) - get_number(); - } - - case 0x39: // Negative integer -1-n (two-byte uint16_t follows) - { - return static_cast(-1) - get_number(); - } - - case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) - { - return static_cast(-1) - get_number(); - } - - case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) - { - return static_cast(-1) - static_cast(get_number()); - } - - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - case 0x7f: // UTF-8 string (indefinite length) - { - return get_cbor_string(); - } - - // array (0x00..0x17 data items follow) - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } - - case 0x98: // array (one-byte uint8_t for n follows) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } - - case 0x99: // array (two-byte uint16_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } - - case 0x9a: // array (four-byte uint32_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } - - case 0x9b: // array (eight-byte uint64_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_cbor()); - } - return result; - } - - case 0x9f: // array (indefinite length) - { - basic_json result = value_t::array; - while (get() != 0xff) - { - result.push_back(parse_cbor(false)); - } - return result; - } - - // map (0x00..0x17 pairs of data items follow) - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } - - case 0xb8: // map (one-byte uint8_t for n follows) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } - - case 0xb9: // map (two-byte uint16_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } - - case 0xba: // map (four-byte uint32_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } - - case 0xbb: // map (eight-byte uint64_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } - - case 0xbf: // map (indefinite length) - { - basic_json result = value_t::object; - while (get() != 0xff) - { - auto key = get_cbor_string(); - result[key] = parse_cbor(); - } - return result; - } - - case 0xf4: // false - { - return false; - } - - case 0xf5: // true - { - return true; - } - - case 0xf6: // null - { - return value_t::null; - } - - case 0xf9: // Half-Precision Float (two-byte IEEE 754) - { - const int byte1 = get(); - check_eof(); - const int byte2 = get(); - check_eof(); - - // code from RFC 7049, Appendix D, Figure 3: - // As half-precision floating-point numbers were only added - // to IEEE 754 in 2008, today's programming platforms often - // still only have limited support for them. It is very - // easy to include at least decoding support for them even - // without such support. An example of a small decoder for - // half-precision floating-point numbers in the C language - // is shown in Fig. 3. - const int half = (byte1 << 8) + byte2; - const int exp = (half >> 10) & 0x1f; - const int mant = half & 0x3ff; - double val; - if (exp == 0) - { - val = std::ldexp(mant, -24); - } - else if (exp != 31) - { - val = std::ldexp(mant + 1024, exp - 25); - } - else - { - val = (mant == 0) - ? std::numeric_limits::infinity() - : std::numeric_limits::quiet_NaN(); - } - return (half & 0x8000) != 0 ? -val : val; - } - - case 0xfa: // Single-Precision Float (four-byte IEEE 754) - { - return get_number(); - } - - case 0xfb: // Double-Precision Float (eight-byte IEEE 754) - { - return get_number(); - } - - default: // anything else (0xFF is handled inside the other types) - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); - } - } - } - - /*! - @brief create a JSON value from MessagePack input - - @return JSON value created from MessagePack input - - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_msgpack() - { - switch (get()) - { - // EOF - case std::char_traits::eof(): - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } - - // positive fixint - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5a: - case 0x5b: - case 0x5c: - case 0x5d: - case 0x5e: - case 0x5f: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7a: - case 0x7b: - case 0x7c: - case 0x7d: - case 0x7e: - case 0x7f: - { - return static_cast(current); - } - - // fixmap - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } - - // fixarray - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } - - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - return get_msgpack_string(); - } - - case 0xc0: // nil - { - return value_t::null; - } - - case 0xc2: // false - { - return false; - } - - case 0xc3: // true - { - return true; - } - - case 0xca: // float 32 - { - return get_number(); - } - - case 0xcb: // float 64 - { - return get_number(); - } - - case 0xcc: // uint 8 - { - return get_number(); - } - - case 0xcd: // uint 16 - { - return get_number(); - } - - case 0xce: // uint 32 - { - return get_number(); - } - - case 0xcf: // uint 64 - { - return get_number(); - } - - case 0xd0: // int 8 - { - return get_number(); - } - - case 0xd1: // int 16 - { - return get_number(); - } - - case 0xd2: // int 32 - { - return get_number(); - } - - case 0xd3: // int 64 - { - return get_number(); - } - - case 0xd9: // str 8 - case 0xda: // str 16 - case 0xdb: // str 32 - { - return get_msgpack_string(); - } - - case 0xdc: // array 16 - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } - - case 0xdd: // array 32 - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } - - case 0xde: // map 16 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } - - case 0xdf: // map 32 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } - - // positive fixint - case 0xe0: - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe4: - case 0xe5: - case 0xe6: - case 0xe7: - case 0xe8: - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xed: - case 0xee: - case 0xef: - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - case 0xff: - { - return static_cast(current); - } - - default: // anything else - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + ss.str())); - } - } - } - - /*! - @brief determine system byte order - - @return true iff system's byte order is little endian - - @note from http://stackoverflow.com/a/1001328/266378 - */ - static bool little_endianess() noexcept - { - int num = 1; - return (*reinterpret_cast(&num) == 1); - } - - private: - /*! - @brief get next character from the input - - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns - `std::char_traits::eof()` in that case. - - @return character read from the input - */ - int get() - { - ++chars_read; - return (current = ia->get_character()); - } - - /* - @brief read a number from the input - - @tparam NumberType the type of the number - - @return number of type @a NumberType - - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. - - @throw parse_error.110 if input has less than `sizeof(NumberType)` - bytes - */ - template - NumberType get_number() - { - // step 1: read input into array with system's byte order - std::array vec; - for (size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - check_eof(); - - // reverse byte order prior to conversion if necessary - if (is_little_endian) - { - vec[sizeof(NumberType) - i - 1] = static_cast(current); - } - else - { - vec[i] = static_cast(current); // LCOV_EXCL_LINE - } - } - - // step 2: convert array into number of type T and return - NumberType result; - std::memcpy(&result, vec.data(), sizeof(NumberType)); - return result; - } - - /*! - @brief create a string by reading characters from the input - - @param[in] len number of bytes to read - - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref check_eof() detects the end of - the input before we run out of string memory. - - @return string created by reading @a len bytes - - @throw parse_error.110 if input has less than @a len bytes - */ - std::string get_string(const size_t len) - { - std::string result; - for (size_t i = 0; i < len; ++i) - { - get(); - check_eof(); - result.append(1, static_cast(current)); - } - return result; - } - - /*! - @brief reads a CBOR string - - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - Additionally, CBOR's strings with indefinite lengths are supported. - - @return string - - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpected byte is read - */ - std::string get_cbor_string() - { - check_eof(); - - switch (current) - { - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - { - const auto len = static_cast(current & 0x1f); - return get_string(len); - } - - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - case 0x7f: // UTF-8 string (indefinite length) - { - std::string result; - while (get() != 0xff) - { - check_eof(); - result.append(1, static_cast(current)); - } - return result; - } - - default: - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); - } - } - } - - /*! - @brief reads a MessagePack string - - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - - @return string - - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpected byte is read - */ - std::string get_msgpack_string() - { - check_eof(); - - switch (current) - { - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - const auto len = static_cast(current & 0x1f); - return get_string(len); - } - - case 0xd9: // str 8 - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - case 0xda: // str 16 - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - case 0xdb: // str 32 - { - const auto len = static_cast(get_number()); - return get_string(len); - } - - default: - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + ss.str())); - } - } - } - - /*! - @brief check if input ended - @throw parse_error.110 if input ended - */ - void check_eof() const - { - if (JSON_UNLIKELY(current == std::char_traits::eof())) - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } - } - - private: - /// input adapter - detail::input_adapter_t ia = nullptr; - - /// the current character - int current = std::char_traits::eof(); - - /// the number of characters read - size_t chars_read = 0; - - /// whether we can assume little endianess - const bool is_little_endian = true; - }; - - /*! - @brief serialization to CBOR and MessagePack values - */ - class binary_writer - { - public: - /*! - @brief create a binary writer - - @param[in] adapter output adapter to write to - */ - explicit binary_writer(output_adapter_t adapter) - : is_little_endian(binary_reader::little_endianess()), oa(adapter) - { - assert(oa); - } - - /*! - @brief[in] j JSON value to serialize - */ - void write_cbor(const basic_json& j) - { - switch (j.type()) - { - case value_t::null: - { - oa->write_character(0xf6); - break; - } - - case value_t::boolean: - { - oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); - break; - } - - case value_t::number_integer: - { - if (j.m_value.number_integer >= 0) - { - // CBOR does not differentiate between positive signed - // integers and unsigned integers. Therefore, we used the - // code from the value_t::number_unsigned case here. - if (j.m_value.number_integer <= 0x17) - { - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_integer)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - // The conversions below encode the sign in the first - // byte, and the value is converted to a positive number. - const auto positive_number = -1 - j.m_value.number_integer; - if (j.m_value.number_integer >= -24) - { - write_number(static_cast(0x20 + positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x38); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x39); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x3a); - write_number(static_cast(positive_number)); - } - else - { - oa->write_character(0x3b); - write_number(static_cast(positive_number)); - } - } - break; - } - - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned <= 0x17) - { - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_unsigned)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_unsigned)); - } - break; - } - - case value_t::number_float: - { - // Double-Precision Float - oa->write_character(0xfb); - write_number(j.m_value.number_float); - break; - } - - case value_t::string: - { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 0x17) - { - write_number(static_cast(0x60 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x78); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x79); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x7a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x7b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP - - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; - } - - case value_t::array: - { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 0x17) - { - write_number(static_cast(0x80 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x98); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x99); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x9a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x9b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP - - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_cbor(el); - } - break; - } - - case value_t::object: - { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 0x17) - { - write_number(static_cast(0xa0 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0xb8); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0xb9); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0xba); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0xbb); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP - - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_cbor(el.first); - write_cbor(el.second); - } - break; - } - - default: - { - break; - } - } - } - - /*! - @brief[in] j JSON value to serialize - */ - void write_msgpack(const basic_json& j) - { - switch (j.type()) - { - case value_t::null: - { - // nil - oa->write_character(0xc0); - break; - } - - case value_t::boolean: - { - // true and false - oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); - break; - } - - case value_t::number_integer: - { - if (j.m_value.number_integer >= 0) - { - // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we - // used the code from the value_t::number_unsigned case - // here. - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - if (j.m_value.number_integer >= -32) - { - // negative fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 8 - oa->write_character(0xd0); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 16 - oa->write_character(0xd1); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 32 - oa->write_character(0xd2); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 64 - oa->write_character(0xd3); - write_number(static_cast(j.m_value.number_integer)); - } - } - break; - } - - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - break; - } - - case value_t::number_float: - { - // float 64 - oa->write_character(0xcb); - write_number(j.m_value.number_float); - break; - } - - case value_t::string: - { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 31) - { - // fixstr - write_number(static_cast(0xa0 | N)); - } - else if (N <= 255) - { - // str 8 - oa->write_character(0xd9); - write_number(static_cast(N)); - } - else if (N <= 65535) - { - // str 16 - oa->write_character(0xda); - write_number(static_cast(N)); - } - else if (N <= 4294967295) - { - // str 32 - oa->write_character(0xdb); - write_number(static_cast(N)); - } - - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; - } - - case value_t::array: - { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 15) - { - // fixarray - write_number(static_cast(0x90 | N)); - } - else if (N <= 0xffff) - { - // array 16 - oa->write_character(0xdc); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - // array 32 - oa->write_character(0xdd); - write_number(static_cast(N)); - } - - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_msgpack(el); - } - break; - } - - case value_t::object: - { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 15) - { - // fixmap - write_number(static_cast(0x80 | (N & 0xf))); - } - else if (N <= 65535) - { - // map 16 - oa->write_character(0xde); - write_number(static_cast(N)); - } - else if (N <= 4294967295) - { - // map 32 - oa->write_character(0xdf); - write_number(static_cast(N)); - } - - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_msgpack(el.first); - write_msgpack(el.second); - } - break; - } - - default: - { - break; - } - } - } - - private: - /* - @brief write a number to output input - - @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. - */ - template - void write_number(NumberType n) - { - // step 1: write number to array of length NumberType - std::array vec; - std::memcpy(vec.data(), &n, sizeof(NumberType)); - - // step 2: write array to output (with possible reordering) - if (is_little_endian) - { - // reverse byte order prior to conversion if necessary - std::reverse(vec.begin(), vec.end()); - } - - oa->write_characters(vec.data(), sizeof(NumberType)); - } - - private: - /// whether we can assume little endianess - const bool is_little_endian = true; - - /// the output - output_adapter_t oa = nullptr; - }; - public: /*! @brief create a CBOR serialization of a given JSON value @@ -13167,7 +13212,7 @@ class basic_json static std::vector to_cbor(const basic_json& j) { std::vector result; - binary_writer bw(output_adapter::create(result)); + binary_writer bw(detail::output_adapter_factory::create(result)); bw.write_cbor(j); return result; } @@ -13249,7 +13294,7 @@ class basic_json static std::vector to_msgpack(const basic_json& j) { std::vector result; - binary_writer bw(output_adapter::create(result)); + binary_writer bw(detail::output_adapter_factory::create(result)); bw.write_msgpack(j); return result; } diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index f757541d..52746028 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -53,7 +53,7 @@ TEST_CASE("convenience functions") const char* escaped) { std::stringstream ss; - json::serializer s(json::output_adapter::create(ss), ' '); + json::serializer s(nlohmann::detail::output_adapter_factory::create(ss), ' '); s.dump_escaped(original); CHECK(ss.str() == escaped); };