Compare commits

..

11 Commits

Author SHA1 Message Date
53f858f55c Remove SlotData 2025-06-02 15:57:34 +02:00
6cdd59034d Clang-format 2025-05-31 18:35:36 +02:00
6ccac18fd4 Fix comments 2025-05-31 18:34:07 +02:00
3f6fff7d0c Fix JsonVariantTests 2025-05-31 16:29:46 +00:00
cc2fea45cd All tests pass 2025-05-31 16:47:03 +02:00
4159af66eb JsonDeserializerTests pass 2025-05-31 16:41:09 +02:00
05942b620a JsonVariantTests pass 2025-05-31 16:38:36 +02:00
23c73b061e JsonArrayTests pass 2025-05-31 16:37:08 +02:00
c2e756d942 ResourceManagerTests pass 2025-05-31 16:31:31 +02:00
94aacf873e Replace extension with eight-byte values 2025-05-31 16:20:08 +02:00
bd2dccda0e Store static strings in a dedicated pool
Because a slot id is smaller than a pointer, this change will ultimately allow reducing the slot size.
2025-05-21 11:09:44 +02:00
54 changed files with 1317 additions and 1164 deletions

View File

@ -179,19 +179,19 @@ jobs:
conf_test_windows:
name: Test configuration on Windows
runs-on: windows-2022
runs-on: windows-2019
needs: [gcc, clang]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: 32-bit
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
cl /Isrc extras/conf_test/x86.cpp
shell: cmd
- name: 64-bit
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
cl /Isrc extras/conf_test/x64.cpp
shell: cmd

View File

