From e297932a987087fa9d6732029949503650335057 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 27 Aug 2024 08:02:09 +0200 Subject: [PATCH] Store 64-bit numbers (`double` and `long long`) in an additional slot This change allows slots to be twices maller on 32-bit architectures. See #1650 and #2103 --- CHANGELOG.md | 8 ++ extras/conf_test/esp8266.cpp | 2 +- extras/conf_test/linux32.cpp | 2 +- extras/conf_test/win32.cpp | 2 +- .../MixedConfiguration/use_long_long_0.cpp | 4 - src/ArduinoJson/Configuration.hpp | 12 +- src/ArduinoJson/Json/JsonDeserializer.hpp | 6 +- src/ArduinoJson/Json/JsonSerializer.hpp | 4 +- src/ArduinoJson/Json/PrettyJsonSerializer.hpp | 4 +- src/ArduinoJson/Memory/ResourceManager.hpp | 17 ++- .../Memory/ResourceManagerImpl.hpp | 25 +++- .../MsgPack/MsgPackDeserializer.hpp | 12 +- src/ArduinoJson/MsgPack/MsgPackSerializer.hpp | 4 +- src/ArduinoJson/Serialization/measure.hpp | 7 +- src/ArduinoJson/Serialization/serialize.hpp | 7 +- src/ArduinoJson/Variant/ConverterImpl.hpp | 23 +-- .../Variant/JsonVariantVisitor.hpp | 2 +- src/ArduinoJson/Variant/VariantContent.hpp | 40 +++++- src/ArduinoJson/Variant/VariantData.hpp | 133 +++++++++++++----- src/ArduinoJson/Variant/VariantImpl.hpp | 83 +++++++++++ 20 files changed, 308 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d6a4defa..d6d458478 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ HEAD ---- * Store object members with two slots: one for the key and one for the value +* Store 64-bit numbers (`double` and `long long`) in an additional slot +* Reduce the slot size (see table below) + +| Architecture | before | after | +|--------------|----------|----------| +| 8-bit | 8 bytes | 6 bytes | +| 32-bit | 16 bytes | 8 bytes | +| 64-bit | 24 bytes | 16 bytes | v7.1.0 (2024-06-27) ------ diff --git a/extras/conf_test/esp8266.cpp b/extras/conf_test/esp8266.cpp index 161390900..0b3d9f63a 100644 --- a/extras/conf_test/esp8266.cpp +++ b/extras/conf_test/esp8266.cpp @@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantData) == 16, +static_assert(sizeof(ArduinoJson::detail::VariantData) == 8, "sizeof(VariantData)"); void setup() {} diff --git a/extras/conf_test/linux32.cpp b/extras/conf_test/linux32.cpp index 1170a40e4..befe73d35 100644 --- a/extras/conf_test/linux32.cpp +++ b/extras/conf_test/linux32.cpp @@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantData) == 12, +static_assert(sizeof(ArduinoJson::detail::VariantData) == 8, "sizeof(VariantData)"); int main() {} diff --git a/extras/conf_test/win32.cpp b/extras/conf_test/win32.cpp index 00620664d..befe73d35 100644 --- a/extras/conf_test/win32.cpp +++ b/extras/conf_test/win32.cpp @@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN"); static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); -static_assert(sizeof(ArduinoJson::detail::VariantData) == 16, +static_assert(sizeof(ArduinoJson::detail::VariantData) == 8, "sizeof(VariantData)"); int main() {} diff --git a/extras/tests/MixedConfiguration/use_long_long_0.cpp b/extras/tests/MixedConfiguration/use_long_long_0.cpp index 2781a76fc..16c07db16 100644 --- a/extras/tests/MixedConfiguration/use_long_long_0.cpp +++ b/extras/tests/MixedConfiguration/use_long_long_0.cpp @@ -32,11 +32,7 @@ TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") { 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/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index ae6089044..8939742bc 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/src/ArduinoJson/Configuration.hpp @@ -107,11 +107,11 @@ // Capacity of each variant pool (in slots) #ifndef ARDUINOJSON_POOL_CAPACITY # if ARDUINOJSON_SIZEOF_POINTER <= 2 -# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes +# define ARDUINOJSON_POOL_CAPACITY 16 // 96 bytes # elif ARDUINOJSON_SIZEOF_POINTER == 4 -# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes +# define ARDUINOJSON_POOL_CAPACITY 128 // 1024 bytes # else -# define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes +# define ARDUINOJSON_POOL_CAPACITY 256 // 4096 bytes # endif #endif @@ -269,6 +269,12 @@ # endif #endif +#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_DOUBLE +# define ARDUINOJSON_USE_EXTENSIONS 1 +#else +# define ARDUINOJSON_USE_EXTENSIONS 0 +#endif + #if defined(nullptr) # error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr // See https://github.com/bblanchon/ArduinoJson/issues/1355 diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index 38d89adb2..1a8b70921 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -520,15 +520,15 @@ class JsonDeserializer { auto number = parseNumber(buffer_); switch (number.type()) { case NumberType::UnsignedInteger: - result.setInteger(number.asUnsignedInteger()); + result.setInteger(number.asUnsignedInteger(), resources_); return DeserializationError::Ok; case NumberType::SignedInteger: - result.setInteger(number.asSignedInteger()); + result.setInteger(number.asSignedInteger(), resources_); return DeserializationError::Ok; case NumberType::Float: - result.setFloat(number.asFloat()); + result.setFloat(number.asFloat(), resources_); return DeserializationError::Ok; default: diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index 187b3bff6..476142e42 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -27,7 +27,7 @@ class JsonSerializer : public VariantDataVisitor { while (slotId != NULL_SLOT) { auto slot = resources_->getVariant(slotId); - slot->accept(*this); + slot->accept(*this, resources_); slotId = slot->next(); @@ -48,7 +48,7 @@ class JsonSerializer : public VariantDataVisitor { while (slotId != NULL_SLOT) { auto slot = resources_->getVariant(slotId); - slot->accept(*this); + slot->accept(*this, resources_); slotId = slot->next(); diff --git a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp index fd2c99fac..b5e9e0f76 100644 --- a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp +++ b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp @@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer { nesting_++; while (!it.done()) { indent(); - it->accept(*this); + it->accept(*this, base::resources_); it.next(base::resources_); base::write(it.done() ? "\r\n" : ",\r\n"); @@ -49,7 +49,7 @@ class PrettyJsonSerializer : public JsonSerializer { while (!it.done()) { if (isKey) indent(); - it->accept(*this); + it->accept(*this, base::resources_); it.next(base::resources_); if (isKey) base::write(": "); diff --git a/src/ArduinoJson/Memory/ResourceManager.hpp b/src/ArduinoJson/Memory/ResourceManager.hpp index e8e594b54..53d425cf9 100644 --- a/src/ArduinoJson/Memory/ResourceManager.hpp +++ b/src/ArduinoJson/Memory/ResourceManager.hpp @@ -18,6 +18,13 @@ class VariantData; class VariantWithId; class ResourceManager { + union SlotData { + VariantData variant; +#if ARDUINOJSON_USE_EXTENSIONS + VariantExtension extension; +#endif + }; + public: ResourceManager(Allocator* allocator = DefaultAllocator::instance()) : allocator_(allocator), overflowed_(false) {} @@ -50,11 +57,15 @@ class ResourceManager { } Slot allocVariant(); - void freeVariant(Slot slot); - VariantData* getVariant(SlotId id) const; +#if ARDUINOJSON_USE_EXTENSIONS + Slot allocExtension(); + void freeExtension(SlotId slot); + VariantExtension* getExtension(SlotId id) const; +#endif + template StringNode* saveString(TAdaptedString str) { if (str.isNull()) @@ -112,7 +123,7 @@ class ResourceManager { Allocator* allocator_; bool overflowed_; StringPool stringPool_; - MemoryPoolList variantPools_; + MemoryPoolList variantPools_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/src/ArduinoJson/Memory/ResourceManagerImpl.hpp b/src/ArduinoJson/Memory/ResourceManagerImpl.hpp index 3e0bc8b1d..427c9ad64 100644 --- a/src/ArduinoJson/Memory/ResourceManagerImpl.hpp +++ b/src/ArduinoJson/Memory/ResourceManagerImpl.hpp @@ -6,6 +6,7 @@ #include #include +#include #include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE @@ -16,16 +17,36 @@ inline Slot ResourceManager::allocVariant() { overflowed_ = true; return {}; } - return {new (p.ptr()) VariantData, p.id()}; + return {new (&p->variant) VariantData, p.id()}; } inline void ResourceManager::freeVariant(Slot variant) { variant->clear(this); - variantPools_.freeSlot(variant); + variantPools_.freeSlot({alias_cast(variant.ptr()), variant.id()}); } inline VariantData* ResourceManager::getVariant(SlotId id) const { return reinterpret_cast(variantPools_.getSlot(id)); } +#if ARDUINOJSON_USE_EXTENSIONS +inline Slot ResourceManager::allocExtension() { + auto p = variantPools_.allocSlot(allocator_); + if (!p) { + overflowed_ = true; + return {}; + } + return {&p->extension, p.id()}; +} + +inline void ResourceManager::freeExtension(SlotId id) { + auto p = getExtension(id); + variantPools_.freeSlot({reinterpret_cast(p), id}); +} + +inline VariantExtension* ResourceManager::getExtension(SlotId id) const { + return &variantPools_.getSlot(id)->extension; +} +#endif + ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 0c80ddee8..7b64299a1 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -91,7 +91,7 @@ class MsgPackDeserializer { if (code <= 0x7f || code >= 0xe0) { // fixint if (allowValue) - variant->setInteger(static_cast(code)); + variant->setInteger(static_cast(code), resources_); return DeserializationError::Ok; } @@ -231,12 +231,12 @@ class MsgPackDeserializer { if (isSigned) { auto truncatedValue = static_cast(signedValue); if (truncatedValue == signedValue) - variant->setInteger(truncatedValue); + variant->setInteger(truncatedValue, resources_); // else set null on overflow } else { auto truncatedValue = static_cast(unsignedValue); if (truncatedValue == unsignedValue) - variant->setInteger(truncatedValue); + variant->setInteger(truncatedValue, resources_); // else set null on overflow } @@ -254,7 +254,7 @@ class MsgPackDeserializer { return err; fixEndianness(value); - variant->setFloat(value); + variant->setFloat(value, resources_); return DeserializationError::Ok; } @@ -270,7 +270,7 @@ class MsgPackDeserializer { return err; fixEndianness(value); - variant->setFloat(value); + variant->setFloat(value, resources_); return DeserializationError::Ok; } @@ -289,7 +289,7 @@ class MsgPackDeserializer { doubleToFloat(i, o); fixEndianness(value); - variant->setFloat(value); + variant->setFloat(value, resources_); return DeserializationError::Ok; } diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index d92997b66..b8374c4ff 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -62,7 +62,7 @@ class MsgPackSerializer : public VariantDataVisitor { auto slotId = array.head(); while (slotId != NULL_SLOT) { auto slot = resources_->getVariant(slotId); - slot->accept(*this); + slot->accept(*this, resources_); slotId = slot->next(); } @@ -84,7 +84,7 @@ class MsgPackSerializer : public VariantDataVisitor { auto slotId = object.head(); while (slotId != NULL_SLOT) { auto slot = resources_->getVariant(slotId); - slot->accept(*this); + slot->accept(*this, resources_); slotId = slot->next(); } diff --git a/src/ArduinoJson/Serialization/measure.hpp b/src/ArduinoJson/Serialization/measure.hpp index fa2c66456..fd2acf276 100644 --- a/src/ArduinoJson/Serialization/measure.hpp +++ b/src/ArduinoJson/Serialization/measure.hpp @@ -11,9 +11,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template