Compare commits

..

5 Commits

Author SHA1 Message Date
cc73fec1cf JsonObject: replace ObjectData* member with VariantData* 2025-06-15 12:55:13 +02:00
535a9b3e86 JsonArray: replace ArrayData* member with VariantData* 2025-06-15 12:35:31 +02:00
72075e9ad2 JsonObjectConst: replace ObjectData* member with VariantData* 2025-06-15 12:34:38 +02:00
56b269bea8 JsonArrayConst: replace ArrayData* member with VariantData* 2025-06-15 12:32:37 +02:00
817998d0fe Add a pool dedicated to 8-byte values (double/int64_t/uint64_t)
This new pool replaced the "extension" slot where a secondary variant slot was used to store 8-byte values.
2025-06-02 19:39:27 +02:00
23 changed files with 221 additions and 189 deletions

View File

@ -12,7 +12,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 0, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 6, "slot size");
static_assert(sizeof(ArduinoJson::detail::VariantData) == 6, "slot size");
void setup() {}
void loop() {}

View File

@ -10,7 +10,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
static_assert(sizeof(ArduinoJson::detail::VariantData) == 8, "slot size");
void setup() {}
void loop() {}

View File

@ -10,7 +10,6 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 16,
"slot size");
static_assert(sizeof(ArduinoJson::detail::VariantData) == 16, "slot size");
int main() {}

View File

@ -10,6 +10,6 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
static_assert(sizeof(ArduinoJson::detail::VariantData) == 8, "slot size");
int main() {}

View File

@ -269,10 +269,10 @@ inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
return sizeof(MemoryPool<VariantData>) * n;
}
template <typename T = ArduinoJson::detail::VariantData>
inline size_t sizeofPool(
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
using namespace ArduinoJson::detail;
return MemoryPool<VariantData>::slotsToBytes(n);
return ArduinoJson::detail::MemoryPool<T>::slotsToBytes(n);
}
inline size_t sizeofStaticStringPool(

View File

@ -8,7 +8,7 @@
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
using namespace ArduinoJson::detail;
TEST_CASE("JsonArray::add(T)") {
SpyingAllocator spy;
@ -33,7 +33,8 @@ TEST_CASE("JsonArray::add(T)") {
REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<bool>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofPool<VariantData>()),
Allocate(sizeofPool<EightByteValue>()),
});
}

View File

@ -7,7 +7,7 @@
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
using namespace ArduinoJson::detail;
TEST_CASE("deserialize JSON array") {
SpyingAllocator spy;
@ -92,8 +92,12 @@ TEST_CASE("deserialize JSON array") {
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
REQUIRE(arr[1] == -7E89);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(4)),
Allocate(sizeofPool<VariantData>()),
Allocate(sizeofPool<EightByteValue>()),
Reallocate(sizeofPool<VariantData>(),
sizeofPool<VariantData>(2)),
Reallocate(sizeofPool<EightByteValue>(),
sizeofPool<EightByteValue>(2)),
});
}

View File

