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
This commit is contained in:
Benoit Blanchon
2024-08-27 08:02:09 +02:00
parent e682337655
commit e297932a98
20 changed files with 308 additions and 89 deletions

View File

@ -5,6 +5,14 @@ HEAD
---- ----
* Store object members with two slots: one for the key and one for the value * 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) v7.1.0 (2024-06-27)
------ ------

View File

@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); 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)"); "sizeof(VariantData)");
void setup() {} void setup() {}

View File

@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); 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)"); "sizeof(VariantData)");
int main() {} int main() {}

View File

@ -8,7 +8,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE"); 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)"); "sizeof(VariantData)");
int main() {} int main() {}

View File

@ -32,11 +32,7 @@ TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") {
deserializeMsgPack(doc, "\xcf\x00\x00\x00\x01\x00\x00\x00\x00"_s); deserializeMsgPack(doc, "\xcf\x00\x00\x00\x01\x00\x00\x00\x00"_s);
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
REQUIRE(doc.as<JsonInteger>() == 0x100000000);
#else
REQUIRE(doc.isNull()); REQUIRE(doc.isNull());
#endif
} }
} }
} }

View File

@ -107,11 +107,11 @@
// Capacity of each variant pool (in slots) // Capacity of each variant pool (in slots)
#ifndef ARDUINOJSON_POOL_CAPACITY #ifndef ARDUINOJSON_POOL_CAPACITY
# if ARDUINOJSON_SIZEOF_POINTER <= 2 # if ARDUINOJSON_SIZEOF_POINTER <= 2
# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes # define ARDUINOJSON_POOL_CAPACITY 16 // 96 bytes
# elif ARDUINOJSON_SIZEOF_POINTER == 4 # elif ARDUINOJSON_SIZEOF_POINTER == 4
# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes # define ARDUINOJSON_POOL_CAPACITY 128 // 1024 bytes
# else # else
# define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes # define ARDUINOJSON_POOL_CAPACITY 256 // 4096 bytes
# endif # endif
#endif #endif
@ -269,6 +269,12 @@
# endif # endif
#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) #if defined(nullptr)
# error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr # error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr
// See https://github.com/bblanchon/ArduinoJson/issues/1355 // See https://github.com/bblanchon/ArduinoJson/issues/1355

View File

