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 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)
------

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(sizeof(ArduinoJson::detail::VariantData) == 16,
static_assert(sizeof(ArduinoJson::detail::VariantData) == 8,
"sizeof(VariantData)");
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(sizeof(ArduinoJson::detail::VariantData) == 12,
static_assert(sizeof(ArduinoJson::detail::VariantData) == 8,
"sizeof(VariantData)");
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(sizeof(ArduinoJson::detail::VariantData) == 16,
static_assert(sizeof(ArduinoJson::detail::VariantData) == 8,
"sizeof(VariantData)");
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);
REQUIRE(err == DeserializationError::Ok);
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
REQUIRE(doc.as<JsonInteger>() == 0x100000000);
#else
REQUIRE(doc.isNull());
#endif
}
}
}

View File

@ -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

View File

@ -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:

View File

@ -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();

View File

@ -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(": ");

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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

View File

@ -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>

View File

@ -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) {

View File

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

View File

@ -24,10 +24,21 @@ enum {
VALUE_IS_BOOLEAN = 0x06,
NUMBER_BIT = 0x08,
VALUE_IS_UNSIGNED_INTEGER = 0x08,
VALUE_IS_SIGNED_INTEGER = 0x0A,
VALUE_IS_FLOAT = 0x0C,
NUMBER_BIT = 0x08, // 0000 1000
VALUE_IS_UINT32 = 0x0A, // 0000 1010
VALUE_IS_INT32 = 0x0C, // 0000 1100
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,
VALUE_IS_OBJECT = 0x20,
@ -37,10 +48,13 @@ enum {
union VariantContent {
VariantContent() {}
JsonFloat asFloat;
float asFloat;
bool asBoolean;
JsonUInt asUnsignedInteger;
JsonInteger asSignedInteger;
uint32_t asUint32;
int32_t asInt32;
#if ARDUINOJSON_USE_EXTENSIONS
SlotId asSlotId;
#endif
ArrayData asArray;
ObjectData asObject;
CollectionData asCollection;
@ -48,4 +62,16 @@ union VariantContent {
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

View File

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