From dc6fc3e079f0f2c2bded7923e5cbc87f85a5e1d9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 11 Dec 2016 13:36:08 +0100 Subject: [PATCH] :memo: clean up and added documentation for #358 --- doc/examples/from_cbor.cpp | 18 +++ doc/examples/from_cbor.link | 1 + doc/examples/from_cbor.output | 4 + doc/examples/from_msgpack.cpp | 18 +++ doc/examples/from_msgpack.link | 1 + doc/examples/from_msgpack.output | 4 + doc/examples/to_cbor.cpp | 19 +++ doc/examples/to_cbor.link | 1 + doc/examples/to_cbor.output | 1 + doc/examples/to_msgpack.cpp | 19 +++ doc/examples/to_msgpack.link | 1 + doc/examples/to_msgpack.output | 1 + src/json.hpp | 266 ++++++++++++++++++++++++------- src/json.hpp.re2c | 266 ++++++++++++++++++++++++------- test/src/unit-msgpack.cpp | 159 ++++++++++++++++++ 15 files changed, 665 insertions(+), 114 deletions(-) create mode 100644 doc/examples/from_cbor.cpp create mode 100644 doc/examples/from_cbor.link create mode 100644 doc/examples/from_cbor.output create mode 100644 doc/examples/from_msgpack.cpp create mode 100644 doc/examples/from_msgpack.link create mode 100644 doc/examples/from_msgpack.output create mode 100644 doc/examples/to_cbor.cpp create mode 100644 doc/examples/to_cbor.link create mode 100644 doc/examples/to_cbor.output create mode 100644 doc/examples/to_msgpack.cpp create mode 100644 doc/examples/to_msgpack.link create mode 100644 doc/examples/to_msgpack.output diff --git a/doc/examples/from_cbor.cpp b/doc/examples/from_cbor.cpp new file mode 100644 index 00000000..92b05225 --- /dev/null +++ b/doc/examples/from_cbor.cpp @@ -0,0 +1,18 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // create byte vector + std::vector v = {0xa2, 0x67, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0xf5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x00 + }; + + // deserialize it with CBOR + json j = json::from_cbor(v); + + // print the deserialized JSON value + std::cout << std::setw(2) << j << std::endl; +} diff --git a/doc/examples/from_cbor.link b/doc/examples/from_cbor.link new file mode 100644 index 00000000..81204989 --- /dev/null +++ b/doc/examples/from_cbor.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/from_cbor.output b/doc/examples/from_cbor.output new file mode 100644 index 00000000..259f63bd --- /dev/null +++ b/doc/examples/from_cbor.output @@ -0,0 +1,4 @@ +{ + "compact": true, + "schema": 0 +} diff --git a/doc/examples/from_msgpack.cpp b/doc/examples/from_msgpack.cpp new file mode 100644 index 00000000..d275f13a --- /dev/null +++ b/doc/examples/from_msgpack.cpp @@ -0,0 +1,18 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // create byte vector + std::vector v = {0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x00 + }; + + // deserialize it with MessagePack + json j = json::from_msgpack(v); + + // print the deserialized JSON value + std::cout << std::setw(2) << j << std::endl; +} diff --git a/doc/examples/from_msgpack.link b/doc/examples/from_msgpack.link new file mode 100644 index 00000000..0d5e7831 --- /dev/null +++ b/doc/examples/from_msgpack.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/from_msgpack.output b/doc/examples/from_msgpack.output new file mode 100644 index 00000000..259f63bd --- /dev/null +++ b/doc/examples/from_msgpack.output @@ -0,0 +1,4 @@ +{ + "compact": true, + "schema": 0 +} diff --git a/doc/examples/to_cbor.cpp b/doc/examples/to_cbor.cpp new file mode 100644 index 00000000..21a5ce86 --- /dev/null +++ b/doc/examples/to_cbor.cpp @@ -0,0 +1,19 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // create a JSON value + json j = R"({"compact": true, "schema": 0})"_json; + + // serialize it to CBOR + std::vector v = json::to_cbor(j); + + // print the vector content + for (auto& byte : v) + { + std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " "; + } + std::cout << std::endl; +} diff --git a/doc/examples/to_cbor.link b/doc/examples/to_cbor.link new file mode 100644 index 00000000..3ab655c1 --- /dev/null +++ b/doc/examples/to_cbor.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/to_cbor.output b/doc/examples/to_cbor.output new file mode 100644 index 00000000..02c9adab --- /dev/null +++ b/doc/examples/to_cbor.output @@ -0,0 +1 @@ +0xa2 0x67 0x63 0x6f 0x6d 0x70 0x61 0x63 0x74 0xf5 0x66 0x73 0x63 0x68 0x65 0x6d 0x61 0x00 diff --git a/doc/examples/to_msgpack.cpp b/doc/examples/to_msgpack.cpp new file mode 100644 index 00000000..21c8817a --- /dev/null +++ b/doc/examples/to_msgpack.cpp @@ -0,0 +1,19 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // create a JSON value + json j = R"({"compact": true, "schema": 0})"_json; + + // serialize it to MessagePack + std::vector v = json::to_msgpack(j); + + // print the vector content + for (auto& byte : v) + { + std::cout << "0x" << std::hex << std::setw(2) << std::setfill('0') << (int)byte << " "; + } + std::cout << std::endl; +} diff --git a/doc/examples/to_msgpack.link b/doc/examples/to_msgpack.link new file mode 100644 index 00000000..4c7d3a27 --- /dev/null +++ b/doc/examples/to_msgpack.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/to_msgpack.output b/doc/examples/to_msgpack.output new file mode 100644 index 00000000..4d6c40ab --- /dev/null +++ b/doc/examples/to_msgpack.output @@ -0,0 +1 @@ +0x82 0xa7 0x63 0x6f 0x6d 0x70 0x61 0x63 0x74 0xc3 0xa6 0x73 0x63 0x68 0x65 0x6d 0x61 0x00 diff --git a/src/json.hpp b/src/json.hpp index 436c91be..d8079b67 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6312,6 +6312,16 @@ class basic_json return result; } + /*! + @brief create a MessagePack serialization of a given JSON value + + This is a straightforward implementation of the MessagePack specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ static void to_msgpack_internal(const basic_json& j, std::vector& v) { switch (j.type()) @@ -6332,34 +6342,72 @@ class basic_json case value_t::number_integer: { - if (j.m_value.number_integer >= -32 and j.m_value.number_integer < 128) + if (j.m_value.number_integer >= 0) { - // negative fixnum and positive fixnum - add_to_vector(v, 1, j.m_value.number_integer); + // 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 + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT8_MAX) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT16_MAX) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT32_MAX) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT64_MAX) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } } - else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX) + else { - // int 8 - v.push_back(0xd0); - add_to_vector(v, 1, j.m_value.number_integer); - } - else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX) - { - // int 16 - v.push_back(0xd1); - add_to_vector(v, 2, j.m_value.number_integer); - } - else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX) - { - // int 32 - v.push_back(0xd2); - add_to_vector(v, 4, j.m_value.number_integer); - } - else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX) - { - // int 64 - v.push_back(0xd3); - add_to_vector(v, 8, j.m_value.number_integer); + if (j.m_value.number_integer >= -32) + { + // negative fixnum + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX) + { + // int 8 + v.push_back(0xd0); + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX) + { + // int 16 + v.push_back(0xd1); + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX) + { + // int 32 + v.push_back(0xd2); + add_to_vector(v, 4, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX) + { + // int 64 + v.push_back(0xd3); + add_to_vector(v, 8, j.m_value.number_integer); + } } break; } @@ -6509,6 +6557,16 @@ class basic_json } } + /*! + @brief create a CBOR serialization of a given JSON value + + This is a straightforward implementation of the CBOR specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://tools.ietf.org/html/rfc7049 + */ static void to_cbor_internal(const basic_json& j, std::vector& v) { switch (j.type()) @@ -6566,7 +6624,7 @@ class basic_json // 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 <= -1 and j.m_value.number_integer >= -24) + if (j.m_value.number_integer >= -24) { v.push_back(static_cast(0x20 + positive_number)); } @@ -6648,32 +6706,30 @@ class basic_json const auto N = j.m_value.string->size(); if (N <= 0x17) { - v.push_back(0x60 + N); + v.push_back(0x60 + N); // 1 byte for string + size } else if (N <= 0xff) { - v.push_back(0x78); - // one-byte uint8_t for N + v.push_back(0x78); // one-byte uint8_t for N add_to_vector(v, 1, N); } else if (N <= 0xffff) { - v.push_back(0x79); - // two-byte uint16_t for N + v.push_back(0x79); // two-byte uint16_t for N add_to_vector(v, 2, N); } else if (N <= 0xffffffff) { - v.push_back(0x7a); - // four-byte uint32_t for N + v.push_back(0x7a); // four-byte uint32_t for N add_to_vector(v, 4, N); } + // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - v.push_back(0x7b); - // eight-byte uint64_t for N + v.push_back(0x7b); // eight-byte uint64_t for N add_to_vector(v, 8, N); } + // LCOV_EXCL_STOP // append string std::copy(j.m_value.string->begin(), j.m_value.string->end(), @@ -6686,33 +6742,30 @@ class basic_json const auto N = j.m_value.array->size(); if (N <= 0x17) { - // 1 byte for array + size - v.push_back(0x80 + N); + v.push_back(0x80 + N); // 1 byte for array + size } else if (N <= 0xff) { - v.push_back(0x98); - // one-byte uint8_t for N + v.push_back(0x98); // one-byte uint8_t for N add_to_vector(v, 1, N); } else if (N <= 0xffff) { - v.push_back(0x99); - // two-byte uint16_t for N + v.push_back(0x99); // two-byte uint16_t for N add_to_vector(v, 2, N); } else if (N <= 0xffffffff) { - v.push_back(0x9a); - // four-byte uint32_t for N + v.push_back(0x9a); // four-byte uint32_t for N add_to_vector(v, 4, N); } + // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - v.push_back(0x9b); - // eight-byte uint64_t for N + v.push_back(0x9b); // eight-byte uint64_t for N add_to_vector(v, 8, N); } + // LCOV_EXCL_STOP // append each element for (const auto& el : *j.m_value.array) @@ -6727,33 +6780,30 @@ class basic_json const auto N = j.m_value.object->size(); if (N <= 0x17) { - // 1 byte for object + size - v.push_back(0xa0 + N); + v.push_back(0xa0 + N); // 1 byte for object + size } else if (N <= 0xff) { v.push_back(0xb8); - // one-byte uint8_t for N - add_to_vector(v, 1, N); + add_to_vector(v, 1, N); // one-byte uint8_t for N } else if (N <= 0xffff) { v.push_back(0xb9); - // two-byte uint16_t for N - add_to_vector(v, 2, N); + add_to_vector(v, 2, N); // two-byte uint16_t for N } else if (N <= 0xffffffff) { v.push_back(0xba); - // four-byte uint32_t for N - add_to_vector(v, 4, N); + add_to_vector(v, 4, N); // four-byte uint32_t for N } + // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { v.push_back(0xbb); - // eight-byte uint64_t for N - add_to_vector(v, 8, N); + add_to_vector(v, 8, N); // eight-byte uint64_t for N } + // LCOV_EXCL_STOP // append each element for (const auto& el : *j.m_value.object) @@ -6772,8 +6822,18 @@ class basic_json } /*! + @brief create a JSON value from a given MessagePack vector + @param[in] v MessagePack serialization @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { @@ -6992,6 +7052,20 @@ class basic_json } } + /*! + @brief create a JSON value from a given CBOR vector + + @param[in] v CBOR serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid CBOR + @throw std::out_of_range if the given vector ends prematurely + + @sa https://tools.ietf.org/html/rfc7049 + */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { // store and increment index @@ -7457,8 +7531,24 @@ class basic_json public: /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + @param[in] j JSON value to serialize - @retuen MessagePack serialization as char vector + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector&) for the analogous + deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format */ static std::vector to_msgpack(const basic_json& j) { @@ -7467,6 +7557,28 @@ class basic_json return result; } + /*! + @brief create a JSON value from a byte vector in MessagePack format + + Deserializes a given byte vector @a v to a JSON value using the MessagePack + serialization format. + + @param[in] v a byte vector in MessagePack format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(const std::vector&) for the related CBOR format + */ static basic_json from_msgpack(const std::vector& v) { size_t i = 0; @@ -7474,8 +7586,25 @@ class basic_json } /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + @param[in] j JSON value to serialize - @retuen CBOR serialization as char vector + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(const std::vector&) for the analogous + deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format */ static std::vector to_cbor(const basic_json& j) { @@ -7484,6 +7613,29 @@ class basic_json return result; } + /*! + @brief create a JSON value from a byte vector in CBOR format + + Deserializes a given byte vector @a v to a JSON value using the CBOR + (Concise Binary Object Representation) serialization format. + + @param[in] v a byte vector in CBOR format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(const std::vector&) for the related + MessagePack format + */ static basic_json from_cbor(const std::vector& v) { size_t i = 0; diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index c3f85dcd..f82695a7 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6312,6 +6312,16 @@ class basic_json return result; } + /*! + @brief create a MessagePack serialization of a given JSON value + + This is a straightforward implementation of the MessagePack specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md + */ static void to_msgpack_internal(const basic_json& j, std::vector& v) { switch (j.type()) @@ -6332,34 +6342,72 @@ class basic_json case value_t::number_integer: { - if (j.m_value.number_integer >= -32 and j.m_value.number_integer < 128) + if (j.m_value.number_integer >= 0) { - // negative fixnum and positive fixnum - add_to_vector(v, 1, j.m_value.number_integer); + // 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 + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT8_MAX) + { + // uint 8 + v.push_back(0xcc); + add_to_vector(v, 1, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT16_MAX) + { + // uint 16 + v.push_back(0xcd); + add_to_vector(v, 2, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT32_MAX) + { + // uint 32 + v.push_back(0xce); + add_to_vector(v, 4, j.m_value.number_unsigned); + } + else if (j.m_value.number_unsigned <= UINT64_MAX) + { + // uint 64 + v.push_back(0xcf); + add_to_vector(v, 8, j.m_value.number_unsigned); + } } - else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX) + else { - // int 8 - v.push_back(0xd0); - add_to_vector(v, 1, j.m_value.number_integer); - } - else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX) - { - // int 16 - v.push_back(0xd1); - add_to_vector(v, 2, j.m_value.number_integer); - } - else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX) - { - // int 32 - v.push_back(0xd2); - add_to_vector(v, 4, j.m_value.number_integer); - } - else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX) - { - // int 64 - v.push_back(0xd3); - add_to_vector(v, 8, j.m_value.number_integer); + if (j.m_value.number_integer >= -32) + { + // negative fixnum + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX) + { + // int 8 + v.push_back(0xd0); + add_to_vector(v, 1, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX) + { + // int 16 + v.push_back(0xd1); + add_to_vector(v, 2, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX) + { + // int 32 + v.push_back(0xd2); + add_to_vector(v, 4, j.m_value.number_integer); + } + else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX) + { + // int 64 + v.push_back(0xd3); + add_to_vector(v, 8, j.m_value.number_integer); + } } break; } @@ -6509,6 +6557,16 @@ class basic_json } } + /*! + @brief create a CBOR serialization of a given JSON value + + This is a straightforward implementation of the CBOR specification. + + @param[in] j JSON value to serialize + @param[in,out] v byte vector to write the serialization to + + @sa https://tools.ietf.org/html/rfc7049 + */ static void to_cbor_internal(const basic_json& j, std::vector& v) { switch (j.type()) @@ -6566,7 +6624,7 @@ class basic_json // 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 <= -1 and j.m_value.number_integer >= -24) + if (j.m_value.number_integer >= -24) { v.push_back(static_cast(0x20 + positive_number)); } @@ -6648,32 +6706,30 @@ class basic_json const auto N = j.m_value.string->size(); if (N <= 0x17) { - v.push_back(0x60 + N); + v.push_back(0x60 + N); // 1 byte for string + size } else if (N <= 0xff) { - v.push_back(0x78); - // one-byte uint8_t for N + v.push_back(0x78); // one-byte uint8_t for N add_to_vector(v, 1, N); } else if (N <= 0xffff) { - v.push_back(0x79); - // two-byte uint16_t for N + v.push_back(0x79); // two-byte uint16_t for N add_to_vector(v, 2, N); } else if (N <= 0xffffffff) { - v.push_back(0x7a); - // four-byte uint32_t for N + v.push_back(0x7a); // four-byte uint32_t for N add_to_vector(v, 4, N); } + // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - v.push_back(0x7b); - // eight-byte uint64_t for N + v.push_back(0x7b); // eight-byte uint64_t for N add_to_vector(v, 8, N); } + // LCOV_EXCL_STOP // append string std::copy(j.m_value.string->begin(), j.m_value.string->end(), @@ -6686,33 +6742,30 @@ class basic_json const auto N = j.m_value.array->size(); if (N <= 0x17) { - // 1 byte for array + size - v.push_back(0x80 + N); + v.push_back(0x80 + N); // 1 byte for array + size } else if (N <= 0xff) { - v.push_back(0x98); - // one-byte uint8_t for N + v.push_back(0x98); // one-byte uint8_t for N add_to_vector(v, 1, N); } else if (N <= 0xffff) { - v.push_back(0x99); - // two-byte uint16_t for N + v.push_back(0x99); // two-byte uint16_t for N add_to_vector(v, 2, N); } else if (N <= 0xffffffff) { - v.push_back(0x9a); - // four-byte uint32_t for N + v.push_back(0x9a); // four-byte uint32_t for N add_to_vector(v, 4, N); } + // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { - v.push_back(0x9b); - // eight-byte uint64_t for N + v.push_back(0x9b); // eight-byte uint64_t for N add_to_vector(v, 8, N); } + // LCOV_EXCL_STOP // append each element for (const auto& el : *j.m_value.array) @@ -6727,33 +6780,30 @@ class basic_json const auto N = j.m_value.object->size(); if (N <= 0x17) { - // 1 byte for object + size - v.push_back(0xa0 + N); + v.push_back(0xa0 + N); // 1 byte for object + size } else if (N <= 0xff) { v.push_back(0xb8); - // one-byte uint8_t for N - add_to_vector(v, 1, N); + add_to_vector(v, 1, N); // one-byte uint8_t for N } else if (N <= 0xffff) { v.push_back(0xb9); - // two-byte uint16_t for N - add_to_vector(v, 2, N); + add_to_vector(v, 2, N); // two-byte uint16_t for N } else if (N <= 0xffffffff) { v.push_back(0xba); - // four-byte uint32_t for N - add_to_vector(v, 4, N); + add_to_vector(v, 4, N); // four-byte uint32_t for N } + // LCOV_EXCL_START else if (N <= 0xffffffffffffffff) { v.push_back(0xbb); - // eight-byte uint64_t for N - add_to_vector(v, 8, N); + add_to_vector(v, 8, N); // eight-byte uint64_t for N } + // LCOV_EXCL_STOP // append each element for (const auto& el : *j.m_value.object) @@ -6772,8 +6822,18 @@ class basic_json } /*! + @brief create a JSON value from a given MessagePack vector + @param[in] v MessagePack serialization @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @sa https://github.com/msgpack/msgpack/blob/master/spec.md */ static basic_json from_msgpack_internal(const std::vector& v, size_t& idx) { @@ -6992,6 +7052,20 @@ class basic_json } } + /*! + @brief create a JSON value from a given CBOR vector + + @param[in] v CBOR serialization + @param[in] idx byte index to start reading from @a v + + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid CBOR + @throw std::out_of_range if the given vector ends prematurely + + @sa https://tools.ietf.org/html/rfc7049 + */ static basic_json from_cbor_internal(const std::vector& v, size_t& idx) { // store and increment index @@ -7457,8 +7531,24 @@ class basic_json public: /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + @param[in] j JSON value to serialize - @retuen MessagePack serialization as char vector + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack(const std::vector&) for the analogous + deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format */ static std::vector to_msgpack(const basic_json& j) { @@ -7467,6 +7557,28 @@ class basic_json return result; } + /*! + @brief create a JSON value from a byte vector in MessagePack format + + Deserializes a given byte vector @a v to a JSON value using the MessagePack + serialization format. + + @param[in] v a byte vector in MessagePack format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from MessagePack were + used in the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(const std::vector&) for the related CBOR format + */ static basic_json from_msgpack(const std::vector& v) { size_t i = 0; @@ -7474,8 +7586,25 @@ class basic_json } /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + @param[in] j JSON value to serialize - @retuen CBOR serialization as char vector + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(const std::vector&) for the analogous + deserialization + @sa @ref to_msgpack(const basic_json& for the related MessagePack format */ static std::vector to_cbor(const basic_json& j) { @@ -7484,6 +7613,29 @@ class basic_json return result; } + /*! + @brief create a JSON value from a byte vector in CBOR format + + Deserializes a given byte vector @a v to a JSON value using the CBOR + (Concise Binary Object Representation) serialization format. + + @param[in] v a byte vector in CBOR format + @return deserialized JSON value + + @throw std::invalid_argument if unsupported features from CBOR were used in + the given vector @a v or if the input is not valid MessagePack + @throw std::out_of_range if the given vector ends prematurely + + @complexity Linear in the size of the byte vector @a v. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(const std::vector&) for the related + MessagePack format + */ static basic_json from_cbor(const std::vector& v) { size_t i = 0; diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 43b16031..7498940a 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -144,6 +144,165 @@ TEST_CASE("MessagePack") } } + SECTION("128..255 (int 8)") + { + 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(0xcc); + expected.push_back(static_cast(i)); + + // compare result + size + const auto result = json::to_msgpack(j); + CHECK(result == expected); + CHECK(result.size() == 2); + + // check individual bytes + CHECK(result[0] == 0xcc); + uint8_t restored = static_cast(result[1]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_msgpack(result) == j); + } + } + + SECTION("256..65535 (int 16)") + { + for (size_t i = 256; i <= 65535; ++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(0xcd); + expected.push_back(static_cast((i >> 8) & 0xff)); + expected.push_back(static_cast(i & 0xff)); + + // compare result + size + const auto result = json::to_msgpack(j); + CHECK(result == expected); + CHECK(result.size() == 3); + + // check individual bytes + CHECK(result[0] == 0xcd); + uint16_t restored = static_cast(result[1]) * 256 + static_cast(result[2]); + CHECK(restored == i); + + // roundtrip + CHECK(json::from_msgpack(result) == j); + } + } + + SECTION("65536..4294967295 (int 32)") + { + for (uint32_t i : + { + 65536u, 77777u, 1048576u, 4294967295u + }) + { + 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(0xce); + 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_msgpack(j); + CHECK(result == expected); + CHECK(result.size() == 5); + + // check individual bytes + CHECK(result[0] == 0xce); + uint32_t restored = static_cast((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_msgpack(result) == j); + } + } + + SECTION("4294967296..9223372036854775807 (int 64)") + { + for (uint64_t i : + { + 4294967296lu, 9223372036854775807lu + }) + { + 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(0xcf); + 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_msgpack(j); + CHECK(result == expected); + CHECK(result.size() == 9); + + // check individual bytes + CHECK(result[0] == 0xcf); + uint64_t restored = static_cast((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_msgpack(result) == j); + } + } + SECTION("-128..-33 (int 8)") { for (auto i = -128; i <= -33; ++i)