From 6b967e8b5b52112f1cc51d0b9ce1e8502dbfc7e5 Mon Sep 17 00:00:00 2001 From: mrabine Date: Mon, 9 Nov 2026 03:18:49 +0100 Subject: [PATCH 1/3] Msgpack binary value type --- data/include/join/json.hpp | 12 ++ data/include/join/pack.hpp | 42 ++++- data/include/join/sax.hpp | 10 ++ data/include/join/value.hpp | 99 +++++++++++- data/include/join/value_builder.hpp | 10 ++ data/tests/json_writer_test.cpp | 14 ++ data/tests/pack_reader_test.cpp | 76 ++------- data/tests/pack_writer_test.cpp | 28 ++++ data/tests/sax_handler_test.cpp | 10 ++ data/tests/value_builder_test.cpp | 21 +++ data/tests/value_test.cpp | 243 ++++++++++++++++++++++++++++ 11 files changed, 500 insertions(+), 65 deletions(-) diff --git a/data/include/join/json.hpp b/data/include/join/json.hpp index 89424582..01589864 100644 --- a/data/include/join/json.hpp +++ b/data/include/join/json.hpp @@ -430,6 +430,18 @@ namespace join return 0; } + /** + * @brief set binary value. + * @param value binary value to set. + * @return -1. + */ + int setBinary ([[maybe_unused]] Binary value) + { + join::lastError = make_error_code (SaxErrc::InvalidValue); + reset (); + return -1; + } + /** * @brief start array. * @param size array size (ignored). diff --git a/data/include/join/pack.hpp b/data/include/join/pack.hpp index af7fc87b..0e3f17b7 100644 --- a/data/include/join/pack.hpp +++ b/data/include/join/pack.hpp @@ -338,6 +338,33 @@ namespace join return 0; } + /** + * @brief set binary value. + * @param value binary value to set. + * @return 0 on success, -1 otherwise. + */ + int setBinary (Binary value) + { + if (value.size () < 256) + { + append (0xc4); + pack (static_cast (value.size ())); + } + else if (value.size () < 65536) + { + append (0xc5); + pack (static_cast (value.size ())); + } + else + { + append (0xc6); + pack (static_cast (value.size ())); + } + append (reinterpret_cast (value.data ()), static_cast (value.size ())); + endValue (); + return 0; + } + /** * @brief start array. * @param size array size. @@ -1226,6 +1253,19 @@ namespace join } } + if constexpr (std::is_same_v) + { + if (JOIN_UNLIKELY (static_cast (document.end () - document.tell ()) < len)) + { + join::lastError = make_error_code (SaxErrc::Truncated); + return -1; + } + + Binary output (reinterpret_cast (document.tell ()), len); + document.seek (document.tell () + len); + return handler.setBinary (output); + } + thread_local std::string cache; std::string output = std::move (cache); output.clear (); @@ -1234,7 +1274,7 @@ namespace join return -1; } - int result = handler.setString (output); + int result = handler.setBinary (Binary (reinterpret_cast (output.data ()), output.size ())); cache = std::move (output); return result; } diff --git a/data/include/join/sax.hpp b/data/include/join/sax.hpp index d80b63e0..514c9cc2 100644 --- a/data/include/join/sax.hpp +++ b/data/include/join/sax.hpp @@ -125,6 +125,16 @@ namespace join return 0; } + /** + * @brief set binary value. + * @param value binary value to set. + * @return 0. + */ + int setBinary ([[maybe_unused]] Binary value) noexcept + { + return 0; + } + /** * @brief start array. * @param size array size, absent when called by the JSON reader. diff --git a/data/include/join/value.hpp b/data/include/join/value.hpp index d32672bc..d39e99bc 100644 --- a/data/include/join/value.hpp +++ b/data/include/join/value.hpp @@ -64,6 +64,9 @@ namespace join /// string. using String = View; + /// binary. + using Binary = View; + /** * @brief member. */ @@ -123,8 +126,9 @@ namespace join Unsigned64Value = 5, /**< index of the 64 bits unsigned integer alternative. */ RealValue = 6, /**< index of the real alternative. */ StringValue = 7, /**< index of the string alternative. */ - ArrayValue = 8, /**< index of the array alternative. */ - ObjectValue = 9, /**< index of the object alternative. */ + BinaryValue = 8, /**< index of the binary alternative. */ + ArrayValue = 9, /**< index of the array alternative. */ + ObjectValue = 10, /**< index of the object alternative. */ }; /** @@ -373,6 +377,27 @@ namespace join _data.template set (copyObject (value)); } + /** + * @brief constructs a binary value. + * @param value binary value. + */ + BasicValue (Binary value) + : BasicValue (nullptr) + { + _data.template set (copyBinary (value)); + } + + /** + * @brief constructs a binary value using an allocator owned by the caller. + * @param value binary value. + * @param allocator allocator providing the memory of the value. + */ + BasicValue (Binary value, Allocator& allocator) + : BasicValue (allocator) + { + _data.template set (copyBinary (value)); + } + /** * @brief copy constructor. * @param other object to copy. @@ -581,6 +606,24 @@ namespace join return *this; } + /** + * @brief assign a binary value. + * @param value binary value. + * @return a reference of the current object. + */ + Value& operator= (Binary value) + { + if (_owner || (_allocator == nullptr)) + { + return *this = BasicValue (value); + } + + _data.template set (copyBinary (value)); + _capacity = 0; + + return *this; + } + /** * @brief move constructor. * @param other object to move. @@ -1441,6 +1484,25 @@ namespace join return _data.template get (); } + /** + * @brief check if the variable held by value is a binary value. + * @return true if the variable held by value is a binary value, false otherwise. + */ + constexpr bool isBinary () const + { + return _data.index () == BinaryValue; + } + + /** + * @brief get variable held by value as a binary value. + * @return variable held by value as a binary value. + * @throw std::bad_cast. + */ + constexpr const Binary& getBinary () const + { + return _data.template get (); + } + /** * @brief returns a reference to the value mapped to key. * @param key key of the element to find. @@ -2609,6 +2671,17 @@ namespace join return pushBack (Value (value, *allocator ())); } + /** + * @brief appends a binary value at the end of the nested container. + * @param value the element to append. + * @return a reference to the pushed object. + * @throw std::bad_cast. + */ + Value& pushBack (Binary value) + { + return pushBack (Value (value, *allocator ())); + } + /** * @brief appends a value at the end of the nested container. * @param value the element to append. @@ -3095,7 +3168,8 @@ namespace join private: /// variant type holding the value. - using Data = Variant; + using Data = + Variant; // friendship with value builder. friend class ValueBuilder; @@ -3167,6 +3241,18 @@ namespace join return String (data, value.size ()); } + /** + * @brief copy binary data into the allocator. + * @param value binary data to copy. + * @return copied binary data. + */ + Binary copyBinary (Binary value) + { + uint8_t* data = allocate (value.size ()); + std::copy_n (value.data (), value.size (), data); + return Binary (data, value.size ()); + } + /** * @brief move values of the open containers of a builder into the allocator. * @param values values to move. @@ -3254,6 +3340,10 @@ namespace join _data.template set (copyObject (other.getObject ())); break; + case BinaryValue: + _data.template set (copyBinary (other.getBinary ())); + break; + default: _data = other._data; break; @@ -3405,6 +3495,9 @@ namespace join case Value::ObjectValue: return setObject (value.getObject (), handler); + case Value::BinaryValue: + return handler.setBinary (value.getBinary ()); + default: return handler.setNull (); } diff --git a/data/include/join/value_builder.hpp b/data/include/join/value_builder.hpp index ae4e82e4..5dbb74b5 100644 --- a/data/include/join/value_builder.hpp +++ b/data/include/join/value_builder.hpp @@ -181,6 +181,16 @@ namespace join return emplaceValue (_root.copyString (value)); } + /** + * @brief set binary value. + * @param value binary value to set. + * @return 0 on success, -1 otherwise. + */ + int setBinary (Binary value) + { + return emplaceValue (_root.copyBinary (value)); + } + /** * @brief start array. * @param size array size (ignored). diff --git a/data/tests/json_writer_test.cpp b/data/tests/json_writer_test.cpp index 10a95273..465e0991 100644 --- a/data/tests/json_writer_test.cpp +++ b/data/tests/json_writer_test.cpp @@ -35,6 +35,7 @@ using join::JsonWriter; using join::Value; using join::Array; +using join::Binary; using join::Object; using join::SaxErrc; @@ -234,6 +235,19 @@ TEST (JsonWriter, setString) EXPECT_EQ (stream.str (), "\"\\\"\\\\\\b\\f\\n\\r\\t\\u0019\""); } +/** + * @brief Test setBinary method. + */ +TEST (JsonWriter, setBinary) +{ + std::stringstream stream; + JsonWriter jsonWriter (stream); + const uint8_t data[] = {0x01}; + + EXPECT_EQ (jsonWriter.setBinary (Binary (data, sizeof (data))), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidValue); +} + /** * @brief Test startArray method. */ diff --git a/data/tests/pack_reader_test.cpp b/data/tests/pack_reader_test.cpp index bf21495f..aea9255b 100644 --- a/data/tests/pack_reader_test.cpp +++ b/data/tests/pack_reader_test.cpp @@ -39,6 +39,7 @@ #include using join::Array; +using join::Binary; using join::Member; using join::Object; using join::Value; @@ -1349,6 +1350,8 @@ TEST (PackReader, str) */ TEST (PackReader, bin) { + const uint8_t hello[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x57, 0x6f, 0x72, 0x6c, 0x64}; + const uint8_t bytes[] = {0x00, 0xff}; std::stringstream stream; Value value; @@ -1357,8 +1360,8 @@ TEST (PackReader, bin) ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), ""); + ASSERT_TRUE (value[0].isBinary ()); + EXPECT_TRUE (value[0].getBinary ().empty ()); stream.clear (); stream.str (std::string ( @@ -1366,8 +1369,8 @@ TEST (PackReader, bin) ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "Hello"); + ASSERT_TRUE (value[0].isBinary ()); + EXPECT_EQ (value[0].getBinary (), Binary (hello, 5)); stream.clear (); stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc6', '\x00', '\x00', '\x00', '\x0b', '\x48', @@ -1375,67 +1378,18 @@ TEST (PackReader, bin) ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "Hello\nWorld"); - - stream.clear (); - stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc4', '\x0b', '\x48', '\x65', '\x6c', '\x6c', - '\x6f', '\x00', '\x57', '\x6f', '\x72', '\x6c', '\x64'})); - ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); - ASSERT_TRUE (value.isArray ()); - ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), std::string_view ("Hello\0World", 11)); - - stream.clear (); - stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc5', '\x00', '\x08', '\x22', '\x5c', '\x2f', - '\x08', '\x0c', '\x0a', '\x0d', '\x09'})); - ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); - ASSERT_TRUE (value.isArray ()); - ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "\"\\/\b\f\n\r\t"); - - stream.clear (); - stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc6', '\x00', '\x00', '\x00', '\x01', '\x24'})); - ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); - ASSERT_TRUE (value.isArray ()); - ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "\x24"); - - stream.clear (); - stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc4', '\x02', '\xc2', '\xa2'})); - ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); - ASSERT_TRUE (value.isArray ()); - ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "\xC2\xA2"); - - stream.clear (); - stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc5', '\x00', '\x03', '\xe2', '\x82', '\xac'})); - ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); - ASSERT_TRUE (value.isArray ()); - ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "\xE2\x82\xAC"); + ASSERT_TRUE (value[0].isBinary ()); + EXPECT_EQ (value[0].getBinary (), Binary (hello, sizeof (hello))); - stream.clear (); - stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc6', '\x00', '\x00', '\x00', '\x04', '\xf0', - '\x9d', '\x84', '\x9e'})); - ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc4', '\x02', '\x00', '\xff'}), value), 0) + << join::lastError.message (); ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); - ASSERT_TRUE (value[0].isString ()); - EXPECT_EQ (value[0].getString (), "\xF0\x9D\x84\x9E"); + ASSERT_TRUE (value[0].isBinary ()); + EXPECT_EQ (value[0].getBinary (), Binary (bytes, sizeof (bytes))); - Nested nested; - ASSERT_EQ ( - PackReader ().deserialize (std::string ({'\x91', '\xc4', '\x05', '\x91', '\xc4', '\x02', 'a', 'b'}), nested), 0) - << join::lastError.message (); - ASSERT_EQ (nested.strings.size (), 2); - EXPECT_EQ (nested.strings[0], "ab"); - EXPECT_EQ (nested.strings[1], std::string ({'\x91', '\xc4', '\x02', 'a', 'b'})); + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc4', '\x05', '\x00', '\xff'}), value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); } /** diff --git a/data/tests/pack_writer_test.cpp b/data/tests/pack_writer_test.cpp index 48b2b47d..2678486a 100644 --- a/data/tests/pack_writer_test.cpp +++ b/data/tests/pack_writer_test.cpp @@ -31,10 +31,12 @@ // C++. #include +#include using join::PackWriter; using join::Value; using join::Array; +using join::Binary; using join::Object; using join::SaxErrc; @@ -378,6 +380,32 @@ TEST (PackWriter, setString) EXPECT_EQ (stream.str ().compare (0, 6, std::string ({'\xDB', '\x00', '\x01', '\x00', '\x00', '\x78'})), 0); } +/** + * @brief Test setBinary method. + */ +TEST (PackWriter, setBinary) +{ + std::stringstream stream; + PackWriter packWriter (stream); + const std::vector data (65536, 0x78); + + stream.str (""); + EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 255)), 0); + EXPECT_EQ (stream.str ().compare (0, 4, std::string ({'\xC4', '\xFF', '\x78', '\x78'})), 0); + + stream.str (""); + EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 256)), 0); + EXPECT_EQ (stream.str ().compare (0, 5, std::string ({'\xC5', '\x01', '\x00', '\x78', '\x78'})), 0); + + stream.str (""); + EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 65535)), 0); + EXPECT_EQ (stream.str ().compare (0, 5, std::string ({'\xC5', '\xFF', '\xFF', '\x78', '\x78'})), 0); + + stream.str (""); + EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 65536)), 0); + EXPECT_EQ (stream.str ().compare (0, 7, std::string ({'\xC6', '\x00', '\x01', '\x00', '\x00', '\x78', '\x78'})), 0); +} + /** * @brief Test startArray method. */ diff --git a/data/tests/sax_handler_test.cpp b/data/tests/sax_handler_test.cpp index 8c570a8e..e6f61721 100644 --- a/data/tests/sax_handler_test.cpp +++ b/data/tests/sax_handler_test.cpp @@ -34,6 +34,7 @@ #include using join::Value; +using join::Binary; using join::Object; using join::SaxHandler; using join::JsonReader; @@ -133,6 +134,15 @@ TEST (SaxHandler, setString) EXPECT_EQ (counter.strings, 2U); } +/** + * @brief Test setBinary method. + */ +TEST (SaxHandler, setBinary) +{ + SaxHandler handler; + EXPECT_EQ (handler.setBinary (Binary ()), 0); +} + /** * @brief Test startArray method. */ diff --git a/data/tests/value_builder_test.cpp b/data/tests/value_builder_test.cpp index ff4179aa..cd3db1ba 100644 --- a/data/tests/value_builder_test.cpp +++ b/data/tests/value_builder_test.cpp @@ -38,6 +38,7 @@ #include using join::Array; +using join::Binary; using join::Object; using join::Value; using join::ValueBuilder; @@ -251,6 +252,26 @@ TEST (ValueBuilder, setString) ASSERT_EQ (value[1], "b"); } +/** + * @brief Test setBinary method. + */ +TEST (ValueBuilder, setBinary) +{ + const uint8_t data[] = {0x01, 0x02}; + Value value; + ValueBuilder builder (value); + ASSERT_EQ (builder.setBinary (Binary (data, sizeof (data))), 0); + ASSERT_TRUE (value.isBinary ()); + ASSERT_EQ (value.getBinary (), Binary (data, sizeof (data))); + ASSERT_NE (value.getBinary ().data (), data); + + ASSERT_EQ (builder.startArray (), 0); + ASSERT_EQ (builder.setBinary (Binary (data, 1)), 0); + ASSERT_EQ (builder.stopArray (), 0); + ASSERT_EQ (value.size (), 1U); + ASSERT_EQ (value[0].getBinary (), Binary (data, 1)); +} + /** * @brief Test startArray method. */ diff --git a/data/tests/value_test.cpp b/data/tests/value_test.cpp index 3a0db4f5..b39a0fc6 100644 --- a/data/tests/value_test.cpp +++ b/data/tests/value_test.cpp @@ -39,6 +39,7 @@ #include using join::Array; +using join::Binary; using join::Member; using join::Object; using join::Value; @@ -232,6 +233,11 @@ TEST (Value, create) Value objectValue (object.getObject ()); ASSERT_TRUE (objectValue.isObject ()); ASSERT_EQ (objectValue["i"], 1); + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + Value binaryValue (Binary (bytes, sizeof (bytes))); + ASSERT_TRUE (binaryValue.isBinary ()); + ASSERT_EQ (binaryValue.getBinary (), Binary (bytes, sizeof (bytes))); } /** @@ -334,6 +340,12 @@ TEST (Value, copy) value = object.getObject (); Value objectValue (value); ASSERT_TRUE (objectValue.isObject ()); + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value = Binary (bytes, sizeof (bytes)); + Value binaryValue (value); + ASSERT_TRUE (binaryValue.isBinary ()); + ASSERT_EQ (binaryValue.getBinary (), Binary (bytes, sizeof (bytes))); } /** @@ -2274,6 +2286,113 @@ TEST (Value, isObject) ASSERT_TRUE (value.isObject ()); } +/** + * @brief Test isBinary method. + */ +TEST (Value, isBinary) +{ + Value array; + array.pushBack (1); + + Value object; + object["i"] = 1; + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + + Value value; + ASSERT_FALSE (value.isBinary ()); + + value = nullptr; + ASSERT_FALSE (value.isBinary ()); + + value = true; + ASSERT_FALSE (value.isBinary ()); + + value = false; + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::min (); + ASSERT_FALSE (value.isBinary ()); + + value = std::numeric_limits::max (); + ASSERT_FALSE (value.isBinary ()); + + value = ""; + ASSERT_FALSE (value.isBinary ()); + + value = "foo"; + ASSERT_FALSE (value.isBinary ()); + + value = "02:42:64:2f:6a:d0"; + ASSERT_FALSE (value.isBinary ()); + + value = "127.0.0.1"; + ASSERT_FALSE (value.isBinary ()); + + value = array.getArray (); + ASSERT_FALSE (value.isBinary ()); + + value = object.getObject (); + ASSERT_FALSE (value.isBinary ()); + + value = Binary (bytes, sizeof (bytes)); + ASSERT_TRUE (value.isBinary ()); +} + /** * @brief Test getBool method. */ @@ -4168,6 +4287,113 @@ TEST (Value, getObject) ASSERT_NO_THROW (value.getObject ()); } +/** + * @brief Test getBinary method. + */ +TEST (Value, getBinary) +{ + Value array; + array.pushBack (1); + + Value object; + object["i"] = 1; + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + + Value value; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = nullptr; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = true; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = false; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = ""; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = "foo"; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = "02:42:64:2f:6a:d0"; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = "127.0.0.1"; + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = array.getArray (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = object.getObject (); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = Binary (bytes, sizeof (bytes)); + ASSERT_EQ (value.getBinary (), Binary (bytes, sizeof (bytes))); +} + /** * @brief Test index method. */ @@ -4528,6 +4754,11 @@ TEST (Value, assign) value["object"] = Object (); ASSERT_TRUE (value["object"].isObject ()); + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value["binary"] = Binary (bytes, sizeof (bytes)); + ASSERT_TRUE (value["binary"].isBinary ()); + ASSERT_EQ (value["binary"].getBinary (), Binary (bytes, sizeof (bytes))); + const std::string string ("foo"); value["braced"] = {string}; ASSERT_TRUE (value["braced"].isString ()); @@ -5324,6 +5555,11 @@ TEST (Value, pushBack) value = object.getObject (); ASSERT_THROW (value.pushBack (1), std::bad_cast); + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value = Array (); + ASSERT_TRUE (value.pushBack (Binary (bytes, sizeof (bytes))).isBinary ()); + ASSERT_EQ (value[0].getBinary (), Binary (bytes, sizeof (bytes))); } /** @@ -5761,6 +5997,13 @@ TEST (Value, serialize) ASSERT_EQ (value.serialize (out, 4), 0) << join::lastError.message (); EXPECT_EQ (out.str (), indented.str ()); + const uint8_t bytes[] = {0x00, 0xff}; + Value binary; + binary.pushBack (Binary (bytes, sizeof (bytes))); + out.str (""); + ASSERT_EQ (binary.serialize (out), 0) << join::lastError.message (); + EXPECT_EQ (out.str (), std::string ({'\x91', '\xc4', '\x02', '\x00', '\xff'})); + out.str (""); PackWriter packWriter (out); ASSERT_EQ (value.serialize (packWriter), 0) << join::lastError.message (); From b4520e471e765455ac3e8fde042b422eacb22649 Mon Sep 17 00:00:00 2001 From: mrabine Date: Mon, 9 Nov 2026 11:36:08 +0100 Subject: [PATCH 2/3] Use std::span for binary values in the public API --- data/include/join/json.hpp | 3 +- data/include/join/pack.hpp | 8 +- data/include/join/sax.hpp | 3 +- data/include/join/value.hpp | 134 ++++----- data/include/join/value_builder.hpp | 3 +- data/tests/json_writer_test.cpp | 4 +- data/tests/pack_reader_test.cpp | 9 +- data/tests/pack_writer_test.cpp | 10 +- data/tests/sax_handler_test.cpp | 4 +- data/tests/value_builder_test.cpp | 11 +- data/tests/value_test.cpp | 445 ++++++++++++++-------------- 11 files changed, 322 insertions(+), 312 deletions(-) diff --git a/data/include/join/json.hpp b/data/include/join/json.hpp index 01589864..efc18198 100644 --- a/data/include/join/json.hpp +++ b/data/include/join/json.hpp @@ -45,6 +45,7 @@ #include #include #include +#include // C. #include @@ -435,7 +436,7 @@ namespace join * @param value binary value to set. * @return -1. */ - int setBinary ([[maybe_unused]] Binary value) + int setBinary ([[maybe_unused]] std::span value) { join::lastError = make_error_code (SaxErrc::InvalidValue); reset (); diff --git a/data/include/join/pack.hpp b/data/include/join/pack.hpp index 0e3f17b7..eaa76b1d 100644 --- a/data/include/join/pack.hpp +++ b/data/include/join/pack.hpp @@ -38,6 +38,7 @@ #include #include #include +#include // C. #include @@ -343,7 +344,7 @@ namespace join * @param value binary value to set. * @return 0 on success, -1 otherwise. */ - int setBinary (Binary value) + int setBinary (std::span value) { if (value.size () < 256) { @@ -1261,7 +1262,7 @@ namespace join return -1; } - Binary output (reinterpret_cast (document.tell ()), len); + std::span output (reinterpret_cast (document.tell ()), len); document.seek (document.tell () + len); return handler.setBinary (output); } @@ -1274,7 +1275,8 @@ namespace join return -1; } - int result = handler.setBinary (Binary (reinterpret_cast (output.data ()), output.size ())); + int result = handler.setBinary ( + std::span (reinterpret_cast (output.data ()), output.size ())); cache = std::move (output); return result; } diff --git a/data/include/join/sax.hpp b/data/include/join/sax.hpp index 514c9cc2..e94b9798 100644 --- a/data/include/join/sax.hpp +++ b/data/include/join/sax.hpp @@ -31,6 +31,7 @@ // C++. #include +#include // C. #include @@ -130,7 +131,7 @@ namespace join * @param value binary value to set. * @return 0. */ - int setBinary ([[maybe_unused]] Binary value) noexcept + int setBinary ([[maybe_unused]] std::span value) noexcept { return 0; } diff --git a/data/include/join/value.hpp b/data/include/join/value.hpp index d39e99bc..5fe54fe8 100644 --- a/data/include/join/value.hpp +++ b/data/include/join/value.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include // C. @@ -335,6 +336,27 @@ namespace join { } + /** + * @brief constructs a binary value. + * @param value binary value. + */ + BasicValue (std::span value) + : BasicValue (nullptr) + { + _data.template set (copyBinary (value)); + } + + /** + * @brief constructs a binary value using an allocator owned by the caller. + * @param value binary value. + * @param allocator allocator providing the memory of the value. + */ + BasicValue (std::span value, Allocator& allocator) + : BasicValue (allocator) + { + _data.template set (copyBinary (value)); + } + /** * @brief constructs an array value. * @param value array value. @@ -377,27 +399,6 @@ namespace join _data.template set (copyObject (value)); } - /** - * @brief constructs a binary value. - * @param value binary value. - */ - BasicValue (Binary value) - : BasicValue (nullptr) - { - _data.template set (copyBinary (value)); - } - - /** - * @brief constructs a binary value using an allocator owned by the caller. - * @param value binary value. - * @param allocator allocator providing the memory of the value. - */ - BasicValue (Binary value, Allocator& allocator) - : BasicValue (allocator) - { - _data.template set (copyBinary (value)); - } - /** * @brief copy constructor. * @param other object to copy. @@ -571,54 +572,54 @@ namespace join } /** - * @brief assign an array value. - * @param value array value. + * @brief assign a binary value. + * @param value binary value. * @return a reference of the current object. */ - Value& operator= (Array value) + Value& operator= (std::span value) { if (_owner || (_allocator == nullptr)) { return *this = BasicValue (value); } - _data.template set (copyArray (value)); + _data.template set (copyBinary (value)); _capacity = 0; return *this; } /** - * @brief assign an object value. - * @param value object value. + * @brief assign an array value. + * @param value array value. * @return a reference of the current object. */ - Value& operator= (Object value) + Value& operator= (Array value) { if (_owner || (_allocator == nullptr)) { return *this = BasicValue (value); } - _data.template set (copyObject (value)); + _data.template set (copyArray (value)); _capacity = 0; return *this; } /** - * @brief assign a binary value. - * @param value binary value. + * @brief assign an object value. + * @param value object value. * @return a reference of the current object. */ - Value& operator= (Binary value) + Value& operator= (Object value) { if (_owner || (_allocator == nullptr)) { return *this = BasicValue (value); } - _data.template set (copyBinary (value)); + _data.template set (copyObject (value)); _capacity = 0; return *this; @@ -1446,6 +1447,26 @@ namespace join return getString (); } + /** + * @brief check if the variable held by value is a binary value. + * @return true if the variable held by value is a binary value, false otherwise. + */ + constexpr bool isBinary () const + { + return _data.index () == BinaryValue; + } + + /** + * @brief get variable held by value as a binary value. + * @return variable held by value as a binary value. + * @throw std::bad_cast. + */ + constexpr std::span getBinary () const + { + const Binary& value = _data.template get (); + return std::span (value.data (), value.size ()); + } + /** * @brief check if the variable held by value is an array. * @return true if the variable held by value is an array, false otherwise. @@ -1484,25 +1505,6 @@ namespace join return _data.template get (); } - /** - * @brief check if the variable held by value is a binary value. - * @return true if the variable held by value is a binary value, false otherwise. - */ - constexpr bool isBinary () const - { - return _data.index () == BinaryValue; - } - - /** - * @brief get variable held by value as a binary value. - * @return variable held by value as a binary value. - * @throw std::bad_cast. - */ - constexpr const Binary& getBinary () const - { - return _data.template get (); - } - /** * @brief returns a reference to the value mapped to key. * @param key key of the element to find. @@ -2650,34 +2652,34 @@ namespace join } /** - * @brief appends an array value at the end of the nested container. + * @brief appends a binary value at the end of the nested container. * @param value the element to append. * @return a reference to the pushed object. * @throw std::bad_cast. */ - Value& pushBack (Array value) + Value& pushBack (std::span value) { return pushBack (Value (value, *allocator ())); } /** - * @brief appends an object value at the end of the nested container. + * @brief appends an array value at the end of the nested container. * @param value the element to append. * @return a reference to the pushed object. * @throw std::bad_cast. */ - Value& pushBack (Object value) + Value& pushBack (Array value) { return pushBack (Value (value, *allocator ())); } /** - * @brief appends a binary value at the end of the nested container. + * @brief appends an object value at the end of the nested container. * @param value the element to append. * @return a reference to the pushed object. * @throw std::bad_cast. */ - Value& pushBack (Binary value) + Value& pushBack (Object value) { return pushBack (Value (value, *allocator ())); } @@ -3246,7 +3248,7 @@ namespace join * @param value binary data to copy. * @return copied binary data. */ - Binary copyBinary (Binary value) + Binary copyBinary (std::span value) { uint8_t* data = allocate (value.size ()); std::copy_n (value.data (), value.size (), data); @@ -3332,6 +3334,10 @@ namespace join _data.template set (copyString (other.getString ())); break; + case BinaryValue: + _data.template set (copyBinary (other.getBinary ())); + break; + case ArrayValue: _data.template set (copyArray (other.getArray ())); break; @@ -3340,10 +3346,6 @@ namespace join _data.template set (copyObject (other.getObject ())); break; - case BinaryValue: - _data.template set (copyBinary (other.getBinary ())); - break; - default: _data = other._data; break; @@ -3489,15 +3491,15 @@ namespace join case Value::StringValue: return handler.setString (value.getString ()); + case Value::BinaryValue: + return handler.setBinary (value.getBinary ()); + case Value::ArrayValue: return setArray (value.getArray (), handler); case Value::ObjectValue: return setObject (value.getObject (), handler); - case Value::BinaryValue: - return handler.setBinary (value.getBinary ()); - default: return handler.setNull (); } diff --git a/data/include/join/value_builder.hpp b/data/include/join/value_builder.hpp index 5dbb74b5..01f53a02 100644 --- a/data/include/join/value_builder.hpp +++ b/data/include/join/value_builder.hpp @@ -34,6 +34,7 @@ #include #include #include +#include // C. #include @@ -186,7 +187,7 @@ namespace join * @param value binary value to set. * @return 0 on success, -1 otherwise. */ - int setBinary (Binary value) + int setBinary (std::span value) { return emplaceValue (_root.copyBinary (value)); } diff --git a/data/tests/json_writer_test.cpp b/data/tests/json_writer_test.cpp index 465e0991..f0695894 100644 --- a/data/tests/json_writer_test.cpp +++ b/data/tests/json_writer_test.cpp @@ -31,11 +31,11 @@ // C++. #include #include +#include using join::JsonWriter; using join::Value; using join::Array; -using join::Binary; using join::Object; using join::SaxErrc; @@ -244,7 +244,7 @@ TEST (JsonWriter, setBinary) JsonWriter jsonWriter (stream); const uint8_t data[] = {0x01}; - EXPECT_EQ (jsonWriter.setBinary (Binary (data, sizeof (data))), -1); + EXPECT_EQ (jsonWriter.setBinary (std::span (data, sizeof (data))), -1); EXPECT_EQ (join::lastError, SaxErrc::InvalidValue); } diff --git a/data/tests/pack_reader_test.cpp b/data/tests/pack_reader_test.cpp index aea9255b..4ea016aa 100644 --- a/data/tests/pack_reader_test.cpp +++ b/data/tests/pack_reader_test.cpp @@ -30,16 +30,17 @@ #include // C++. +#include #include #include #include #include +#include // C. #include using join::Array; -using join::Binary; using join::Member; using join::Object; using join::Value; @@ -1370,7 +1371,7 @@ TEST (PackReader, bin) ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); ASSERT_TRUE (value[0].isBinary ()); - EXPECT_EQ (value[0].getBinary (), Binary (hello, 5)); + EXPECT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (hello, 5))); stream.clear (); stream.str (std::string ({'\xdd', '\x00', '\x00', '\x00', '\x01', '\xc6', '\x00', '\x00', '\x00', '\x0b', '\x48', @@ -1379,14 +1380,14 @@ TEST (PackReader, bin) ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); ASSERT_TRUE (value[0].isBinary ()); - EXPECT_EQ (value[0].getBinary (), Binary (hello, sizeof (hello))); + EXPECT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (hello, sizeof (hello)))); ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc4', '\x02', '\x00', '\xff'}), value), 0) << join::lastError.message (); ASSERT_TRUE (value.isArray ()); ASSERT_FALSE (value.empty ()); ASSERT_TRUE (value[0].isBinary ()); - EXPECT_EQ (value[0].getBinary (), Binary (bytes, sizeof (bytes))); + EXPECT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (bytes, sizeof (bytes)))); ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc4', '\x05', '\x00', '\xff'}), value), -1); EXPECT_EQ (join::lastError, SaxErrc::Truncated); diff --git a/data/tests/pack_writer_test.cpp b/data/tests/pack_writer_test.cpp index 2678486a..a0c712ca 100644 --- a/data/tests/pack_writer_test.cpp +++ b/data/tests/pack_writer_test.cpp @@ -32,11 +32,11 @@ // C++. #include #include +#include using join::PackWriter; using join::Value; using join::Array; -using join::Binary; using join::Object; using join::SaxErrc; @@ -390,19 +390,19 @@ TEST (PackWriter, setBinary) const std::vector data (65536, 0x78); stream.str (""); - EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 255)), 0); + EXPECT_EQ (packWriter.setBinary (std::span (data.data (), 255)), 0); EXPECT_EQ (stream.str ().compare (0, 4, std::string ({'\xC4', '\xFF', '\x78', '\x78'})), 0); stream.str (""); - EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 256)), 0); + EXPECT_EQ (packWriter.setBinary (std::span (data.data (), 256)), 0); EXPECT_EQ (stream.str ().compare (0, 5, std::string ({'\xC5', '\x01', '\x00', '\x78', '\x78'})), 0); stream.str (""); - EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 65535)), 0); + EXPECT_EQ (packWriter.setBinary (std::span (data.data (), 65535)), 0); EXPECT_EQ (stream.str ().compare (0, 5, std::string ({'\xC5', '\xFF', '\xFF', '\x78', '\x78'})), 0); stream.str (""); - EXPECT_EQ (packWriter.setBinary (Binary (data.data (), 65536)), 0); + EXPECT_EQ (packWriter.setBinary (std::span (data.data (), 65536)), 0); EXPECT_EQ (stream.str ().compare (0, 7, std::string ({'\xC6', '\x00', '\x01', '\x00', '\x00', '\x78', '\x78'})), 0); } diff --git a/data/tests/sax_handler_test.cpp b/data/tests/sax_handler_test.cpp index e6f61721..36605a56 100644 --- a/data/tests/sax_handler_test.cpp +++ b/data/tests/sax_handler_test.cpp @@ -32,9 +32,9 @@ // C++. #include #include +#include using join::Value; -using join::Binary; using join::Object; using join::SaxHandler; using join::JsonReader; @@ -140,7 +140,7 @@ TEST (SaxHandler, setString) TEST (SaxHandler, setBinary) { SaxHandler handler; - EXPECT_EQ (handler.setBinary (Binary ()), 0); + EXPECT_EQ (handler.setBinary (std::span ()), 0); } /** diff --git a/data/tests/value_builder_test.cpp b/data/tests/value_builder_test.cpp index cd3db1ba..5a19d569 100644 --- a/data/tests/value_builder_test.cpp +++ b/data/tests/value_builder_test.cpp @@ -33,12 +33,13 @@ // C++. #include +#include #include #include #include +#include using join::Array; -using join::Binary; using join::Object; using join::Value; using join::ValueBuilder; @@ -260,16 +261,16 @@ TEST (ValueBuilder, setBinary) const uint8_t data[] = {0x01, 0x02}; Value value; ValueBuilder builder (value); - ASSERT_EQ (builder.setBinary (Binary (data, sizeof (data))), 0); + ASSERT_EQ (builder.setBinary (std::span (data, sizeof (data))), 0); ASSERT_TRUE (value.isBinary ()); - ASSERT_EQ (value.getBinary (), Binary (data, sizeof (data))); + ASSERT_TRUE (std::ranges::equal (value.getBinary (), std::span (data, sizeof (data)))); ASSERT_NE (value.getBinary ().data (), data); ASSERT_EQ (builder.startArray (), 0); - ASSERT_EQ (builder.setBinary (Binary (data, 1)), 0); + ASSERT_EQ (builder.setBinary (std::span (data, 1)), 0); ASSERT_EQ (builder.stopArray (), 0); ASSERT_EQ (value.size (), 1U); - ASSERT_EQ (value[0].getBinary (), Binary (data, 1)); + ASSERT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (data, 1))); } /** diff --git a/data/tests/value_test.cpp b/data/tests/value_test.cpp index b39a0fc6..29c64d54 100644 --- a/data/tests/value_test.cpp +++ b/data/tests/value_test.cpp @@ -33,13 +33,14 @@ // C++. #include +#include #include #include #include #include +#include using join::Array; -using join::Binary; using join::Member; using join::Object; using join::Value; @@ -225,6 +226,11 @@ TEST (Value, create) ASSERT_TRUE (viewStringValue.isString ()); ASSERT_EQ (viewStringValue.getString (), "quu"); + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + Value binaryValue (std::span (bytes, sizeof (bytes))); + ASSERT_TRUE (binaryValue.isBinary ()); + ASSERT_TRUE (std::ranges::equal (binaryValue.getBinary (), std::span (bytes, sizeof (bytes)))); + Value arrayValue (array.getArray ()); ASSERT_TRUE (arrayValue.isArray ()); ASSERT_EQ (arrayValue.size (), 1U); @@ -233,11 +239,6 @@ TEST (Value, create) Value objectValue (object.getObject ()); ASSERT_TRUE (objectValue.isObject ()); ASSERT_EQ (objectValue["i"], 1); - - const uint8_t bytes[] = {0x00, 0x01, 0xff}; - Value binaryValue (Binary (bytes, sizeof (bytes))); - ASSERT_TRUE (binaryValue.isBinary ()); - ASSERT_EQ (binaryValue.getBinary (), Binary (bytes, sizeof (bytes))); } /** @@ -333,6 +334,12 @@ TEST (Value, copy) Value stringValue (value); ASSERT_TRUE (stringValue.isString ()); + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value = std::span (bytes, sizeof (bytes)); + Value binaryValue (value); + ASSERT_TRUE (binaryValue.isBinary ()); + ASSERT_TRUE (std::ranges::equal (binaryValue.getBinary (), std::span (bytes, sizeof (bytes)))); + value = array.getArray (); Value arrayValue (value); ASSERT_TRUE (arrayValue.isArray ()); @@ -340,12 +347,6 @@ TEST (Value, copy) value = object.getObject (); Value objectValue (value); ASSERT_TRUE (objectValue.isObject ()); - - const uint8_t bytes[] = {0x00, 0x01, 0xff}; - value = Binary (bytes, sizeof (bytes)); - Value binaryValue (value); - ASSERT_TRUE (binaryValue.isBinary ()); - ASSERT_EQ (binaryValue.getBinary (), Binary (bytes, sizeof (bytes))); } /** @@ -2083,9 +2084,9 @@ TEST (Value, isString) } /** - * @brief Test isArray method. + * @brief Test isBinary method. */ -TEST (Value, isArray) +TEST (Value, isBinary) { Value array; array.pushBack (1); @@ -2093,101 +2094,106 @@ TEST (Value, isArray) Value object; object["i"] = 1; + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + Value value; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = nullptr; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = true; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = false; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = ""; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = "foo"; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = "02:42:64:2f:6a:d0"; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = "127.0.0.1"; - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); + + value = std::span (bytes, sizeof (bytes)); + ASSERT_TRUE (value.isBinary ()); value = array.getArray (); - ASSERT_TRUE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); value = object.getObject (); - ASSERT_FALSE (value.isArray ()); + ASSERT_FALSE (value.isBinary ()); } /** - * @brief Test isObject method. + * @brief Test isArray method. */ -TEST (Value, isObject) +TEST (Value, isArray) { Value array; array.pushBack (1); @@ -2196,100 +2202,100 @@ TEST (Value, isObject) object["i"] = 1; Value value; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = nullptr; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = true; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = false; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = ""; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = "foo"; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = "02:42:64:2f:6a:d0"; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = "127.0.0.1"; - ASSERT_FALSE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); value = array.getArray (); - ASSERT_FALSE (value.isObject ()); + ASSERT_TRUE (value.isArray ()); value = object.getObject (); - ASSERT_TRUE (value.isObject ()); + ASSERT_FALSE (value.isArray ()); } /** - * @brief Test isBinary method. + * @brief Test isObject method. */ -TEST (Value, isBinary) +TEST (Value, isObject) { Value array; array.pushBack (1); @@ -2297,100 +2303,95 @@ TEST (Value, isBinary) Value object; object["i"] = 1; - const uint8_t bytes[] = {0x00, 0x01, 0xff}; - Value value; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = nullptr; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = true; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = false; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::min (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = std::numeric_limits::max (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = ""; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = "foo"; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = "02:42:64:2f:6a:d0"; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = "127.0.0.1"; - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = array.getArray (); - ASSERT_FALSE (value.isBinary ()); + ASSERT_FALSE (value.isObject ()); value = object.getObject (); - ASSERT_FALSE (value.isBinary ()); - - value = Binary (bytes, sizeof (bytes)); - ASSERT_TRUE (value.isBinary ()); + ASSERT_TRUE (value.isObject ()); } /** @@ -4084,9 +4085,9 @@ TEST (Value, getString) } /** - * @brief Test getArray method. + * @brief Test getBinary method. */ -TEST (Value, getArray) +TEST (Value, getBinary) { Value array; array.pushBack (1); @@ -4094,101 +4095,106 @@ TEST (Value, getArray) Value object; object["i"] = 1; + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + Value value; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = nullptr; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = true; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = false; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = ""; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = "foo"; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = "02:42:64:2f:6a:d0"; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = "127.0.0.1"; - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); + + value = std::span (bytes, sizeof (bytes)); + ASSERT_TRUE (std::ranges::equal (value.getBinary (), std::span (bytes, sizeof (bytes)))); value = array.getArray (); - ASSERT_NO_THROW (value.getArray ()); + ASSERT_THROW (value.getBinary (), std::bad_cast); value = object.getObject (); - ASSERT_THROW (value.getArray (), std::bad_cast); + ASSERT_THROW (value.getBinary (), std::bad_cast); } /** - * @brief Test getObject method. + * @brief Test getArray method. */ -TEST (Value, getObject) +TEST (Value, getArray) { Value array; array.pushBack (1); @@ -4197,100 +4203,100 @@ TEST (Value, getObject) object["i"] = 1; Value value; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = nullptr; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = true; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = false; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = ""; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = "foo"; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = "02:42:64:2f:6a:d0"; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = "127.0.0.1"; - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_THROW (value.getArray (), std::bad_cast); value = array.getArray (); - ASSERT_THROW (value.getObject (), std::bad_cast); + ASSERT_NO_THROW (value.getArray ()); value = object.getObject (); - ASSERT_NO_THROW (value.getObject ()); + ASSERT_THROW (value.getArray (), std::bad_cast); } /** - * @brief Test getBinary method. + * @brief Test getObject method. */ -TEST (Value, getBinary) +TEST (Value, getObject) { Value array; array.pushBack (1); @@ -4298,100 +4304,95 @@ TEST (Value, getBinary) Value object; object["i"] = 1; - const uint8_t bytes[] = {0x00, 0x01, 0xff}; - Value value; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = nullptr; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = true; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = false; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::min (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = std::numeric_limits::max (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = ""; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = "foo"; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = "02:42:64:2f:6a:d0"; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = "127.0.0.1"; - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = array.getArray (); - ASSERT_THROW (value.getBinary (), std::bad_cast); + ASSERT_THROW (value.getObject (), std::bad_cast); value = object.getObject (); - ASSERT_THROW (value.getBinary (), std::bad_cast); - - value = Binary (bytes, sizeof (bytes)); - ASSERT_EQ (value.getBinary (), Binary (bytes, sizeof (bytes))); + ASSERT_NO_THROW (value.getObject ()); } /** @@ -4748,17 +4749,17 @@ TEST (Value, assign) value["string"] = "foobar"; ASSERT_TRUE (value["string"].isString ()); + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value["binary"] = std::span (bytes, sizeof (bytes)); + ASSERT_TRUE (value["binary"].isBinary ()); + ASSERT_TRUE (std::ranges::equal (value["binary"].getBinary (), std::span (bytes, sizeof (bytes)))); + value["array"] = Array (); ASSERT_TRUE (value["array"].isArray ()); value["object"] = Object (); ASSERT_TRUE (value["object"].isObject ()); - const uint8_t bytes[] = {0x00, 0x01, 0xff}; - value["binary"] = Binary (bytes, sizeof (bytes)); - ASSERT_TRUE (value["binary"].isBinary ()); - ASSERT_EQ (value["binary"].getBinary (), Binary (bytes, sizeof (bytes))); - const std::string string ("foo"); value["braced"] = {string}; ASSERT_TRUE (value["braced"].isString ()); @@ -5558,8 +5559,8 @@ TEST (Value, pushBack) const uint8_t bytes[] = {0x00, 0x01, 0xff}; value = Array (); - ASSERT_TRUE (value.pushBack (Binary (bytes, sizeof (bytes))).isBinary ()); - ASSERT_EQ (value[0].getBinary (), Binary (bytes, sizeof (bytes))); + ASSERT_TRUE (value.pushBack (std::span (bytes, sizeof (bytes))).isBinary ()); + ASSERT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (bytes, sizeof (bytes)))); } /** @@ -5999,7 +6000,7 @@ TEST (Value, serialize) const uint8_t bytes[] = {0x00, 0xff}; Value binary; - binary.pushBack (Binary (bytes, sizeof (bytes))); + binary.pushBack (std::span (bytes, sizeof (bytes))); out.str (""); ASSERT_EQ (binary.serialize (out), 0) << join::lastError.message (); EXPECT_EQ (out.str (), std::string ({'\x91', '\xc4', '\x02', '\x00', '\xff'})); From b3894365e0b7a4fe55b274f6a3501da57debc684 Mon Sep 17 00:00:00 2001 From: mrabine Date: Tue, 10 Nov 2026 06:55:08 +0100 Subject: [PATCH 3/3] Support msgpack extensions and keys of any type --- core/include/join/variant.hpp | 2 +- data/include/join/json.hpp | 29 ++++- data/include/join/pack.hpp | 162 ++++++++++++++++++++++++-- data/include/join/sax.hpp | 26 ++++- data/include/join/value.hpp | 76 +++++++++--- data/include/join/value_builder.hpp | 40 ++++++- data/tests/json_writer_test.cpp | 17 +++ data/tests/pack_reader_test.cpp | 91 ++++++++++++++- data/tests/pack_writer_test.cpp | 53 ++++++++- data/tests/sax_handler_test.cpp | 12 ++ data/tests/value_builder_test.cpp | 43 ++++++- data/tests/value_test.cpp | 173 +++++++++++++++++++++++++++- 12 files changed, 685 insertions(+), 39 deletions(-) diff --git a/core/include/join/variant.hpp b/core/include/join/variant.hpp index 8cade182..a77c6dcc 100644 --- a/core/include/join/variant.hpp +++ b/core/include/join/variant.hpp @@ -716,7 +716,7 @@ namespace join alignas (Ts...) unsigned char _data[std::max ({sizeof (Ts)...})]; /// index of the alternative that is currently held by the variant. - uint32_t _which = 0; + uint16_t _which = 0; }; /** diff --git a/data/include/join/json.hpp b/data/include/join/json.hpp index efc18198..27c5fc1c 100644 --- a/data/include/join/json.hpp +++ b/data/include/join/json.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -436,7 +437,8 @@ namespace join * @param value binary value to set. * @return -1. */ - int setBinary ([[maybe_unused]] std::span value) + int setBinary ([[maybe_unused]] std::span value, + [[maybe_unused]] std::optional subtype = std::nullopt) { join::lastError = make_error_code (SaxErrc::InvalidValue); reset (); @@ -669,6 +671,31 @@ namespace join return setKey (std::string_view (key)); } + /** + * @brief set binary key. + * @param key object key. + * @param subtype binary subtype. + * @return -1. + */ + int setKey ([[maybe_unused]] std::span key, + [[maybe_unused]] std::optional subtype = std::nullopt) + { + join::lastError = make_error_code (SaxErrc::InvalidKey); + reset (); + return -1; + } + + /** + * @brief start an array or object key. + * @return -1. + */ + int startKey () + { + join::lastError = make_error_code (SaxErrc::InvalidKey); + reset (); + return -1; + } + /** * @brief stop object. * @return 0 on success, -1 otherwise. diff --git a/data/include/join/pack.hpp b/data/include/join/pack.hpp index eaa76b1d..a4e75261 100644 --- a/data/include/join/pack.hpp +++ b/data/include/join/pack.hpp @@ -36,6 +36,7 @@ // C++. #include +#include #include #include #include @@ -342,11 +343,56 @@ namespace join /** * @brief set binary value. * @param value binary value to set. + * @param subtype binary subtype. * @return 0 on success, -1 otherwise. */ - int setBinary (std::span value) + int setBinary (std::span value, std::optional subtype = std::nullopt) { - if (value.size () < 256) + if (subtype) + { + switch (value.size ()) + { + case 1: + append (0xd4); + break; + + case 2: + append (0xd5); + break; + + case 4: + append (0xd6); + break; + + case 8: + append (0xd7); + break; + + case 16: + append (0xd8); + break; + + default: + if (value.size () < 256) + { + append (0xc7); + pack (static_cast (value.size ())); + } + else if (value.size () < 65536) + { + append (0xc8); + pack (static_cast (value.size ())); + } + else + { + append (0xc9); + pack (static_cast (value.size ())); + } + break; + } + pack (*subtype); + } + else if (value.size () < 256) { append (0xc4); pack (static_cast (value.size ())); @@ -582,6 +628,38 @@ namespace join return setKey (std::string_view (key)); } + /** + * @brief set binary key. + * @param key object key. + * @param subtype binary subtype. + * @return 0 on success, -1 otherwise. + */ + int setKey (std::span key, std::optional subtype = std::nullopt) + { + if (JOIN_UNLIKELY (_depth == 0)) + { + join::lastError = make_error_code (SaxErrc::InvalidParent); + reset (); + return -1; + } + return setBinary (key, subtype); + } + + /** + * @brief start an array or object key. + * @return 0 on success, -1 otherwise. + */ + int startKey () + { + if (JOIN_UNLIKELY (_depth == 0)) + { + join::lastError = make_error_code (SaxErrc::InvalidParent); + reset (); + return -1; + } + return 0; + } + /** * @brief stop object. * @return 0 on success, -1 otherwise. @@ -1096,7 +1174,7 @@ namespace join while (len) { - if (JOIN_UNLIKELY (readKey (document, handler) == -1)) + if (JOIN_UNLIKELY (readKey (document, handler, depth + 1) == -1)) { return -1; } @@ -1116,10 +1194,11 @@ namespace join * @brief parse a key of any type. * @param document document to parse. * @param handler handler receiving the document. + * @param depth number of enclosing containers. * @return 0 on success, -1 otherwise. */ template - int readKey (ProbeType& document, Handler& handler) + int readKey (ProbeType& document, Handler& handler, size_t depth) { int ch = document.peek (); if (JOIN_UNLIKELY (ch == std::char_traits::eof ())) @@ -1146,10 +1225,22 @@ namespace join { return readString (document, handler, true); } + else if (isBin (head)) + { + return readBin (document, handler, true); + } else if (isNumber (head)) { return readNumber (document, handler, true); } + else if (isArray (head) || isObject (head)) + { + if (JOIN_UNLIKELY (handler.startKey () == -1)) + { + return -1; + } + return readValue (document, handler, depth); + } else { join::lastError = make_error_code (SaxErrc::InvalidValue); @@ -1225,12 +1316,14 @@ namespace join * @brief parse binary data. * @param document document to parse. * @param handler handler receiving the document. + * @param isKey indicate whether the element to parse is a key or not. * @return 0 on success, -1 otherwise. */ template - int readBin (ProbeType& document, Handler& handler) + int readBin (ProbeType& document, Handler& handler, bool isKey = false) { uint32_t len = 0; + std::optional subtype; if (document.getIf (0xc6)) { @@ -1253,6 +1346,57 @@ namespace join return -1; } } + else + { + if (document.getIf (0xd4)) + { + len = 1; + } + else if (document.getIf (0xd5)) + { + len = 2; + } + else if (document.getIf (0xd6)) + { + len = 4; + } + else if (document.getIf (0xd7)) + { + len = 8; + } + else if (document.getIf (0xd8)) + { + len = 16; + } + else if (document.getIf (0xc9)) + { + if (JOIN_UNLIKELY (unpack (document, len) == -1)) + { + return -1; + } + } + else if (document.getIf (0xc8)) + { + if (JOIN_UNLIKELY (unpack (document, len) == -1)) + { + return -1; + } + } + else if (document.getIf (0xc7)) + { + if (JOIN_UNLIKELY (unpack (document, len) == -1)) + { + return -1; + } + } + + int8_t type = 0; + if (JOIN_UNLIKELY (unpack (document, type) == -1)) + { + return -1; + } + subtype = type; + } if constexpr (std::is_same_v) { @@ -1264,7 +1408,7 @@ namespace join std::span output (reinterpret_cast (document.tell ()), len); document.seek (document.tell () + len); - return handler.setBinary (output); + return isKey ? handler.setKey (output, subtype) : handler.setBinary (output, subtype); } thread_local std::string cache; @@ -1275,8 +1419,8 @@ namespace join return -1; } - int result = handler.setBinary ( - std::span (reinterpret_cast (output.data ()), output.size ())); + std::span bytes (reinterpret_cast (output.data ()), output.size ()); + int result = isKey ? handler.setKey (bytes, subtype) : handler.setBinary (bytes, subtype); cache = std::move (output); return result; } @@ -1554,7 +1698,7 @@ namespace join */ constexpr bool isBin (uint8_t c) { - return (c == 0xc4) || (c == 0xc5) || (c == 0xc6); + return ((c >= 0xc4) && (c <= 0xc9)) || ((c >= 0xd4) && (c <= 0xd8)); } /** diff --git a/data/include/join/sax.hpp b/data/include/join/sax.hpp index e94b9798..59977e31 100644 --- a/data/include/join/sax.hpp +++ b/data/include/join/sax.hpp @@ -31,6 +31,7 @@ // C++. #include +#include #include // C. @@ -129,9 +130,11 @@ namespace join /** * @brief set binary value. * @param value binary value to set. + * @param subtype binary subtype. * @return 0. */ - int setBinary ([[maybe_unused]] std::span value) noexcept + int setBinary ([[maybe_unused]] std::span value, + [[maybe_unused]] std::optional subtype = std::nullopt) noexcept { return 0; } @@ -266,6 +269,27 @@ namespace join return 0; } + /** + * @brief set binary key. + * @param key object key. + * @param subtype binary subtype. + * @return 0. + */ + int setKey ([[maybe_unused]] std::span key, + [[maybe_unused]] std::optional subtype = std::nullopt) noexcept + { + return 0; + } + + /** + * @brief start an array or object key. + * @return 0. + */ + int startKey () noexcept + { + return 0; + } + /** * @brief stop object. * @return 0. diff --git a/data/include/join/value.hpp b/data/include/join/value.hpp index 5fe54fe8..975eb0a9 100644 --- a/data/include/join/value.hpp +++ b/data/include/join/value.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -65,8 +66,31 @@ namespace join /// string. using String = View; - /// binary. - using Binary = View; + /** + * @brief binary. + */ + struct Binary + { + /** + * @brief compare binaries. + * @param other binary to compare to. + * @return true if equal. + */ + constexpr bool operator== (const Binary& other) const = default; + + /** + * @brief three-way compare binaries. + * @param other binary to compare to. + * @return ordering of this binary relative to other. + */ + constexpr std::partial_ordering operator<=> (const Binary& other) const = default; + + /// binary data. + View data; + + /// binary subtype. + std::optional subtype; + }; /** * @brief member. @@ -339,22 +363,24 @@ namespace join /** * @brief constructs a binary value. * @param value binary value. + * @param subtype binary subtype. */ - BasicValue (std::span value) + BasicValue (std::span value, std::optional subtype = std::nullopt) : BasicValue (nullptr) { - _data.template set (copyBinary (value)); + _data.template set (copyBinary (value, subtype)); } /** * @brief constructs a binary value using an allocator owned by the caller. * @param value binary value. + * @param subtype binary subtype. * @param allocator allocator providing the memory of the value. */ - BasicValue (std::span value, Allocator& allocator) + BasicValue (std::span value, std::optional subtype, Allocator& allocator) : BasicValue (allocator) { - _data.template set (copyBinary (value)); + _data.template set (copyBinary (value, subtype)); } /** @@ -583,7 +609,7 @@ namespace join return *this = BasicValue (value); } - _data.template set (copyBinary (value)); + _data.template set (copyBinary (value, std::nullopt)); _capacity = 0; return *this; @@ -1464,7 +1490,17 @@ namespace join constexpr std::span getBinary () const { const Binary& value = _data.template get (); - return std::span (value.data (), value.size ()); + return std::span (value.data.data (), value.data.size ()); + } + + /** + * @brief get the subtype of the binary value held by value. + * @return subtype of the binary value, std::nullopt if none. + * @throw std::bad_cast. + */ + constexpr std::optional getSubtype () const + { + return _data.template get ().subtype; } /** @@ -2654,12 +2690,13 @@ namespace join /** * @brief appends a binary value at the end of the nested container. * @param value the element to append. + * @param subtype binary subtype. * @return a reference to the pushed object. * @throw std::bad_cast. */ - Value& pushBack (std::span value) + Value& pushBack (std::span value, std::optional subtype = std::nullopt) { - return pushBack (Value (value, *allocator ())); + return pushBack (Value (value, subtype, *allocator ())); } /** @@ -3246,13 +3283,14 @@ namespace join /** * @brief copy binary data into the allocator. * @param value binary data to copy. + * @param subtype binary subtype. * @return copied binary data. */ - Binary copyBinary (std::span value) + Binary copyBinary (std::span value, std::optional subtype) { uint8_t* data = allocate (value.size ()); std::copy_n (value.data (), value.size (), data); - return Binary (data, value.size ()); + return Binary{View (data, value.size ()), subtype}; } /** @@ -3335,7 +3373,7 @@ namespace join break; case BinaryValue: - _data.template set (copyBinary (other.getBinary ())); + _data.template set (copyBinary (other.getBinary (), other.getSubtype ())); break; case ArrayValue: @@ -3492,7 +3530,7 @@ namespace join return handler.setString (value.getString ()); case Value::BinaryValue: - return handler.setBinary (value.getBinary ()); + return handler.setBinary (value.getBinary (), value.getSubtype ()); case Value::ArrayValue: return setArray (value.getArray (), handler); @@ -3580,9 +3618,17 @@ namespace join result = handler.setKey (member.first.getDouble ()); break; + case Value::BinaryValue: + result = handler.setKey (member.first.getBinary (), member.first.getSubtype ()); + break; + case Value::ArrayValue: case Value::ObjectValue: - join::lastError = make_error_code (SaxErrc::InvalidKey); + result = handler.startKey (); + if (result == 0) + { + result = setValue (member.first, handler); + } break; default: diff --git a/data/include/join/value_builder.hpp b/data/include/join/value_builder.hpp index 01f53a02..0ce3589e 100644 --- a/data/include/join/value_builder.hpp +++ b/data/include/join/value_builder.hpp @@ -32,6 +32,7 @@ // C++. #include +#include #include #include #include @@ -185,11 +186,12 @@ namespace join /** * @brief set binary value. * @param value binary value to set. + * @param subtype binary subtype. * @return 0 on success, -1 otherwise. */ - int setBinary (std::span value) + int setBinary (std::span value, std::optional subtype = std::nullopt) { - return emplaceValue (_root.copyBinary (value)); + return emplaceValue (_root.copyBinary (value, subtype)); } /** @@ -421,6 +423,40 @@ namespace join return setKey (std::string_view (key)); } + /** + * @brief set binary key. + * @param key object key to set. + * @param subtype binary subtype. + * @return 0 on success, -1 otherwise. + */ + int setKey (std::span key, std::optional subtype = std::nullopt) + { + if (JOIN_UNLIKELY (_depth == 0)) + { + join::lastError = make_error_code (SaxErrc::InvalidParent); + return -1; + } + + _stack.emplace_back (_root.copyBinary (key, subtype)); + + return 0; + } + + /** + * @brief start an array or object key. + * @return 0 on success, -1 otherwise. + */ + int startKey () + { + if (JOIN_UNLIKELY (_depth == 0)) + { + join::lastError = make_error_code (SaxErrc::InvalidParent); + return -1; + } + + return 0; + } + /** * @brief stop object. * @return 0 on success, -1 otherwise. diff --git a/data/tests/json_writer_test.cpp b/data/tests/json_writer_test.cpp index f0695894..94278993 100644 --- a/data/tests/json_writer_test.cpp +++ b/data/tests/json_writer_test.cpp @@ -246,6 +246,8 @@ TEST (JsonWriter, setBinary) EXPECT_EQ (jsonWriter.setBinary (std::span (data, sizeof (data))), -1); EXPECT_EQ (join::lastError, SaxErrc::InvalidValue); + EXPECT_EQ (jsonWriter.setBinary (std::span (data, sizeof (data)), -1), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidValue); } /** @@ -374,6 +376,8 @@ TEST (JsonWriter, setKey) EXPECT_EQ (join::lastError, SaxErrc::InvalidKey); EXPECT_EQ (invalid (6.5), -1); EXPECT_EQ (join::lastError, SaxErrc::InvalidKey); + EXPECT_EQ (invalid (std::span ()), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidKey); EXPECT_EQ (stream.str (), ""); jsonWriter.reset (); @@ -398,6 +402,19 @@ TEST (JsonWriter, setKey) EXPECT_EQ (stream.str (), "{\"\\\"\\\\\\b\\f\\n\\r\\t\\u0019\":"); } +/** + * @brief Test startKey method. + */ +TEST (JsonWriter, startKey) +{ + std::stringstream stream; + JsonWriter jsonWriter (stream); + + EXPECT_EQ (jsonWriter.startObject (), 0); + EXPECT_EQ (jsonWriter.startKey (), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidKey); +} + /** * @brief Test stopObject method. */ diff --git a/data/tests/pack_reader_test.cpp b/data/tests/pack_reader_test.cpp index 4ea016aa..a7f7bc57 100644 --- a/data/tests/pack_reader_test.cpp +++ b/data/tests/pack_reader_test.cpp @@ -32,6 +32,7 @@ // C++. #include #include +#include #include #include #include @@ -776,6 +777,11 @@ TEST (PackReader, fail) stream.str (std::string ({'\xd7'})); ASSERT_NE (PackReader ().deserialize (stream, value), 0); + stream.clear (); + stream.str (std::string ({'\x91', '\xc1'})); + ASSERT_NE (PackReader ().deserialize (stream, value), 0); + ASSERT_EQ (join::lastError, SaxErrc::InvalidValue); + stream.clear (); stream.str (std::string ({'\x92', '\x01'})); ASSERT_NE (PackReader ().deserialize (stream, value), 0); @@ -1393,11 +1399,65 @@ TEST (PackReader, bin) EXPECT_EQ (join::lastError, SaxErrc::Truncated); } +/** + * @brief Test MessagePack parse ext. + */ +TEST (PackReader, ext) +{ + const std::vector data (65536, 0x78); + const uint8_t bytes[] = {0x00, 0xff}; + Value value; + + auto ext = [&] (const std::string& head, size_t size) { + std::stringstream stream (head + std::string (size, 'x')); + if (PackReader ().deserialize (stream, value) == -1) + { + return false; + } + return value.isBinary () && (value.getSubtype () == -1) && + std::ranges::equal (value.getBinary (), std::span (data.data (), size)); + }; + + EXPECT_TRUE (ext (std::string ({'\xd4', '\xff'}), 1)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xd5', '\xff'}), 2)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xd6', '\xff'}), 4)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xd7', '\xff'}), 8)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xd8', '\xff'}), 16)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xc7', '\x00', '\xff'}), 0)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xc7', '\xff', '\xff'}), 255)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xc8', '\xff', '\xff', '\xff'}), 65535)) << join::lastError.message (); + EXPECT_TRUE (ext (std::string ({'\xc9', '\x00', '\x01', '\x00', '\x00', '\xff'}), 65536)) + << join::lastError.message (); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xd5', '\x01', '\x00', '\xff'}), value), 0) + << join::lastError.message (); + ASSERT_TRUE (value.isArray ()); + ASSERT_TRUE (value[0].isBinary ()); + EXPECT_EQ (value[0].getSubtype (), 1); + EXPECT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (bytes, sizeof (bytes)))); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xd5', '\x01', '\x00'}), value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xd5'}), value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc7'}), value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc8', '\x00'}), value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x91', '\xc9', '\x00'}), value), -1); + EXPECT_EQ (join::lastError, SaxErrc::Truncated); +} + /** * @brief Test MessagePack parse key. */ TEST (PackReader, key) { + const uint8_t bytes[] = {0x00, 0xff}; std::stringstream stream; Value value; @@ -1461,16 +1521,43 @@ TEST (PackReader, key) ASSERT_TRUE (value.isObject ()); ASSERT_TRUE (value.contains (1.0)); + stream.clear (); + stream.str (std::string ({'\x81', '\xc4', '\x02', '\x00', '\xff', '\x01'})); + ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); + ASSERT_TRUE (value.isObject ()); + ASSERT_TRUE (value.contains (Value (std::span (bytes, sizeof (bytes))))); + + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x81', '\xd5', '\xff', '\x00', '\xff', '\x01'}), value), 0) + << join::lastError.message (); + ASSERT_TRUE (value.isObject ()); + ASSERT_TRUE (value.contains (Value (std::span (bytes, sizeof (bytes)), -1))); + + Value arrayKey; + arrayKey.pushBack (1); stream.clear (); stream.str (std::string ({'\x81', '\x91', '\x01', '\x01'})); - ASSERT_NE (PackReader ().deserialize (stream, value), 0); - ASSERT_EQ (join::lastError, SaxErrc::InvalidValue); + ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); + ASSERT_TRUE (value.isObject ()); + ASSERT_TRUE (value.contains (arrayKey)); + Value objectKey; + objectKey["a"] = 1; stream.clear (); stream.str (std::string ({'\x81', '\x81', '\xa1', '\x61', '\x01', '\x01'})); + ASSERT_EQ (PackReader ().deserialize (stream, value), 0) << join::lastError.message (); + ASSERT_TRUE (value.isObject ()); + ASSERT_TRUE (value.contains (objectKey)); + + stream.clear (); + stream.str (std::string ({'\x81', '\xc1', '\x01'})); ASSERT_NE (PackReader ().deserialize (stream, value), 0); ASSERT_EQ (join::lastError, SaxErrc::InvalidValue); + std::stringstream out; + JsonWriter jsonWriter (out); + ASSERT_EQ (PackReader ().deserialize (std::string ({'\x81', '\x91', '\x01', '\x01'}), jsonWriter), -1); + ASSERT_EQ (join::lastError, SaxErrc::InvalidKey); + stream.clear (); stream.str (std::string ({'\x81', '\xcc'})); ASSERT_NE (PackReader ().deserialize (stream, value), 0); diff --git a/data/tests/pack_writer_test.cpp b/data/tests/pack_writer_test.cpp index a0c712ca..4350231d 100644 --- a/data/tests/pack_writer_test.cpp +++ b/data/tests/pack_writer_test.cpp @@ -404,6 +404,25 @@ TEST (PackWriter, setBinary) stream.str (""); EXPECT_EQ (packWriter.setBinary (std::span (data.data (), 65536)), 0); EXPECT_EQ (stream.str ().compare (0, 7, std::string ({'\xC6', '\x00', '\x01', '\x00', '\x00', '\x78', '\x78'})), 0); + + auto ext = [&] (size_t size) { + stream.str (""); + packWriter.setBinary (std::span (data.data (), size), -1); + packWriter.flush (); + return stream.str ().substr (0, stream.str ().size () - size); + }; + + EXPECT_EQ (ext (1), std::string ({'\xD4', '\xFF'})); + EXPECT_EQ (ext (2), std::string ({'\xD5', '\xFF'})); + EXPECT_EQ (ext (4), std::string ({'\xD6', '\xFF'})); + EXPECT_EQ (ext (8), std::string ({'\xD7', '\xFF'})); + EXPECT_EQ (ext (16), std::string ({'\xD8', '\xFF'})); + EXPECT_EQ (ext (0), std::string ({'\xC7', '\x00', '\xFF'})); + EXPECT_EQ (ext (3), std::string ({'\xC7', '\x03', '\xFF'})); + EXPECT_EQ (ext (255), std::string ({'\xC7', '\xFF', '\xFF'})); + EXPECT_EQ (ext (256), std::string ({'\xC8', '\x01', '\x00', '\xFF'})); + EXPECT_EQ (ext (65535), std::string ({'\xC8', '\xFF', '\xFF', '\xFF'})); + EXPECT_EQ (ext (65536), std::string ({'\xC9', '\x00', '\x01', '\x00', '\x00', '\xFF'})); } /** @@ -512,11 +531,11 @@ TEST (PackWriter, setKey) std::stringstream stream; PackWriter packWriter (stream); - auto key = [&] (auto value) { + auto key = [&] (auto... value) { stream.str (""); packWriter.reset (); packWriter.startObject (1); - int result = packWriter.setKey (value); + int result = packWriter.setKey (value...); packWriter.flush (); return result; }; @@ -539,6 +558,8 @@ TEST (PackWriter, setKey) EXPECT_EQ (join::lastError, SaxErrc::InvalidParent); EXPECT_EQ (packWriter.setKey ("eight"), -1); EXPECT_EQ (join::lastError, SaxErrc::InvalidParent); + EXPECT_EQ (packWriter.setKey (std::span ()), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidParent); EXPECT_EQ (key (nullptr), 0); EXPECT_EQ (stream.str ().substr (1), std::string ({'\xC0'})); @@ -594,6 +615,28 @@ TEST (PackWriter, setKey) EXPECT_EQ (key ("a"), 0); EXPECT_EQ (stream.str ().substr (1), std::string ({'\xA1', 'a'})); + + const uint8_t bytes[] = {0x00, 0xff}; + EXPECT_EQ (key (std::span (bytes, sizeof (bytes))), 0); + EXPECT_EQ (stream.str ().substr (1), std::string ({'\xC4', '\x02', '\x00', '\xFF'})); + + EXPECT_EQ (key (std::span (bytes, sizeof (bytes)), -1), 0); + EXPECT_EQ (stream.str ().substr (1), std::string ({'\xD5', '\xFF', '\x00', '\xFF'})); +} + +/** + * @brief Test startKey method. + */ +TEST (PackWriter, startKey) +{ + std::stringstream stream; + PackWriter packWriter (stream); + + EXPECT_EQ (packWriter.startKey (), -1); + EXPECT_EQ (join::lastError, SaxErrc::InvalidParent); + + EXPECT_EQ (packWriter.startObject (1), 0); + EXPECT_EQ (packWriter.startKey (), 0); } /** @@ -683,12 +726,12 @@ TEST (PackWriter, document) value = Object (); value.insert (key, 1); stream.str (""); - EXPECT_EQ (value.serialize (packWriter), -1); - EXPECT_EQ (join::lastError, SaxErrc::InvalidKey); - EXPECT_EQ (stream.str (), ""); + EXPECT_EQ (value.serialize (packWriter), 0) << join::lastError.message (); + EXPECT_EQ (stream.str (), std::string ({'\x81', '\x91', '\x01', '\x01'})); value = nullptr; value.pushBack (7); + stream.str (""); EXPECT_EQ (value.serialize (packWriter), 0) << join::lastError.message (); EXPECT_EQ (stream.str (), std::string ({'\x91', '\x07'})); } diff --git a/data/tests/sax_handler_test.cpp b/data/tests/sax_handler_test.cpp index 36605a56..73bbc021 100644 --- a/data/tests/sax_handler_test.cpp +++ b/data/tests/sax_handler_test.cpp @@ -141,6 +141,7 @@ TEST (SaxHandler, setBinary) { SaxHandler handler; EXPECT_EQ (handler.setBinary (std::span ()), 0); + EXPECT_EQ (handler.setBinary (std::span (), -1), 0); } /** @@ -200,6 +201,17 @@ TEST (SaxHandler, setKey) EXPECT_EQ (handler.setKey (6.5), 0); EXPECT_EQ (handler.setKey (std::string_view ("seven")), 0); EXPECT_EQ (handler.setKey ("eight"), 0); + EXPECT_EQ (handler.setKey (std::span ()), 0); + EXPECT_EQ (handler.setKey (std::span (), -1), 0); +} + +/** + * @brief Test startKey method. + */ +TEST (SaxHandler, startKey) +{ + SaxHandler handler; + EXPECT_EQ (handler.startKey (), 0); } /** diff --git a/data/tests/value_builder_test.cpp b/data/tests/value_builder_test.cpp index 5a19d569..403013db 100644 --- a/data/tests/value_builder_test.cpp +++ b/data/tests/value_builder_test.cpp @@ -34,6 +34,7 @@ // C++. #include #include +#include #include #include #include @@ -271,6 +272,11 @@ TEST (ValueBuilder, setBinary) ASSERT_EQ (builder.stopArray (), 0); ASSERT_EQ (value.size (), 1U); ASSERT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (data, 1))); + + ASSERT_EQ (builder.setBinary (std::span (data, sizeof (data)), -1), 0); + ASSERT_TRUE (value.isBinary ()); + ASSERT_TRUE (std::ranges::equal (value.getBinary (), std::span (data, sizeof (data)))); + ASSERT_EQ (value.getSubtype (), -1); } /** @@ -295,6 +301,18 @@ TEST (ValueBuilder, startArray) ASSERT_EQ (value.size (), 1U); ASSERT_TRUE (value[0].isArray ()); ASSERT_TRUE (value[0].empty ()); + + builder.reset (); + ASSERT_EQ (builder.startObject (), 0); + ASSERT_EQ (builder.startArray (), 0); + ASSERT_EQ (builder.setInt (1), 0); + ASSERT_EQ (builder.stopArray (), 0); + ASSERT_EQ (builder.setInt (2), 0); + ASSERT_EQ (builder.stopObject (), 0); + ASSERT_EQ (value.size (), 1U); + ASSERT_TRUE (value.getObject ()[0].first.isArray ()); + ASSERT_EQ (value.getObject ()[0].first.at (0), 1); + ASSERT_EQ (value.getObject ()[0].second, 2); } /** @@ -400,7 +418,10 @@ TEST (ValueBuilder, setKey) ASSERT_EQ (join::lastError, SaxErrc::InvalidParent); ASSERT_EQ (builder.setKey (6.5), -1); ASSERT_EQ (join::lastError, SaxErrc::InvalidParent); + ASSERT_EQ (builder.setKey (std::span ()), -1); + ASSERT_EQ (join::lastError, SaxErrc::InvalidParent); + const uint8_t data[] = {0x01, 0x02}; ASSERT_EQ (builder.startObject (), 0); ASSERT_EQ (builder.setKey (nullptr), 0); ASSERT_EQ (builder.setInt (0), 0); @@ -420,8 +441,12 @@ TEST (ValueBuilder, setKey) ASSERT_EQ (builder.setInt (7), 0); ASSERT_EQ (builder.setKey ("eight"), 0); ASSERT_EQ (builder.setInt (8), 0); + ASSERT_EQ (builder.setKey (std::span (data, sizeof (data))), 0); + ASSERT_EQ (builder.setInt (9), 0); + ASSERT_EQ (builder.setKey (std::span (data, sizeof (data)), -1), 0); + ASSERT_EQ (builder.setInt (10), 0); ASSERT_EQ (builder.stopObject (), 0); - ASSERT_EQ (value.size (), 9U); + ASSERT_EQ (value.size (), 11U); ASSERT_TRUE (value.getObject ()[0].first.isNull ()); ASSERT_TRUE (value.getObject ()[1].first.isBool ()); ASSERT_TRUE (value.getObject ()[2].first.isInt ()); @@ -431,6 +456,22 @@ TEST (ValueBuilder, setKey) ASSERT_TRUE (value.getObject ()[6].first.isDouble ()); ASSERT_EQ (value["seven"], 7); ASSERT_EQ (value["eight"], 8); + ASSERT_EQ (value[Value (std::span (data, sizeof (data)))], 9); + ASSERT_EQ (value[Value (std::span (data, sizeof (data)), -1)], 10); +} + +/** + * @brief Test startKey method. + */ +TEST (ValueBuilder, startKey) +{ + Value value; + ValueBuilder builder (value); + ASSERT_EQ (builder.startKey (), -1); + ASSERT_EQ (join::lastError, SaxErrc::InvalidParent); + + ASSERT_EQ (builder.startObject (), 0); + ASSERT_EQ (builder.startKey (), 0); } /** diff --git a/data/tests/value_test.cpp b/data/tests/value_test.cpp index 29c64d54..c531c361 100644 --- a/data/tests/value_test.cpp +++ b/data/tests/value_test.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -230,6 +231,12 @@ TEST (Value, create) Value binaryValue (std::span (bytes, sizeof (bytes))); ASSERT_TRUE (binaryValue.isBinary ()); ASSERT_TRUE (std::ranges::equal (binaryValue.getBinary (), std::span (bytes, sizeof (bytes)))); + ASSERT_EQ (binaryValue.getSubtype (), std::nullopt); + + Value extensionValue (std::span (bytes, sizeof (bytes)), -1); + ASSERT_TRUE (extensionValue.isBinary ()); + ASSERT_TRUE (std::ranges::equal (extensionValue.getBinary (), std::span (bytes, sizeof (bytes)))); + ASSERT_EQ (extensionValue.getSubtype (), -1); Value arrayValue (array.getArray ()); ASSERT_TRUE (arrayValue.isArray ()); @@ -340,6 +347,12 @@ TEST (Value, copy) ASSERT_TRUE (binaryValue.isBinary ()); ASSERT_TRUE (std::ranges::equal (binaryValue.getBinary (), std::span (bytes, sizeof (bytes)))); + value = Value (std::span (bytes, sizeof (bytes)), -1); + Value extensionValue (value); + ASSERT_TRUE (extensionValue.isBinary ()); + ASSERT_TRUE (std::ranges::equal (extensionValue.getBinary (), std::span (bytes, sizeof (bytes)))); + ASSERT_EQ (extensionValue.getSubtype (), -1); + value = array.getArray (); Value arrayValue (value); ASSERT_TRUE (arrayValue.isArray ()); @@ -4191,6 +4204,116 @@ TEST (Value, getBinary) ASSERT_THROW (value.getBinary (), std::bad_cast); } +/** + * @brief Test getSubtype method. + */ +TEST (Value, getSubtype) +{ + Value array; + array.pushBack (1); + + Value object; + object["i"] = 1; + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + + Value value; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = nullptr; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = true; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = false; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::min (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::numeric_limits::max (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = ""; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = "foo"; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = "02:42:64:2f:6a:d0"; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = "127.0.0.1"; + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = std::span (bytes, sizeof (bytes)); + ASSERT_EQ (value.getSubtype (), std::nullopt); + + value = Value (std::span (bytes, sizeof (bytes)), -1); + ASSERT_EQ (value.getSubtype (), -1); + + value = array.getArray (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); + + value = object.getObject (); + ASSERT_THROW (value.getSubtype (), std::bad_cast); +} + /** * @brief Test getArray method. */ @@ -4717,6 +4840,7 @@ TEST (Value, subscript) ASSERT_EQ (value[uint32_t (1)], 1); ASSERT_EQ (value[int64_t (2)], 2); ASSERT_EQ (value[uint64_t (3)], 3); + ASSERT_EQ (value[Value (uint64_t (3))], 3); ASSERT_THROW (value[nullptr], std::bad_cast); ASSERT_THROW (value[true], std::bad_cast); ASSERT_THROW (value[1.0], std::bad_cast); @@ -5561,6 +5685,8 @@ TEST (Value, pushBack) value = Array (); ASSERT_TRUE (value.pushBack (std::span (bytes, sizeof (bytes))).isBinary ()); ASSERT_TRUE (std::ranges::equal (value[0].getBinary (), std::span (bytes, sizeof (bytes)))); + ASSERT_EQ (value.pushBack (std::span (bytes, sizeof (bytes)), -1).getSubtype (), -1); + ASSERT_TRUE (std::ranges::equal (value[1].getBinary (), std::span (bytes, sizeof (bytes)))); } /** @@ -5836,6 +5962,8 @@ TEST (Value, contains) ASSERT_FALSE (value.contains (int64_t (-4))); ASSERT_FALSE (value.contains (uint64_t (5))); ASSERT_FALSE (value.contains (6.5)); + ASSERT_TRUE (value.contains (Value ("seven"))); + ASSERT_FALSE (value.contains (Value ("eight"))); value = nullptr; value.pushBack (0); @@ -5850,6 +5978,8 @@ TEST (Value, contains) ASSERT_FALSE (value.contains (int64_t (-1))); ASSERT_TRUE (value.contains (uint64_t (1))); ASSERT_FALSE (value.contains (uint64_t (2))); + ASSERT_TRUE (value.contains (Value (1))); + ASSERT_FALSE (value.contains (Value (2))); ASSERT_THROW (value.contains (nullptr), std::bad_cast); ASSERT_THROW (value.contains (true), std::bad_cast); ASSERT_THROW (value.contains (1.0), std::bad_cast); @@ -5961,6 +6091,9 @@ TEST (Value, serialize) Value array; array.pushBack (1); + Value object; + object["i"] = 1; + Value keys; keys[nullptr] = 0; keys[true] = 1; @@ -6005,6 +6138,18 @@ TEST (Value, serialize) ASSERT_EQ (binary.serialize (out), 0) << join::lastError.message (); EXPECT_EQ (out.str (), std::string ({'\x91', '\xc4', '\x02', '\x00', '\xff'})); + Value extension; + extension.pushBack (std::span (bytes, sizeof (bytes)), -1); + out.str (""); + ASSERT_EQ (extension.serialize (out), 0) << join::lastError.message (); + EXPECT_EQ (out.str (), std::string ({'\x91', '\xd5', '\xff', '\x00', '\xff'})); + + Value binaryKey; + binaryKey[Value (std::span (bytes, sizeof (bytes)), -1)] = 1; + out.str (""); + ASSERT_EQ (binaryKey.serialize (out), 0) << join::lastError.message (); + EXPECT_EQ (out.str (), std::string ({'\x81', '\xd5', '\xff', '\x00', '\xff', '\x01'})); + out.str (""); PackWriter packWriter (out); ASSERT_EQ (value.serialize (packWriter), 0) << join::lastError.message (); @@ -6040,9 +6185,18 @@ TEST (Value, serialize) value = Object (); value.insert (array, 1); out.str (""); - ASSERT_EQ (value.serialize (packWriter), -1); + ASSERT_EQ (value.serialize (packWriter), 0) << join::lastError.message (); + EXPECT_EQ (out.str (), std::string ({'\x81', '\x91', '\x01', '\x01'})); + + value = Object (); + value.insert (object, 1); + out.str (""); + ASSERT_EQ (value.serialize (packWriter), 0) << join::lastError.message (); + EXPECT_EQ (out.str (), std::string ({'\x81', '\x81', '\xa1', '\x69', '\x01', '\x01'})); + + out.str (""); + ASSERT_EQ (value.serialize (out), -1); ASSERT_EQ (join::lastError, SaxErrc::InvalidKey); - ASSERT_EQ (out.str (), ""); value = nullptr; value.pushBack (7); @@ -6481,6 +6635,15 @@ TEST (Value, equal) ASSERT_FALSE ("foo" == value); ASSERT_FALSE (array.getArray () == value); ASSERT_TRUE (object.getObject () == value); + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value = std::span (bytes, sizeof (bytes)); + ASSERT_TRUE (value == Value (std::span (bytes, sizeof (bytes)))); + ASSERT_FALSE (value == Value (std::span (bytes, sizeof (bytes)), 0)); + + value = Value (std::span (bytes, sizeof (bytes)), 0); + ASSERT_TRUE (value == Value (std::span (bytes, sizeof (bytes)), 0)); + ASSERT_FALSE (value == Value (std::span (bytes, sizeof (bytes)), -1)); } /** @@ -7158,6 +7321,12 @@ TEST (Value, lower) ASSERT_TRUE ("foo" < value); ASSERT_TRUE (array.getArray () < value); ASSERT_FALSE (object.getObject () < value); + + const uint8_t bytes[] = {0x00, 0x01, 0xff}; + value = Value (std::span (bytes, sizeof (bytes)), 0); + ASSERT_TRUE (Value (std::span (bytes, sizeof (bytes))) < value); + ASSERT_TRUE (Value (std::span (bytes, sizeof (bytes)), -1) < value); + ASSERT_FALSE (Value (std::span (bytes, sizeof (bytes)), 1) < value); } /**