@ -520,15 +520,15 @@ class JsonDeserializer {
auto number = parseNumber(buffer_); auto number = parseNumber(buffer_);
switch (number.type()) { switch (number.type()) {
case NumberType::UnsignedInteger: case NumberType::UnsignedInteger:
result.setInteger(number.asUnsignedInteger()); result.setInteger(number.asUnsignedInteger(), resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
case NumberType::SignedInteger: case NumberType::SignedInteger:
result.setInteger(number.asSignedInteger()); result.setInteger(number.asSignedInteger(), resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
case NumberType::Float: case NumberType::Float:
result.setFloat(number.asFloat()); result.setFloat(number.asFloat(), resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
default: default:

View File

@ -27,7 +27,7 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this); slot->accept(*this, resources_);
slotId = slot->next(); slotId = slot->next();
@ -48,7 +48,7 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this); slot->accept(*this, resources_);
slotId = slot->next(); slotId = slot->next();

View File

@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
nesting_++; nesting_++;
while (!it.done()) { while (!it.done()) {
indent(); indent();
it->accept(*this); it->accept(*this, base::resources_);
it.next(base::resources_); it.next(base::resources_);
base::write(it.done() ? "\r\n" : ",\r\n"); base::write(it.done() ? "\r\n" : ",\r\n");
@ -49,7 +49,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
while (!it.done()) { while (!it.done()) {
if (isKey) if (isKey)
indent(); indent();
it->accept(*this); it->accept(*this, base::resources_);
it.next(base::resources_); it.next(base::resources_);
if (isKey) if (isKey)
base::write(": "); base::write(": ");

View File

@ -18,6 +18,13 @@ class VariantData;
class VariantWithId; class VariantWithId;
class ResourceManager { class ResourceManager {
union SlotData {
VariantData variant;
#if ARDUINOJSON_USE_EXTENSIONS
VariantExtension extension;
#endif
};
public: public:
ResourceManager(Allocator* allocator = DefaultAllocator::instance()) ResourceManager(Allocator* allocator = DefaultAllocator::instance())
: allocator_(allocator), overflowed_(false) {} : allocator_(allocator), overflowed_(false) {}
@ -50,11 +57,15 @@ class ResourceManager {
} }
Slot<VariantData> allocVariant(); Slot<VariantData> allocVariant();
void freeVariant(Slot<VariantData> slot); void freeVariant(Slot<VariantData> slot);
VariantData* getVariant(SlotId id) const; 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> template <typename TAdaptedString>
StringNode* saveString(TAdaptedString str) { StringNode* saveString(TAdaptedString str) {
if (str.isNull()) if (str.isNull())
@ -112,7 +123,7 @@ class ResourceManager {
Allocator* allocator_; Allocator* allocator_;
bool overflowed_; bool overflowed_;
StringPool stringPool_; StringPool stringPool_;
MemoryPoolList<VariantData> variantPools_; MemoryPoolList<SlotData> variantPools_;
}; };
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -6,6 +6,7 @@
#include <ArduinoJson/Collection/CollectionData.hpp> #include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Variant/VariantData.hpp> #include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -16,16 +17,36 @@ inline Slot<VariantData> ResourceManager::allocVariant() {
overflowed_ = true; overflowed_ = true;
return {}; return {};
} }
return {new (p.ptr()) VariantData, p.id()}; return {new (&p->variant) VariantData, p.id()};
} }
inline void ResourceManager::freeVariant(Slot<VariantData> variant) { inline void ResourceManager::freeVariant(Slot<VariantData> variant) {
variant->clear(this); variant->clear(this);
variantPools_.freeSlot(variant); variantPools_.freeSlot({alias_cast<SlotData*>(variant.ptr()), variant.id()});
} }
inline VariantData* ResourceManager::getVariant(SlotId id) const { inline VariantData* ResourceManager::getVariant(SlotId id) const {
return reinterpret_cast<VariantData*>(variantPools_.getSlot(id)); 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 ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -91,7 +91,7 @@ class MsgPackDeserializer {
if (code <= 0x7f || code >= 0xe0) { // fixint if (code <= 0x7f || code >= 0xe0) { // fixint
if (allowValue) if (allowValue)
variant->setInteger(static_cast<int8_t>(code)); variant->setInteger(static_cast<int8_t>(code), resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -231,12 +231,12 @@ class MsgPackDeserializer {
if (isSigned) { if (isSigned) {
auto truncatedValue = static_cast<JsonInteger>(signedValue); auto truncatedValue = static_cast<JsonInteger>(signedValue);
if (truncatedValue == signedValue) if (truncatedValue == signedValue)
variant->setInteger(truncatedValue); variant->setInteger(truncatedValue, resources_);
// else set null on overflow // else set null on overflow
} else { } else {
auto truncatedValue = static_cast<JsonUInt>(unsignedValue); auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
if (truncatedValue == unsignedValue) if (truncatedValue == unsignedValue)
variant->setInteger(truncatedValue); variant->setInteger(truncatedValue, resources_);
// else set null on overflow // else set null on overflow
} }
@ -254,7 +254,7 @@ class MsgPackDeserializer {
return err; return err;
fixEndianness(value); fixEndianness(value);
variant->setFloat(value); variant->setFloat(value, resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -270,7 +270,7 @@ class MsgPackDeserializer {
return err; return err;
fixEndianness(value); fixEndianness(value);
variant->setFloat(value); variant->setFloat(value, resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -289,7 +289,7 @@ class MsgPackDeserializer {
doubleToFloat(i, o); doubleToFloat(i, o);
fixEndianness(value); fixEndianness(value);
variant->setFloat(value); variant->setFloat(value, resources_);
return DeserializationError::Ok; return DeserializationError::Ok;
} }

View File

@ -62,7 +62,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = array.head(); auto slotId = array.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this); slot->accept(*this, resources_);
slotId = slot->next(); slotId = slot->next();
} }
@ -84,7 +84,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = object.head(); auto slotId = object.head();
while (slotId != NULL_SLOT) { while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId); auto slot = resources_->getVariant(slotId);
slot->accept(*this); slot->accept(*this, resources_);
slotId = slot->next(); slotId = slot->next();
} }

View File

@ -11,9 +11,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <template <typename> class TSerializer> template <template <typename> class TSerializer>
size_t measure(ArduinoJson::JsonVariantConst source) { size_t measure(ArduinoJson::JsonVariantConst source) {
DummyWriter dp; DummyWriter dp;
TSerializer<DummyWriter> serializer( auto data = VariantAttorney::getData(source);
dp, VariantAttorney::getResourceManager(source)); auto resources = VariantAttorney::getResourceManager(source);
return VariantData::accept(VariantAttorney::getData(source), serializer); TSerializer<DummyWriter> serializer(dp, resources);
return VariantData::accept(data, resources, serializer);
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

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

View File

@ -65,19 +65,21 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
return false; return false;
auto resources = getResourceManager(dst); auto resources = getResourceManager(dst);
data->clear(resources); data->clear(resources);
data->setInteger(src); data->setInteger(src, resources);
return true; return true;
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
auto data = getData(src); 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) { static bool checkJson(JsonVariantConst src) {
auto data = getData(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) { static T fromJson(JsonVariantConst src) {
auto data = getData(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) { static bool checkJson(JsonVariantConst src) {
auto data = getData(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) { static bool fromJson(JsonVariantConst src) {
auto data = getData(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) { static bool checkJson(JsonVariantConst src) {
@ -131,13 +137,14 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
return false; return false;
auto resources = getResourceManager(dst); auto resources = getResourceManager(dst);
data->clear(resources); data->clear(resources);
data->setFloat(static_cast<JsonFloat>(src)); data->setFloat(src, resources);
return true; return true;
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {
auto data = getData(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) { static bool checkJson(JsonVariantConst src) {

View File

@ -55,7 +55,7 @@ typename TVisitor::result_type accept(JsonVariantConst variant,
return visit.visit(nullptr); return visit.visit(nullptr);
auto resources = VariantAttorney::getResourceManager(variant); auto resources = VariantAttorney::getResourceManager(variant);
VisitorAdapter<TVisitor> adapter(visit, resources); VisitorAdapter<TVisitor> adapter(visit, resources);
return data->accept(adapter); return data->accept(adapter, resources);
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -24,10 +24,21 @@ enum {
VALUE_IS_BOOLEAN = 0x06, VALUE_IS_BOOLEAN = 0x06,
NUMBER_BIT = 0x08, NUMBER_BIT = 0x08, // 0000 1000
VALUE_IS_UNSIGNED_INTEGER = 0x08, VALUE_IS_UINT32 = 0x0A, // 0000 1010
VALUE_IS_SIGNED_INTEGER = 0x0A, VALUE_IS_INT32 = 0x0C, // 0000 1100
VALUE_IS_FLOAT = 0x0C, VALUE_IS_FLOAT = 0x0E, // 0000 1110
#if ARDUINOJSON_USE_EXTENSIONS
EXTENSION_BIT = 0x10, // 0001 0000
#endif
#if ARDUINOJSON_USE_LONG_LONG
VALUE_IS_UINT64 = 0x1A, // 0001 1010
VALUE_IS_INT64 = 0x1C, // 0001 1100
#endif
#if ARDUINOJSON_USE_DOUBLE
VALUE_IS_DOUBLE = 0x1E, // 0001 1110
#endif
COLLECTION_MASK = 0x60, COLLECTION_MASK = 0x60,
VALUE_IS_OBJECT = 0x20, VALUE_IS_OBJECT = 0x20,
@ -37,10 +48,13 @@ enum {
union VariantContent { union VariantContent {
VariantContent() {} VariantContent() {}
JsonFloat asFloat; float asFloat;
bool asBoolean; bool asBoolean;
JsonUInt asUnsignedInteger; uint32_t asUint32;
JsonInteger asSignedInteger; int32_t asInt32;
#if ARDUINOJSON_USE_EXTENSIONS
SlotId asSlotId;
#endif
ArrayData asArray; ArrayData asArray;
ObjectData asObject; ObjectData asObject;
CollectionData asCollection; CollectionData asCollection;
@ -48,4 +62,16 @@ union VariantContent {
struct StringNode* asOwnedString; struct StringNode* asOwnedString;
}; };
#if ARDUINOJSON_USE_EXTENSIONS
union VariantExtension {
# if ARDUINOJSON_USE_LONG_LONG
uint64_t asUint64;
int64_t asInt64;
# endif
# if ARDUINOJSON_USE_DOUBLE
double asDouble;
# endif
};
#endif
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -41,10 +41,17 @@ class VariantData {
} }
template <typename TVisitor> template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visit) const { typename TVisitor::result_type accept(
TVisitor& visit, const ResourceManager* resources) const {
(void)resources; // silence warning
switch (type_) { switch (type_) {
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
return visit.visit(content_.asFloat); return visit.visit(static_cast<JsonFloat>(content_.asFloat));
#if ARDUINOJSON_USE_DOUBLE
case VALUE_IS_DOUBLE:
return visit.visit(getExtension(resources)->asDouble);
#endif
case VALUE_IS_ARRAY: case VALUE_IS_ARRAY:
return visit.visit(content_.asArray); return visit.visit(content_.asArray);
@ -64,11 +71,19 @@ class VariantData {
return visit.visit(RawString(content_.asOwnedString->data, return visit.visit(RawString(content_.asOwnedString->data,
content_.asOwnedString->length)); content_.asOwnedString->length));
case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_INT32:
return visit.visit(content_.asSignedInteger); return visit.visit(static_cast<JsonInteger>(content_.asInt32));
case VALUE_IS_UNSIGNED_INTEGER: case VALUE_IS_UINT32:
return visit.visit(content_.asUnsignedInteger); return visit.visit(static_cast<JsonUInt>(content_.asUint32));
#if ARDUINOJSON_USE_LONG_LONG
case VALUE_IS_INT64:
return visit.visit(getExtension(resources)->asInt64);
case VALUE_IS_UINT64:
return visit.visit(getExtension(resources)->asUint64);
#endif
case VALUE_IS_BOOLEAN: case VALUE_IS_BOOLEAN:
return visit.visit(content_.asBoolean != 0); return visit.visit(content_.asBoolean != 0);
@ -80,9 +95,10 @@ class VariantData {
template <typename TVisitor> template <typename TVisitor>
static typename TVisitor::result_type accept(const VariantData* var, static typename TVisitor::result_type accept(const VariantData* var,
const ResourceManager* resources,
TVisitor& visit) { TVisitor& visit) {
if (var != 0) if (var != 0)
return var->accept(visit); return var->accept(visit, resources);
else else
return visit.visit(nullptr); return visit.visit(nullptr);
} }
@ -113,17 +129,27 @@ class VariantData {
return var->addValue(value, resources); return var->addValue(value, resources);
} }
bool asBoolean() const { bool asBoolean(const ResourceManager* resources) const {
(void)resources; // silence warning
switch (type_) { switch (type_) {
case VALUE_IS_BOOLEAN: case VALUE_IS_BOOLEAN:
return content_.asBoolean; return content_.asBoolean;
case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_UINT32:
case VALUE_IS_UNSIGNED_INTEGER: case VALUE_IS_INT32:
return content_.asUnsignedInteger != 0; return content_.asUint32 != 0;
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
return content_.asFloat != 0; return content_.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE
case VALUE_IS_DOUBLE:
return getExtension(resources)->asDouble != 0;
#endif
case VALUE_IS_NULL: case VALUE_IS_NULL:
return false; return false;
#if ARDUINOJSON_USE_LONG_LONG
case VALUE_IS_UINT64:
case VALUE_IS_INT64:
return getExtension(resources)->asUint64 != 0;
#endif
default: default:
return true; return true;
} }
@ -146,41 +172,63 @@ class VariantData {
} }
template <typename T> template <typename T>
T asFloat() const { T asFloat(const ResourceManager* resources) const {
static_assert(is_floating_point<T>::value, "T must be a floating point"); static_assert(is_floating_point<T>::value, "T must be a floating point");
(void)resources; // silence warning
switch (type_) { switch (type_) {
case VALUE_IS_BOOLEAN: case VALUE_IS_BOOLEAN:
return static_cast<T>(content_.asBoolean); return static_cast<T>(content_.asBoolean);
case VALUE_IS_UNSIGNED_INTEGER: case VALUE_IS_UINT32:
return static_cast<T>(content_.asUnsignedInteger); return static_cast<T>(content_.asUint32);
case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_INT32:
return static_cast<T>(content_.asSignedInteger); return static_cast<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VALUE_IS_UINT64:
return static_cast<T>(getExtension(resources)->asUint64);
case VALUE_IS_INT64:
return static_cast<T>(getExtension(resources)->asInt64);
#endif
case VALUE_IS_LINKED_STRING: case VALUE_IS_LINKED_STRING:
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return parseNumber<T>(content_.asOwnedString->data); return parseNumber<T>(content_.asOwnedString->data);
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
return static_cast<T>(content_.asFloat); return static_cast<T>(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VALUE_IS_DOUBLE:
return static_cast<T>(getExtension(resources)->asDouble);
#endif
default: default:
return 0; return 0;
} }
} }
template <typename T> template <typename T>
T asIntegral() const { T asIntegral(const ResourceManager* resources) const {
static_assert(is_integral<T>::value, "T must be an integral type"); static_assert(is_integral<T>::value, "T must be an integral type");
(void)resources; // silence warning
switch (type_) { switch (type_) {
case VALUE_IS_BOOLEAN: case VALUE_IS_BOOLEAN:
return content_.asBoolean; return content_.asBoolean;
case VALUE_IS_UNSIGNED_INTEGER: case VALUE_IS_UINT32:
return convertNumber<T>(content_.asUnsignedInteger); return convertNumber<T>(content_.asUint32);
case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_INT32:
return convertNumber<T>(content_.asSignedInteger); return convertNumber<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VALUE_IS_UINT64:
return convertNumber<T>(getExtension(resources)->asUint64);
case VALUE_IS_INT64:
return convertNumber<T>(getExtension(resources)->asInt64);
#endif
case VALUE_IS_LINKED_STRING: case VALUE_IS_LINKED_STRING:
return parseNumber<T>(content_.asLinkedString); return parseNumber<T>(content_.asLinkedString);
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return parseNumber<T>(content_.asOwnedString->data); return parseNumber<T>(content_.asOwnedString->data);
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
return convertNumber<T>(content_.asFloat); return convertNumber<T>(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VALUE_IS_DOUBLE:
return convertNumber<T>(getExtension(resources)->asDouble);
#endif
default: default:
return 0; return 0;
} }
@ -216,6 +264,10 @@ class VariantData {
} }
} }
#if ARDUINOJSON_USE_EXTENSIONS
const VariantExtension* getExtension(const ResourceManager* resources) const;
#endif
VariantData* getElement(size_t index, VariantData* getElement(size_t index,
const ResourceManager* resources) const { const ResourceManager* resources) const {
return ArrayData::getElement(asArray(), index, resources); return ArrayData::getElement(asArray(), index, resources);
@ -274,13 +326,22 @@ class VariantData {
} }
template <typename T> template <typename T>
bool isInteger() const { bool isInteger(const ResourceManager* resources) const {
(void)resources; // silence warning
switch (type_) { switch (type_) {
case VALUE_IS_UNSIGNED_INTEGER: case VALUE_IS_UINT32:
return canConvertNumber<T>(content_.asUnsignedInteger); return canConvertNumber<T>(content_.asUint32);
case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_INT32:
return canConvertNumber<T>(content_.asSignedInteger); return canConvertNumber<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VALUE_IS_UINT64:
return canConvertNumber<T>(getExtension(resources)->asUint64);
case VALUE_IS_INT64:
return canConvertNumber<T>(getExtension(resources)->asInt64);
#endif
default: default:
return false; return false;
@ -354,25 +415,23 @@ class VariantData {
content_.asBoolean = value; content_.asBoolean = value;
} }
void setFloat(JsonFloat value) { template <typename T>
typename enable_if<sizeof(T) == 4>::type setFloat(T value, ResourceManager*) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
type_ = VALUE_IS_FLOAT; type_ = VALUE_IS_FLOAT;
content_.asFloat = value; content_.asFloat = value;
} }
template <typename T> template <typename T>
enable_if_t<is_signed<T>::value> setInteger(T value) { typename enable_if<sizeof(T) == 8>::type setFloat(T value, ResourceManager*);
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
type_ = VALUE_IS_SIGNED_INTEGER;
content_.asSignedInteger = value;
}
template <typename T> template <typename T>
enable_if_t<is_unsigned<T>::value> setInteger(T value) { enable_if_t<is_signed<T>::value> setInteger(T value,
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ResourceManager* resources);
type_ = VALUE_IS_UNSIGNED_INTEGER;
content_.asUnsignedInteger = static_cast<JsonUInt>(value); template <typename T>
} enable_if_t<is_unsigned<T>::value> setInteger(T value,
ResourceManager* resources);
void setRawString(StringNode* s) { void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first

View File

@ -44,6 +44,11 @@ inline void VariantData::clear(ResourceManager* resources) {
if (type_ & OWNED_VALUE_BIT) if (type_ & OWNED_VALUE_BIT)
resources->dereferenceString(content_.asOwnedString->data); resources->dereferenceString(content_.asOwnedString->data);
#if ARDUINOJSON_USE_EXTENSIONS
if (type_ & EXTENSION_BIT)
resources->freeExtension(content_.asSlotId);
#endif
auto collection = asCollection(); auto collection = asCollection();
if (collection) if (collection)
collection->clear(resources); collection->clear(resources);
@ -51,4 +56,82 @@ inline void VariantData::clear(ResourceManager* resources) {
type_ = VALUE_IS_NULL; type_ = VALUE_IS_NULL;
} }
#if ARDUINOJSON_USE_EXTENSIONS
inline const VariantExtension* VariantData::getExtension(
const ResourceManager* resources) const {
ARDUINOJSON_ASSERT(type_ & EXTENSION_BIT);
return resources->getExtension(content_.asSlotId);
}
#endif
template <typename T>
typename enable_if<sizeof(T) == 8>::type VariantData::setFloat(
T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
(void)resources; // silence warning
float valueAsFloat = static_cast<float>(value);
#if ARDUINOJSON_USE_DOUBLE
if (value == valueAsFloat) {
type_ = VALUE_IS_FLOAT;
content_.asFloat = valueAsFloat;
} else {
auto extension = resources->allocExtension();
if (extension) {
type_ = VALUE_IS_DOUBLE;
content_.asSlotId = extension.id();
extension->asDouble = value;
}
}
#else
type_ = VALUE_IS_FLOAT;
content_.asFloat = valueAsFloat;
#endif
}
template <typename T>
enable_if_t<is_signed<T>::value> VariantData::setInteger(
T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
(void)resources; // silence warning
if (canConvertNumber<int32_t>(value)) {
type_ = VALUE_IS_INT32;
content_.asInt32 = static_cast<int32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto extension = resources->allocExtension();
if (extension) {
type_ = VALUE_IS_INT64;
content_.asSlotId = extension.id();
extension->asInt64 = value;
}
}
#endif
}
template <typename T>
enable_if_t<is_unsigned<T>::value> VariantData::setInteger(
T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
(void)resources; // silence warning
if (canConvertNumber<uint32_t>(value)) {
type_ = VALUE_IS_UINT32;
content_.asUint32 = static_cast<uint32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto extension = resources->allocExtension();
if (extension) {
type_ = VALUE_IS_UINT64;
content_.asSlotId = extension.id();
extension->asUint64 = value;
}
}
#endif
}
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE