Compare commits

..

10 Commits

Author SHA1 Message Date
5bc6a3077a Move VariantImpl definition to VariantImpl.hpp 2025-06-26 20:48:13 +02:00
31a729d35c Extract VariantImpl 2025-06-26 20:48:13 +02:00
48dec78781 Extract ArrayImpl, CollectionImpl, and ObjectImpl 2025-06-26 20:48:13 +02:00
8cc190b7bf JsonObject: replace ObjectData* member with VariantData* 2025-06-26 20:48:13 +02:00
b1a58f3c0c JsonArray: replace ArrayData* member with VariantData* 2025-06-26 20:48:13 +02:00
fe6de3a82b JsonObjectConst: replace ObjectData* member with VariantData* 2025-06-26 20:48:13 +02:00
e27439f3cd JsonArrayConst: replace ArrayData* member with VariantData* 2025-06-26 20:48:13 +02:00
7e093ffc25 Add a pool dedicated to 8-byte values (double/int64_t/uint64_t)
This new pool replaced the "extension" slot where a secondary variant slot was used to store 8-byte values.
2025-06-26 20:48:13 +02:00
8e3286aac8 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-06-26 20:48:13 +02:00
c07744dc0d CI: upgrade Windows runner 2025-06-26 20:47:57 +02:00
14 changed files with 512 additions and 538 deletions

View File

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

View File

@ -51,7 +51,6 @@
#include "ArduinoJson/Variant/ConverterImpl.hpp" #include "ArduinoJson/Variant/ConverterImpl.hpp"
#include "ArduinoJson/Variant/JsonVariantCopier.hpp" #include "ArduinoJson/Variant/JsonVariantCopier.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp" #include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp" #include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp" #include "ArduinoJson/Json/JsonDeserializer.hpp"

View File

@ -308,8 +308,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
template <typename TString, template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) { void remove(const TString& key) {
detail::VariantImpl(getData(), getResourceManager()) getVariantImpl().removeMember(detail::adaptString(key));
.removeMember(detail::adaptString(key));
} }
// Removes a member of the root object or an element of the root array. // Removes a member of the root object or an element of the root array.

View File

@ -4,10 +4,9 @@
#pragma once #pragma once
#include <ArduinoJson/Collection/CollectionData.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp> #include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Variant/VariantData.hpp> #include <ArduinoJson/Variant/VariantImpl.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE

View File

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

View File

@ -56,9 +56,7 @@ class MemberProxy
} }
VariantData* getData() const { VariantData* getData() const {
VariantImpl variant(VariantAttorney::getData(upstream_), return VariantAttorney::getVariantImpl(upstream_).getMember(key_);
VariantAttorney::getResourceManager(upstream_));
return variant.getMember(key_);
} }
VariantData* getOrCreateData() const { VariantData* getOrCreateData() const {

View File

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

View File

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

View File

@ -240,8 +240,7 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
auto data = detail::VariantAttorney::getData(dst); auto data = detail::VariantAttorney::getData(dst);
if (!resources || !data) if (!resources || !data)
return; return;
detail::VariantImpl impl(data, resources); detail::VariantImpl(data, resources).clear();
impl.clear();
detail::StringBuilderPrint print(resources); detail::StringBuilderPrint print(resources);
src.printTo(print); src.printTo(print);
if (print.overflowed()) if (print.overflowed())

View File

@ -50,9 +50,8 @@ typename TVisitor::result_type accept(JsonVariantConst variant,
TVisitor& visit) { TVisitor& visit) {
auto data = VariantAttorney::getData(variant); auto data = VariantAttorney::getData(variant);
auto resources = VariantAttorney::getResourceManager(variant); auto resources = VariantAttorney::getResourceManager(variant);
VariantImpl impl(data, resources);
VisitorAdapter<TVisitor> adapter(visit); VisitorAdapter<TVisitor> adapter(visit);
return impl.accept(adapter); return VariantImpl(data, resources).accept(adapter);
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -6,7 +6,7 @@
#include <ArduinoJson/Polyfills/attributes.hpp> #include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp> #include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantData.hpp> #include <ArduinoJson/Variant/VariantImpl.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp> #include <ArduinoJson/Variant/VariantTo.hpp>
#include "JsonVariantConst.hpp" #include "JsonVariantConst.hpp"

View File

@ -4,12 +4,7 @@
#pragma once #pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringNode.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> #include <ArduinoJson/Variant/VariantContent.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
@ -86,422 +81,4 @@ struct VariantData {
} }
}; };
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;
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;
#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);
template <typename T>
enable_if_t<is_signed<T>::value, bool> setInteger(T value);
template <typename T>
enable_if_t<is_unsigned<T>::value, bool> setInteger(T value);
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_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -5,10 +5,506 @@
#pragma once #pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp> #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> #include <ArduinoJson/Variant/VariantData.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE 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> template <typename T>
inline void VariantImpl::setRawString(SerializedValue<T> value) { inline void VariantImpl::setRawString(SerializedValue<T> value) {
if (!data_) if (!data_)
@ -75,93 +571,4 @@ inline void VariantImpl::clear() {
data_->type = VariantType::Null; data_->type = VariantType::Null;
} }
#if ARDUINOJSON_USE_8_BYTE_POOL
inline const EightByteValue* VariantImpl::getEightByte() const {
return type() & VariantTypeBits::EightByteBit
? resources_->getEightByte(data_->content.asSlotId)
: 0;
}
#endif
inline const char* VariantImpl::asLinkedString() const {
ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
return resources_->getStaticString(data_->content.asSlotId);
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> VariantImpl::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> VariantImpl::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> VariantImpl::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;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -69,22 +69,20 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
template <typename TDerived> template <typename TDerived>
template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>> template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
inline T VariantRefBase<TDerived>::add() const { inline T VariantRefBase<TDerived>::add() const {
detail::VariantImpl variant(getOrCreateData(), getResourceManager()); return JsonVariant(getOrCreateVariantImpl().addElement(),
return JsonVariant(variant.addElement(), getResourceManager()); getResourceManager());
} }
template <typename TDerived> template <typename TDerived>
template <typename TString, enable_if_t<IsString<TString>::value, int>> template <typename TString, enable_if_t<IsString<TString>::value, int>>
inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const { inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const {
detail::VariantImpl variant(getData(), getResourceManager()); return getVariantImpl().getMember(adaptString(key)) != 0;
return variant.getMember(adaptString(key)) != 0;
} }
template <typename TDerived> template <typename TDerived>
template <typename TChar, enable_if_t<IsString<TChar*>::value, int>> template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const { inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const {
detail::VariantImpl variant(getData(), getResourceManager()); return getVariantImpl().getMember(adaptString(key)) != 0;
return variant.getMember(adaptString(key)) != 0;
} }
template <typename TDerived> template <typename TDerived>