diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f22103c..91be7fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/extras/tests/JsonVariant/as.cpp b/extras/tests/JsonVariant/as.cpp index 15026f22..711c12a8 100644 --- a/extras/tests/JsonVariant/as.cpp +++ b/extras/tests/JsonVariant/as.cpp @@ -26,6 +26,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE("null" == variant.as()); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(4.2)") { @@ -38,6 +39,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == 4U); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(0.0)") { @@ -47,6 +49,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == 0L); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(false)") { @@ -58,6 +61,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == "false"); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(true)") { @@ -69,6 +73,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == "true"); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(42)") { @@ -81,6 +86,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == "42"); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(42L)") { @@ -205,6 +211,13 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as().isNull()); } + SECTION("set(serialized(\"hello\"))") { + variant.set(serialized("hello")); + + REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); + } + SECTION("to()") { JsonObject obj = variant.to(); obj["key"] = "value"; @@ -262,10 +275,4 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == ONE); } - - SECTION("SerializedValue as MsgPackBinary") { - variant.set(serialized("hello")); - - REQUIRE(variant.as().data() == nullptr); - } } diff --git a/extras/tests/JsonVariant/unbound.cpp b/extras/tests/JsonVariant/unbound.cpp index 7b4e31f8..be1317dc 100644 --- a/extras/tests/JsonVariant/unbound.cpp +++ b/extras/tests/JsonVariant/unbound.cpp @@ -23,6 +23,8 @@ TEST_CASE("Unbound JsonVariant") { CHECK(variant.as().isNull()); CHECK(variant.as().data() == nullptr); CHECK(variant.as().size() == 0); + CHECK(variant.as().data() == nullptr); + CHECK(variant.as().size() == 0); } SECTION("is()") { @@ -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()") { diff --git a/extras/tests/MixedConfiguration/string_length_size_1.cpp b/extras/tests/MixedConfiguration/string_length_size_1.cpp index 7a5fec97..738973ee 100644 --- a/extras/tests/MixedConfiguration/string_length_size_1.cpp +++ b/extras/tests/MixedConfiguration/string_length_size_1.cpp @@ -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, '?') + "\""; @@ -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); + } } } diff --git a/extras/tests/MixedConfiguration/string_length_size_2.cpp b/extras/tests/MixedConfiguration/string_length_size_2.cpp index 50db74ba..e326d13d 100644 --- a/extras/tests/MixedConfiguration/string_length_size_2.cpp +++ b/extras/tests/MixedConfiguration/string_length_size_2.cpp @@ -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, '?') + "\""; @@ -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); + } } } diff --git a/extras/tests/MixedConfiguration/string_length_size_4.cpp b/extras/tests/MixedConfiguration/string_length_size_4.cpp index 5178bcf2..c015f5db 100644 --- a/extras/tests/MixedConfiguration/string_length_size_4.cpp +++ b/extras/tests/MixedConfiguration/string_length_size_4.cpp @@ -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, '?') + "\""; @@ -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") { @@ -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()); + auto value = doc.as(); + REQUIRE(value.type() == 42); + REQUIRE(value.size() == 65536); + REQUIRE(value.data() != nullptr); + REQUIRE(std::string(reinterpret_cast(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); + } } diff --git a/extras/tests/MsgPackDeserializer/deserializeVariant.cpp b/extras/tests/MsgPackDeserializer/deserializeVariant.cpp index 0738aefe..9e961f65 100644 --- a/extras/tests/MsgPackDeserializer/deserializeVariant.cpp +++ b/extras/tests/MsgPackDeserializer/deserializeVariant.cpp @@ -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; @@ -169,6 +168,127 @@ TEST_CASE("deserialize MsgPack value") { REQUIRE(std::string(reinterpret_cast(binary.data()), binary.size()) == str); } + + SECTION("fixext 1") { + JsonDocument doc; + + auto error = deserializeMsgPack(doc, "\xd4\x01\x02"); + + REQUIRE(error == DeserializationError::Ok); + REQUIRE(doc.is()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 1); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 2); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 4); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 8); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 16); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 2); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 2); + auto data = reinterpret_cast(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()); + auto ext = doc.as(); + REQUIRE(ext.type() == 1); + REQUIRE(ext.size() == 2); + auto data = reinterpret_cast(ext.data()); + REQUIRE(data[0] == 3); + REQUIRE(data[1] == 4); + } } TEST_CASE("deserializeMsgPack() under memory constaints") { diff --git a/extras/tests/MsgPackDeserializer/errors.cpp b/extras/tests/MsgPackDeserializer/errors.cpp index 821d311b..01e761af 100644 --- a/extras/tests/MsgPackDeserializer/errors.cpp +++ b/extras/tests/MsgPackDeserializer/errors.cpp @@ -184,53 +184,6 @@ TEST_CASE("deserializeMsgPack() returns IncompleteInput") { } } -static std::string msgPackToJson(const char* input, size_t inputSize) { - JsonDocument doc; - auto err = deserializeMsgPack(doc, input, inputSize); - REQUIRE(err == DeserializationError::Ok); - return doc.as(); -} - -TEST_CASE("deserializeMsgPack() replaces ext types by null") { - SECTION("ext 8") { - REQUIRE(msgPackToJson("\x92\xc7\x01\x01\x01\x2A", 6) == "[null,42]"); - } - - SECTION("ext 16") { - REQUIRE(msgPackToJson("\x92\xc8\x00\x01\x01\x01\x2A", 7) == "[null,42]"); - } - - SECTION("ext 32") { - REQUIRE(msgPackToJson("\x92\xc9\x00\x00\x00\x01\x01\x01\x2A", 9) == - "[null,42]"); - } - - SECTION("fixext 1") { - REQUIRE(msgPackToJson("\x92\xd4\x01\x01\x2A", 5) == "[null,42]"); - } - - SECTION("fixext 2") { - REQUIRE(msgPackToJson("\x92\xd5\x01\x01\x02\x2A", 6) == "[null,42]"); - } - - SECTION("fixext 4") { - REQUIRE(msgPackToJson("\x92\xd6\x01\x01\x02\x03\x04\x2A", 8) == - "[null,42]"); - } - - SECTION("fixext 8") { - REQUIRE(msgPackToJson("\x92\xd7\x01\x01\x02\x03\x04\x05\x06\x07\x08\x2A", - 12) == "[null,42]"); - } - - SECTION("fixext 16") { - REQUIRE(msgPackToJson("\x92\xd8\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A" - "\x0B\x0C\x0D\x0E" - "\x0F\x10\x2A", - 20) == "[null,42]"); - } -} - TEST_CASE( "deserializeMsgPack() returns NoMemory when string allocation fails") { TimebombAllocator allocator(0); diff --git a/extras/tests/MsgPackSerializer/serializeVariant.cpp b/extras/tests/MsgPackSerializer/serializeVariant.cpp index 202c587b..bcf7d02b 100644 --- a/extras/tests/MsgPackSerializer/serializeVariant.cpp +++ b/extras/tests/MsgPackSerializer/serializeVariant.cpp @@ -147,16 +147,56 @@ TEST_CASE("serialize MsgPack value") { } SECTION("bin 8") { - auto str = std::string(1, 1); - checkVariant(MsgPackBinary(str.data(), str.size()), "\xC4\x01\x01"); + checkVariant(MsgPackBinary("?", 1), "\xC4\x01?"); } SECTION("bin 16") { - auto str = std::string(256, 1); + auto str = std::string(256, '?'); checkVariant(MsgPackBinary(str.data(), str.size()), std::string("\xC5\x01\x00", 3) + str); } + // bin 32 is tested in string_length_size_4.cpp + + SECTION("fixext 1") { + checkVariant(MsgPackExtension(1, "\x02", 1), "\xD4\x01\x02"); + } + + SECTION("fixext 2") { + checkVariant(MsgPackExtension(1, "\x03\x04", 2), "\xD5\x01\x03\x04"); + } + + SECTION("fixext 4") { + checkVariant(MsgPackExtension(1, "\x05\x06\x07\x08", 4), + "\xD6\x01\x05\x06\x07\x08"); + } + + SECTION("fixext 8") { + checkVariant(MsgPackExtension(1, "????????", 8), "\xD7\x01????????"); + } + + SECTION("fixext 16") { + checkVariant(MsgPackExtension(1, "????????????????", 16), + "\xD8\x01????????????????"); + } + + SECTION("ext 8") { + checkVariant(MsgPackExtension(2, "???", 3), "\xC7\x03\x02???"); + checkVariant(MsgPackExtension(2, "?????", 5), "\xC7\x05\x02?????"); + checkVariant(MsgPackExtension(2, "???????", 7), "\xC7\x07\x02???????"); + checkVariant(MsgPackExtension(2, "?????????", 9), "\xC7\x09\x02?????????"); + checkVariant(MsgPackExtension(2, "???????????????", 15), + "\xC7\x0F\x02???????????????"); + checkVariant(MsgPackExtension(2, "?????????????????", 17), + "\xC7\x11\x02?????????????????"); + } + + SECTION("ext 16") { + auto str = std::string(256, '?'); + checkVariant(MsgPackExtension(2, str.data(), str.size()), + std::string("\xC8\x01\x00\x02", 4) + str); + } + SECTION("serialize round double as integer") { // Issue #1718 checkVariant(-32768.0, "\xD1\x80\x00"); checkVariant(-129.0, "\xD1\xFF\x7F"); diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index 0149d87d..085507a0 100644 --- a/src/ArduinoJson.hpp +++ b/src/ArduinoJson.hpp @@ -50,6 +50,7 @@ #include "ArduinoJson/Json/PrettyJsonSerializer.hpp" #include "ArduinoJson/MsgPack/MsgPackBinary.hpp" #include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp" +#include "ArduinoJson/MsgPack/MsgPackExtension.hpp" #include "ArduinoJson/MsgPack/MsgPackSerializer.hpp" #include "ArduinoJson/compatibility.hpp" diff --git a/src/ArduinoJson/MsgPack/MsgPackBinary.hpp b/src/ArduinoJson/MsgPack/MsgPackBinary.hpp index 1bc3a127..bd275d44 100644 --- a/src/ArduinoJson/MsgPack/MsgPackBinary.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackBinary.hpp @@ -1,5 +1,7 @@ #pragma once +#include + ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE class MsgPackBinary { diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 2d492f47..f94e2ed0 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -38,11 +38,13 @@ class MsgPackDeserializer { DeserializationOption::NestingLimit nestingLimit) { DeserializationError::Code err; - uint8_t code = 0; // TODO: why do we need to initialize this variable? - err = readByte(code); + uint8_t header[5]; + err = readBytes(header, 1); if (err) return err; + const uint8_t& code = header[0]; + foundSomething_ = true; bool allowValue = filter.allowValue(); @@ -52,82 +54,6 @@ class MsgPackDeserializer { ARDUINOJSON_ASSERT(variant != 0); } - size_t size = 0; - - switch (code) { - case 0xc4: // bin 8 - case 0xc7: // ext 8 - case 0xd9: // str 8 - uint8_t size8; - err = readInteger(size8); - size = size8; - break; - - case 0xc5: // bin 16 - case 0xc8: // ext 16 - case 0xda: // str 16 - case 0xdc: // array 16 - case 0xde: // map 16 - uint16_t size16; - err = readInteger(size16); - size = size16; - break; - - case 0xc6: // bin 32 - case 0xc9: // ext 32 - case 0xdb: // str 32 - case 0xdd: // array 32 - case 0xdf: // map 32 - uint32_t size32; - err = readInteger(size32); - size = size32; - break; - } - - if (err) - return err; - - switch (code & 0xf0) { - case 0x90: // fixarray - case 0x80: // fixmap - size = code & 0x0F; - break; - } - - switch (code & 0xe0) { - case 0xa0: // fixstr - size = code & 0x1f; - break; - } - - // array 16, 32 and fixarray - if (code == 0xdc || code == 0xdd || (code & 0xf0) == 0x90) - return readArray(variant, size, filter, nestingLimit); - - // map 16, 32 and fixmap - if (code == 0xde || code == 0xdf || (code & 0xf0) == 0x80) - return readObject(variant, size, filter, nestingLimit); - - // str 8, 16, 32 and fixstr - if (code == 0xd9 || code == 0xda || code == 0xdb || (code & 0xe0) == 0xa0) { - if (allowValue) - return readString(variant, size); - else - return skipBytes(size); - } - - // bin 8, 16, 32 - if (code == 0xc4 || code == 0xc5 || code == 0xc6) { - if (allowValue) - return readBinary(variant, size); - else - return skipBytes(size); - } - - // ext 8, 16, 32 - if (code == 0xc7 || code == 0xc8 || code == 0xc9) - return skipBytes(size + 1); - switch (code) { case 0xc0: // already null @@ -209,31 +135,96 @@ class MsgPackDeserializer { if (allowValue) return readInteger(variant); else - return skipBytes(8); // not supported + return skipBytes(8); #else - return skipBytes(8); + return skipBytes(8); // not supported #endif - - case 0xd4: // fixext 1 (not supported) - return skipBytes(2); - - case 0xd5: // fixext 2 (not supported) - return skipBytes(3); - - case 0xd6: // fixext 4 (not supported) - return skipBytes(5); - - case 0xd7: // fixext 8 (not supported) - return skipBytes(9); - - case 0xd8: // fixext 16 (not supported) - return skipBytes(17); - - default: // fixint - if (allowValue) - variant->setInteger(static_cast(code)); - return DeserializationError::Ok; } + + if (code <= 0x7f || code >= 0xe0) { // fixint + if (allowValue) + variant->setInteger(static_cast(code)); + return DeserializationError::Ok; + } + + uint8_t sizeBytes = 0; + size_t size = 0; + bool isExtension = code >= 0xc7 && code <= 0xc9; + + switch (code) { + case 0xc4: // bin 8 + case 0xc7: // ext 8 + case 0xd9: // str 8 + sizeBytes = 1; + break; + + case 0xc5: // bin 16 + case 0xc8: // ext 16 + case 0xda: // str 16 + case 0xdc: // array 16 + case 0xde: // map 16 + sizeBytes = 2; + break; + + case 0xc6: // bin 32 + case 0xc9: // ext 32 + case 0xdb: // str 32 + case 0xdd: // array 32 + case 0xdf: // map 32 + sizeBytes = 4; + break; + } + + if (code >= 0xd4 && code <= 0xd8) { // fixext + size = size_t(1) << (code - 0xd4); + isExtension = true; + } + + switch (code & 0xf0) { + case 0x90: // fixarray + case 0x80: // fixmap + size = code & 0x0F; + break; + } + + switch (code & 0xe0) { + case 0xa0: // fixstr + size = code & 0x1f; + break; + } + + if (sizeBytes) { + err = readBytes(header + 1, sizeBytes); + if (err) + return err; + + for (size_t i = 0; i < sizeBytes; i++) + size = (size << 8) | header[i + 1]; + } + + // array 16, 32 and fixarray + if (code == 0xdc || code == 0xdd || (code & 0xf0) == 0x90) + return readArray(variant, size, filter, nestingLimit); + + // map 16, 32 and fixmap + if (code == 0xde || code == 0xdf || (code & 0xf0) == 0x80) + return readObject(variant, size, filter, nestingLimit); + + // str 8, 16, 32 and fixstr + if (code == 0xd9 || code == 0xda || code == 0xdb || (code & 0xe0) == 0xa0) { + if (allowValue) + return readString(variant, size); + else + return skipBytes(size); + } + + if (isExtension) + size++; // to include the type + + if (allowValue) + return readRawString(variant, header, uint8_t(1 + sizeBytes), size); + else + return skipBytes(size); } DeserializationError::Code readByte(uint8_t& value) { @@ -372,47 +363,16 @@ class MsgPackDeserializer { return readBytes(p, n); } - DeserializationError::Code readBinary(VariantData* variant, size_t n) { - uint8_t headerSize; - - if (n <= 0xFF) { - headerSize = 2; - } -#if ARDUINOJSON_STRING_LENGTH_SIZE >= 2 - else if (n <= 0xFFFF) { - headerSize = 3; - } -#endif -#if ARDUINOJSON_STRING_LENGTH_SIZE >= 4 - else { - headerSize = 5; - } -#else - else { - return DeserializationError::NoMemory; - } -#endif - + DeserializationError::Code readRawString(VariantData* variant, + const void* header, + uint8_t headerSize, size_t n) { char* p = stringBuffer_.reserve(headerSize + n); if (!p) return DeserializationError::NoMemory; - if (n <= 0xFF) { - *p++ = '\xc4'; - *p++ = char(n & 0xFF); - } else if (n <= 0xFFFF) { - *p++ = '\xc5'; - *p++ = char(n >> 8 & 0xFF); - *p++ = char(n & 0xFF); - } else { - *p++ = '\xc6'; - *p++ = char(n >> 24 & 0xFF); - *p++ = char(n >> 16 & 0xFF); - *p++ = char(n >> 8 & 0xFF); - *p++ = char(n & 0xFF); - } + memcpy(p, header, headerSize); - auto err = readBytes(p, n); + auto err = readBytes(p + headerSize, n); if (err) return err; diff --git a/src/ArduinoJson/MsgPack/MsgPackExtension.hpp b/src/ArduinoJson/MsgPack/MsgPackExtension.hpp new file mode 100644 index 00000000..5be4e503 --- /dev/null +++ b/src/ArduinoJson/MsgPack/MsgPackExtension.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include + +ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE + +class MsgPackExtension { + public: + MsgPackExtension() : data_(nullptr), size_(0), type_(0) {} + explicit MsgPackExtension(int8_t type, const void* data, size_t size) + : data_(data), size_(size), type_(type) {} + + int8_t type() const { + return type_; + } + + const void* data() const { + return data_; + } + + size_t size() const { + return size_; + } + + private: + const void* data_; + size_t size_; + int8_t type_; +}; + +template <> +struct Converter : private detail::VariantAttorney { + static void toJson(MsgPackExtension src, JsonVariant dst) { + auto data = VariantAttorney::getData(dst); + if (!data) + return; + auto resources = getResourceManager(dst); + if (src.data()) { + uint8_t format, sizeBytes; + if (src.size() >= 0x10000) { + format = 0xc9; // ext 32 + sizeBytes = 4; + } else if (src.size() >= 0x100) { + format = 0xc8; // ext 16 + sizeBytes = 2; + } else if (src.size() == 16) { + format = 0xd8; // fixext 16 + sizeBytes = 0; + } else if (src.size() == 8) { + format = 0xd7; // fixext 8 + sizeBytes = 0; + } else if (src.size() == 4) { + format = 0xd6; // fixext 4 + sizeBytes = 0; + } else if (src.size() == 2) { + format = 0xd5; // fixext 2 + sizeBytes = 0; + } else if (src.size() == 1) { + format = 0xd4; // fixext 1 + sizeBytes = 0; + } else { + format = 0xc7; // ext 8 + sizeBytes = 1; + } + + auto str = resources->createString(src.size() + 2 + sizeBytes); + if (str) { + resources->saveString(str); + auto ptr = reinterpret_cast(str->data); + *ptr++ = uint8_t(format); + for (uint8_t i = 0; i < sizeBytes; i++) + *ptr++ = uint8_t(src.size() >> (sizeBytes - i - 1) * 8 & 0xff); + *ptr++ = uint8_t(src.type()); + memcpy(ptr, src.data(), src.size()); + data->setRawString(str); + return; + } + } + data->setNull(); + } + + static MsgPackExtension fromJson(JsonVariantConst src) { + auto data = getData(src); + if (!data) + return {}; + auto rawstr = data->asRawString(); + if (rawstr.size() == 0) + return {}; + auto p = reinterpret_cast(rawstr.c_str()); + + size_t payloadSize = 0; + uint8_t headerSize = 0; + + const uint8_t& code = p[0]; + + if (code >= 0xd4 && code <= 0xd8) { // fixext 1 + headerSize = 2; + payloadSize = size_t(1) << (code - 0xd4); + } + + if (code >= 0xc7 && code <= 0xc9) { + uint8_t sizeBytes = uint8_t(1 << (code - 0xc7)); + for (uint8_t i = 0; i < sizeBytes; i++) + payloadSize = (payloadSize << 8) | p[1 + i]; + headerSize = uint8_t(2 + sizeBytes); + } + + if (rawstr.size() == headerSize + payloadSize) + return MsgPackExtension(int8_t(p[headerSize - 1]), p + headerSize, + payloadSize); + + return {}; + } + + static bool checkJson(JsonVariantConst src) { + return fromJson(src).data() != nullptr; + } +}; + +ARDUINOJSON_END_PUBLIC_NAMESPACE