Skip to content

Commit

Permalink
Add support for MsgPack extension
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Jun 6, 2024
1 parent aec642b commit e4f3fd8
Show file tree
Hide file tree
Showing 13 changed files with 508 additions and 188 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ HEAD
----

* Add `ARDUINOJSON_STRING_LENGTH_SIZE` to the namespace name
* Add MsgPack bin8/bin16/bin32 support (PR #2078 by @Sanae6)
* Add support for MsgPack binary (PR #2078 by @Sanae6)
* Add support for MsgPack extension
* Make string support even more generic (PR #2084 by @d-a-v)
* Optimize `deserializeMsgPack()`
* Allow using a `JsonVariant` as a key or index (issue #2080)
Expand Down
19 changes: 13 additions & 6 deletions extras/tests/JsonVariant/as.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE("null" == variant.as<std::string>());
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("set(4.2)") {
Expand All @@ -38,6 +39,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<unsigned>() == 4U);
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("set(0.0)") {
Expand All @@ -47,6 +49,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("set(false)") {
Expand All @@ -58,6 +61,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<std::string>() == "false");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("set(true)") {
Expand All @@ -69,6 +73,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<std::string>() == "true");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("set(42)") {
Expand All @@ -81,6 +86,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<std::string>() == "42");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("set(42L)") {
Expand Down Expand Up @@ -205,6 +211,13 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<JsonString>().isNull());
}

SECTION("set(serialized(\"hello\"))") {
variant.set(serialized("hello"));

REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}

SECTION("to<JsonObject>()") {
JsonObject obj = variant.to<JsonObject>();
obj["key"] = "value";
Expand Down Expand Up @@ -262,10 +275,4 @@ TEST_CASE("JsonVariant::as()") {

REQUIRE(variant.as<MY_ENUM>() == ONE);
}

SECTION("SerializedValue as MsgPackBinary") {
variant.set(serialized("hello"));

REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
}
}
3 changes: 3 additions & 0 deletions extras/tests/JsonVariant/unbound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ TEST_CASE("Unbound JsonVariant") {
CHECK(variant.as<JsonString>().isNull());
CHECK(variant.as<MsgPackBinary>().data() == nullptr);
CHECK(variant.as<MsgPackBinary>().size() == 0);
CHECK(variant.as<MsgPackExtension>().data() == nullptr);
CHECK(variant.as<MsgPackExtension>().size() == 0);
}

SECTION("is<T>()") {
Expand All @@ -49,6 +51,7 @@ TEST_CASE("Unbound JsonVariant") {
CHECK_FALSE(variant.set(serialized(std::string("42"))));
CHECK_FALSE(variant.set(true));
CHECK_FALSE(variant.set(MsgPackBinary("hello", 5)));
CHECK_FALSE(variant.set(MsgPackExtension(1, "hello", 5)));
}

SECTION("add()") {
Expand Down
34 changes: 34 additions & 0 deletions extras/tests/MixedConfiguration/string_length_size_1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 1") {
}
}

SECTION("set(MsgPackExtension)") {
SECTION("returns true if size <= 252") {
auto str = std::string(252, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));

REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}

SECTION("returns false if size >= 253") {
auto str = std::string(253, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));

REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}

SECTION("deserializeJson()") {
SECTION("returns Ok if string length <= 255") {
auto input = "\"" + std::string(255, '?') + "\"";
Expand Down Expand Up @@ -91,5 +109,21 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 1") {

REQUIRE(err == DeserializationError::NoMemory);
}

SECTION("returns Ok if extension size <= 252") {
auto input = "\xc7\xfc\x01" + std::string(252, '?');

auto err = deserializeMsgPack(doc, input);

REQUIRE(err == DeserializationError::Ok);
}

SECTION("returns NoMemory if binary size >= 253") {
auto input = "\xc7\xfd\x01" + std::string(253, '?');

auto err = deserializeMsgPack(doc, input);

REQUIRE(err == DeserializationError::NoMemory);
}
}
}
34 changes: 34 additions & 0 deletions extras/tests/MixedConfiguration/string_length_size_2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 2") {
}
}

SECTION("set(MsgPackExtension)") {
SECTION("returns true if size <= 65531") {
auto str = std::string(65531, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));

REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}

SECTION("returns false if size >= 65532") {
auto str = std::string(65532, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));

REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}

