Skip to content

Commit

Permalink
Store 64-bit numbers (double and long long) in an additional slot
Browse files Browse the repository at this point in the history
This change allows slots to be twices maller on 32-bit architectures.
See #1650 and #2103
bblanchon committed Aug 27, 2024
1 parent e682337 commit e297932
Showing 20 changed files with 308 additions and 89 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
------
2 changes: 1 addition & 1 deletion extras/conf_test/esp8266.cpp
Original file line number Diff line number Diff line change
@@ -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() {}
2 changes: 1 addition & 1 deletion extras/conf_test/linux32.cpp
Original file line number Diff line number Diff line change
@@ -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() {}
2 changes: 1 addition & 1 deletion extras/conf_test/win32.cpp
Original file line number Diff line number Diff line change
@@ -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() {}
4 changes: 0 additions & 4 deletions extras/tests/MixedConfiguration/use_long_long_0.cpp
Original file line number Diff line number Diff line change
@@ -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<JsonInteger>() == 0x100000000);
#else
REQUIRE(doc.isNull());
#endif
}
}
}
12 changes: 9 additions & 3 deletions src/ArduinoJson/Configuration.hpp
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions src/ArduinoJson/Json/JsonDeserializer.hpp
Original file line number Diff line number Diff line change
@@ -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:
4 changes: 2 additions & 2 deletions src/ArduinoJson/Json/JsonSerializer.hpp
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
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<size_t> {

while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
slot->accept(*this);
slot->accept(*this, resources_);

slotId = slot->next();

4 changes: 2 additions & 2 deletions src/ArduinoJson/Json/PrettyJsonSerializer.hpp
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
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<TWriter> {
while (!it.done()) {
if (isKey)
indent();
it->accept(*this);
it->accept(*this, base::resources_);
it.next(base::resources_);
if (isKey)
base::write(": ");
17 changes: 14 additions & 3 deletions src/ArduinoJson/Memory/ResourceManager.hpp
Original file line number Diff line number Diff line change
@@ -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<VariantData> allocVariant();

void freeVariant(Slot<VariantData> slot);

VariantData* getVariant(SlotId id) const;

#if ARDUINOJSON_USE_EXTENSIONS
Slot<VariantExtension> allocExtension();
void freeExtension(SlotId slot);
VariantExtension* getExtension(SlotId id) const;
#endif

template <typename TAdaptedString>
StringNode* saveString(TAdaptedString str) {
if (str.isNull())
@@ -112,7 +123,7 @@ class ResourceManager {
Allocator* allocator_;
bool overflowed_;
StringPool stringPool_;
MemoryPoolList<VariantData> variantPools_;
MemoryPoolList<SlotData> variantPools_;
};

ARDUINOJSON_END_PRIVATE_NAMESPACE
25 changes: 23 additions & 2 deletions src/ArduinoJson/Memory/ResourceManagerImpl.hpp
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@

#include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>

ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@@ -16,16 +17,36 @@ inline Slot<VariantData> ResourceManager::allocVariant() {
overflowed_ = true;
return {};
}
return {new (p.ptr()) VariantData, p.id()};
return {new (&p->variant) VariantData, p.id()};
}

inline void ResourceManager::freeVariant(Slot<VariantData> variant) {
variant->clear(this);
variantPools_.freeSlot(variant);
variantPools_.freeSlot({alias_cast<SlotData*>(variant.ptr()), variant.id()});
}

inline VariantData* ResourceManager::getVariant(SlotId id) const {
return reinterpret_cast<VariantData*>(variantPools_.getSlot(id));
}

#if ARDUINOJSON_USE_EXTENSIONS
inline Slot<VariantExtension> 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<SlotData*>(p), id});
}

inline VariantExtension* ResourceManager::getExtension(SlotId id) const {
return &variantPools_.getSlot(id)->extension;
}
#endif

