From 556785dc1edfad49ac8901456863e7f84e3fe8dc Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 18 Jun 2024 13:50:50 +0200 Subject: [PATCH] Read MsgPack's 64-bit ints even if `ARDUINOJSON_USE_LONG_LONG` is `0` --- CHANGELOG.md | 2 + .../MixedConfiguration/use_long_long_0.cpp | 36 +++++- .../MsgPack/MsgPackDeserializer.hpp | 105 ++++++------------ 3 files changed, 66 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91be7fc6..2cfa94d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ HEAD Note: works only for reading, not for writing * Support `ElementProxy` and `MemberProxy` in `JsonDocument`'s constructor * Don't add partial objects when allocation fails (issue #2081) +* Read MsgPack's 64-bit integers even if `ARDUINOJSON_USE_LONG_LONG` is `0` + (they are set to `null` if they don't fit in a `long`) v7.0.4 (2024-03-12) ------ diff --git a/extras/tests/MixedConfiguration/use_long_long_0.cpp b/extras/tests/MixedConfiguration/use_long_long_0.cpp index 4c7f0ade..2781a76f 100644 --- a/extras/tests/MixedConfiguration/use_long_long_0.cpp +++ b/extras/tests/MixedConfiguration/use_long_long_0.cpp @@ -3,14 +3,40 @@ #include +#include "Literals.hpp" + TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") { JsonDocument doc; - doc["A"] = 42; - doc["B"] = 84; + SECTION("smoke test") { + doc["A"] = 42; + doc["B"] = 84; - std::string json; - serializeJson(doc, json); + std::string json; + serializeJson(doc, json); - REQUIRE(json == "{\"A\":42,\"B\":84}"); + REQUIRE(json == "{\"A\":42,\"B\":84}"); + } + + SECTION("deserializeMsgPack()") { + SECTION("cf 00 00 00 00 ff ff ff ff") { + auto err = + deserializeMsgPack(doc, "\xcf\x00\x00\x00\x00\xff\xff\xff\xff"_s); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.as() == 0xFFFFFFFF); + } + + SECTION("cf 00 00 00 01 00 00 00 00") { + auto err = + deserializeMsgPack(doc, "\xcf\x00\x00\x00\x01\x00\x00\x00\x00"_s); + + REQUIRE(err == DeserializationError::Ok); +#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8 + REQUIRE(doc.as() == 0x100000000); +#else + REQUIRE(doc.isNull()); +#endif + } + } } diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 4d30629c..d9ab87b5 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -54,6 +54,14 @@ class MsgPackDeserializer { ARDUINOJSON_ASSERT(variant != 0); } + if (code >= 0xcc && code <= 0xd3) { + auto width = uint8_t(1U << ((code - 0xcc) % 4)); + if (allowValue) + return readInteger(variant, width, code >= 0xd0); + else + return skipBytes(width); + } + switch (code) { case 0xc0: // already null @@ -83,62 +91,6 @@ class MsgPackDeserializer { return readDouble(variant); else return skipBytes(8); - - case 0xcc: - if (allowValue) - return readInteger(variant); - else - return skipBytes(1); - - case 0xcd: - if (allowValue) - return readInteger(variant); - else - return skipBytes(2); - - case 0xce: - if (allowValue) - return readInteger(variant); - else - return skipBytes(4); - - case 0xcf: -#if ARDUINOJSON_USE_LONG_LONG - if (allowValue) - return readInteger(variant); - else - return skipBytes(8); -#else - return skipBytes(8); // not supported -#endif - - case 0xd0: - if (allowValue) - return readInteger(variant); - else - return skipBytes(1); - - case 0xd1: - if (allowValue) - return readInteger(variant); - else - return skipBytes(2); - - case 0xd2: - if (allowValue) - return readInteger(variant); - else - return skipBytes(4); - - case 0xd3: -#if ARDUINOJSON_USE_LONG_LONG - if (allowValue) - return readInteger(variant); - else - return skipBytes(8); -#else - return skipBytes(8); // not supported -#endif } if (code <= 0x7f || code >= 0xe0) { // fixint @@ -259,29 +211,38 @@ class MsgPackDeserializer { return DeserializationError::Ok; } - template - DeserializationError::Code readInteger(T& value) { - DeserializationError::Code err; + DeserializationError::Code readInteger(VariantData* variant, uint8_t width, + bool isSigned) { + uint8_t buffer[8]; - err = readBytes(value); + auto err = readBytes(buffer, width); if (err) return err; - fixEndianness(value); + union { + int64_t signedValue; + uint64_t unsignedValue; + }; - return DeserializationError::Ok; - } + if (isSigned) + signedValue = static_cast(buffer[0]); // propagate sign bit + else + unsignedValue = static_cast(buffer[0]); - template - DeserializationError::Code readInteger(VariantData* variant) { - DeserializationError::Code err; - T value; + for (uint8_t i = 1; i < width; i++) + unsignedValue = (unsignedValue << 8) | buffer[i]; - err = readInteger(value); - if (err) - return err; - - variant->setInteger(value); + if (isSigned) { + auto truncatedValue = static_cast(signedValue); + if (truncatedValue == signedValue) + variant->setInteger(truncatedValue); + // else set null on overflow + } else { + auto truncatedValue = static_cast(unsignedValue); + if (truncatedValue == unsignedValue) + variant->setInteger(truncatedValue); + // else set null on overflow + } return DeserializationError::Ok; }