SECTION("deserializeJson()") {
SECTION("returns Ok if string length <= 65535") {
auto input = "\"" + std::string(65535, '?') + "\"";
Expand Down Expand Up @@ -92,5 +110,21 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 2") {

REQUIRE(err == DeserializationError::NoMemory);
}

SECTION("returns Ok if extension size <= 65531") {
auto input = "\xc8\xff\xfb\x01" + std::string(65531, '?');

auto err = deserializeMsgPack(doc, input);

REQUIRE(err == DeserializationError::Ok);
}

SECTION("returns NoMemory if binary size >= 65532") {
auto input = "\xc8\xff\xfc\x01" + std::string(65532, '?');

auto err = deserializeMsgPack(doc, input);

REQUIRE(err == DeserializationError::NoMemory);
}
}
}
45 changes: 45 additions & 0 deletions extras/tests/MixedConfiguration/string_length_size_4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 4") {
}
}

SECTION("set(MsgPackExtension)") {
SECTION("returns true if size >= 65532") {
auto str = std::string(65532, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));

REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
}

SECTION("deserializeJson()") {
SECTION("returns Ok if string length >= 65536") {
auto input = "\"" + std::string(65536, '?') + "\"";
Expand All @@ -52,6 +62,14 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 4") {

REQUIRE(err == DeserializationError::Ok);
}

SECTION("returns Ok if extension size >= 65532") {
auto input = "\xc8\xff\xfb\x01" + std::string(65532, '?');

auto err = deserializeMsgPack(doc, input);

REQUIRE(err == DeserializationError::Ok);
}
}

SECTION("bin 32 deserialization") {
Expand Down Expand Up @@ -79,4 +97,31 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 4") {
REQUIRE(result == 5 + str.size());
REQUIRE(output == std::string("\xc6\x00\x01\x00\x00", 5) + str);
}

SECTION("ext 32 deserialization") {
auto str = std::string(65536, '?');
auto input = std::string("\xc9\x00\x01\x00\x00\x2a", 6) + str;

auto err = deserializeMsgPack(doc, input);

REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto value = doc.as<MsgPackExtension>();
REQUIRE(value.type() == 42);
REQUIRE(value.size() == 65536);
REQUIRE(value.data() != nullptr);
REQUIRE(std::string(reinterpret_cast<const char*>(value.data()),
value.size()) == str);
}

SECTION("ext 32 serialization") {
auto str = std::string(65536, '?');
doc.set(MsgPackExtension(42, str.data(), str.size()));

std::string output;
auto result = serializeMsgPack(doc, output);

REQUIRE(result == 6 + str.size());
REQUIRE(output == std::string("\xc9\x00\x01\x00\x00\x2a", 6) + str);
}
}
122 changes: 121 additions & 1 deletion extras/tests/MsgPackDeserializer/deserializeVariant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ TEST_CASE("deserialize MsgPack value") {

SECTION("bin 16") {
JsonDocument doc;

auto str = std::string(256, '?');
auto input = std::string("\xc5\x01\x00", 3) + str;

Expand All @@ -169,6 +168,127 @@ TEST_CASE("deserialize MsgPack value") {
REQUIRE(std::string(reinterpret_cast<const char*>(binary.data()),
binary.size()) == str);
}

SECTION("fixext 1") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xd4\x01\x02");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 1);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 2);
}

SECTION("fixext 2") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xd5\x01\x02\x03");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 2);
REQUIRE(data[1] == 3);
}

SECTION("fixext 4") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xd6\x01\x02\x03\x04\x05");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 4);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 2);
REQUIRE(data[1] == 3);
REQUIRE(data[2] == 4);
REQUIRE(data[3] == 5);
}

SECTION("fixext 8") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xd7\x01????????");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 8);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == '?');
REQUIRE(data[7] == '?');
}

SECTION("fixext 16") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xd8\x01?????????????????");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 16);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == '?');
REQUIRE(data[15] == '?');
}

SECTION("ext 8") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xc7\x02\x01\x03\x04");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 3);
REQUIRE(data[1] == 4);
}

SECTION("ext 16") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xc8\x00\x02\x01\x03\x04");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 3);
REQUIRE(data[1] == 4);
}

SECTION("ext 32") {
JsonDocument doc;

auto error = deserializeMsgPack(doc, "\xc9\x00\x00\x00\x02\x01\x03\x04");

REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 3);
REQUIRE(data[1] == 4);
}
}

TEST_CASE("deserializeMsgPack() under memory constaints") {
Expand Down
Loading

0 comments on commit e4f3fd8

Please sign in to comment.