ARDUINOJSON_END_PRIVATE_NAMESPACE
12 changes: 6 additions & 6 deletions src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ class MsgPackDeserializer {

if (code <= 0x7f || code >= 0xe0) { // fixint
if (allowValue)
variant->setInteger(static_cast<int8_t>(code));
variant->setInteger(static_cast<int8_t>(code), resources_);
return DeserializationError::Ok;
}

@@ -231,12 +231,12 @@ class MsgPackDeserializer {
if (isSigned) {
auto truncatedValue = static_cast<JsonInteger>(signedValue);
if (truncatedValue == signedValue)
variant->setInteger(truncatedValue);
variant->setInteger(truncatedValue, resources_);
// else set null on overflow
} else {
auto truncatedValue = static_cast<JsonUInt>(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;
}
4 changes: 2 additions & 2 deletions src/ArduinoJson/MsgPack/MsgPackSerializer.hpp
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
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<size_t> {
auto slotId = object.head();
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
slot->accept(*this);
slot->accept(*this, resources_);
slotId = slot->next();
}

7 changes: 4 additions & 3 deletions src/ArduinoJson/Serialization/measure.hpp
Original file line number Diff line number Diff line change
@@ -11,9 +11,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <template <typename> class TSerializer>
size_t measure(ArduinoJson::JsonVariantConst source) {
DummyWriter dp;
TSerializer<DummyWriter> serializer(
dp, VariantAttorney::getResourceManager(source));
return VariantData::accept(VariantAttorney::getData(source), serializer);
auto data = VariantAttorney::getData(source);
auto resources = VariantAttorney::getResourceManager(source);
TSerializer<DummyWriter> serializer(dp, resources);
return VariantData::accept(data, resources, serializer);
}

ARDUINOJSON_END_PRIVATE_NAMESPACE
7 changes: 4 additions & 3 deletions src/ArduinoJson/Serialization/serialize.hpp
Original file line number Diff line number Diff line change
@@ -10,9 +10,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE

template <template <typename> class TSerializer, typename TWriter>
size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
TSerializer<TWriter> serializer(writer,
VariantAttorney::getResourceManager(source));
return VariantData::accept(VariantAttorney::getData(source), serializer);
auto data = VariantAttorney::getData(source);
auto resources = VariantAttorney::getResourceManager(source);
TSerializer<TWriter> serializer(writer, resources);
return VariantData::accept(data, resources, serializer);
}

template <template <typename> class TSerializer, typename TDestination>
23 changes: 15 additions & 8 deletions src/ArduinoJson/Variant/ConverterImpl.hpp
Original file line number Diff line number Diff line change
@@ -65,19 +65,21 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
return false;
auto resources = getResourceManager(dst);
data->clear(resources);
data->setInteger(src);
data->setInteger(src, resources);
return true;
}

static T fromJson(JsonVariantConst src) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
auto data = getData(src);
return data ? data->template asIntegral<T>() : T();
auto resources = getResourceManager(src);
return data ? data->template asIntegral<T>(resources) : T();
}

static bool checkJson(JsonVariantConst src) {
auto data = getData(src);
return data && data->template isInteger<T>();
auto resources = getResourceManager(src);
return data && data->template isInteger<T>(resources);
}
};

@@ -90,12 +92,15 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>

static T fromJson(JsonVariantConst src) {
auto data = getData(src);
return data ? static_cast<T>(data->template asIntegral<int>()) : T();
auto resources = getResourceManager(src);
return data ? static_cast<T>(data->template asIntegral<int>(resources))
: T();
}

static bool checkJson(JsonVariantConst src) {
auto data = getData(src);
return data && data->template isInteger<int>();
auto resources = getResourceManager(src);
return data && data->template isInteger<int>(resources);
}
};

@@ -113,7 +118,8 @@ struct Converter<bool> : private detail::VariantAttorney {

static bool fromJson(JsonVariantConst src) {
auto data = getData(src);
return data ? data->asBoolean() : false;
auto resources = getResourceManager(src);
return data ? data->asBoolean(resources) : false;
}

static bool checkJson(JsonVariantConst src) {
@@ -131,13 +137,14 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
return false;
auto resources = getResourceManager(dst);
data->clear(resources);
data->setFloat(static_cast<JsonFloat>(src));
data->setFloat(src, resources);
return true;
}

static T fromJson(JsonVariantConst src) {
auto data = getData(src);
return data ? data->template asFloat<T>() : 0;
auto resources = getResourceManager(src);
return data ? data->template asFloat<T>(resources) : 0;
}

static bool checkJson(JsonVariantConst src) {
Loading

0 comments on commit e297932

Please sign in to comment.