@ -38,11 +38,6 @@ HEAD
> doc["key"] = str; // same as previous line for supported string classes (`String`, `std::string`, etc.)
> ```
v7.4.2 (2025-06-20)
------
* Fix truncated strings on Arduino Due (issue #2181)
v7.4.1 (2025-04-11)
------

View File

@ -10,7 +10,7 @@ if(ESP_PLATFORM)
return()
endif()
project(ArduinoJson VERSION 7.4.2)
project(ArduinoJson VERSION 7.4.1)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)

View File

@ -139,6 +139,11 @@ See the [tutorial on arduinojson.org](https://arduinojson.org/v7/doc/serializati
ArduinoJson is thankful to its sponsors. Please give them a visit; they deserve it!
<p>
<a href="https://www.programmingelectronics.com/" rel="sponsored">
<img src="https://arduinojson.org/images/2021/10/programmingeleactronicsacademy.png" alt="Programming Electronics Academy" width="200">
</a>
</p>
<p>
<a href="https://github.com/1technophile" rel="sponsored">
<img alt="1technophile" src="https://avatars.githubusercontent.com/u/12672732?s=40&v=4">

View File

@ -1,4 +1,4 @@
version: 7.4.2.{build}
version: 7.4.1.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022

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(sizeof(ArduinoJson::detail::VariantData) == 8, "slot size");
static_assert(sizeof(VariantData) == 8, "slot size");
void setup() {}
void loop() {}

View File

@ -29,23 +29,3 @@ set_tests_properties(Misc
PROPERTIES
LABELS "Catch"
)
add_executable(Issue2181
issue2181.cpp # Cannot be linked with other tests
)
set_target_properties(Issue2181 PROPERTIES UNITY_BUILD OFF)
add_test(Issue2181 Issue2181)
set_tests_properties(Issue2181
PROPERTIES
LABELS "Catch"
)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(Issue2181
PRIVATE
-Wno-keyword-macro # keyword is hidden by macro definition
)
endif()

View File

@ -1,15 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define true 0x1
#define false 0x0
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Issue #2181") {
JsonDocument doc;
doc["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}

View File

@ -21,8 +21,8 @@ TEST_CASE("StringBuffer") {
strcpy(ptr, "hi!");
sb.save(&variant);
REQUIRE(variant.type == VariantType::TinyString);
REQUIRE(VariantImpl(&variant, &resources).asString() == "hi!");
REQUIRE(variant.type() == VariantType::TinyString);
REQUIRE(variant.asString(&resources) == "hi!");
}
SECTION("Tiny string can't contain NUL") {
@ -30,9 +30,9 @@ TEST_CASE("StringBuffer") {
memcpy(ptr, "a\0b", 3);
sb.save(&variant);
REQUIRE(variant.type == VariantType::OwnedString);
REQUIRE(variant.type() == VariantType::OwnedString);
auto str = VariantImpl(&variant, &resources).asString();
auto str = variant.asString(&resources);
REQUIRE(str.size() == 3);
REQUIRE(str.c_str()[0] == 'a');
REQUIRE(str.c_str()[1] == 0);
@ -44,7 +44,7 @@ TEST_CASE("StringBuffer") {
strcpy(ptr, "alfa");
sb.save(&variant);
REQUIRE(variant.type == VariantType::OwnedString);
REQUIRE(VariantImpl(&variant, &resources).asString() == "alfa");
REQUIRE(variant.type() == VariantType::OwnedString);
REQUIRE(variant.asString(&resources) == "alfa");
}
}

View File

@ -26,7 +26,7 @@ TEST_CASE("StringBuilder") {
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofStringBuffer()),
});
REQUIRE(data.type == VariantType::TinyString);
REQUIRE(data.type() == VariantType::TinyString);
}
SECTION("Tiny string") {
@ -45,8 +45,8 @@ TEST_CASE("StringBuilder") {
str.save(&data);
REQUIRE(resources.overflowed() == false);
REQUIRE(data.type == VariantType::TinyString);
REQUIRE(VariantImpl(&data, &resources).asString() == "url");
REQUIRE(data.type() == VariantType::TinyString);
REQUIRE(data.asString(&resources) == "url");
}
SECTION("Short string fits in first allocation") {
@ -134,10 +134,10 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
auto s2 = saveString(builder, "world");
auto s3 = saveString(builder, "hello");
REQUIRE(VariantImpl(&s1, &resources).asString() == "hello");
REQUIRE(VariantImpl(&s2, &resources).asString() == "world");
REQUIRE(+VariantImpl(&s1, &resources).asString().c_str() ==
+VariantImpl(&s3, &resources).asString().c_str()); // same address
REQUIRE(s1.asString(&resources) == "hello");
REQUIRE(s2.asString(&resources) == "world");
REQUIRE(+s1.asString(&resources).c_str() ==
+s3.asString(&resources).c_str()); // same address
REQUIRE(spy.log() ==
AllocatorLog{
@ -153,11 +153,10 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
auto s1 = saveString(builder, "hello world");
auto s2 = saveString(builder, "hello");
REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
REQUIRE(VariantImpl(&s2, &resources).asString() == "hello");
REQUIRE(
+VariantImpl(&s1, &resources).asString().c_str() !=
+VariantImpl(&s2, &resources).asString().c_str()); // different address
REQUIRE(s1.asString(&resources) == "hello world");
REQUIRE(s2.asString(&resources) == "hello");
REQUIRE(+s2.asString(&resources).c_str() !=
+s1.asString(&resources).c_str()); // different address
REQUIRE(spy.log() ==
AllocatorLog{
@ -172,11 +171,10 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
auto s1 = saveString(builder, "hello world");
auto s2 = saveString(builder, "worl");
REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
REQUIRE(VariantImpl(&s2, &resources).asString() == "worl");
REQUIRE(
VariantImpl(&s1, &resources).asString().c_str() !=
VariantImpl(&s2, &resources).asString().c_str()); // different address
REQUIRE(s1.asString(&resources) == "hello world");
REQUIRE(s2.asString(&resources) == "worl");
REQUIRE(s2.asString(&resources).c_str() !=
s1.asString(&resources).c_str()); // different address
REQUIRE(spy.log() ==
AllocatorLog{

View File

@ -1,7 +1,7 @@
version: "7.4.2"
version: "7.4.1"
description: >-
A simple and efficient JSON library for embedded C++.
★ 6953 stars on GitHub!
★ 6898 stars on GitHub!
Supports serialization, deserialization, MessagePack, streams, filtering, and more.
Fully tested and documented.
url: https://arduinojson.org/

View File

@ -1,13 +1,13 @@
{
"name": "ArduinoJson",
"keywords": "json, rest, http, web",
"description": "A simple and efficient JSON library for embedded C++. ⭐ 6953 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
"description": "A simple and efficient JSON library for embedded C++. ⭐ 6898 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
"homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json",
"repository": {
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "7.4.2",
"version": "7.4.1",
"authors": {
"name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr"

View File

@ -1,9 +1,9 @@
name=ArduinoJson
version=7.4.2
version=7.4.1
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.
paragraph=⭐ 6953 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.
paragraph=⭐ 6898 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.
category=Data Processing
url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties
architectures=*

View File

@ -26,15 +26,6 @@
# endif
#endif
// Remove true and false macros defined by some cores, such as Arduino Due's
// See issues #2181 and arduino/ArduinoCore-sam#50
#ifdef true
# undef true
#endif
#ifdef false
# undef false
#endif
#include "ArduinoJson/Array/JsonArray.hpp"
#include "ArduinoJson/Object/JsonObject.hpp"
#include "ArduinoJson/Variant/JsonVariantConst.hpp"
@ -51,6 +42,7 @@
#include "ArduinoJson/Variant/ConverterImpl.hpp"
#include "ArduinoJson/Variant/JsonVariantCopier.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp"

View File

@ -8,30 +8,59 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class ArrayImpl : public CollectionImpl {
class ArrayData : public CollectionData {
public:
ArrayImpl() {}
VariantData* addElement(ResourceManager* resources);
ArrayImpl(CollectionData* data, ResourceManager* resources)
: CollectionImpl(data, resources) {}
VariantData* addElement();
static VariantData* addElement(ArrayData* array, ResourceManager* resources) {
if (!array)
return nullptr;
return array->addElement(resources);
}
template <typename T>
bool addValue(const T& value);
bool addValue(const T& value, ResourceManager* resources);
VariantData* getOrAddElement(size_t index);
template <typename T>
static bool addValue(ArrayData* array, const T& value,
ResourceManager* resources) {
if (!array)
return false;
return array->addValue(value, resources);
}
VariantData* getElement(size_t index) const;
VariantData* getOrAddElement(size_t index, ResourceManager* resources);
void removeElement(size_t index);
VariantData* getElement(size_t index, const ResourceManager* resources) const;
void remove(iterator it) {
CollectionImpl::removeOne(it);
static VariantData* getElement(const ArrayData* array, size_t index,
const ResourceManager* resources) {
if (!array)
return nullptr;
return array->getElement(index, resources);
}
void removeElement(size_t index, ResourceManager* resources);
static void removeElement(ArrayData* array, size_t index,
ResourceManager* resources) {
if (!array)
return;
array->removeElement(index, resources);
}
void remove(iterator it, ResourceManager* resources) {
CollectionData::removeOne(it, resources);
}
static void remove(ArrayData* array, iterator it,
ResourceManager* resources) {
if (array)
return array->remove(it, resources);
}
private:
iterator at(size_t index) const;
iterator at(size_t index, const ResourceManager* resources) const;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -10,37 +10,36 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline ArrayImpl::iterator ArrayImpl::at(size_t index) const {
auto it = createIterator();
inline ArrayData::iterator ArrayData::at(
size_t index, const ResourceManager* resources) const {
auto it = createIterator(resources);
while (!it.done() && index) {
it.next(resources_);
it.next(resources);
--index;
}
return it;
}
inline VariantData* ArrayImpl::addElement() {
if (!data_)
return nullptr;
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto slot = resources_->allocVariant();
inline VariantData* ArrayData::addElement(ResourceManager* resources) {
auto slot = resources->allocVariant();
if (!slot)
return nullptr;
CollectionImpl::appendOne(slot);
CollectionData::appendOne(slot, resources);
return slot.ptr();
}
inline VariantData* ArrayImpl::getOrAddElement(size_t index) {
auto it = createIterator();
inline VariantData* ArrayData::getOrAddElement(size_t index,
ResourceManager* resources) {
auto it = createIterator(resources);
while (!it.done() && index > 0) {
it.next(resources_);
it.next(resources);
index--;
}
if (it.done())
index++;
VariantData* element = it.data();
while (index > 0) {
element = addElement();
element = addElement(resources);
if (!element)
return nullptr;
index--;
@ -48,28 +47,27 @@ inline VariantData* ArrayImpl::getOrAddElement(size_t index) {
return element;
}
inline VariantData* ArrayImpl::getElement(size_t index) const {
return at(index).data();
inline VariantData* ArrayData::getElement(
size_t index, const ResourceManager* resources) const {
return at(index, resources).data();
}
inline void ArrayImpl::removeElement(size_t index) {
remove(at(index));
inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
remove(at(index, resources), resources);
}
template <typename T>
inline bool ArrayImpl::addValue(const T& value) {
if (!data_)
return false;
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto slot = resources_->allocVariant();
inline bool ArrayData::addValue(const T& value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(resources != nullptr);
auto slot = resources->allocVariant();
if (!slot)
return false;
JsonVariant variant(slot.ptr(), resources_);
JsonVariant variant(slot.ptr(), resources);
if (!variant.set(value)) {
resources_->freeVariant(slot);
resources->freeVariant(slot);
return false;
}
CollectionImpl::appendOne(slot);
CollectionData::appendOne(slot, resources);
return true;
}

View File

@ -55,12 +55,17 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
}
FORCE_INLINE VariantData* getData() const {
return VariantAttorney::getVariantImpl(upstream_).getElement(index_);
return VariantData::getElement(
VariantAttorney::getData(upstream_), index_,
VariantAttorney::getResourceManager(upstream_));
}
VariantData* getOrCreateData() const {
return VariantAttorney::getOrCreateVariantImpl(upstream_).getOrAddElement(
index_);
auto data = VariantAttorney::getOrCreateData(upstream_);
if (!data)
return nullptr;
return data->getOrAddElement(
index_, VariantAttorney::getResourceManager(upstream_));
}
TUpstream upstream_;

View File

@ -20,25 +20,24 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
using iterator = JsonArrayIterator;
// Constructs an unbound reference.
JsonArray() {}
JsonArray() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonArray(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(detail::VariantImpl(data, resources).asArray()) {}
// INTERNAL USE ONLY
JsonArray(const detail::ArrayImpl& impl) : impl_(impl) {}
JsonArray(detail::ArrayData* data, detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
// Returns a JsonVariant pointing to the array.
// https://arduinojson.org/v7/api/jsonvariant/
operator JsonVariant() {
return JsonVariant(getData(), getResourceManager());
void* data = data_; // prevent warning cast-align
return JsonVariant(reinterpret_cast<detail::VariantData*>(data),
resources_);
}
// Returns a read-only reference to the array.
// https://arduinojson.org/v7/api/jsonarrayconst/
operator JsonArrayConst() const {
return JsonArrayConst(getData(), getResourceManager());
return JsonArrayConst(data_, resources_);
}
// Appends a new (empty) element to the array.
@ -56,14 +55,15 @@ 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(impl_.addElement(), impl_.getResourceManager());
return JsonVariant(detail::ArrayData::addElement(data_, resources_),
resources_);
}
// Appends a value to the array.
// https://arduinojson.org/v7/api/jsonarray/add/
template <typename T>
bool add(const T& value) const {
return impl_.addValue(value);
return detail::ArrayData::addValue(data_, value, resources_);
}
// Appends a value to the array.
@ -71,13 +71,15 @@ 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 impl_.addValue(value);
return detail::ArrayData::addValue(data_, value, resources_);
}
// Returns an iterator to the first element of the array.
// https://arduinojson.org/v7/api/jsonarray/begin/
iterator begin() const {
return iterator(impl_.createIterator(), impl_.getResourceManager());
if (!data_)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
}
// Returns an iterator following the last element of the array.
@ -89,7 +91,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Copies an array.
// https://arduinojson.org/v7/api/jsonarray/set/
bool set(JsonArrayConst src) const {
if (isNull())
if (!data_)
return false;
clear();
@ -104,13 +106,13 @@ 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 {
impl_.remove(it.iterator_);
detail::ArrayData::remove(data_, it.iterator_, resources_);
}
// Removes the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/remove/
void remove(size_t index) const {
impl_.removeElement(index);
detail::ArrayData::removeElement(data_, index, resources_);
}
// Removes the element at the specified index.
@ -125,7 +127,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Removes all the elements of the array.
// https://arduinojson.org/v7/api/jsonarray/clear/
void clear() const {
impl_.clear();
detail::ArrayData::clear(data_, resources_);
}
// Gets or sets the element at the specified index.
@ -148,31 +150,31 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
}
operator JsonVariantConst() const {
return JsonVariantConst(getData(), getResourceManager());
return JsonVariantConst(collectionToVariant(data_), resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonarray/isnull/
bool isNull() const {
return impl_.isNull();
return data_ == 0;
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonarray/isnull/
operator bool() const {
return !isNull();
return data_ != 0;
}
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsonarray/nesting/
size_t nesting() const {
return impl_.nesting();
return detail::VariantData::nesting(collectionToVariant(data_), resources_);
}
// Returns the number of elements in the array.
// https://arduinojson.org/v7/api/jsonarray/size/
size_t size() const {
return impl_.size();
return data_ ? data_->size(resources_) : 0;
}
// DEPRECATED: use add<JsonVariant>() instead
@ -199,18 +201,19 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
private:
detail::ResourceManager* getResourceManager() const {
return impl_.getResourceManager();
return resources_;
}
detail::VariantData* getData() const {
return impl_.getData();
return collectionToVariant(data_);
}
detail::VariantData* getOrCreateData() const {
return impl_.getData();
return collectionToVariant(data_);
}
mutable detail::ArrayImpl impl_;
detail::ArrayData* data_;
detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -24,7 +24,9 @@ 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 {
return iterator(impl_.createIterator(), impl_.getResourceManager());
if (!data_)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
}
// Returns an iterator to the element following the last element of the array.
@ -34,22 +36,21 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
}
// Creates an unbound reference.
JsonArrayConst() {}
JsonArrayConst() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonArrayConst(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(detail::VariantImpl(data, resources).asArray()) {}
// INTERNAL USE ONLY
JsonArrayConst(const detail::ArrayImpl& impl) : impl_(impl) {}
JsonArrayConst(const detail::ArrayData* data,
const detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
// Returns the element at the specified index.
// https://arduinojson.org/v7/api/jsonarrayconst/subscript/
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
JsonVariantConst operator[](T index) const {
return JsonVariantConst(impl_.getElement(size_t(index)),
impl_.getResourceManager());
return JsonVariantConst(
detail::ArrayData::getElement(data_, size_t(index), resources_),
resources_);
}
// Returns the element at the specified index.
@ -64,31 +65,31 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
}
operator JsonVariantConst() const {
return JsonVariantConst(impl_.getData(), impl_.getResourceManager());
return JsonVariantConst(getData(), resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonarrayconst/isnull/
bool isNull() const {
return impl_.isNull();
return data_ == 0;
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonarrayconst/isnull/
operator bool() const {
return !isNull();
return data_ != 0;
}
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsonarrayconst/nesting/
size_t nesting() const {
return impl_.nesting();
return detail::VariantData::nesting(getData(), resources_);
}
// Returns the number of elements in the array.
// https://arduinojson.org/v7/api/jsonarrayconst/size/
size_t size() const {
return impl_.size();
return data_ ? data_->size(resources_) : 0;
}
// DEPRECATED: always returns zero
@ -99,10 +100,11 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
private:
const detail::VariantData* getData() const {
return impl_.getData();
return collectionToVariant(data_);
}
detail::ArrayImpl impl_;
const detail::ArrayData* data_;
const detail::ResourceManager* resources_;
};
// Compares the content of two arrays.

View File

@ -30,7 +30,7 @@ class JsonArrayIterator {
public:
JsonArrayIterator() {}
explicit JsonArrayIterator(detail::ArrayImpl::iterator iterator,
explicit JsonArrayIterator(detail::ArrayData::iterator iterator,
detail::ResourceManager* resources)
: iterator_(iterator), resources_(resources) {}
@ -55,7 +55,7 @@ class JsonArrayIterator {
}
private:
detail::ArrayImpl::iterator iterator_;
detail::ArrayData::iterator iterator_;
detail::ResourceManager* resources_;
};
@ -64,8 +64,8 @@ class JsonArrayConstIterator {
public:
JsonArrayConstIterator() {}
explicit JsonArrayConstIterator(detail::ArrayImpl::iterator iterator,
detail::ResourceManager* resources)
explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator,
const detail::ResourceManager* resources)
: iterator_(iterator), resources_(resources) {}
JsonVariantConst operator*() const {
@ -89,8 +89,8 @@ class JsonArrayConstIterator {
}
private:
mutable detail::ArrayImpl::iterator iterator_;
mutable detail::ResourceManager* resources_;
detail::ArrayData::iterator iterator_;
const detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -12,11 +12,11 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
struct VariantData;
class VariantData;
class ResourceManager;
class CollectionIterator {
friend class CollectionImpl;
friend class CollectionData;
public:
CollectionIterator() : slot_(nullptr), currentId_(NULL_SLOT) {}
@ -51,11 +51,11 @@ class CollectionIterator {
}
VariantData* data() {
return slot_;
return reinterpret_cast<VariantData*>(slot_);
}
const VariantData* data() const {
return slot_;
return reinterpret_cast<const VariantData*>(slot_);
}
private:
@ -65,68 +65,58 @@ class CollectionIterator {
SlotId currentId_, nextId_;
};
struct CollectionData {
SlotId head = NULL_SLOT;
SlotId tail = NULL_SLOT;
class CollectionData {
SlotId head_ = NULL_SLOT;
SlotId tail_ = NULL_SLOT;
public:
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
};
class CollectionImpl {
protected:
CollectionData* data_;
ResourceManager* resources_;
public:
using iterator = CollectionIterator;
CollectionImpl() : data_(nullptr), resources_(nullptr) {}
iterator createIterator(const ResourceManager* resources) const;
CollectionImpl(CollectionData* data, ResourceManager* resources)
: data_(data), resources_(resources) {}
size_t size(const ResourceManager*) const;
size_t nesting(const ResourceManager*) const;
explicit operator bool() const {
return data_ != nullptr;
void clear(ResourceManager* resources);
static void clear(CollectionData* collection, ResourceManager* resources) {
if (!collection)
return;
collection->clear(resources);
}
bool isNull() const {
return data_ == nullptr;
}
VariantData* getData() const {
void* data = data_; // prevent warning cast-align
return reinterpret_cast<VariantData*>(data);
}
ResourceManager* getResourceManager() const {
return resources_;
}
iterator createIterator() const;
size_t size() const;
size_t nesting() const;
void clear();
SlotId head() const {
return data_->head;
return head_;
}
protected:
void appendOne(Slot<VariantData> slot);
void appendPair(Slot<VariantData> key, Slot<VariantData> value);
void appendOne(Slot<VariantData> slot, const ResourceManager* resources);
void appendPair(Slot<VariantData> key, Slot<VariantData> value,
const ResourceManager* resources);
void removeOne(iterator it);
void removePair(iterator it);
void removeOne(iterator it, ResourceManager* resources);
void removePair(iterator it, ResourceManager* resources);
private:
Slot<VariantData> getPreviousSlot(VariantData*) const;
Slot<VariantData> getPreviousSlot(VariantData*, const ResourceManager*) const;
};
inline const VariantData* collectionToVariant(
const CollectionData* collection) {
const void* data = collection; // prevent warning cast-align
return reinterpret_cast<const VariantData*>(data);
}
inline VariantData* collectionToVariant(CollectionData* collection) {
void* data = collection; // prevent warning cast-align
return reinterpret_cast<VariantData*>(data);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -14,7 +14,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId)
: slot_(slot), currentId_(slotId) {
nextId_ = slot_ ? slot_->next : NULL_SLOT;
nextId_ = slot_ ? slot_->next() : NULL_SLOT;
}
inline void CollectionIterator::next(const ResourceManager* resources) {
@ -22,123 +22,114 @@ inline void CollectionIterator::next(const ResourceManager* resources) {
slot_ = resources->getVariant(nextId_);
currentId_ = nextId_;
if (slot_)
nextId_ = slot_->next;
nextId_ = slot_->next();
}
inline CollectionImpl::iterator CollectionImpl::createIterator() const {
if (!data_)
return iterator();
return iterator(resources_->getVariant(data_->head), data_->head);
inline CollectionData::iterator CollectionData::createIterator(
const ResourceManager* resources) const {
return iterator(resources->getVariant(head_), head_);
}
inline void CollectionImpl::appendOne(Slot<VariantData> slot) {
ARDUINOJSON_ASSERT(data_ != nullptr);
ARDUINOJSON_ASSERT(resources_ != nullptr);
if (data_->tail != NULL_SLOT) {
auto tail = resources_->getVariant(data_->tail);
tail->next = slot.id();
data_->tail = slot.id();
inline void CollectionData::appendOne(Slot<VariantData> slot,
const ResourceManager* resources) {
if (tail_ != NULL_SLOT) {
auto tail = resources->getVariant(tail_);
tail->setNext(slot.id());
tail_ = slot.id();
} else {
data_->head = slot.id();
data_->tail = slot.id();
head_ = slot.id();
tail_ = slot.id();
}
}
inline void CollectionImpl::appendPair(Slot<VariantData> key,
Slot<VariantData> value) {
ARDUINOJSON_ASSERT(data_ != nullptr);
ARDUINOJSON_ASSERT(resources_ != nullptr);
inline void CollectionData::appendPair(Slot<VariantData> key,
Slot<VariantData> value,
const ResourceManager* resources) {
key->setNext(value.id());
key->next = value.id();
if (data_->tail != NULL_SLOT) {
auto tail = resources_->getVariant(data_->tail);
tail->next = key.id();
data_->tail = value.id();
if (tail_ != NULL_SLOT) {
auto tail = resources->getVariant(tail_);
tail->setNext(key.id());
tail_ = value.id();
} else {
data_->head = key.id();
data_->tail = value.id();
head_ = key.id();
tail_ = value.id();
}
}
inline void CollectionImpl::clear() {
if (!data_)
return;
auto next = data_->head;
inline void CollectionData::clear(ResourceManager* resources) {
auto next = head_;
while (next != NULL_SLOT) {
auto currId = next;
auto slot = resources_->getVariant(next);
next = slot->next;
resources_->freeVariant({slot, currId});
auto slot = resources->getVariant(next);
next = slot->next();
resources->freeVariant({slot, currId});
}
data_->head = NULL_SLOT;
data_->tail = NULL_SLOT;
head_ = NULL_SLOT;
tail_ = NULL_SLOT;
}
inline Slot<VariantData> CollectionImpl::getPreviousSlot(
VariantData* target) const {
inline Slot<VariantData> CollectionData::getPreviousSlot(
VariantData* target, const ResourceManager* resources) const {
auto prev = Slot<VariantData>();
auto currentId = data_->head;
auto currentId = head_;
while (currentId != NULL_SLOT) {
auto currentSlot = resources_->getVariant(currentId);
auto currentSlot = resources->getVariant(currentId);
if (currentSlot == target)
break;
prev = Slot<VariantData>(currentSlot, currentId);
currentId = currentSlot->next;
currentId = currentSlot->next();
}
return prev;
}
inline void CollectionImpl::removeOne(iterator it) {
inline void CollectionData::removeOne(iterator it, ResourceManager* resources) {
if (it.done())
return;
auto curr = it.slot_;
auto prev = getPreviousSlot(curr);
auto next = curr->next;
auto prev = getPreviousSlot(curr, resources);
auto next = curr->next();
if (prev)
prev->next = next;
prev->setNext(next);
else
data_->head = next;
head_ = next;
if (next == NULL_SLOT)
data_->tail = prev.id();
resources_->freeVariant({it.slot_, it.currentId_});
tail_ = prev.id();
resources->freeVariant({it.slot_, it.currentId_});
}
inline void CollectionImpl::removePair(ObjectImpl::iterator it) {
inline void CollectionData::removePair(ObjectData::iterator it,
ResourceManager* resources) {
if (it.done())
return;
auto keySlot = it.slot_;
auto valueId = it.nextId_;
auto valueSlot = resources_->getVariant(valueId);
auto valueSlot = resources->getVariant(valueId);
// remove value slot
keySlot->next = valueSlot->next;
resources_->freeVariant({valueSlot, valueId});
keySlot->setNext(valueSlot->next());
resources->freeVariant({valueSlot, valueId});
// remove key slot
removeOne(it);
removeOne(it, resources);
}
inline size_t CollectionImpl::nesting() const {
if (!data_)
return 0;
inline size_t CollectionData::nesting(const ResourceManager* resources) const {
size_t maxChildNesting = 0;
for (auto it = createIterator(); !it.done(); it.next(resources_)) {
VariantImpl variant(it.data(), resources_);
size_t childNesting = variant.nesting();
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
size_t childNesting = it->nesting(resources);
if (childNesting > maxChildNesting)
maxChildNesting = childNesting;
}
return maxChildNesting + 1;
}
inline size_t CollectionImpl::size() const {
inline size_t CollectionData::size(const ResourceManager* resources) const {
size_t count = 0;
for (auto it = createIterator(); !it.done(); it.next(resources_))
for (auto it = createIterator(resources); !it.done(); it.next(resources))
count++;
return count;
}

View File

@ -50,7 +50,7 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader,
auto resources = VariantAttorney::getResourceManager(dst);
dst.clear();
auto err = TDeserializer<TReader>(resources, reader)
.parse(data, options.filter, options.nestingLimit);
.parse(*data, options.filter, options.nestingLimit);
shrinkJsonDocument(dst);
return err;
}

View File

@ -88,7 +88,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// https://arduinojson.org/v7/api/jsondocument/clear/
void clear() {
resources_.clear();
data_.type = detail::VariantType::Null;
data_.reset();
}
// Returns true if the root is of the specified type.
@ -120,13 +120,13 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Returns the depth (nesting level) of the array.
// https://arduinojson.org/v7/api/jsondocument/nesting/
size_t nesting() const {
return getVariantImpl().nesting();
return data_.nesting(&resources_);
}
// Returns the number of elements in the root array or object.
// https://arduinojson.org/v7/api/jsondocument/size/
size_t size() const {
return getVariantImpl().size();
return data_.size(&resources_);
}
// Copies the specified document.
@ -165,7 +165,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TChar>
ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return getVariantImpl().getMember(detail::adaptString(key)) != 0;
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -174,7 +174,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
bool containsKey(const TString& key) const {
return getVariantImpl().getMember(detail::adaptString(key)) != 0;
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -212,7 +212,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(
getVariantImpl().getMember(detail::adaptString(key)), &resources_);
data_.getMember(detail::adaptString(key), &resources_), &resources_);
}
// Gets a root object's member.
@ -223,7 +223,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(
getVariantImpl().getMember(detail::adaptString(key)), &resources_);
data_.getMember(detail::adaptString(key), &resources_), &resources_);
}
// Gets or sets a root array's element.
@ -237,7 +237,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets a root array's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
JsonVariantConst operator[](size_t index) const {
return JsonVariantConst(getVariantImpl().getElement(index), &resources_);
return JsonVariantConst(data_.getElement(index, &resources_), &resources_);
}
// Gets or sets a root object's member.
@ -267,14 +267,14 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename T, detail::enable_if_t<
detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() {
return JsonVariant(getVariantImpl().addElement(), &resources_);
return JsonVariant(data_.addElement(&resources_), &resources_);
}
// Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename TValue>
bool add(const TValue& value) {
return getVariantImpl().addValue(value);
return data_.addValue(value, &resources_);
}
// Appends a value to the root array.
@ -282,7 +282,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TChar,
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
bool add(TChar* value) {
return getVariantImpl().addValue(value);
return data_.addValue(value, &resources_);
}
// Removes an element of the root array.
@ -290,7 +290,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
void remove(T index) {
getVariantImpl().removeElement(size_t(index));
detail::VariantData::removeElement(getData(), size_t(index),
getResourceManager());
}
// Removes a member of the root object.
@ -300,7 +301,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
!detail::is_const<TChar>::value,
int> = 0>
void remove(TChar* key) {
getVariantImpl().removeMember(detail::adaptString(key));
detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager());
}
// Removes a member of the root object.
@ -308,7 +310,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) {
getVariantImpl().removeMember(detail::adaptString(key));
detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager());
}
// Removes a member of the root object or an element of the root array.
@ -388,10 +391,6 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
}
private:
detail::VariantImpl getVariantImpl() const {
return detail::VariantImpl(&data_, &resources_);
}
JsonVariant getVariant() {
return JsonVariant(&data_, &resources_);
}
@ -416,8 +415,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
return &data_;
}
mutable detail::ResourceManager resources_;
mutable detail::VariantData data_;
detail::ResourceManager resources_;
detail::VariantData data_;
};
inline void convertToJson(const JsonDocument& src, JsonVariant dst) {

View File

@ -28,13 +28,13 @@ class JsonDeserializer {
resources_(resources) {}
template <typename TFilter>
DeserializationError parse(VariantData* variant, TFilter filter,
DeserializationError parse(VariantData& variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
err = parseVariant(variant, filter, nestingLimit);
if (!err && latch_.last() != 0 && variant->isFloat()) {
if (!err && latch_.last() != 0 && variant.isFloat()) {
// We don't detect trailing characters earlier, so we need to check now
return DeserializationError::InvalidInput;
}
@ -60,7 +60,7 @@ class JsonDeserializer {
template <typename TFilter>
DeserializationError::Code parseVariant(
VariantData* variant, TFilter filter,
VariantData& variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
@ -71,15 +71,13 @@ class JsonDeserializer {
switch (current()) {
case '[':
if (filter.allowArray())
return parseArray(VariantImpl(variant, resources_).toArray(), filter,
nestingLimit);
return parseArray(variant.toArray(), filter, nestingLimit);
else
return skipArray(nestingLimit);
case '{':
if (filter.allowObject())
return parseObject(VariantImpl(variant, resources_).toObject(),
filter, nestingLimit);
return parseObject(variant.toObject(), filter, nestingLimit);
else
return skipObject(nestingLimit);
@ -92,12 +90,12 @@ class JsonDeserializer {
case 't':
if (filter.allowValue())
variant->setBoolean(true);
variant.setBoolean(true);
return skipKeyword("true");
case 'f':
if (filter.allowValue())
variant->setBoolean(false);
variant.setBoolean(false);
return skipKeyword("false");
case 'n':
@ -148,7 +146,7 @@ class JsonDeserializer {
template <typename TFilter>
DeserializationError::Code parseArray(
ArrayImpl array, TFilter filter,
ArrayData& array, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
@ -174,12 +172,12 @@ class JsonDeserializer {
for (;;) {
if (elementFilter.allow()) {
// Allocate slot in array
VariantData* value = array.addElement();
VariantData* value = array.addElement(resources_);
if (!value)
return DeserializationError::NoMemory;
// 1 - Parse value
err = parseVariant(value, elementFilter, nestingLimit.decrement());
err = parseVariant(*value, elementFilter, nestingLimit.decrement());
if (err)
return err;
} else {
@ -234,7 +232,7 @@ class JsonDeserializer {
template <typename TFilter>
DeserializationError::Code parseObject(
ObjectImpl object, TFilter filter,
ObjectData& object, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
@ -275,19 +273,19 @@ class JsonDeserializer {
TFilter memberFilter = filter[key];
if (memberFilter.allow()) {
auto member = object.getMember(adaptString(key));
auto member = object.getMember(adaptString(key), resources_);
if (!member) {
auto keyVariant = object.addPair(&member);
auto keyVariant = object.addPair(&member, resources_);
if (!keyVariant)
return DeserializationError::NoMemory;
stringBuilder_.save(keyVariant);
} else {
VariantImpl(member, resources_).clear();
member->clear(resources_);
}
// Parse value
err = parseVariant(member, memberFilter, nestingLimit.decrement());
err = parseVariant(*member, memberFilter, nestingLimit.decrement());
if (err)
return err;
} else {
@ -381,7 +379,7 @@ class JsonDeserializer {
}
}
DeserializationError::Code parseStringValue(VariantData* variant) {
DeserializationError::Code parseStringValue(VariantData& variant) {
DeserializationError::Code err;
stringBuilder_.startString();
@ -390,7 +388,7 @@ class JsonDeserializer {
if (err)
return err;
stringBuilder_.save(variant);
stringBuilder_.save(&variant);
return DeserializationError::Ok;
}
@ -506,7 +504,7 @@ class JsonDeserializer {
return DeserializationError::Ok;
}
DeserializationError::Code parseNumericValue(VariantData* result) {
DeserializationError::Code parseNumericValue(VariantData& result) {
uint8_t n = 0;
char c = current();
@ -520,28 +518,26 @@ class JsonDeserializer {
auto number = parseNumber(buffer_);
switch (number.type()) {
case NumberType::UnsignedInteger:
if (VariantImpl(result, resources_)
.setInteger(number.asUnsignedInteger()))
if (result.setInteger(number.asUnsignedInteger(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
case NumberType::SignedInteger:
if (VariantImpl(result, resources_)
.setInteger(number.asSignedInteger()))
if (result.setInteger(number.asSignedInteger(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
case NumberType::Float:
if (VariantImpl(result, resources_).setFloat(number.asFloat()))
if (result.setFloat(number.asFloat(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
#if ARDUINOJSON_USE_DOUBLE
case NumberType::Double:
if (VariantImpl(result, resources_).setFloat(number.asDouble()))
if (result.setFloat(number.asDouble(), resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;

View File

@ -16,10 +16,10 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
public:
static const bool producesText = true;
JsonSerializer(TWriter writer, ResourceManager* resources)
JsonSerializer(TWriter writer, const ResourceManager* resources)
: formatter_(writer), resources_(resources) {}
size_t visit(const ArrayImpl& array) {
size_t visit(const ArrayData& array) {
write('[');
auto slotId = array.head();
@ -27,9 +27,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
VariantImpl(slot, resources_).accept(*this);
slot->accept(*this, resources_);
slotId = slot->next;
slotId = slot->next();
if (slotId != NULL_SLOT)
write(',');
@ -39,7 +39,7 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
return bytesWritten();
}
size_t visit(const ObjectImpl& object) {
size_t visit(const ObjectData& object) {
write('{');
auto slotId = object.head();
@ -48,9 +48,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
VariantImpl(slot, resources_).accept(*this);
slot->accept(*this, resources_);
slotId = slot->next;
slotId = slot->next();
if (slotId != NULL_SLOT)
write(isKey ? ':' : ',');
@ -120,7 +120,7 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
TextFormatter<TWriter> formatter_;
protected:
ResourceManager* resources_;
const ResourceManager* resources_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -16,17 +16,17 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
using base = JsonSerializer<TWriter>;
public:
PrettyJsonSerializer(TWriter writer, ResourceManager* resources)
PrettyJsonSerializer(TWriter writer, const ResourceManager* resources)
: base(writer, resources), nesting_(0) {}
size_t visit(const ArrayImpl& array) {
auto it = array.createIterator();
size_t visit(const ArrayData& array) {
auto it = array.createIterator(base::resources_);
if (!it.done()) {
base::write("[\r\n");
nesting_++;
while (!it.done()) {
indent();
VariantImpl(it.data(), base::resources_).accept(*this);
it->accept(*this, base::resources_);
it.next(base::resources_);
base::write(it.done() ? "\r\n" : ",\r\n");
@ -40,8 +40,8 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
return this->bytesWritten();
}
size_t visit(const ObjectImpl& object) {
auto it = object.createIterator();
size_t visit(const ObjectData& object) {
auto it = object.createIterator(base::resources_);
if (!it.done()) {
base::write("{\r\n");
nesting_++;
@ -49,7 +49,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
while (!it.done()) {
if (isKey)
indent();
VariantImpl(it.data(), base::resources_).accept(*this);
it->accept(*this, base::resources_);
it.next(base::resources_);
if (isKey)
base::write(": ");

View File

@ -14,7 +14,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
struct VariantData;
class VariantData;
class VariantWithId;
class ResourceManager {

View File

@ -4,9 +4,10 @@
#pragma once
#include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -21,7 +22,7 @@ inline Slot<VariantData> ResourceManager::allocVariant() {
}
inline void ResourceManager::freeVariant(Slot<VariantData> slot) {
VariantImpl(slot.ptr(), this).clear();
slot->clear(this);
variantPools_.freeSlot(slot);
}

View File

@ -5,7 +5,6 @@
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE

View File

@ -29,7 +29,7 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
if (!data)
return;
auto resources = getResourceManager(dst);
detail::VariantImpl(data, resources).clear();
data->clear(resources);
if (src.data()) {
size_t headerSize = src.size() >= 0x10000 ? 5
: src.size() >= 0x100 ? 3
@ -66,8 +66,10 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
}
static MsgPackBinary fromJson(JsonVariantConst src) {
auto variant = VariantAttorney::getVariantImpl(src);
auto rawstr = variant.asRawString();
auto data = getData(src);
if (!data)
return {};
auto rawstr = data->asRawString();
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
auto n = rawstr.size();
if (n >= 2 && p[0] == 0xc4) { // bin 8

View File

@ -24,10 +24,10 @@ class MsgPackDeserializer {
foundSomething_(false) {}
template <typename TFilter>
DeserializationError parse(VariantData* variant, TFilter filter,
DeserializationError parse(VariantData& variant, TFilter filter,
DeserializationOption::NestingLimit nestingLimit) {
DeserializationError::Code err;
err = parseVariant(variant, filter, nestingLimit);
err = parseVariant(&variant, filter, nestingLimit);
return foundSomething_ ? err : DeserializationError::EmptyInput;
}
@ -91,7 +91,7 @@ class MsgPackDeserializer {
if (code <= 0x7f || code >= 0xe0) { // fixint
if (allowValue)
VariantImpl(variant, resources_).setInteger(static_cast<int8_t>(code));
variant->setInteger(static_cast<int8_t>(code), resources_);
return DeserializationError::Ok;
}
@ -231,14 +231,14 @@ class MsgPackDeserializer {
if (isSigned) {
auto truncatedValue = static_cast<JsonInteger>(signedValue);
if (truncatedValue == signedValue) {
if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
if (!variant->setInteger(truncatedValue, resources_))
return DeserializationError::NoMemory;
}
// else set null on overflow
} else {
auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
if (truncatedValue == unsignedValue)
if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
if (!variant->setInteger(truncatedValue, resources_))
return DeserializationError::NoMemory;
// else set null on overflow
}
@ -257,7 +257,7 @@ class MsgPackDeserializer {
return err;
fixEndianness(value);
VariantImpl(variant, resources_).setFloat(value);
variant->setFloat(value, resources_);
return DeserializationError::Ok;
}
@ -273,7 +273,7 @@ class MsgPackDeserializer {
return err;
fixEndianness(value);
if (VariantImpl(variant, resources_).setFloat(value))
if (variant->setFloat(value, resources_))
return DeserializationError::Ok;
else
return DeserializationError::NoMemory;
@ -293,7 +293,7 @@ class MsgPackDeserializer {
doubleToFloat(i, o);
fixEndianness(value);
VariantImpl(variant, resources_).setFloat(value);
variant->setFloat(value, resources_);
return DeserializationError::Ok;
}
@ -349,10 +349,12 @@ class MsgPackDeserializer {
bool allowArray = filter.allowArray();
ArrayImpl array;
ArrayData* array;
if (allowArray) {
ARDUINOJSON_ASSERT(variant != 0);
array = VariantImpl(variant, resources_).toArray();
array = &variant->toArray();
} else {
array = 0;
}
TFilter elementFilter = filter[0U];
@ -361,7 +363,8 @@ class MsgPackDeserializer {
VariantData* value;
if (elementFilter.allow()) {
value = array.addElement();
ARDUINOJSON_ASSERT(array != 0);
value = array->addElement(resources_);
if (!value)
return DeserializationError::NoMemory;
} else {
@ -385,10 +388,12 @@ class MsgPackDeserializer {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
ObjectImpl object;
ObjectData* object;
if (filter.allowObject()) {
ARDUINOJSON_ASSERT(variant != 0);
object = VariantImpl(variant, resources_).toObject();
object = &variant->toObject();
} else {
object = 0;
}
for (; n; --n) {
@ -401,7 +406,9 @@ class MsgPackDeserializer {
VariantData* member = 0;
if (memberFilter.allow()) {
auto keyVariant = object.addPair(&member);
ARDUINOJSON_ASSERT(object != 0);
auto keyVariant = object->addPair(&member, resources_);
if (!keyVariant)
return DeserializationError::NoMemory;

View File

@ -31,11 +31,11 @@ class MsgPackExtension {
template <>
struct Converter<MsgPackExtension> : private detail::VariantAttorney {
static void toJson(MsgPackExtension src, JsonVariant dst) {
auto data = getData(dst);
auto data = VariantAttorney::getData(dst);
if (!data)
return;
auto resources = getResourceManager(dst);
detail::VariantImpl(data, resources).clear();
data->clear(resources);
if (src.data()) {
uint8_t format, sizeBytes;
if (src.size() >= 0x10000) {
@ -80,8 +80,10 @@ struct Converter<MsgPackExtension> : private detail::VariantAttorney {
}
static MsgPackExtension fromJson(JsonVariantConst src) {
auto variant = VariantAttorney::getVariantImpl(src);
auto rawstr = variant.asRawString();
auto data = getData(src);
if (!data)
return {};
auto rawstr = data->asRawString();
if (rawstr.size() == 0)
return {};
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());

View File

@ -19,7 +19,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
public:
static const bool producesText = false;
MsgPackSerializer(TWriter writer, ResourceManager* resources)
MsgPackSerializer(TWriter writer, const ResourceManager* resources)
: writer_(writer), resources_(resources) {}
template <typename T>
@ -47,8 +47,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
return bytesWritten();
}
size_t visit(const ArrayImpl& array) {
size_t n = array.size();
size_t visit(const ArrayData& array) {
size_t n = array.size(resources_);
if (n < 0x10) {
writeByte(uint8_t(0x90 + n));
} else if (n < 0x10000) {
@ -62,15 +62,15 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = array.head();
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
VariantImpl(slot, resources_).accept(*this);
slotId = slot->next;
slot->accept(*this, resources_);
slotId = slot->next();
}
return bytesWritten();
}
size_t visit(const ObjectImpl& object) {
size_t n = object.size();
size_t visit(const ObjectData& object) {
size_t n = object.size(resources_);
if (n < 0x10) {
writeByte(uint8_t(0x80 + n));
} else if (n < 0x10000) {
@ -84,8 +84,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
auto slotId = object.head();
while (slotId != NULL_SLOT) {
auto slot = resources_->getVariant(slotId);
VariantImpl(slot, resources_).accept(*this);
slotId = slot->next;
slot->accept(*this, resources_);
slotId = slot->next();
}
return bytesWritten();
@ -209,7 +209,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
}
CountingDecorator<TWriter> writer_;
ResourceManager* resources_;
const ResourceManager* resources_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -20,55 +20,56 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
using iterator = JsonObjectIterator;
// Creates an unbound reference.
JsonObject() {}
JsonObject() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonObject(const detail::ObjectImpl& impl) : impl_(impl) {}
// INTERNAL USE ONLY
JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
: impl_(detail::VariantImpl(data, resource).asObject()) {}
JsonObject(detail::ObjectData* data, detail::ResourceManager* resource)
: data_(data), resources_(resource) {}
operator JsonVariant() const {
return JsonVariant(getData(), getResourceManager());
void* data = data_; // prevent warning cast-align
return JsonVariant(reinterpret_cast<detail::VariantData*>(data),
resources_);
}
operator JsonObjectConst() const {
return JsonObjectConst(getData(), getResourceManager());
return JsonObjectConst(data_, resources_);
}
operator JsonVariantConst() const {
return JsonVariantConst(getData(), getResourceManager());
return JsonVariantConst(collectionToVariant(data_), resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonobject/isnull/
bool isNull() const {
return impl_.isNull();
return data_ == 0;
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonobject/isnull/
operator bool() const {
return !isNull();
return data_ != 0;
}
// Returns the depth (nesting level) of the object.
// https://arduinojson.org/v7/api/jsonobject/nesting/
size_t nesting() const {
return impl_.nesting();
return detail::VariantData::nesting(collectionToVariant(data_), resources_);
}
// Returns the number of members in the object.
// https://arduinojson.org/v7/api/jsonobject/size/
size_t size() const {
return impl_.size();
return data_ ? data_->size(resources_) : 0;
}
// Returns an iterator to the first key-value pair of the object.
// https://arduinojson.org/v7/api/jsonobject/begin/
iterator begin() const {
return iterator(impl_.createIterator(), impl_.getResourceManager());
if (!data_)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
}
// Returns an iterator following the last key-value pair of the object.
@ -80,13 +81,13 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Removes all the members of the object.
// https://arduinojson.org/v7/api/jsonobject/clear/
void clear() const {
impl_.clear();
detail::ObjectData::clear(data_, resources_);
}
// Copies an object.
// https://arduinojson.org/v7/api/jsonobject/set/
bool set(JsonObjectConst src) {
if (isNull() || src.isNull())
if (!data_ || !src.data_)
return false;
clear();
@ -130,7 +131,7 @@ 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 {
impl_.remove(it.iterator_);
detail::ObjectData::remove(data_, it.iterator_, resources_);
}
// Removes the member with the specified key.
@ -138,7 +139,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 {
impl_.removeMember(detail::adaptString(key));
detail::ObjectData::removeMember(data_, detail::adaptString(key),
resources_);
}
// Removes the member with the specified key.
@ -154,7 +156,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// https://arduinojson.org/v7/api/jsonobject/remove/
template <typename TChar>
FORCE_INLINE void remove(TChar* key) const {
impl_.removeMember(detail::adaptString(key));
detail::ObjectData::removeMember(data_, detail::adaptString(key),
resources_);
}
// DEPRECATED: use obj[key].is<T>() instead
@ -163,7 +166,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 impl_.getMember(detail::adaptString(key)) != 0;
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj["key"].is<T>() instead
@ -174,7 +178,8 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return impl_.getMember(detail::adaptString(key)) != 0;
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -222,18 +227,19 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
private:
detail::ResourceManager* getResourceManager() const {
return impl_.getResourceManager();
return resources_;
}
detail::VariantData* getData() const {
return impl_.getData();
return detail::collectionToVariant(data_);
}
detail::VariantData* getOrCreateData() const {
return impl_.getData();
return detail::collectionToVariant(data_);
}
mutable detail::ObjectImpl impl_;
detail::ObjectData* data_;
detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -19,47 +19,47 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
using iterator = JsonObjectConstIterator;
// Creates an unbound reference.
JsonObjectConst() {}
JsonObjectConst() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonObjectConst(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(detail::VariantImpl(data, resources).asObject()) {}
// INTERNAL USE ONLY
JsonObjectConst(const detail::ObjectImpl& impl) : impl_(impl) {}
JsonObjectConst(const detail::ObjectData* data,
const detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
operator JsonVariantConst() const {
return JsonVariantConst(impl_.getData(), impl_.getResourceManager());
return JsonVariantConst(getData(), resources_);
}
// Returns true if the reference is unbound.
// https://arduinojson.org/v7/api/jsonobjectconst/isnull/
bool isNull() const {
return impl_.isNull();
return data_ == 0;
}
// Returns true if the reference is bound.
// https://arduinojson.org/v7/api/jsonobjectconst/isnull/
operator bool() const {
return !isNull();
return data_ != 0;
}
// Returns the depth (nesting level) of the object.
// https://arduinojson.org/v7/api/jsonobjectconst/nesting/
size_t nesting() const {
return impl_.nesting();
return detail::VariantData::nesting(getData(), resources_);
}
// Returns the number of members in the object.
// https://arduinojson.org/v7/api/jsonobjectconst/size/
size_t size() const {
return impl_.size();
return data_ ? data_->size(resources_) : 0;
}
// Returns an iterator to the first key-value pair of the object.
// https://arduinojson.org/v7/api/jsonobjectconst/begin/
iterator begin() const {
return iterator(impl_.createIterator(), impl_.getResourceManager());
if (!data_)
return iterator();
return iterator(data_->createIterator(resources_), resources_);
}
// Returns an iterator following the last key-value pair of the object.
@ -74,7 +74,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 impl_.getMember(detail::adaptString(key)) != 0;
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj["key"].is<T>() instead
@ -82,7 +83,8 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
template <typename TChar>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return impl_.getMember(detail::adaptString(key)) != 0;
return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -99,8 +101,9 @@ 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(impl_.getMember(detail::adaptString(key)),
impl_.getResourceManager());
return JsonVariantConst(detail::ObjectData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
// Gets the member with specified key.
@ -110,8 +113,9 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
!detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
impl_.getResourceManager());
return JsonVariantConst(detail::ObjectData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
// Gets the member with specified key.
@ -133,10 +137,11 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
private:
const detail::VariantData* getData() const {
return impl_.getData();
return collectionToVariant(data_);
}
detail::ObjectImpl impl_;
const detail::ObjectData* data_;
const detail::ResourceManager* resources_;
};
inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) {

View File

@ -14,7 +14,7 @@ class JsonObjectIterator {
public:
JsonObjectIterator() {}
explicit JsonObjectIterator(detail::ObjectImpl::iterator iterator,
explicit JsonObjectIterator(detail::ObjectData::iterator iterator,
detail::ResourceManager* resources)
: iterator_(iterator), resources_(resources) {}
@ -40,7 +40,7 @@ class JsonObjectIterator {
}
private:
detail::ObjectImpl::iterator iterator_;
detail::ObjectData::iterator iterator_;
detail::ResourceManager* resources_;
};
@ -50,8 +50,8 @@ class JsonObjectConstIterator {
public:
JsonObjectConstIterator() {}
explicit JsonObjectConstIterator(detail::ObjectImpl::iterator iterator,
detail::ResourceManager* resources)
explicit JsonObjectConstIterator(detail::ObjectData::iterator iterator,
const detail::ResourceManager* resources)
: iterator_(iterator), resources_(resources) {}
JsonPairConst operator*() const {
@ -76,8 +76,8 @@ class JsonObjectConstIterator {
}
private:
detail::ObjectImpl::iterator iterator_;
detail::ResourceManager* resources_;
detail::ObjectData::iterator iterator_;
const detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -15,11 +15,10 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
class JsonPair {
public:
// INTERNAL USE ONLY
JsonPair(detail::ObjectImpl::iterator iterator,
JsonPair(detail::ObjectData::iterator iterator,
detail::ResourceManager* resources) {
if (!iterator.done()) {
detail::VariantImpl variant(iterator.data(), resources);
key_ = variant.asString();
key_ = iterator->asString(resources);
iterator.next(resources);
value_ = JsonVariant(iterator.data(), resources);
}
@ -44,11 +43,10 @@ class JsonPair {
// https://arduinojson.org/v7/api/jsonobjectconst/begin_end/
class JsonPairConst {
public:
JsonPairConst(detail::ObjectImpl::iterator iterator,
detail::ResourceManager* resources) {
JsonPairConst(detail::ObjectData::iterator iterator,
const detail::ResourceManager* resources) {
if (!iterator.done()) {
detail::VariantImpl variant(iterator.data(), resources);
key_ = variant.asString();
key_ = iterator->asString(resources);
iterator.next(resources);
value_ = JsonVariantConst(iterator.data(), resources);
}

View File

@ -56,12 +56,17 @@ class MemberProxy
}
VariantData* getData() const {
return VariantAttorney::getVariantImpl(upstream_).getMember(key_);
return VariantData::getMember(
VariantAttorney::getData(upstream_), key_,
VariantAttorney::getResourceManager(upstream_));
}
VariantData* getOrCreateData() const {
return VariantAttorney::getOrCreateVariantImpl(upstream_).getOrAddMember(
key_);
auto data = VariantAttorney::getOrCreateData(upstream_);
if (!data)
return nullptr;
return data->getOrAddMember(key_,
VariantAttorney::getResourceManager(upstream_));
}
private:

View File

@ -8,40 +8,63 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantImpl;
class ObjectImpl : public CollectionImpl {
class ObjectData : public CollectionData {
public:
ObjectImpl() {}
template <typename TAdaptedString>
VariantData* addMember(TAdaptedString key, ResourceManager* resources);
ObjectImpl(CollectionData* data, ResourceManager* resources)
: CollectionImpl(data, resources) {}
VariantData* addPair(VariantData** value, ResourceManager* resources);
template <typename TAdaptedString>
VariantData* addMember(TAdaptedString key);
VariantData* addPair(VariantData** value);
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources);
template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key);
VariantData* getMember(TAdaptedString key,
const ResourceManager* resources) const;
template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key) const;
template <typename TAdaptedString>
void removeMember(TAdaptedString key);
void remove(iterator it) {
CollectionImpl::removePair(it);
static VariantData* getMember(const ObjectData* object, TAdaptedString key,
const ResourceManager* resources) {
if (!object)
return nullptr;
return object->getMember(key, resources);
}
size_t size() const {
return CollectionImpl::size() / 2;
template <typename TAdaptedString>
void removeMember(TAdaptedString key, ResourceManager* resources);
template <typename TAdaptedString>
static void removeMember(ObjectData* obj, TAdaptedString key,
ResourceManager* resources) {
if (!obj)
return;
obj->removeMember(key, resources);
}
void remove(iterator it, ResourceManager* resources) {
CollectionData::removePair(it, resources);
}
static void remove(ObjectData* obj, ObjectData::iterator it,
ResourceManager* resources) {
if (!obj)
return;
obj->remove(it, resources);
}
size_t size(const ResourceManager* resources) const {
return CollectionData::size(resources) / 2;
}
static size_t size(const ObjectData* obj, const ResourceManager* resources) {
if (!obj)
return 0;
return obj->size(resources);
}
private:
template <typename TAdaptedString>
iterator findKey(TAdaptedString key) const;
iterator findKey(TAdaptedString key, const ResourceManager* resources) const;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -11,30 +11,32 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TAdaptedString>
inline VariantData* ObjectImpl::getMember(TAdaptedString key) const {
auto it = findKey(key);
inline VariantData* ObjectData::getMember(
TAdaptedString key, const ResourceManager* resources) const {
auto it = findKey(key, resources);
if (it.done())
return nullptr;
it.next(resources_);
it.next(resources);
return it.data();
}
template <typename TAdaptedString>
VariantData* ObjectImpl::getOrAddMember(TAdaptedString key) {
auto data = getMember(key);
VariantData* ObjectData::getOrAddMember(TAdaptedString key,
ResourceManager* resources) {
auto data = getMember(key, resources);
if (data)
return data;
return addMember(key);
return addMember(key, resources);
}
template <typename TAdaptedString>
inline ObjectImpl::iterator ObjectImpl::findKey(TAdaptedString key) const {
inline ObjectData::iterator ObjectData::findKey(
TAdaptedString key, const ResourceManager* resources) const {
if (key.isNull())
return iterator();
bool isKey = true;
for (auto it = createIterator(); !it.done(); it.next(resources_)) {
VariantImpl variant(it.data(), resources_);
if (isKey && stringEquals(key, adaptString(variant.asString())))
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
if (isKey && stringEquals(key, adaptString(it->asString(resources))))
return it;
isKey = !isKey;
}
@ -42,48 +44,42 @@ inline ObjectImpl::iterator ObjectImpl::findKey(TAdaptedString key) const {
}
template <typename TAdaptedString>
inline void ObjectImpl::removeMember(TAdaptedString key) {
remove(findKey(key));
inline void ObjectData::removeMember(TAdaptedString key,
ResourceManager* resources) {
remove(findKey(key, resources), resources);
}
template <typename TAdaptedString>
inline VariantData* ObjectImpl::addMember(TAdaptedString key) {
if (!data_)
return nullptr;
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto keySlot = resources_->allocVariant();
inline VariantData* ObjectData::addMember(TAdaptedString key,
ResourceManager* resources) {
auto keySlot = resources->allocVariant();
if (!keySlot)
return nullptr;
auto valueSlot = resources_->allocVariant();
auto valueSlot = resources->allocVariant();
if (!valueSlot)
return nullptr;
VariantImpl keyImpl(keySlot.ptr(), resources_);
if (!keyImpl.setString(key))
if (!keySlot->setString(key, resources))
return nullptr;
CollectionImpl::appendPair(keySlot, valueSlot);
CollectionData::appendPair(keySlot, valueSlot, resources);
return valueSlot.ptr();
}
inline VariantData* ObjectImpl::addPair(VariantData** value) {
if (!data_)
return nullptr;
ARDUINOJSON_ASSERT(resources_ != nullptr);
auto keySlot = resources_->allocVariant();
inline VariantData* ObjectData::addPair(VariantData** value,
ResourceManager* resources) {
auto keySlot = resources->allocVariant();
if (!keySlot)
return nullptr;
auto valueSlot = resources_->allocVariant();
auto valueSlot = resources->allocVariant();
if (!valueSlot)
return nullptr;
*value = valueSlot.ptr();
CollectionImpl::appendPair(keySlot, valueSlot);
CollectionData::appendPair(keySlot, valueSlot, resources);
return keySlot.ptr();
}

View File

@ -14,7 +14,7 @@ size_t measure(ArduinoJson::JsonVariantConst source) {
auto data = VariantAttorney::getData(source);
auto resources = VariantAttorney::getResourceManager(source);
TSerializer<DummyWriter> serializer(dp, resources);
return VariantImpl(data, resources).accept(serializer);
return VariantData::accept(data, resources, serializer);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

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

View File

@ -60,18 +60,25 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
: private detail::VariantAttorney {
static bool toJson(T src, JsonVariant dst) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
auto variant = getVariantImpl(dst);
variant.clear();
return variant.setInteger(src);
auto data = getData(dst);
if (!data)
return false;
auto resources = getResourceManager(dst);
data->clear(resources);
return data->setInteger(src, resources);
}
static T fromJson(JsonVariantConst src) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
return getVariantImpl(src).template asIntegral<T>();
auto data = getData(src);
auto resources = getResourceManager(src);
return data ? data->template asIntegral<T>(resources) : T();
}
static bool checkJson(JsonVariantConst src) {
return getVariantImpl(src).template isInteger<T>();
auto data = getData(src);
auto resources = getResourceManager(src);
return data && data->template isInteger<T>(resources);
}
};
@ -83,29 +90,40 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>
}
static T fromJson(JsonVariantConst src) {
return static_cast<T>(getVariantImpl(src).template asIntegral<int>());
auto data = getData(src);
auto resources = getResourceManager(src);
return data ? static_cast<T>(data->template asIntegral<int>(resources))
: T();
}
static bool checkJson(JsonVariantConst src) {
return getVariantImpl(src).template isInteger<int>();
auto data = getData(src);
auto resources = getResourceManager(src);
return data && data->template isInteger<int>(resources);
}
};
template <>
struct Converter<bool> : private detail::VariantAttorney {
static bool toJson(bool src, JsonVariant dst) {
auto variant = getVariantImpl(dst);
variant.clear();
return variant.setBoolean(src);
auto data = getData(dst);
if (!data)
return false;
auto resources = getResourceManager(dst);
data->clear(resources);
data->setBoolean(src);
return true;
}
static bool fromJson(JsonVariantConst src) {
return getVariantImpl(src).asBoolean();
auto data = getData(src);
auto resources = getResourceManager(src);
return data ? data->asBoolean(resources) : false;
}
static bool checkJson(JsonVariantConst src) {
auto data = getData(src);
return data && data->type == detail::VariantType::Boolean;
return data && data->isBoolean();
}
};
@ -113,13 +131,18 @@ template <typename T>
struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
: private detail::VariantAttorney {
static bool toJson(T src, JsonVariant dst) {
auto variant = getVariantImpl(dst);
variant.clear();
return variant.setFloat(src);
auto data = getData(dst);
if (!data)
return false;
auto resources = getResourceManager(dst);
data->clear(resources);
return data->setFloat(src, resources);
}
static T fromJson(JsonVariantConst src) {
return getVariantImpl(src).template asFloat<T>();
auto data = getData(src);
auto resources = getResourceManager(src);
return data ? data->template asFloat<T>(resources) : 0;
}
static bool checkJson(JsonVariantConst src) {
@ -131,13 +154,13 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
template <>
struct Converter<const char*> : private detail::VariantAttorney {
static void toJson(const char* src, JsonVariant dst) {
auto variant = getVariantImpl(dst);
variant.clear();
variant.setString(detail::adaptString(src));
detail::VariantData::setString(getData(dst), detail::adaptString(src),
getResourceManager(dst));
}
static const char* fromJson(JsonVariantConst src) {
return getVariantImpl(src).asString().c_str();
auto data = getData(src);
return data ? data->asString(getResourceManager(src)).c_str() : 0;
}
static bool checkJson(JsonVariantConst src) {
@ -149,13 +172,13 @@ struct Converter<const char*> : private detail::VariantAttorney {
template <>
struct Converter<JsonString> : private detail::VariantAttorney {
static void toJson(JsonString src, JsonVariant dst) {
auto variant = getVariantImpl(dst);
variant.clear();
variant.setString(detail::adaptString(src));
detail::VariantData::setString(getData(dst), detail::adaptString(src),
getResourceManager(dst));
}
static JsonString fromJson(JsonVariantConst src) {
return getVariantImpl(src).asString();
auto data = getData(src);
return data ? data->asString(getResourceManager(src)) : JsonString();
}
static bool checkJson(JsonVariantConst src) {
@ -167,9 +190,10 @@ struct Converter<JsonString> : private detail::VariantAttorney {
template <typename T>
inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
const T& src, JsonVariant dst) {
auto variant = detail::VariantAttorney::getVariantImpl(dst);
variant.clear();
variant.setString(detail::adaptString(src));
using namespace detail;
auto data = VariantAttorney::getData(dst);
auto resources = VariantAttorney::getResourceManager(dst);
detail::VariantData::setString(data, adaptString(src), resources);
}
// SerializedValue<std::string>
@ -178,23 +202,22 @@ inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
template <typename T>
struct Converter<SerializedValue<T>> : private detail::VariantAttorney {
static void toJson(SerializedValue<T> src, JsonVariant dst) {
auto variant = getVariantImpl(dst);
variant.clear();
variant.setRawString(src);
detail::VariantData::setRawString(getData(dst), src,
getResourceManager(dst));
}
};
template <>
struct Converter<detail::nullptr_t> : private detail::VariantAttorney {
static void toJson(detail::nullptr_t, JsonVariant dst) {
getVariantImpl(dst).clear();
detail::VariantData::clear(getData(dst), getResourceManager(dst));
}
static detail::nullptr_t fromJson(JsonVariantConst) {
return nullptr;
}
static bool checkJson(JsonVariantConst src) {
auto data = getData(src);
return data == 0 || data->type == detail::VariantType::Null;
return data == 0 || data->isNull();
}
};
@ -240,7 +263,7 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
auto data = detail::VariantAttorney::getData(dst);
if (!resources || !data)
return;
detail::VariantImpl(data, resources).clear();
data->clear(resources);
detail::StringBuilderPrint print(resources);
src.printTo(print);
if (print.overflowed())
@ -306,12 +329,14 @@ struct Converter<JsonArrayConst> : private detail::VariantAttorney {
}
static JsonArrayConst fromJson(JsonVariantConst src) {
return JsonArrayConst(getData(src), getResourceManager(src));
auto data = getData(src);
auto array = data ? data->asArray() : nullptr;
return JsonArrayConst(array, getResourceManager(src));
}
static bool checkJson(JsonVariantConst src) {
auto data = getData(src);
return data && data->type == detail::VariantType::Array;
return data && data->isArray();
}
};
@ -325,12 +350,14 @@ struct Converter<JsonArray> : private detail::VariantAttorney {
}
static JsonArray fromJson(JsonVariant src) {
return JsonArray(getData(src), getResourceManager(src));
auto data = getData(src);
auto resources = getResourceManager(src);
return JsonArray(data != 0 ? data->asArray() : 0, resources);
}
static bool checkJson(JsonVariant src) {
auto data = getData(src);
return data && data->type == detail::VariantType::Array;
return data && data->isArray();
}
};
@ -344,12 +371,14 @@ struct Converter<JsonObjectConst> : private detail::VariantAttorney {
}
static JsonObjectConst fromJson(JsonVariantConst src) {
return JsonObjectConst(getData(src), getResourceManager(src));
auto data = getData(src);
auto object = data != 0 ? data->asObject() : nullptr;
return JsonObjectConst(object, getResourceManager(src));
}
static bool checkJson(JsonVariantConst src) {
auto data = getData(src);
return data && data->type == detail::VariantType::Object;
return data && data->isObject();
}
};
@ -363,12 +392,14 @@ struct Converter<JsonObject> : private detail::VariantAttorney {
}
static JsonObject fromJson(JsonVariant src) {
return JsonObject(getData(src), getResourceManager(src));
auto data = getData(src);
auto resources = getResourceManager(src);
return JsonObject(data != 0 ? data->asObject() : 0, resources);
}
static bool checkJson(JsonVariant src) {
auto data = getData(src);
return data && data->type == detail::VariantType::Object;
return data && data->isObject();
}
};

View File

@ -16,29 +16,27 @@ class JsonVariant : public detail::VariantRefBase<JsonVariant>,
public:
// Creates an unbound reference.
JsonVariant() {}
JsonVariant() : data_(0), resources_(0) {}
// INTERNAL USE ONLY
JsonVariant(detail::VariantData* data, detail::ResourceManager* resources)
: impl_(data, resources) {}
// INTERNAL USE ONLY
JsonVariant(detail::VariantImpl impl) : impl_(impl) {}
: data_(data), resources_(resources) {}
private:
detail::ResourceManager* getResourceManager() const {
return impl_.getResourceManager();
return resources_;
}
detail::VariantData* getData() const {
return impl_.getData();
return data_;
}
detail::VariantData* getOrCreateData() const {
return impl_.getData();
return data_;
}
mutable detail::VariantImpl impl_;
detail::VariantData* data_;
detail::ResourceManager* resources_;
};
namespace detail {

View File

@ -35,34 +35,34 @@ class JsonVariantConst : public detail::VariantTag,
public:
// Creates an unbound reference.
JsonVariantConst() {}
JsonVariantConst() : data_(nullptr), resources_(nullptr) {}
// INTERNAL USE ONLY
explicit JsonVariantConst(detail::VariantData* data,
detail::ResourceManager* resources)
: impl_(data, resources) {}
explicit JsonVariantConst(const detail::VariantData* data,
const detail::ResourceManager* resources)
: data_(data), resources_(resources) {}
// Returns true if the value is null or the reference is unbound.
// https://arduinojson.org/v7/api/jsonvariantconst/isnull/
bool isNull() const {
return impl_.isNull();
return detail::VariantData::isNull(data_);
}
// Returns true if the reference is unbound.
bool isUnbound() const {
return impl_.getData() == nullptr;
return !data_;
}
// Returns the depth (nesting level) of the value.
// https://arduinojson.org/v7/api/jsonvariantconst/nesting/
size_t nesting() const {
return impl_.nesting();
return detail::VariantData::nesting(data_, resources_);
}
// Returns the size of the array or object.
// https://arduinojson.org/v7/api/jsonvariantconst/size/
size_t size() const {
return impl_.size();
return detail::VariantData::size(data_, resources_);
}
// Casts the value to the specified type.
@ -104,8 +104,9 @@ class JsonVariantConst : public detail::VariantTag,
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
JsonVariantConst operator[](T index) const {
return JsonVariantConst(impl_.getElement(size_t(index)),
impl_.getResourceManager());
return JsonVariantConst(
detail::VariantData::getElement(data_, size_t(index), resources_),
resources_);
}
// Gets object's member with specified key.
@ -113,8 +114,9 @@ class JsonVariantConst : public detail::VariantTag,
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
impl_.getResourceManager());
return JsonVariantConst(detail::VariantData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
// Gets object's member with specified key.
@ -124,8 +126,9 @@ class JsonVariantConst : public detail::VariantTag,
!detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
impl_.getResourceManager());
return JsonVariantConst(detail::VariantData::getMember(
data_, detail::adaptString(key), resources_),
resources_);
}
// Gets object's member with specified key or the array's element at the
@ -146,7 +149,8 @@ class JsonVariantConst : public detail::VariantTag,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
bool containsKey(const TString& key) const {
return impl_.getMember(detail::adaptString(key)) != 0;
return detail::VariantData::getMember(getData(), detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj["key"].is<T>() instead
@ -157,7 +161,8 @@ class JsonVariantConst : public detail::VariantTag,
int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
bool containsKey(TChar* key) const {
return impl_.getMember(detail::adaptString(key)) != 0;
return detail::VariantData::getMember(getData(), detail::adaptString(key),
resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
@ -176,16 +181,17 @@ class JsonVariantConst : public detail::VariantTag,
}
protected:
detail::VariantData* getData() const {
return impl_.getData();
const detail::VariantData* getData() const {
return data_;
}
detail::ResourceManager* getResourceManager() const {
return impl_.getResourceManager();
const detail::ResourceManager* getResourceManager() const {
return resources_;
}
private:
mutable detail::VariantImpl impl_;
const detail::VariantData* data_;
const detail::ResourceManager* resources_;
};
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -26,14 +26,15 @@ class VisitorAdapter {
public:
using result_type = typename TVisitor::result_type;
VisitorAdapter(TVisitor& visitor) : visitor_(&visitor) {}
VisitorAdapter(TVisitor& visitor, const ResourceManager* resources)
: visitor_(&visitor), resources_(resources) {}
result_type visit(const ArrayImpl& array) {
return visitor_->visit(JsonArrayConst(array));
result_type visit(const ArrayData& value) {
return visitor_->visit(JsonArrayConst(&value, resources_));
}
result_type visit(const ObjectImpl& object) {
return visitor_->visit(JsonObjectConst(object));
result_type visit(const ObjectData& value) {
return visitor_->visit(JsonObjectConst(&value, resources_));
}
template <typename T>
@ -43,15 +44,18 @@ class VisitorAdapter {
private:
TVisitor* visitor_;
const ResourceManager* resources_;
};
template <typename TVisitor>
typename TVisitor::result_type accept(JsonVariantConst variant,
TVisitor& visit) {
auto data = VariantAttorney::getData(variant);
if (!data)
return visit.visit(nullptr);
auto resources = VariantAttorney::getResourceManager(variant);
VisitorAdapter<TVisitor> adapter(visit);
return VariantImpl(data, resources).accept(adapter);
VisitorAdapter<TVisitor> adapter(visit, resources);
return data->accept(adapter, resources);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -6,7 +6,7 @@
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp>
#include "JsonVariantConst.hpp"
@ -26,16 +26,6 @@ class VariantAttorney {
return client.getData();
}
template <typename TClient>
static VariantImpl getVariantImpl(TClient& client) {
return VariantImpl(client.getData(), client.getResourceManager());
}
template <typename TClient>
static VariantImpl getOrCreateVariantImpl(TClient& client) {
return VariantImpl(client.getOrCreateData(), client.getResourceManager());
}
template <typename TClient>
static VariantData* getOrCreateData(TClient& client) {
return client.getOrCreateData();

View File

@ -16,9 +16,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
enum class VariantTypeBits : uint8_t {
OwnedStringBit = 0x01, // 0000 0001
NumberBit = 0x08, // 0000 1000
#if ARDUINOJSON_USE_8_BYTE_POOL
EightByteBit = 0x10, // 0001 0000
#endif
EightByteBit = 0x10, // 0001 0000
CollectionMask = 0x60,
};
@ -57,6 +55,8 @@ union VariantContent {
uint32_t asUint32;
int32_t asInt32;
SlotId asSlotId;
ArrayData asArray;
ObjectData asObject;
CollectionData asCollection;
struct StringNode* asOwnedString;
char asTinyString[tinyStringMaxLength + 1];

View File

@ -4,7 +4,12 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringNode.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -22,11 +27,12 @@ static bool isTinyString(const T& s, size_t n) {
return !containsNul;
}
struct VariantData {
VariantContent content; // must be first to allow cast from array to variant
VariantType type = VariantType::Null;
SlotId next = NULL_SLOT;
class VariantData {
VariantContent content_; // must be first to allow cast from array to variant
VariantType type_;
SlotId next_;
public:
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
@ -34,50 +40,566 @@ struct VariantData {
static void operator delete(void*, void*) noexcept {}
void setBoolean(bool value) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
type = VariantType::Boolean;
content.asBoolean = value;
VariantData() : type_(VariantType::Null), next_(NULL_SLOT) {}
SlotId next() const {
return next_;
}
void setNext(SlotId slot) {
next_ = slot;
}
template <typename TVisitor>
typename TVisitor::result_type accept(
TVisitor& visit, const ResourceManager* resources) const {
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
switch (type_) {
case VariantType::Float:
return visit.visit(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return visit.visit(eightByteValue->asDouble);
#endif
case VariantType::Array:
return visit.visit(content_.asArray);
case VariantType::Object:
return visit.visit(content_.asObject);
case VariantType::TinyString:
return visit.visit(JsonString(content_.asTinyString));
case VariantType::LinkedString:
return visit.visit(JsonString(asLinkedString(resources), true));
case VariantType::OwnedString:
return visit.visit(JsonString(content_.asOwnedString->data,
content_.asOwnedString->length));
case VariantType::RawString:
return visit.visit(RawString(content_.asOwnedString->data,
content_.asOwnedString->length));
case VariantType::Int32:
return visit.visit(static_cast<JsonInteger>(content_.asInt32));
case VariantType::Uint32:
return visit.visit(static_cast<JsonUInt>(content_.asUint32));
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Int64:
return visit.visit(eightByteValue->asInt64);
case VariantType::Uint64:
return visit.visit(eightByteValue->asUint64);
#endif
case VariantType::Boolean:
return visit.visit(content_.asBoolean != 0);
default:
return visit.visit(nullptr);
}
}
template <typename TVisitor>
static typename TVisitor::result_type accept(const VariantData* var,
const ResourceManager* resources,
TVisitor& visit) {
if (var != 0)
return var->accept(visit, resources);
else
return visit.visit(nullptr);
}
VariantData* addElement(ResourceManager* resources) {
auto array = isNull() ? &toArray() : asArray();
return detail::ArrayData::addElement(array, resources);
}
static VariantData* addElement(VariantData* var, ResourceManager* resources) {
if (!var)
return nullptr;
return var->addElement(resources);
}
template <typename T>
bool addValue(const T& value, ResourceManager* resources) {
auto array = isNull() ? &toArray() : asArray();
return detail::ArrayData::addValue(array, value, resources);
}
template <typename T>
static bool addValue(VariantData* var, const T& value,
ResourceManager* resources) {
if (!var)
return false;
return var->addValue(value, resources);
}
bool asBoolean(const ResourceManager* resources) const {
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
switch (type_) {
case VariantType::Boolean:
return content_.asBoolean;
case VariantType::Uint32:
case VariantType::Int32:
return content_.asUint32 != 0;
case VariantType::Float:
return content_.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return eightByteValue->asDouble != 0;
#endif
case VariantType::Null:
return false;
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
case VariantType::Int64:
return eightByteValue->asUint64 != 0;
#endif
default:
return true;
}
}
ArrayData* asArray() {
return isArray() ? &content_.asArray : 0;
}
const ArrayData* asArray() const {
return const_cast<VariantData*>(this)->asArray();
}
CollectionData* asCollection() {
return isCollection() ? &content_.asCollection : 0;
}
const CollectionData* asCollection() const {
return const_cast<VariantData*>(this)->asCollection();
}
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_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
const char* str = nullptr;
switch (type_) {
case VariantType::Boolean:
return static_cast<T>(content_.asBoolean);
case VariantType::Uint32:
return static_cast<T>(content_.asUint32);
case VariantType::Int32:
return static_cast<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return static_cast<T>(eightByteValue->asUint64);
case VariantType::Int64:
return static_cast<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = content_.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString(resources);
break;
case VariantType::OwnedString:
str = content_.asOwnedString->data;
break;
case VariantType::Float:
return static_cast<T>(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return static_cast<T>(eightByteValue->asDouble);
#endif
default:
return 0.0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
template <typename T>
T asIntegral(const ResourceManager* resources) const {
static_assert(is_integral<T>::value, "T must be an integral type");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
const char* str = nullptr;
switch (type_) {
case VariantType::Boolean:
return content_.asBoolean;
case VariantType::Uint32:
return convertNumber<T>(content_.asUint32);
case VariantType::Int32:
return convertNumber<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return convertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return convertNumber<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = content_.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString(resources);
break;
case VariantType::OwnedString:
str = content_.asOwnedString->data;
break;
case VariantType::Float:
return convertNumber<T>(content_.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return convertNumber<T>(eightByteValue->asDouble);
#endif
default:
return 0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
ObjectData* asObject() {
return isObject() ? &content_.asObject : 0;
}
const ObjectData* asObject() const {
return const_cast<VariantData*>(this)->asObject();
}
JsonString asRawString() const {
switch (type_) {
case VariantType::RawString:
return JsonString(content_.asOwnedString->data,
content_.asOwnedString->length);
default:
return JsonString();
}
}
const char* asLinkedString(const ResourceManager* resources) const;
JsonString asString(const ResourceManager* resources) const {
switch (type_) {
case VariantType::TinyString:
return JsonString(content_.asTinyString);
case VariantType::LinkedString:
return JsonString(asLinkedString(resources), true);
case VariantType::OwnedString:
return JsonString(content_.asOwnedString->data,
content_.asOwnedString->length);
default:
return JsonString();
}
}
#if ARDUINOJSON_USE_8_BYTE_POOL
const EightByteValue* getEightByte(const ResourceManager* resources) const;
#endif
VariantData* getElement(size_t index,
const ResourceManager* resources) const {
return ArrayData::getElement(asArray(), index, resources);
}
static VariantData* getElement(const VariantData* var, size_t index,
const ResourceManager* resources) {
return var != 0 ? var->getElement(index, resources) : 0;
}
template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key,
const ResourceManager* resources) const {
return ObjectData::getMember(asObject(), key, resources);
}
template <typename TAdaptedString>
static VariantData* getMember(const VariantData* var, TAdaptedString key,
const ResourceManager* resources) {
if (!var)
return 0;
return var->getMember(key, resources);
}
VariantData* getOrAddElement(size_t index, ResourceManager* resources) {
auto array = isNull() ? &toArray() : asArray();
if (!array)
return nullptr;
return array->getOrAddElement(index, resources);
}
template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources) {
if (key.isNull())
return nullptr;
auto obj = isNull() ? &toObject() : asObject();
if (!obj)
return nullptr;
return obj->getOrAddMember(key, resources);
}
bool isArray() const {
return type_ == VariantType::Array;
}
bool isBoolean() const {
return type_ == VariantType::Boolean;
}
bool isCollection() const {
return type_ & VariantTypeBits::CollectionMask;
}
bool isFloat() const {
return type_ & VariantTypeBits::NumberBit;
}
template <typename T>
bool isInteger(const ResourceManager* resources) const {
#if ARDUINOJSON_USE_LONG_LONG
auto eightByteValue = getEightByte(resources);
#else
(void)resources; // silence warning
#endif
switch (type_) {
case VariantType::Uint32:
return canConvertNumber<T>(content_.asUint32);
case VariantType::Int32:
return canConvertNumber<T>(content_.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return canConvertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return canConvertNumber<T>(eightByteValue->asInt64);
#endif
default:
return false;
}
}
bool isNull() const {
return type_ == VariantType::Null;
}
static bool isNull(const VariantData* var) {
if (!var)
return true;
return var->isNull();
}
bool isObject() const {
return type_ == VariantType::Object;
}
bool isString() const {
return type_ == VariantType::LinkedString ||
type_ == VariantType::OwnedString ||
type_ == VariantType::TinyString;
}
size_t nesting(const ResourceManager* resources) const {
auto collection = asCollection();
if (collection)
return collection->nesting(resources);
else
return 0;
}
static size_t nesting(const VariantData* var,
const ResourceManager* resources) {
if (!var)
return 0;
return var->nesting(resources);
}
void removeElement(size_t index, ResourceManager* resources) {
ArrayData::removeElement(asArray(), index, resources);
}
static void removeElement(VariantData* var, size_t index,
ResourceManager* resources) {
if (!var)
return;
var->removeElement(index, resources);
}
template <typename TAdaptedString>
void removeMember(TAdaptedString key, ResourceManager* resources) {
ObjectData::removeMember(asObject(), key, resources);
}
template <typename TAdaptedString>
static void removeMember(VariantData* var, TAdaptedString key,
ResourceManager* resources) {
if (!var)
return;
var->removeMember(key, resources);
}
void reset() { // TODO: remove
type_ = VariantType::Null;
}
void setBoolean(bool value) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Boolean;
content_.asBoolean = value;
}
template <typename T>
enable_if_t<sizeof(T) == 4, bool> setFloat(T value, ResourceManager*) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Float;
content_.asFloat = value;
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
template <typename T>
enable_if_t<is_signed<T>::value, bool> setInteger(T value,
ResourceManager* resources);
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> setInteger(
T value, ResourceManager* resources);
void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
type_ = VariantType::RawString;
content_.asOwnedString = s;
}
template <typename T>
void setRawString(SerializedValue<T> value, ResourceManager* resources);
template <typename T>
static void setRawString(VariantData* var, SerializedValue<T> value,
ResourceManager* resources) {
if (!var)
return;
var->clear(resources);
var->setRawString(value, resources);
}
template <typename TAdaptedString>
bool setString(TAdaptedString value, ResourceManager* resources);
template <typename TAdaptedString>
static void setString(VariantData* var, TAdaptedString value,
ResourceManager* resources) {
if (!var)
return;
var->clear(resources);
var->setString(value, resources);
}
bool setLinkedString(const char* s, ResourceManager* resources);
template <typename TAdaptedString>
void setTinyString(const TAdaptedString& s) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s.size() <= tinyStringMaxLength);
type = VariantType::TinyString;
type_ = VariantType::TinyString;
auto n = uint8_t(s.size());
for (uint8_t i = 0; i < n; i++) {
char c = s[i];
ARDUINOJSON_ASSERT(c != 0); // no NUL in tiny string
content.asTinyString[i] = c;
content_.asTinyString[i] = c;
}
content.asTinyString[n] = 0;
content_.asTinyString[n] = 0;
}
void setOwnedString(StringNode* s) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
type = VariantType::OwnedString;
content.asOwnedString = s;
type_ = VariantType::OwnedString;
content_.asOwnedString = s;
}
void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(type == VariantType::Null);
ARDUINOJSON_ASSERT(s);
type = VariantType::RawString;
content.asOwnedString = s;
size_t size(const ResourceManager* resources) const {
if (isObject())
return content_.asObject.size(resources);
if (isArray())
return content_.asArray.size(resources);
return 0;
}
bool isFloat() const {
return type & VariantTypeBits::NumberBit;
static size_t size(const VariantData* var, const ResourceManager* resources) {
return var != 0 ? var->size(resources) : 0;
}
bool isString() const {
return type == VariantType::LinkedString ||
type == VariantType::OwnedString || type == VariantType::TinyString;
ArrayData& toArray() {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Array;
new (&content_.asArray) ArrayData();
return content_.asArray;
}
static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
if (!var)
return 0;
var->clear(resources);
return &var->toArray();
}
ObjectData& toObject() {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
type_ = VariantType::Object;
new (&content_.asObject) ObjectData();
return content_.asObject;
}
static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
if (!var)
return 0;
var->clear(resources);
return &var->toObject();
}
VariantType type() const {
return type_;
}
// Release the resources used by this variant and set it to null.
void clear(ResourceManager* resources);
static void clear(VariantData* var, ResourceManager* resources) {
if (!var)
return;
var->clear(resources);
}
};

View File

@ -5,570 +5,160 @@
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantImpl {
public:
VariantImpl() : data_(nullptr), resources_(nullptr) {}
VariantImpl(VariantData* data, ResourceManager* resources)
: data_(data), resources_(resources) {}
VariantData* getData() const {
return data_;
}
ResourceManager* getResourceManager() const {
return resources_;
}
template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visit) {
if (!data_)
return visit.visit(nullptr);
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Float:
return visit.visit(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return visit.visit(eightByteValue->asDouble);
#endif
case VariantType::Array:
return visit.visit(asArray());
case VariantType::Object:
return visit.visit(asObject());
case VariantType::TinyString:
return visit.visit(JsonString(data_->content.asTinyString));
case VariantType::LinkedString:
return visit.visit(JsonString(asLinkedString(), true));
case VariantType::OwnedString:
return visit.visit(JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length));
case VariantType::RawString:
return visit.visit(RawString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length));
case VariantType::Int32:
return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
case VariantType::Uint32:
return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Int64:
return visit.visit(eightByteValue->asInt64);
case VariantType::Uint64:
return visit.visit(eightByteValue->asUint64);
#endif
case VariantType::Boolean:
return visit.visit(data_->content.asBoolean != 0);
default:
return visit.visit(nullptr);
}
}
VariantData* addElement() {
auto array = isNull() ? toArray() : asArray();
return array.addElement();
}
template <typename T>
bool addValue(const T& value) {
auto array = isNull() ? toArray() : asArray();
return array.addValue(value);
}
bool asBoolean() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Boolean:
return data_->content.asBoolean;
case VariantType::Uint32:
case VariantType::Int32:
return data_->content.asUint32 != 0;
case VariantType::Float:
return data_->content.asFloat != 0;
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return eightByteValue->asDouble != 0;
#endif
case VariantType::Null:
return false;
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
case VariantType::Int64:
return eightByteValue->asUint64 != 0;
#endif
default:
return true;
}
}
ArrayImpl asArray() {
return ArrayImpl(isArray() ? &data_->content.asCollection : nullptr,
resources_);
}
CollectionImpl asCollection() {
return CollectionImpl(
isCollection() ? &data_->content.asCollection : nullptr, resources_);
}
template <typename T>
T asFloat() const {
if (!data_)
return 0.0;
static_assert(is_floating_point<T>::value, "T must be a floating point");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
const char* str = nullptr;
switch (data_->type) {
case VariantType::Boolean:
return static_cast<T>(data_->content.asBoolean);
case VariantType::Uint32:
return static_cast<T>(data_->content.asUint32);
case VariantType::Int32:
return static_cast<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return static_cast<T>(eightByteValue->asUint64);
case VariantType::Int64:
return static_cast<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = data_->content.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString();
break;
case VariantType::OwnedString:
str = data_->content.asOwnedString->data;
break;
case VariantType::Float:
return static_cast<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return static_cast<T>(eightByteValue->asDouble);
#endif
default:
return 0.0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
template <typename T>
T asIntegral() const {
if (!data_)
return 0;
static_assert(is_integral<T>::value, "T must be an integral type");
#if ARDUINOJSON_USE_8_BYTE_POOL
auto eightByteValue = getEightByte();
#endif
const char* str = nullptr;
switch (data_->type) {
case VariantType::Boolean:
return data_->content.asBoolean;
case VariantType::Uint32:
return convertNumber<T>(data_->content.asUint32);
case VariantType::Int32:
return convertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return convertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return convertNumber<T>(eightByteValue->asInt64);
#endif
case VariantType::TinyString:
str = data_->content.asTinyString;
break;
case VariantType::LinkedString:
str = asLinkedString();
break;
case VariantType::OwnedString:
str = data_->content.asOwnedString->data;
break;
case VariantType::Float:
return convertNumber<T>(data_->content.asFloat);
#if ARDUINOJSON_USE_DOUBLE
case VariantType::Double:
return convertNumber<T>(eightByteValue->asDouble);
#endif
default:
return 0;
}
ARDUINOJSON_ASSERT(str != nullptr);
return parseNumber<T>(str);
}
ObjectImpl asObject() {
return ObjectImpl(isObject() ? &data_->content.asCollection : nullptr,
resources_);
}
JsonString asRawString() const {
switch (type()) {
case VariantType::RawString:
return JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length);
default:
return JsonString();
}
}
const char* asLinkedString() const {
ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
return resources_->getStaticString(data_->content.asSlotId);
}
JsonString asString() const {
switch (type()) {
case VariantType::TinyString:
return JsonString(data_->content.asTinyString);
case VariantType::LinkedString:
return JsonString(asLinkedString(), true);
case VariantType::OwnedString:
return JsonString(data_->content.asOwnedString->data,
data_->content.asOwnedString->length);
default:
return JsonString();
}
}
#if ARDUINOJSON_USE_8_BYTE_POOL
const EightByteValue* getEightByte() const {
return type() & VariantTypeBits::EightByteBit
? resources_->getEightByte(data_->content.asSlotId)
: 0;
}
#endif
VariantData* getElement(size_t index) {
return asArray().getElement(index);
}
template <typename TAdaptedString>
VariantData* getMember(TAdaptedString key) {
return asObject().getMember(key);
}
VariantData* getOrAddElement(size_t index) {
auto array = isNull() ? toArray() : asArray();
return array.getOrAddElement(index);
}
template <typename TAdaptedString>
VariantData* getOrAddMember(TAdaptedString key) {
if (key.isNull())
return nullptr;
auto obj = isNull() ? toObject() : asObject();
return obj.getOrAddMember(key);
}
bool isArray() const {
return type() == VariantType::Array;
}
bool isBoolean() const {
return type() == VariantType::Boolean;
}
bool isCollection() const {
return type() & VariantTypeBits::CollectionMask;
}
bool isFloat() const {
return data_ && data_->isFloat();
}
template <typename T>
bool isInteger() const {
if (!data_)
return false;
#if ARDUINOJSON_USE_LONG_LONG
auto eightByteValue = getEightByte();
#endif
switch (data_->type) {
case VariantType::Uint32:
return canConvertNumber<T>(data_->content.asUint32);
case VariantType::Int32:
return canConvertNumber<T>(data_->content.asInt32);
#if ARDUINOJSON_USE_LONG_LONG
case VariantType::Uint64:
return canConvertNumber<T>(eightByteValue->asUint64);
case VariantType::Int64:
return canConvertNumber<T>(eightByteValue->asInt64);
#endif
default:
return false;
}
}
bool isNull() const {
return type() == VariantType::Null;
}
bool isObject() const {
return type() == VariantType::Object;
}
bool isString() const {
return data_ && data_->isString();
}
size_t nesting() {
return asCollection().nesting();
}
void removeElement(size_t index) {
asArray().removeElement(index);
}
template <typename TAdaptedString>
void removeMember(TAdaptedString key) {
asObject().removeMember(key);
}
bool setBoolean(bool value) {
if (!data_)
return false;
data_->setBoolean(value);
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return false;
data_->type = VariantType::Float;
data_->content.asFloat = value;
return true;
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
float valueAsFloat = static_cast<float>(value);
#if ARDUINOJSON_USE_DOUBLE
if (value == valueAsFloat) {
data_->type = VariantType::Float;
data_->content.asFloat = valueAsFloat;
} else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Double;
data_->content.asSlotId = slot.id();
slot->asDouble = value;
}
#else
data_->type = VariantType::Float;
data_->content.asFloat = valueAsFloat;
#endif
return true;
}
template <typename T>
enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
if (canConvertNumber<int32_t>(value)) {
data_->type = VariantType::Int32;
data_->content.asInt32 = static_cast<int32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Int64;
data_->content.asSlotId = slot.id();
slot->asInt64 = value;
}
#endif
return true;
}
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
if (canConvertNumber<uint32_t>(value)) {
data_->type = VariantType::Uint32;
data_->content.asUint32 = static_cast<uint32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources_->allocEightByte();
if (!slot)
return false;
data_->type = VariantType::Uint64;
data_->content.asSlotId = slot.id();
slot->asUint64 = value;
}
#endif
return true;
}
template <typename T>
void setRawString(SerializedValue<T> value);
template <typename TAdaptedString>
bool setString(TAdaptedString value);
bool setLinkedString(const char* s);
size_t size() {
if (isObject())
return asObject().size();
if (isArray())
return asArray().size();
return 0;
}
ArrayImpl toArray() {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return ArrayImpl();
data_->type = VariantType::Array;
return ArrayImpl(new (&data_->content.asCollection) CollectionData(),
resources_);
}
ObjectImpl toObject() {
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
if (!data_)
return ObjectImpl();
data_->type = VariantType::Object;
return ObjectImpl(new (&data_->content.asCollection) CollectionData(),
resources_);
}
VariantType type() const {
return data_ ? data_->type : VariantType::Null;
}
// Release the resources used by this variant and set it to null.
void clear();
private:
VariantData* data_;
ResourceManager* resources_;
};
template <typename T>
inline void VariantImpl::setRawString(SerializedValue<T> value) {
if (!data_)
return;
auto dup = resources_->saveString(adaptString(value.data(), value.size()));
inline void VariantData::setRawString(SerializedValue<T> value,
ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
auto dup = resources->saveString(adaptString(value.data(), value.size()));
if (dup)
data_->setRawString(dup);
setRawString(dup);
}
inline bool VariantImpl::setLinkedString(const char* s) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
inline bool VariantData::setLinkedString(const char* s,
ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
auto slotId = resources_->saveStaticString(s);
auto slotId = resources->saveStaticString(s);
if (slotId == NULL_SLOT)
return false;
data_->type = VariantType::LinkedString;
data_->content.asSlotId = slotId;
type_ = VariantType::LinkedString;
content_.asSlotId = slotId;
return true;
}
template <typename TAdaptedString>
inline bool VariantImpl::setString(TAdaptedString value) {
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
if (!data_)
return false;
inline bool VariantData::setString(TAdaptedString value,
ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
if (value.isNull())
return false;
if (value.isStatic())
return setLinkedString(value.data());
return setLinkedString(value.data(), resources);
if (isTinyString(value, value.size())) {
data_->setTinyString(value);
setTinyString(value);
return true;
}
auto dup = resources_->saveString(value);
auto dup = resources->saveString(value);
if (dup) {
data_->setOwnedString(dup);
setOwnedString(dup);
return true;
}
return false;
}
inline void VariantImpl::clear() {
if (!data_)
return;
if (data_->type & VariantTypeBits::OwnedStringBit)
resources_->dereferenceString(data_->content.asOwnedString->data);
inline void VariantData::clear(ResourceManager* resources) {
if (type_ & VariantTypeBits::OwnedStringBit)
resources->dereferenceString(content_.asOwnedString->data);
#if ARDUINOJSON_USE_8_BYTE_POOL
if (data_->type & VariantTypeBits::EightByteBit)
resources_->freeEightByte(data_->content.asSlotId);
if (type_ & VariantTypeBits::EightByteBit)
resources->freeEightByte(content_.asSlotId);
#endif
asCollection().clear();
auto collection = asCollection();
if (collection)
collection->clear(resources);
data_->type = VariantType::Null;
type_ = VariantType::Null;
}
#if ARDUINOJSON_USE_8_BYTE_POOL
inline const EightByteValue* VariantData::getEightByte(
const ResourceManager* resources) const {
return type_ & VariantTypeBits::EightByteBit
? resources->getEightByte(content_.asSlotId)
: 0;
}
#endif
inline const char* VariantData::asLinkedString(
const ResourceManager* resources) const {
ARDUINOJSON_ASSERT(type_ == VariantType::LinkedString);
return resources->getStaticString(content_.asSlotId);
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
(void)resources; // silence warning
float valueAsFloat = static_cast<float>(value);
#if ARDUINOJSON_USE_DOUBLE
if (value == valueAsFloat) {
type_ = VariantType::Float;
content_.asFloat = valueAsFloat;
} else {
auto slot = resources->allocEightByte();
if (!slot)
return false;
type_ = VariantType::Double;
content_.asSlotId = slot.id();
slot->asDouble = value;
}
#else
type_ = VariantType::Float;
content_.asFloat = valueAsFloat;
#endif
return true;
}
template <typename T>
enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
(void)resources; // silence warning
if (canConvertNumber<int32_t>(value)) {
type_ = VariantType::Int32;
content_.asInt32 = static_cast<int32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources->allocEightByte();
if (!slot)
return false;
type_ = VariantType::Int64;
content_.asSlotId = slot.id();
slot->asInt64 = value;
}
#endif
return true;
}
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> VariantData::setInteger(
T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
(void)resources; // silence warning
if (canConvertNumber<uint32_t>(value)) {
type_ = VariantType::Uint32;
content_.asUint32 = static_cast<uint32_t>(value);
}
#if ARDUINOJSON_USE_LONG_LONG
else {
auto slot = resources->allocEightByte();
if (!slot)
return false;
type_ = VariantType::Uint64;
content_.asSlotId = slot.id();
slot->asUint64 = value;
}
#endif
return true;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -29,13 +29,13 @@ class VariantRefBase : public VariantTag {
// Sets the value to null.
// https://arduinojson.org/v7/api/jsonvariant/clear/
void clear() const {
getOrCreateVariantImpl().clear();
VariantData::clear(getOrCreateData(), getResourceManager());
}
// Returns true if the value is null or the reference is unbound.
// https://arduinojson.org/v7/api/jsonvariant/isnull/
bool isNull() const {
return getVariantImpl().isNull();
return VariantData::isNull(getData());
}
// Returns true if the reference is unbound.
@ -93,13 +93,13 @@ class VariantRefBase : public VariantTag {
// Returns the size of the array or object.
// https://arduinojson.org/v7/api/jsonvariant/size/
size_t size() const {
return getVariantImpl().size();
return VariantData::size(getData(), getResourceManager());
}
// Returns the depth (nesting level) of the value.
// https://arduinojson.org/v7/api/jsonvariant/nesting/
size_t nesting() const {
return getVariantImpl().nesting();
return VariantData::nesting(getData(), getResourceManager());
}
// Appends a new (empty) element to the array.
@ -120,34 +120,38 @@ class VariantRefBase : public VariantTag {
// https://arduinojson.org/v7/api/jsonvariant/add/
template <typename T>
bool add(const T& value) const {
return getOrCreateVariantImpl().addValue(value);
return detail::VariantData::addValue(getOrCreateData(), value,
getResourceManager());
}
// Appends a value to the array.
// https://arduinojson.org/v7/api/jsonvariant/add/
template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
bool add(T* value) const {
return getOrCreateVariantImpl().addValue(value);
return detail::VariantData::addValue(getOrCreateData(), value,
getResourceManager());
}
// Removes an element of the array.
// https://arduinojson.org/v7/api/jsonvariant/remove/
void remove(size_t index) const {
getVariantImpl().removeElement(index);
VariantData::removeElement(getData(), index, getResourceManager());
}
// Removes a member of the object.
// https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
void remove(TChar* key) const {
getVariantImpl().removeMember(adaptString(key));
VariantData::removeMember(getData(), adaptString(key),
getResourceManager());
}
// Removes a member of the object.
// https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
void remove(const TString& key) const {
getVariantImpl().removeMember(adaptString(key));
VariantData::removeMember(getData(), adaptString(key),
getResourceManager());
}
// Removes a member of the object or an element of the array.
@ -271,14 +275,6 @@ class VariantRefBase : public VariantTag {
return VariantAttorney::getOrCreateData(derived());
}
VariantImpl getVariantImpl() const {
return VariantImpl(getData(), getResourceManager());
}
VariantImpl getOrCreateVariantImpl() const {
return VariantImpl(getOrCreateData(), getResourceManager());
}
FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {

View File

@ -69,20 +69,23 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
inline T VariantRefBase<TDerived>::add() const {
return JsonVariant(getOrCreateVariantImpl().addElement(),
getResourceManager());
return JsonVariant(
detail::VariantData::addElement(getOrCreateData(), getResourceManager()),
getResourceManager());
}
template <typename TDerived>
template <typename TString, enable_if_t<IsString<TString>::value, int>>
inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const {
return getVariantImpl().getMember(adaptString(key)) != 0;
return VariantData::getMember(getData(), adaptString(key),
getResourceManager()) != 0;
}
template <typename TDerived>
template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const {
return getVariantImpl().getMember(adaptString(key)) != 0;
return VariantData::getMember(getData(), adaptString(key),
getResourceManager()) != 0;
}
template <typename TDerived>
@ -147,25 +150,26 @@ inline bool VariantRefBase<TDerived>::doSet(const T& value, true_type) const {
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonArray>::value, int>>
inline JsonArray VariantRefBase<TDerived>::to() const {
auto variant = getOrCreateVariantImpl();
variant.clear();
return JsonArray(variant.toArray());
return JsonArray(
VariantData::toArray(getOrCreateData(), getResourceManager()),
getResourceManager());
}
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonObject>::value, int>>
JsonObject VariantRefBase<TDerived>::to() const {
auto variant = getOrCreateVariantImpl();
variant.clear();
return JsonObject(variant.toObject());
return JsonObject(
VariantData::toObject(getOrCreateData(), getResourceManager()),
getResourceManager());
}
template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
JsonVariant VariantRefBase<TDerived>::to() const {
detail::VariantImpl impl(getOrCreateData(), getResourceManager());
impl.clear();
return JsonVariant(impl);
auto data = getOrCreateData();
auto resources = getResourceManager();
detail::VariantData::clear(data, resources);
return JsonVariant(data, resources);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -4,8 +4,8 @@
#pragma once
#define ARDUINOJSON_VERSION "7.4.2"
#define ARDUINOJSON_VERSION "7.4.1"
#define ARDUINOJSON_VERSION_MAJOR 7
#define ARDUINOJSON_VERSION_MINOR 4
#define ARDUINOJSON_VERSION_REVISION 2
#define ARDUINOJSON_VERSION_MACRO V742
#define ARDUINOJSON_VERSION_REVISION 1
#define ARDUINOJSON_VERSION_MACRO V741