@ -121,7 +121,7 @@ TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
}
}
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
TEST_CASE("deserializeJson() returns NoMemory if 8-bit slot allocation fails") {
JsonDocument doc(FailingAllocator::instance());
SECTION("uint32_t should pass") {

View File

@ -8,7 +8,7 @@
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject;
using namespace ArduinoJson::detail;
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
@ -197,11 +197,11 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true);
REQUIRE(variant.is<double>() == true);
REQUIRE(variant.as<double>() == 1.2);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
});
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool<EightByteValue>()),
Reallocate(sizeofPool<EightByteValue>(),
sizeofPool<EightByteValue>(1)),
});
}
SECTION("int32_t") {
@ -220,11 +220,11 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true);
REQUIRE(variant.is<int64_t>() == true);
REQUIRE(variant.as<int64_t>() == -2147483649LL);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
});
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool<EightByteValue>()),
Reallocate(sizeofPool<EightByteValue>(),
sizeofPool<EightByteValue>(1)),
});
}
SECTION("uint32_t") {
@ -243,11 +243,11 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true);
REQUIRE(variant.is<uint64_t>() == true);
REQUIRE(variant.as<uint64_t>() == 4294967296);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
});
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool<EightByteValue>()),
Reallocate(sizeofPool<EightByteValue>(),
sizeofPool<EightByteValue>(1)),
});
}
SECTION("JsonDocument") {
@ -378,7 +378,7 @@ TEST_CASE("JsonVariant::set() releases the previous value") {
}
SECTION("float") {
v.set(1.2);
v.set(1.2f);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
@ -393,7 +393,7 @@ TEST_CASE("JsonVariant::set() releases the previous value") {
}
}
TEST_CASE("JsonVariant::set() reuses extension slot") {
TEST_CASE("JsonVariant::set() reuses 8-bit slot") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();

View File

@ -73,7 +73,7 @@ inline bool ArrayData::addValue(const T& value, ResourceManager* resources) {
// Returns the size (in bytes) of an array with n elements.
constexpr size_t sizeofArray(size_t n) {
return n * ResourceManager::slotSize;
return n * sizeof(VariantData);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -23,7 +23,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
JsonArray() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonArray(detail::ArrayData* data, detail::ResourceManager* resources)
JsonArray(detail::VariantData* data, detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
// Returns a JsonVariant pointing to the array.
@ -37,7 +37,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Returns a read-only reference to the array.
// https://arduinojson.org/v7/api/jsonarrayconst/
operator JsonArrayConst() const {
return JsonArrayConst(data_, resources_);
return JsonArrayConst(getData(), resources_);
}
// Appends a new (empty) element to the array.
@ -55,7 +55,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
template <typename T, detail::enable_if_t<
detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() const {
return JsonVariant(detail::ArrayData::addElement(data_, resources_),
return JsonVariant(detail::VariantData::addElement(data_, resources_),
resources_);
}
@ -63,7 +63,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// https://arduinojson.org/v7/api/jsonarray/add/
template <typename T>
bool add(const T& value) const {
return detail::ArrayData::addValue(data_, value, resources_);
return detail::VariantData::addValue(data_, value, resources_);
}
// Appends a value to the array.
@ -71,15 +71,16 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
template <typename T,
detail::enable_if_t<!detail::is_const<T>::value, int> = 0>
bool add(T* value) const {
return detail::ArrayData::addValue(data_, value, resources_);
return detail::VariantData::addValue(data_, value, resources_);
}
// Returns an iterator to the first element of the array.
// https://arduinojson.org/v7/api/jsonarray/begin/
iterator begin() const {
if (!data_)
auto array = detail::VariantData::asArray(data_);
if (!array)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
return iterator(array->createIterator(resources_), resources_);
}
// Returns an iterator following the last element of the array.
@ -106,13 +107,14 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Removes the element at the specified iterator.
// https://arduinojson.org/v7/api/jsonarray/remove/
void remove(iterator it) const {
detail::ArrayData::remove(data_, it.iterator_, resources_);
detail::ArrayData::remove(detail::VariantData::asArray(data_), it.iterator_,
resources_);
}
// Removes the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/remove/
void remove(size_t index) const {
detail::ArrayData::removeElement(data_, index, resources_);
detail::VariantData::removeElement(data_, index, resources_);
}
// Removes the element at the specified index.
@ -127,7 +129,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Removes all the elements of the array.
// https://arduinojson.org/v7/api/jsonarray/clear/
void clear() const {
detail::ArrayData::clear(data_, resources_);
detail::ArrayData::clear(detail::VariantData::asArray(data_), resources_);
}
// Gets or sets the element at the specified index.
@ -150,25 +152,25 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
}
operator JsonVariantConst() const {
return JsonVariantConst(collectionToVariant(data_), resources_);
return JsonVariantConst(data_, resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonarray/isnull/
bool isNull() const {
return data_ == 0;
return !data_ || !data_->isArray();
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonarray/isnull/
operator bool() const {
return data_ != 0;
return !isNull();
}
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsonarray/nesting/
size_t nesting() const {
return detail::VariantData::nesting(collectionToVariant(data_), resources_);
return detail::VariantData::nesting(data_, resources_);
}
// Returns the number of elements in the array.
@ -205,14 +207,14 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
}
detail::VariantData* getData() const {
return collectionToVariant(data_);
return data_;
}
detail::VariantData* getOrCreateData() const {
return collectionToVariant(data_);
return data_;
}
detail::ArrayData* data_;
detail::VariantData* data_;
detail::ResourceManager* resources_;
};

View File

@ -24,9 +24,10 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// Returns an iterator to the first element of the array.
// https://arduinojson.org/v7/api/jsonarrayconst/begin/
iterator begin() const {
if (!data_)
auto array = detail::VariantData::asArray(data_);
if (!array)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
return iterator(array->createIterator(resources_), resources_);
}
// Returns an iterator to the element following the last element of the array.
@ -39,7 +40,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
JsonArrayConst() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonArrayConst(const detail::ArrayData* data,
JsonArrayConst(const detail::VariantData* data,
const detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
@ -49,7 +50,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
JsonVariantConst operator[](T index) const {
return JsonVariantConst(
detail::ArrayData::getElement(data_, size_t(index), resources_),
detail::VariantData::getElement(data_, size_t(index), resources_),
resources_);
}
@ -71,13 +72,13 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonarrayconst/isnull/
bool isNull() const {
return data_ == 0;
return !data_ || !data_->isArray();
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonarrayconst/isnull/
operator bool() const {
return data_ != 0;
return !isNull();
}
// Returns the depth (nesting level) of the array.
@ -100,10 +101,10 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
private:
const detail::VariantData* getData() const {
return collectionToVariant(data_);
return data_;
}
const detail::ArrayData* data_;
const detail::VariantData* data_;
const detail::ResourceManager* resources_;
};

View File

@ -274,9 +274,9 @@
#endif
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_DOUBLE
# define ARDUINOJSON_USE_EXTENSIONS 1
# define ARDUINOJSON_USE_8_BYTE_POOL 1
#else
# define ARDUINOJSON_USE_EXTENSIONS 0
# define ARDUINOJSON_USE_8_BYTE_POOL 0
#endif
#if defined(nullptr)

View File

@ -18,16 +18,7 @@ class VariantData;
class VariantWithId;
class ResourceManager {
union SlotData {
VariantData variant;
#if ARDUINOJSON_USE_EXTENSIONS
VariantExtension extension;
#endif
};
public:
constexpr static size_t slotSize = sizeof(SlotData);
ResourceManager(Allocator* allocator = DefaultAllocator::instance())
: allocator_(allocator), overflowed_(false) {}
@ -35,6 +26,9 @@ class ResourceManager {
stringPool_.clear(allocator_);
variantPools_.clear(allocator_);
staticStringsPools_.clear(allocator_);
#if ARDUINOJSON_USE_8_BYTE_POOL
eightBytePools_.clear(allocator_);
#endif
}
ResourceManager(const ResourceManager&) = delete;
@ -44,6 +38,9 @@ class ResourceManager {
swap(a.stringPool_, b.stringPool_);
swap(a.variantPools_, b.variantPools_);
swap(a.staticStringsPools_, b.staticStringsPools_);
#if ARDUINOJSON_USE_8_BYTE_POOL
swap(a.eightBytePools_, b.eightBytePools_);
#endif
swap_(a.allocator_, b.allocator_);
swap_(a.overflowed_, b.overflowed_);
}
@ -64,10 +61,10 @@ class ResourceManager {
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;
#if ARDUINOJSON_USE_8_BYTE_POOL
Slot<EightByteValue> allocEightByte();
void freeEightByte(SlotId slot);
EightByteValue* getEightByte(SlotId id) const;
#endif
template <typename TAdaptedString>
@ -136,19 +133,28 @@ class ResourceManager {
variantPools_.clear(allocator_);
stringPool_.clear(allocator_);
staticStringsPools_.clear(allocator_);
#if ARDUINOJSON_USE_8_BYTE_POOL
eightBytePools_.clear(allocator_);
#endif
}
void shrinkToFit() {
variantPools_.shrinkToFit(allocator_);
staticStringsPools_.shrinkToFit(allocator_);
#if ARDUINOJSON_USE_8_BYTE_POOL
eightBytePools_.shrinkToFit(allocator_);
#endif
}
private:
Allocator* allocator_;
bool overflowed_;
StringPool stringPool_;
MemoryPoolList<SlotData> variantPools_;
MemoryPoolList<VariantData> variantPools_;
MemoryPoolList<const char*> staticStringsPools_;
#if ARDUINOJSON_USE_8_BYTE_POOL
MemoryPoolList<EightByteValue> eightBytePools_;
#endif
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -12,40 +12,41 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline Slot<VariantData> ResourceManager::allocVariant() {
auto p = variantPools_.allocSlot(allocator_);
if (!p) {
auto slot = variantPools_.allocSlot(allocator_);
if (!slot) {
overflowed_ = true;
return {};
}
return {new (&p->variant) VariantData, p.id()};
new (slot.ptr()) VariantData();
return slot;
}
inline void ResourceManager::freeVariant(Slot<VariantData> variant) {
variant->clear(this);
variantPools_.freeSlot({alias_cast<SlotData*>(variant.ptr()), variant.id()});
inline void ResourceManager::freeVariant(Slot<VariantData> slot) {
slot->clear(this);
variantPools_.freeSlot(slot);
}
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) {
#if ARDUINOJSON_USE_8_BYTE_POOL
inline Slot<EightByteValue> ResourceManager::allocEightByte() {
auto slot = eightBytePools_.allocSlot(allocator_);
if (!slot) {
overflowed_ = true;
return {};
}
return {&p->extension, p.id()};
return slot;
}
inline void ResourceManager::freeExtension(SlotId id) {
auto p = getExtension(id);
variantPools_.freeSlot({reinterpret_cast<SlotData*>(p), id});
inline void ResourceManager::freeEightByte(SlotId id) {
auto p = getEightByte(id);
eightBytePools_.freeSlot({p, id});
}
inline VariantExtension* ResourceManager::getExtension(SlotId id) const {
return &variantPools_.getSlot(id)->extension;
inline EightByteValue* ResourceManager::getEightByte(SlotId id) const {
return eightBytePools_.getSlot(id);
}
#endif

View File

@ -23,7 +23,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
JsonObject() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonObject(detail::ObjectData* data, detail::ResourceManager* resource)
JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
: data_(data), resources_(resource) {}
operator JsonVariant() const {
@ -37,25 +37,25 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
}
operator JsonVariantConst() const {
return JsonVariantConst(collectionToVariant(data_), resources_);
return JsonVariantConst(data_, resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonobject/isnull/
bool isNull() const {
return data_ == 0;
return !data_ || !data_->isObject();
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonobject/isnull/
operator bool() const {
return data_ != 0;
return !isNull();
}
// Returns the depth (nesting level) of the object.
// https://arduinojson.org/v7/api/jsonobject/nesting/
size_t nesting() const {
return detail::VariantData::nesting(collectionToVariant(data_), resources_);
return detail::VariantData::nesting(data_, resources_);
}
// Returns the number of members in the object.
@ -67,9 +67,10 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Returns an iterator to the first key-value pair of the object.
// https://arduinojson.org/v7/api/jsonobject/begin/
iterator begin() const {
if (!data_)
auto obj = detail::VariantData::asObject(data_);
if (!obj)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
return iterator(obj->createIterator(resources_), resources_);
}
// Returns an iterator following the last key-value pair of the object.
@ -81,7 +82,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Removes all the members of the object.
// https://arduinojson.org/v7/api/jsonobject/clear/
void clear() const {
detail::ObjectData::clear(data_, resources_);
detail::ObjectData::clear(detail::VariantData::asObject(data_), resources_);
}
// Copies an object.
@ -131,7 +132,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Removes the member at the specified iterator.
// https://arduinojson.org/v7/api/jsonobject/remove/
FORCE_INLINE void remove(iterator it) const {
detail::ObjectData::remove(data_, it.iterator_, resources_);
detail::ObjectData::remove(detail::VariantData::asObject(data_),
it.iterator_, resources_);
}
// Removes the member with the specified key.
@ -139,8 +141,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) const {
detail::ObjectData::removeMember(data_, detail::adaptString(key),
resources_);
detail::VariantData::removeMember(data_, detail::adaptString(key),
resources_);
}
// Removes the member with the specified key.
@ -156,8 +158,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// https://arduinojson.org/v7/api/jsonobject/remove/
template <typename TChar>
FORCE_INLINE void remove(TChar* key) const {
detail::ObjectData::removeMember(data_, detail::adaptString(key),
resources_);
detail::VariantData::removeMember(data_, detail::adaptString(key),
resources_);
}
// DEPRECATED: use obj[key].is<T>() instead
@ -166,8 +168,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
bool containsKey(const TString& key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
return detail::VariantData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj["key"].is<T>() instead
@ -178,8 +180,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
return detail::VariantData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -231,14 +233,14 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
}
detail::VariantData* getData() const {
return detail::collectionToVariant(data_);
return data_;
}
detail::VariantData* getOrCreateData() const {
return detail::collectionToVariant(data_);
return data_;
}
detail::ObjectData* data_;
detail::VariantData* data_;
detail::ResourceManager* resources_;
};

View File

@ -22,7 +22,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
JsonObjectConst() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonObjectConst(const detail::ObjectData* data,
JsonObjectConst(const detail::VariantData* data,
const detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
@ -33,13 +33,13 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonobjectconst/isnull/
bool isNull() const {
return data_ == 0;
return !data_ || !data_->isObject();
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonobjectconst/isnull/
operator bool() const {
return data_ != 0;
return !isNull();
}
// Returns the depth (nesting level) of the object.
@ -57,9 +57,10 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// Returns an iterator to the first key-value pair of the object.
// https://arduinojson.org/v7/api/jsonobjectconst/begin/
iterator begin() const {
if (!data_)
auto obj = detail::VariantData::asObject(data_);
if (!obj)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
return iterator(obj->createIterator(resources_), resources_);
}
// Returns an iterator following the last key-value pair of the object.
@ -74,8 +75,8 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
bool containsKey(const TString& key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
return detail::VariantData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj["key"].is<T>() instead
@ -83,8 +84,8 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
template <typename TChar>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
return detail::VariantData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -101,7 +102,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(detail::ObjectData::getMember(
return JsonVariantConst(detail::VariantData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
@ -113,7 +114,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
!detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(detail::ObjectData::getMember(
return JsonVariantConst(detail::VariantData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
@ -137,10 +138,10 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
private:
const detail::VariantData* getData() const {
return collectionToVariant(data_);
return data_;
}
const detail::ObjectData* data_;
const detail::VariantData* data_;
const detail::ResourceManager* resources_;
};

View File

@ -86,7 +86,7 @@ inline VariantData* ObjectData::addPair(VariantData** value,
// Returns the size (in bytes) of an object with n members.
constexpr size_t sizeofObject(size_t n) {
return 2 * n * ResourceManager::slotSize;
return 2 * n * sizeof(VariantData);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -329,9 +329,7 @@ struct Converter<JsonArrayConst> : private detail::VariantAttorney {
}
static JsonArrayConst fromJson(JsonVariantConst src) {
auto data = getData(src);
auto array = data ? data->asArray() : nullptr;
return JsonArrayConst(array, getResourceManager(src));
return JsonArrayConst(getData(src), getResourceManager(src));
}
static bool checkJson(JsonVariantConst src) {
@ -350,9 +348,7 @@ struct Converter<JsonArray> : private detail::VariantAttorney {
}
static JsonArray fromJson(JsonVariant src) {
auto data = getData(src);
auto resources = getResourceManager(src);
return JsonArray(data != 0 ? data->asArray() : 0, resources);
return JsonArray(getData(src), getResourceManager(src));
}
static bool checkJson(JsonVariant src) {
@ -371,9 +367,7 @@ struct Converter<JsonObjectConst> : private detail::VariantAttorney {
}
static JsonObjectConst fromJson(JsonVariantConst src) {
auto data = getData(src);
auto object = data != 0 ? data->asObject() : nullptr;
return JsonObjectConst(object, getResourceManager(src));
return JsonObjectConst(getData(src), getResourceManager(src));
}
static bool checkJson(JsonVariantConst src) {
@ -392,9 +386,7 @@ struct Converter<JsonObject> : private detail::VariantAttorney {
}
static JsonObject fromJson(JsonVariant src) {
auto data = getData(src);
auto resources = getResourceManager(src);
return JsonObject(data != 0 ? data->asObject() : 0, resources);
return JsonObject(getData(src), getResourceManager(src));
}
static bool checkJson(JsonVariant src) {

View File

@ -30,11 +30,13 @@ class VisitorAdapter {
: visitor_(&visitor), resources_(resources) {}
result_type visit(const ArrayData& value) {
return visitor_->visit(JsonArrayConst(&value, resources_));
return visitor_->visit(
JsonArrayConst(collectionToVariant(&value), resources_));
}
result_type visit(const ObjectData& value) {
return visitor_->visit(JsonObjectConst(&value, resources_));
return visitor_->visit(
JsonObjectConst(collectionToVariant(&value), resources_));
}
template <typename T>

View File

@ -16,8 +16,8 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
enum class VariantTypeBits : uint8_t {
OwnedStringBit = 0x01, // 0000 0001
NumberBit = 0x08, // 0000 1000
#if ARDUINOJSON_USE_EXTENSIONS
ExtensionBit = 0x10, // 0001 0000
#if ARDUINOJSON_USE_8_BYTE_POOL
EightByteBit = 0x10, // 0001 0000
#endif
CollectionMask = 0x60,
};
@ -64,8 +64,8 @@ union VariantContent {
char asTinyString[tinyStringMaxLength + 1];
};
#if ARDUINOJSON_USE_EXTENSIONS
union VariantExtension {
#if ARDUINOJSON_USE_8_BYTE_POOL
union EightByteValue {
# if ARDUINOJSON_USE_LONG_LONG
uint64_t asUint64;
int64_t asInt64;
@ -74,6 +74,9 @@ union VariantExtension {
double asDouble;
# endif
};
static_assert(sizeof(EightByteValue) == 8,
"sizeof(EightByteValue) must be 8 bytes");
#endif
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -53,8 +53,8 @@ class VariantData {
template <typename TVisitor>
typename TVisitor::result_type accept(
TVisitor& visit, const ResourceManager* resources) const {
#if ARDUINOJSON_USE_EXTENSIONS
auto extension = getExtension(resources);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
@ -64,7 +64,7 @@ class VariantData {
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return visit.visit(extension->asDouble);
return visit.visit(eightByteValue->asDouble);
#endif
case VariantType::Array:
@ -95,10 +95,10 @@ class VariantData {
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Int64:
return visit.visit(extension->asInt64);
return visit.visit(eightByteValue->asInt64);
case VariantType::Uint64:
return visit.visit(extension->asUint64);
return visit.visit(eightByteValue->asUint64);
#endif
case VariantType::Boolean:
@ -145,8 +145,8 @@ class VariantData {
}
bool asBoolean(const ResourceManager* resources) const {
#if ARDUINOJSON_USE_EXTENSIONS
auto extension = getExtension(resources);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
@ -160,14 +160,14 @@ class VariantData {
return content_.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return extension->asDouble != 0;
return eightByteValue->asDouble != 0;
#endif
case VariantType::Null:
return false;
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
case VariantType::Int64:
return extension->asUint64 != 0;
return eightByteValue->asUint64 != 0;
#endif
default:
return true;
@ -182,6 +182,14 @@ class VariantData {
return const_cast<VariantData*>(this)->asArray();
}
static ArrayData* asArray(VariantData* var) {
return var ? var->asArray() : 0;
}
static const ArrayData* asArray(const VariantData* var) {
return var ? var->asArray() : 0;
}
CollectionData* asCollection() {
return isCollection() ? &content_.asCollection : 0;
}
@ -193,8 +201,8 @@ class VariantData {
template <typename T>
T asFloat(const ResourceManager* resources) const {
static_assert(is_floating_point<T>::value, "T must be a floating point");
#if ARDUINOJSON_USE_EXTENSIONS
auto extension = getExtension(resources);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
@ -208,9 +216,9 @@ class VariantData {
return static_cast<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return static_cast<T>(extension->asUint64);
return static_cast<T>(eightByteValue->asUint64);
case VariantType::Int64:
return static_cast<T>(extension->asInt64);
return static_cast<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = content_.asTinyString;
@ -225,7 +233,7 @@ class VariantData {
return static_cast<T>(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return static_cast<T>(extension->asDouble);
return static_cast<T>(eightByteValue->asDouble);
#endif
default:
return 0.0;
@ -238,8 +246,8 @@ class VariantData {
template <typename T>
T asIntegral(const ResourceManager* resources) const {
static_assert(is_integral<T>::value, "T must be an integral type");
#if ARDUINOJSON_USE_EXTENSIONS
auto extension = getExtension(resources);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
@ -253,9 +261,9 @@ class VariantData {
return convertNumber<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return convertNumber<T>(extension->asUint64);
return convertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return convertNumber<T>(extension->asInt64);
return convertNumber<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = content_.asTinyString;
@ -270,7 +278,7 @@ class VariantData {
return convertNumber<T>(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return convertNumber<T>(extension->asDouble);
return convertNumber<T>(eightByteValue->asDouble);
#endif
default:
return 0;
@ -288,6 +296,14 @@ class VariantData {
return const_cast<VariantData*>(this)->asObject();
}
static ObjectData* asObject(VariantData* var) {
return var ? var->asObject() : 0;
}
static const ObjectData* asObject(const VariantData* var) {
return var ? var->asObject() : 0;
}
JsonString asRawString() const {
switch (type_) {
case VariantType::RawString:
@ -314,8 +330,8 @@ class VariantData {
}
}
#if ARDUINOJSON_USE_EXTENSIONS
const VariantExtension* getExtension(const ResourceManager* resources) const;
#if ARDUINOJSON_USE_8_BYTE_POOL
const EightByteValue* getEightByte(const ResourceManager* resources) const;
#endif
VariantData* getElement(size_t index,
@ -378,7 +394,7 @@ class VariantData {
template <typename T>
bool isInteger(const ResourceManager* resources) const {
#if ARDUINOJSON_USE_LONG_LONG
auto extension = getExtension(resources);
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
@ -391,10 +407,10 @@ class VariantData {
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return canConvertNumber<T>(extension->asUint64);
return canConvertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return canConvertNumber<T>(extension->asInt64);
return canConvertNumber<T>(eightByteValue->asInt64);
#endif
default:
@ -568,11 +584,12 @@ class VariantData {
return content_.asArray;
}
static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
static VariantData* toArray(VariantData* var, ResourceManager* resources) {
if (!var)
return 0;
var->clear(resources);
return &var->toArray();
var->toArray();
return var;
}
ObjectData& toObject() {
@ -582,11 +599,12 @@ class VariantData {
return content_.asObject;
}
static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
static VariantData* toObject(VariantData* var, ResourceManager* resources) {
if (!var)
return 0;
var->clear(resources);
return &var->toObject();
var->toObject();
return var;
}
VariantType type() const {

View File

@ -61,9 +61,9 @@ inline void VariantData::clear(ResourceManager* resources) {
if (type_ & VariantTypeBits::OwnedStringBit)
resources->dereferenceString(content_.asOwnedString->data);
#if ARDUINOJSON_USE_EXTENSIONS
if (type_ & VariantTypeBits::ExtensionBit)
resources->freeExtension(content_.asSlotId);
#if ARDUINOJSON_USE_8_BYTE_POOL
if (type_ & VariantTypeBits::EightByteBit)
resources->freeEightByte(content_.asSlotId);
#endif
auto collection = asCollection();
@ -73,12 +73,12 @@ inline void VariantData::clear(ResourceManager* resources) {
type_ = VariantType::Null;
}
#if ARDUINOJSON_USE_EXTENSIONS
inline const VariantExtension* VariantData::getExtension(
#if ARDUINOJSON_USE_8_BYTE_POOL
inline const EightByteValue* VariantData::getEightByte(
const ResourceManager* resources) const {
return type_ & VariantTypeBits::ExtensionBit
? resources->getExtension(content_.asSlotId)
: nullptr;
return type_ & VariantTypeBits::EightByteBit
? resources->getEightByte(content_.asSlotId)
: 0;
}
#endif
@ -101,12 +101,12 @@ enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
type_ = VariantType::Float;
content_.asFloat = valueAsFloat;
} else {
auto extension = resources->allocExtension();
if (!extension)
auto slot = resources->allocEightByte();
if (!slot)
return false;
type_ = VariantType::Double;
content_.asSlotId = extension.id();
extension->asDouble = value;
content_.asSlotId = slot.id();
slot->asDouble = value;
}
#else
type_ = VariantType::Float;
@ -127,12 +127,12 @@ enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto extension = resources->allocExtension();
if (!extension)
auto slot = resources->allocEightByte();
if (!slot)
return false;
type_ = VariantType::Int64;
content_.asSlotId = extension.id();
extension->asInt64 = value;
content_.asSlotId = slot.id();
slot->asInt64 = value;
}
#endif
return true;
@ -150,12 +150,12 @@ enable_if_t<is_unsigned<T>::value, bool> VariantData::setInteger(
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto extension = resources->allocExtension();
if (!extension)
auto slot = resources->allocEightByte();
if (!slot)
return false;
type_ = VariantType::Uint64;
content_.asSlotId = extension.id();
extension->asUint64 = value;
content_.asSlotId = slot.id();
slot->asUint64 = value;
}
#endif
return true;