mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-12-23 07:02:34 +01:00
Compare commits
1 Commits
7.6-with-s
...
string-lit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd2dccda0e |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
------
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 7.4.2.{build}
|
||||
version: 7.4.1.{build}
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
|
||||
@@ -12,7 +12,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
||||
|
||||
static_assert(ARDUINOJSON_USE_DOUBLE == 0, "ARDUINOJSON_USE_DOUBLE");
|
||||
|
||||
static_assert(sizeof(ArduinoJson::detail::VariantData) == 6, "slot size");
|
||||
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 6, "slot size");
|
||||
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
@@ -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(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
|
||||
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
@@ -10,6 +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) == 16, "slot size");
|
||||
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 16,
|
||||
"slot size");
|
||||
|
||||
int main() {}
|
||||
|
||||
@@ -10,6 +10,6 @@ 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(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
|
||||
|
||||
int main() {}
|
||||
|
||||
@@ -269,10 +269,10 @@ inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
|
||||
return sizeof(MemoryPool<VariantData>) * n;
|
||||
}
|
||||
|
||||
template <typename T = ArduinoJson::detail::VariantData>
|
||||
inline size_t sizeofPool(
|
||||
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
|
||||
return ArduinoJson::detail::MemoryPool<T>::slotsToBytes(n);
|
||||
using namespace ArduinoJson::detail;
|
||||
return MemoryPool<VariantData>::slotsToBytes(n);
|
||||
}
|
||||
|
||||
inline size_t sizeofStaticStringPool(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("JsonArray::add(T)") {
|
||||
SpyingAllocator spy;
|
||||
@@ -33,8 +33,7 @@ TEST_CASE("JsonArray::add(T)") {
|
||||
REQUIRE(array[0].is<double>());
|
||||
REQUIRE_FALSE(array[0].is<bool>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool<VariantData>()),
|
||||
Allocate(sizeofPool<EightByteValue>()),
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("deserialize JSON array") {
|
||||
SpyingAllocator spy;
|
||||
@@ -92,12 +92,8 @@ TEST_CASE("deserialize JSON array") {
|
||||
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
|
||||
REQUIRE(arr[1] == -7E89);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool<VariantData>()),
|
||||
Allocate(sizeofPool<EightByteValue>()),
|
||||
Reallocate(sizeofPool<VariantData>(),
|
||||
sizeofPool<VariantData>(2)),
|
||||
Reallocate(sizeofPool<EightByteValue>(),
|
||||
sizeofPool<EightByteValue>(2)),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(4)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns NoMemory if 8-bit slot allocation fails") {
|
||||
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
|
||||
SECTION("uint32_t should pass") {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
|
||||
|
||||
@@ -197,11 +197,11 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<double>() == true);
|
||||
REQUIRE(variant.as<double>() == 1.2);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool<EightByteValue>()),
|
||||
Reallocate(sizeofPool<EightByteValue>(),
|
||||
sizeofPool<EightByteValue>(1)),
|
||||
});
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("int32_t") {
|
||||
@@ -220,11 +220,11 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<int64_t>() == true);
|
||||
REQUIRE(variant.as<int64_t>() == -2147483649LL);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool<EightByteValue>()),
|
||||
Reallocate(sizeofPool<EightByteValue>(),
|
||||
sizeofPool<EightByteValue>(1)),
|
||||
});
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("uint32_t") {
|
||||
@@ -243,11 +243,11 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<uint64_t>() == true);
|
||||
REQUIRE(variant.as<uint64_t>() == 4294967296);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool<EightByteValue>()),
|
||||
Reallocate(sizeofPool<EightByteValue>(),
|
||||
sizeofPool<EightByteValue>(1)),
|
||||
});
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("JsonDocument") {
|
||||
@@ -378,7 +378,7 @@ TEST_CASE("JsonVariant::set() releases the previous value") {
|
||||
}
|
||||
|
||||
SECTION("float") {
|
||||
v.set(1.2f);
|
||||
v.set(1.2);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("world")),
|
||||
});
|
||||
@@ -393,7 +393,7 @@ TEST_CASE("JsonVariant::set() releases the previous value") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::set() reuses 8-bit slot") {
|
||||
TEST_CASE("JsonVariant::set() reuses extension slot") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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\"}");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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=*
|
||||
|
||||
@@ -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"
|
||||
@@ -45,10 +36,13 @@
|
||||
#include "ArduinoJson/Array/ElementProxy.hpp"
|
||||
#include "ArduinoJson/Array/Utilities.hpp"
|
||||
#include "ArduinoJson/Collection/CollectionImpl.hpp"
|
||||
#include "ArduinoJson/Memory/ResourceManagerImpl.hpp"
|
||||
#include "ArduinoJson/Object/MemberProxy.hpp"
|
||||
#include "ArduinoJson/Object/ObjectImpl.hpp"
|
||||
#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"
|
||||
|
||||
66
src/ArduinoJson/Array/ArrayData.hpp
Normal file
66
src/ArduinoJson/Array/ArrayData.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class ArrayData : public CollectionData {
|
||||
public:
|
||||
VariantData* addElement(ResourceManager* resources);
|
||||
|
||||
static VariantData* addElement(ArrayData* array, ResourceManager* resources) {
|
||||
if (!array)
|
||||
return nullptr;
|
||||
return array->addElement(resources);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool addValue(const T& value, ResourceManager* resources);
|
||||
|
||||
template <typename T>
|
||||
static bool addValue(ArrayData* array, const T& value,
|
||||
ResourceManager* resources) {
|
||||
if (!array)
|
||||
return false;
|
||||
return array->addValue(value, resources);
|
||||
}
|
||||
|
||||
VariantData* getOrAddElement(size_t index, ResourceManager* resources);
|
||||
|
||||
VariantData* getElement(size_t index, const ResourceManager* resources) const;
|
||||
|
||||
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 ResourceManager* resources) const;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -4,57 +4,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionIterator.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <ArduinoJson/Array/ArrayData.hpp>
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline VariantImpl::iterator VariantImpl::at(size_t index) const {
|
||||
if (!isArray())
|
||||
return iterator();
|
||||
|
||||
auto it = createIterator();
|
||||
inline ArrayData::iterator ArrayData::at(
|
||||
size_t index, const ResourceManager* resources) const {
|
||||
auto it = createIterator(resources);
|
||||
while (!it.done() && index) {
|
||||
it.move();
|
||||
it.next(resources);
|
||||
--index;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::addElement() {
|
||||
if (!isArray())
|
||||
return nullptr;
|
||||
auto slot = allocVariant();
|
||||
inline VariantData* ArrayData::addElement(ResourceManager* resources) {
|
||||
auto slot = resources->allocVariant();
|
||||
if (!slot)
|
||||
return nullptr;
|
||||
addElement(slot);
|
||||
CollectionData::appendOne(slot, resources);
|
||||
return slot.ptr();
|
||||
}
|
||||
|
||||
inline void VariantImpl::addElement(Slot<VariantData> slot) {
|
||||
auto coll = getCollectionData();
|
||||
|
||||
if (coll->tail != NULL_SLOT) {
|
||||
auto tail = getVariant(coll->tail);
|
||||
tail->next = slot.id();
|
||||
coll->tail = slot.id();
|
||||
} else {
|
||||
coll->head = slot.id();
|
||||
coll->tail = slot.id();
|
||||
}
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::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.move();
|
||||
it.next(resources);
|
||||
index--;
|
||||
}
|
||||
if (it.done())
|
||||
index++;
|
||||
VariantData* element = it->data();
|
||||
VariantData* element = it.data();
|
||||
while (index > 0) {
|
||||
element = addElement();
|
||||
element = addElement(resources);
|
||||
if (!element)
|
||||
return nullptr;
|
||||
index--;
|
||||
@@ -62,48 +47,33 @@ inline VariantData* VariantImpl::getOrAddElement(size_t index) {
|
||||
return element;
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::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 VariantImpl::removeElement(iterator it) {
|
||||
if (!isArray())
|
||||
return;
|
||||
removeOne(it);
|
||||
inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
|
||||
remove(at(index, resources), resources);
|
||||
}
|
||||
|
||||
inline void VariantImpl::removeElement(size_t index) {
|
||||
removeElement(at(index));
|
||||
}
|
||||
|
||||
inline bool VariantImpl::copyArray(const VariantImpl& src) {
|
||||
ARDUINOJSON_ASSERT(isNull());
|
||||
|
||||
if (!data_)
|
||||
template <typename T>
|
||||
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);
|
||||
if (!variant.set(value)) {
|
||||
resources->freeVariant(slot);
|
||||
return false;
|
||||
|
||||
data_->toArray();
|
||||
|
||||
for (auto it = src.createIterator(); !it.done(); it.move()) {
|
||||
auto slot = allocVariant();
|
||||
if (!slot)
|
||||
return false;
|
||||
|
||||
VariantImpl element(slot.ptr(), resources_);
|
||||
if (!element.copyVariant(*it)) {
|
||||
freeVariant(slot);
|
||||
return false;
|
||||
}
|
||||
|
||||
addElement(slot);
|
||||
}
|
||||
|
||||
CollectionData::appendOne(slot, resources);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the size (in bytes) of an array with n elements.
|
||||
constexpr size_t sizeofArray(size_t n) {
|
||||
return n * sizeof(VariantData);
|
||||
return n * ResourceManager::slotSize;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -50,15 +50,22 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
|
||||
: upstream_(src.upstream_), index_(src.index_) {}
|
||||
// clang-format on
|
||||
|
||||
VariantImpl getImpl() const {
|
||||
auto impl = VariantAttorney::getImpl(upstream_);
|
||||
return VariantImpl(impl.getElement(index_), impl.resources());
|
||||
ResourceManager* getResourceManager() const {
|
||||
return VariantAttorney::getResourceManager(upstream_);
|
||||
}
|
||||
|
||||
VariantImpl getOrCreateImpl() const {
|
||||
auto impl = VariantAttorney::getOrCreateImpl(upstream_);
|
||||
impl.toArrayIfNull();
|
||||
return VariantImpl(impl.getOrAddElement(index_), impl.resources());
|
||||
FORCE_INLINE VariantData* getData() const {
|
||||
return VariantData::getElement(
|
||||
VariantAttorney::getData(upstream_), index_,
|
||||
VariantAttorney::getResourceManager(upstream_));
|
||||
}
|
||||
|
||||
VariantData* getOrCreateData() const {
|
||||
auto data = VariantAttorney::getOrCreateData(upstream_);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
return data->getOrAddElement(
|
||||
index_, VariantAttorney::getResourceManager(upstream_));
|
||||
}
|
||||
|
||||
TUpstream upstream_;
|
||||
|
||||
@@ -20,21 +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::VariantImpl 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(impl_);
|
||||
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(impl_);
|
||||
return JsonArrayConst(data_, resources_);
|
||||
}
|
||||
|
||||
// Appends a new (empty) element to the array.
|
||||
@@ -52,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_.resources());
|
||||
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 doAdd(value);
|
||||
return detail::ArrayData::addValue(data_, value, resources_);
|
||||
}
|
||||
|
||||
// Appends a value to the array.
|
||||
@@ -67,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 doAdd(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());
|
||||
if (!data_)
|
||||
return iterator();
|
||||
return iterator(data_->createIterator(resources_), resources_);
|
||||
}
|
||||
|
||||
// Returns an iterator following the last element of the array.
|
||||
@@ -85,20 +91,28 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
// Copies an array.
|
||||
// https://arduinojson.org/v7/api/jsonarray/set/
|
||||
bool set(JsonArrayConst src) const {
|
||||
impl_.clear();
|
||||
return impl_.copyArray(detail::VariantAttorney::getImpl(src));
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
clear();
|
||||
for (auto element : src) {
|
||||
if (!add(element))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes the element at the specified iterator.
|
||||
// https://arduinojson.org/v7/api/jsonarray/remove/
|
||||
void remove(iterator it) const {
|
||||
impl_.removeElement(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.
|
||||
@@ -113,8 +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 {
|
||||
if (impl_.isArray())
|
||||
impl_.empty();
|
||||
detail::ArrayData::clear(data_, resources_);
|
||||
}
|
||||
|
||||
// Gets or sets the element at the specified index.
|
||||
@@ -137,31 +150,31 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(impl_);
|
||||
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
|
||||
@@ -187,36 +200,20 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
}
|
||||
|
||||
private:
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return resources_;
|
||||
}
|
||||
|
||||
const detail::VariantImpl& getOrCreateImpl() const {
|
||||
return impl_;
|
||||
detail::VariantData* getData() const {
|
||||
return collectionToVariant(data_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool doAdd(const T& value) const {
|
||||
if (!impl_.isArray())
|
||||
return false;
|
||||
|
||||
auto resources = impl_.resources();
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto slot = resources->allocVariant();
|
||||
if (!slot)
|
||||
return false;
|
||||
|
||||
if (!JsonVariant(slot.ptr(), resources).set(value)) {
|
||||
detail::VariantImpl(slot.ptr(), resources).clear();
|
||||
resources->freeVariant(slot);
|
||||
return false;
|
||||
}
|
||||
impl_.addElement(slot);
|
||||
return true;
|
||||
detail::VariantData* getOrCreateData() const {
|
||||
return collectionToVariant(data_);
|
||||
}
|
||||
|
||||
mutable detail::VariantImpl impl_;
|
||||
detail::ArrayData* data_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -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());
|
||||
if (!data_)
|
||||
return iterator();
|
||||
return iterator(data_->createIterator(resources_), resources_);
|
||||
}
|
||||
|
||||
// Returns an iterator to the element following the last element of the array.
|
||||
@@ -34,17 +36,21 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
}
|
||||
|
||||
// Creates an unbound reference.
|
||||
JsonArrayConst() {}
|
||||
JsonArrayConst() : data_(0), resources_(0) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonArrayConst(const detail::VariantImpl& 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_.resources());
|
||||
return JsonVariantConst(
|
||||
detail::ArrayData::getElement(data_, size_t(index), resources_),
|
||||
resources_);
|
||||
}
|
||||
|
||||
// Returns the element at the specified index.
|
||||
@@ -59,31 +65,31 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(impl_);
|
||||
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
|
||||
@@ -93,11 +99,12 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
}
|
||||
|
||||
private:
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
const detail::VariantData* getData() const {
|
||||
return collectionToVariant(data_);
|
||||
}
|
||||
|
||||
detail::VariantImpl impl_;
|
||||
const detail::ArrayData* data_;
|
||||
const detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
// Compares the content of two arrays.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionIterator.hpp>
|
||||
#include <ArduinoJson/Variant/JsonVariant.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
@@ -31,11 +30,12 @@ class JsonArrayIterator {
|
||||
|
||||
public:
|
||||
JsonArrayIterator() {}
|
||||
explicit JsonArrayIterator(const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
explicit JsonArrayIterator(detail::ArrayData::iterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
|
||||
JsonVariant operator*() {
|
||||
return JsonVariant(*iterator_);
|
||||
return JsonVariant(iterator_.data(), resources_);
|
||||
}
|
||||
Ptr<JsonVariant> operator->() {
|
||||
return operator*();
|
||||
@@ -50,12 +50,13 @@ class JsonArrayIterator {
|
||||
}
|
||||
|
||||
JsonArrayIterator& operator++() {
|
||||
iterator_.move();
|
||||
iterator_.next(resources_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl::iterator iterator_;
|
||||
detail::ArrayData::iterator iterator_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
class JsonArrayConstIterator {
|
||||
@@ -63,11 +64,12 @@ class JsonArrayConstIterator {
|
||||
|
||||
public:
|
||||
JsonArrayConstIterator() {}
|
||||
explicit JsonArrayConstIterator(const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
explicit JsonArrayConstIterator(detail::ArrayData::iterator iterator,
|
||||
const detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
|
||||
JsonVariantConst operator*() const {
|
||||
return JsonVariantConst(*iterator_);
|
||||
return JsonVariantConst(iterator_.data(), resources_);
|
||||
}
|
||||
Ptr<JsonVariantConst> operator->() {
|
||||
return operator*();
|
||||
@@ -82,12 +84,13 @@ class JsonArrayConstIterator {
|
||||
}
|
||||
|
||||
JsonArrayConstIterator& operator++() {
|
||||
iterator_.move();
|
||||
iterator_.next(resources_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable detail::VariantImpl::iterator iterator_;
|
||||
detail::ArrayData::iterator iterator_;
|
||||
const detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
122
src/ArduinoJson/Collection/CollectionData.hpp
Normal file
122
src/ArduinoJson/Collection/CollectionData.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class VariantData;
|
||||
class ResourceManager;
|
||||
|
||||
class CollectionIterator {
|
||||
friend class CollectionData;
|
||||
|
||||
public:
|
||||
CollectionIterator() : slot_(nullptr), currentId_(NULL_SLOT) {}
|
||||
|
||||
void next(const ResourceManager* resources);
|
||||
|
||||
bool done() const {
|
||||
return slot_ == nullptr;
|
||||
}
|
||||
|
||||
bool operator==(const CollectionIterator& other) const {
|
||||
return slot_ == other.slot_;
|
||||
}
|
||||
|
||||
bool operator!=(const CollectionIterator& other) const {
|
||||
return slot_ != other.slot_;
|
||||
}
|
||||
|
||||
VariantData* operator->() {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return data();
|
||||
}
|
||||
|
||||
VariantData& operator*() {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return *data();
|
||||
}
|
||||
|
||||
const VariantData& operator*() const {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return *data();
|
||||
}
|
||||
|
||||
VariantData* data() {
|
||||
return reinterpret_cast<VariantData*>(slot_);
|
||||
}
|
||||
|
||||
const VariantData* data() const {
|
||||
return reinterpret_cast<const VariantData*>(slot_);
|
||||
}
|
||||
|
||||
private:
|
||||
CollectionIterator(VariantData* slot, SlotId slotId);
|
||||
|
||||
VariantData* slot_;
|
||||
SlotId currentId_, nextId_;
|
||||
};
|
||||
|
||||
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 {}
|
||||
|
||||
using iterator = CollectionIterator;
|
||||
|
||||
iterator createIterator(const ResourceManager* resources) const;
|
||||
|
||||
size_t size(const ResourceManager*) const;
|
||||
size_t nesting(const ResourceManager*) const;
|
||||
|
||||
void clear(ResourceManager* resources);
|
||||
|
||||
static void clear(CollectionData* collection, ResourceManager* resources) {
|
||||
if (!collection)
|
||||
return;
|
||||
collection->clear(resources);
|
||||
}
|
||||
|
||||
SlotId head() const {
|
||||
return head_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void appendOne(Slot<VariantData> slot, const ResourceManager* resources);
|
||||
void appendPair(Slot<VariantData> key, Slot<VariantData> value,
|
||||
const ResourceManager* resources);
|
||||
|
||||
void removeOne(iterator it, ResourceManager* resources);
|
||||
void removePair(iterator it, ResourceManager* resources);
|
||||
|
||||
private:
|
||||
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
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionData.hpp>
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
@@ -11,104 +12,125 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline VariantImpl::iterator VariantImpl::createIterator() const {
|
||||
if (!data_ || !data_->isCollection())
|
||||
return iterator();
|
||||
auto coll = getCollectionData();
|
||||
return iterator(coll->head, resources_);
|
||||
inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId)
|
||||
: slot_(slot), currentId_(slotId) {
|
||||
nextId_ = slot_ ? slot_->next() : NULL_SLOT;
|
||||
}
|
||||
|
||||
inline void VariantImpl::addMember(Slot<VariantData> key,
|
||||
Slot<VariantData> value) {
|
||||
ARDUINOJSON_ASSERT(isObject());
|
||||
ARDUINOJSON_ASSERT(key);
|
||||
ARDUINOJSON_ASSERT(value);
|
||||
inline void CollectionIterator::next(const ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT);
|
||||
slot_ = resources->getVariant(nextId_);
|
||||
currentId_ = nextId_;
|
||||
if (slot_)
|
||||
nextId_ = slot_->next();
|
||||
}
|
||||
|
||||
auto coll = getCollectionData();
|
||||
inline CollectionData::iterator CollectionData::createIterator(
|
||||
const ResourceManager* resources) const {
|
||||
return iterator(resources->getVariant(head_), head_);
|
||||
}
|
||||
|
||||
key->next = value.id();
|
||||
|
||||
if (coll->tail != NULL_SLOT) {
|
||||
auto tail = getVariant(coll->tail);
|
||||
tail->next = key.id();
|
||||
coll->tail = value.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 {
|
||||
coll->head = key.id();
|
||||
coll->tail = value.id();
|
||||
head_ = slot.id();
|
||||
tail_ = slot.id();
|
||||
}
|
||||
}
|
||||
inline Slot<VariantData> VariantImpl::getPreviousSlot(
|
||||
VariantData* target) const {
|
||||
auto coll = getCollectionData();
|
||||
|
||||
inline void CollectionData::appendPair(Slot<VariantData> key,
|
||||
Slot<VariantData> value,
|
||||
const ResourceManager* resources) {
|
||||
key->setNext(value.id());
|
||||
|
||||
if (tail_ != NULL_SLOT) {
|
||||
auto tail = resources->getVariant(tail_);
|
||||
tail->setNext(key.id());
|
||||
tail_ = value.id();
|
||||
} else {
|
||||
head_ = key.id();
|
||||
tail_ = value.id();
|
||||
}
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
head_ = NULL_SLOT;
|
||||
tail_ = NULL_SLOT;
|
||||
}
|
||||
|
||||
inline Slot<VariantData> CollectionData::getPreviousSlot(
|
||||
VariantData* target, const ResourceManager* resources) const {
|
||||
auto prev = Slot<VariantData>();
|
||||
auto currentId = coll->head;
|
||||
auto currentId = head_;
|
||||
while (currentId != NULL_SLOT) {
|
||||
auto currentSlot = 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 VariantImpl::removeOne(iterator it) {
|
||||
inline void CollectionData::removeOne(iterator it, ResourceManager* resources) {
|
||||
if (it.done())
|
||||
return;
|
||||
auto coll = getCollectionData();
|
||||
auto curr = it->data();
|
||||
auto prev = getPreviousSlot(curr);
|
||||
auto next = curr->next;
|
||||
auto curr = it.slot_;
|
||||
auto prev = getPreviousSlot(curr, resources);
|
||||
auto next = curr->next();
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
prev->setNext(next);
|
||||
else
|
||||
coll->head = next;
|
||||
head_ = next;
|
||||
if (next == NULL_SLOT)
|
||||
coll->tail = prev.id();
|
||||
freeVariant({it->data(), it.slotId()});
|
||||
tail_ = prev.id();
|
||||
resources->freeVariant({it.slot_, it.currentId_});
|
||||
}
|
||||
|
||||
inline void VariantImpl::removePair(VariantImpl::iterator it) {
|
||||
inline void CollectionData::removePair(ObjectData::iterator it,
|
||||
ResourceManager* resources) {
|
||||
if (it.done())
|
||||
return;
|
||||
|
||||
auto keySlot = it->data();
|
||||
auto keySlot = it.slot_;
|
||||
|
||||
auto valueId = keySlot->next;
|
||||
auto valueSlot = getVariant(valueId);
|
||||
auto valueId = it.nextId_;
|
||||
auto valueSlot = resources->getVariant(valueId);
|
||||
|
||||
// remove value slot
|
||||
keySlot->next = valueSlot->next;
|
||||
freeVariant({valueSlot, valueId});
|
||||
keySlot->setNext(valueSlot->next());
|
||||
resources->freeVariant({valueSlot, valueId});
|
||||
|
||||
// remove key slot
|
||||
removeOne(it);
|
||||
removeOne(it, resources);
|
||||
}
|
||||
|
||||
inline size_t VariantImpl::nesting() const {
|
||||
if (!data_ || !data_->isCollection())
|
||||
return 0;
|
||||
inline size_t CollectionData::nesting(const ResourceManager* resources) const {
|
||||
size_t maxChildNesting = 0;
|
||||
for (auto it = createIterator(); !it.done(); it.move()) {
|
||||
auto childNesting = it->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 VariantImpl::size() const {
|
||||
if (!data_)
|
||||
return 0;
|
||||
|
||||
inline size_t CollectionData::size(const ResourceManager* resources) const {
|
||||
size_t count = 0;
|
||||
|
||||
for (auto it = createIterator(); !it.done(); it.move())
|
||||
for (auto it = createIterator(resources); !it.done(); it.next(resources))
|
||||
count++;
|
||||
|
||||
if (data_->type == VariantType::Object)
|
||||
count /= 2; // TODO: do this in JsonObject?
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class CollectionIterator {
|
||||
public:
|
||||
CollectionIterator() {}
|
||||
|
||||
CollectionIterator(SlotId slotId, ResourceManager* resources)
|
||||
: value_(resources->getVariant(slotId), resources), slotId_(slotId) {}
|
||||
|
||||
void move() {
|
||||
ARDUINOJSON_ASSERT(!done());
|
||||
auto nextId = value_.data()->next;
|
||||
auto resources = value_.resources();
|
||||
value_ = VariantImpl(resources->getVariant(nextId), resources);
|
||||
slotId_ = nextId;
|
||||
}
|
||||
|
||||
const VariantImpl& operator*() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
const VariantImpl* operator->() const {
|
||||
return &value_;
|
||||
}
|
||||
|
||||
bool done() const {
|
||||
return value_.isUnbound();
|
||||
}
|
||||
|
||||
bool operator==(const CollectionIterator& other) const {
|
||||
return value_.data() == other->data();
|
||||
}
|
||||
|
||||
bool operator!=(const CollectionIterator& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
SlotId slotId() const {
|
||||
return slotId_;
|
||||
}
|
||||
|
||||
private:
|
||||
VariantImpl value_;
|
||||
SlotId slotId_ = NULL_SLOT;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -274,9 +274,9 @@
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_DOUBLE
|
||||
# define ARDUINOJSON_USE_8_BYTE_POOL 1
|
||||
# define ARDUINOJSON_USE_EXTENSIONS 1
|
||||
#else
|
||||
# define ARDUINOJSON_USE_8_BYTE_POOL 0
|
||||
# define ARDUINOJSON_USE_EXTENSIONS 0
|
||||
#endif
|
||||
|
||||
#if defined(nullptr)
|
||||
|
||||
@@ -44,13 +44,13 @@ template <template <typename> class TDeserializer, typename TDestination,
|
||||
typename TReader, typename TOptions>
|
||||
DeserializationError doDeserialize(TDestination&& dst, TReader reader,
|
||||
TOptions options) {
|
||||
auto impl = VariantAttorney::getOrCreateImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
auto data = VariantAttorney::getOrCreateData(dst);
|
||||
if (!data)
|
||||
return DeserializationError::NoMemory;
|
||||
auto resources = impl.resources();
|
||||
auto resources = VariantAttorney::getResourceManager(dst);
|
||||
dst.clear();
|
||||
auto err = TDeserializer<TReader>(resources, reader)
|
||||
.parse(impl.data(), options.filter, options.nestingLimit);
|
||||
.parse(*data, options.filter, options.nestingLimit);
|
||||
shrinkJsonDocument(dst);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -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 getImpl().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 getImpl().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 getImpl().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 getImpl().getMember(detail::adaptString(key)) != 0;
|
||||
return data_.getMember(detail::adaptString(key), &resources_) != 0;
|
||||
}
|
||||
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
@@ -211,8 +211,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
template <typename TString,
|
||||
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
|
||||
JsonVariantConst operator[](const TString& key) const {
|
||||
return JsonVariantConst(getImpl().getMember(detail::adaptString(key)),
|
||||
&resources_);
|
||||
return JsonVariantConst(
|
||||
data_.getMember(detail::adaptString(key), &resources_), &resources_);
|
||||
}
|
||||
|
||||
// Gets a root object's member.
|
||||
@@ -222,8 +222,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
JsonVariantConst operator[](TChar* key) const {
|
||||
return JsonVariantConst(getImpl().getMember(detail::adaptString(key)),
|
||||
&resources_);
|
||||
return JsonVariantConst(
|
||||
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(getImpl().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 getOrCreateArray().add<T>();
|
||||
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 getOrCreateArray().add(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 getOrCreateArray().add(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) {
|
||||
getImpl().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) {
|
||||
getImpl().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) {
|
||||
getImpl().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,20 +391,6 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl getImpl() const {
|
||||
return {&data_, &resources_};
|
||||
}
|
||||
|
||||
detail::VariantImpl getOrCreateImpl() {
|
||||
return {&data_, &resources_};
|
||||
}
|
||||
|
||||
JsonArray getOrCreateArray() {
|
||||
auto impl = getImpl();
|
||||
impl.toArrayIfNull();
|
||||
return JsonArray(impl);
|
||||
}
|
||||
|
||||
JsonVariant getVariant() {
|
||||
return JsonVariant(&data_, &resources_);
|
||||
}
|
||||
@@ -410,12 +399,28 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
return JsonVariantConst(&data_, &resources_);
|
||||
}
|
||||
|
||||
mutable detail::ResourceManager resources_;
|
||||
mutable detail::VariantData data_;
|
||||
detail::ResourceManager* getResourceManager() {
|
||||
return &resources_;
|
||||
}
|
||||
|
||||
detail::VariantData* getData() {
|
||||
return &data_;
|
||||
}
|
||||
|
||||
const detail::VariantData* getData() const {
|
||||
return &data_;
|
||||
}
|
||||
|
||||
detail::VariantData* getOrCreateData() {
|
||||
return &data_;
|
||||
}
|
||||
|
||||
detail::ResourceManager resources_;
|
||||
detail::VariantData data_;
|
||||
};
|
||||
|
||||
inline bool convertToJson(const JsonDocument& src, JsonVariant dst) {
|
||||
return dst.set(src.as<JsonVariantConst>());
|
||||
inline void convertToJson(const JsonDocument& src, JsonVariant dst) {
|
||||
dst.set(src.as<JsonVariantConst>());
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -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,12 +60,10 @@ class JsonDeserializer {
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError::Code parseVariant(
|
||||
VariantData* variant, TFilter filter,
|
||||
VariantData& variant, TFilter filter,
|
||||
DeserializationOption::NestingLimit nestingLimit) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
ARDUINOJSON_ASSERT(variant != nullptr);
|
||||
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
@@ -73,13 +71,13 @@ class JsonDeserializer {
|
||||
switch (current()) {
|
||||
case '[':
|
||||
if (filter.allowArray())
|
||||
return parseArray(variant->toArray(), filter, nestingLimit);
|
||||
return parseArray(variant.toArray(), filter, nestingLimit);
|
||||
else
|
||||
return skipArray(nestingLimit);
|
||||
|
||||
case '{':
|
||||
if (filter.allowObject())
|
||||
return parseObject(variant->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(
|
||||
VariantData* arrayData, TFilter filter,
|
||||
ArrayData& array, TFilter filter,
|
||||
DeserializationOption::NestingLimit nestingLimit) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
@@ -173,15 +171,13 @@ class JsonDeserializer {
|
||||
// Read each value
|
||||
for (;;) {
|
||||
if (elementFilter.allow()) {
|
||||
VariantImpl array(arrayData, resources_);
|
||||
|
||||
// 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 {
|
||||
@@ -236,7 +232,7 @@ class JsonDeserializer {
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError::Code parseObject(
|
||||
VariantData* objectData, TFilter filter,
|
||||
ObjectData& object, TFilter filter,
|
||||
DeserializationOption::NestingLimit nestingLimit) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
@@ -277,27 +273,19 @@ class JsonDeserializer {
|
||||
TFilter memberFilter = filter[key];
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
VariantImpl object(objectData, resources_);
|
||||
auto member = object.getMember(adaptString(key));
|
||||
auto member = object.getMember(adaptString(key), resources_);
|
||||
if (!member) {
|
||||
auto keySlot = resources_->allocVariant();
|
||||
if (!keySlot)
|
||||
auto keyVariant = object.addPair(&member, resources_);
|
||||
if (!keyVariant)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
auto valueSlot = resources_->allocVariant();
|
||||
if (!valueSlot)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
object.addMember(keySlot, valueSlot);
|
||||
|
||||
stringBuilder_.save(keySlot.ptr());
|
||||
member = valueSlot.ptr();
|
||||
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 {
|
||||
@@ -391,7 +379,7 @@ class JsonDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError::Code parseStringValue(VariantData* variant) {
|
||||
DeserializationError::Code parseStringValue(VariantData& variant) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
stringBuilder_.startString();
|
||||
@@ -400,7 +388,7 @@ class JsonDeserializer {
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
stringBuilder_.save(variant);
|
||||
stringBuilder_.save(&variant);
|
||||
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
@@ -516,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();
|
||||
@@ -530,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;
|
||||
|
||||
@@ -16,42 +16,46 @@ 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 visitArray(const VariantImpl& array) {
|
||||
size_t visit(const ArrayData& array) {
|
||||
write('[');
|
||||
|
||||
bool first = true;
|
||||
auto slotId = array.head();
|
||||
|
||||
for (auto it = array.createIterator(); !it.done(); it.move()) {
|
||||
if (!first)
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
|
||||
slot->accept(*this, resources_);
|
||||
|
||||
slotId = slot->next();
|
||||
|
||||
if (slotId != NULL_SLOT)
|
||||
write(',');
|
||||
|
||||
it->accept(*this);
|
||||
first = false;
|
||||
}
|
||||
|
||||
write(']');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(const VariantImpl& object) {
|
||||
size_t visit(const ObjectData& object) {
|
||||
write('{');
|
||||
|
||||
bool first = true;
|
||||
bool isValue = false;
|
||||
auto slotId = object.head();
|
||||
|
||||
for (auto it = object.createIterator(); !it.done(); it.move()) {
|
||||
if (isValue)
|
||||
write(':');
|
||||
else if (!first)
|
||||
write(',');
|
||||
bool isKey = true;
|
||||
|
||||
it->accept(*this);
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
slot->accept(*this, resources_);
|
||||
|
||||
first = false;
|
||||
isValue = !isValue;
|
||||
slotId = slot->next();
|
||||
|
||||
if (slotId != NULL_SLOT)
|
||||
write(isKey ? ':' : ',');
|
||||
|
||||
isKey = !isKey;
|
||||
}
|
||||
|
||||
write('}');
|
||||
@@ -116,7 +120,7 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
||||
TextFormatter<TWriter> formatter_;
|
||||
|
||||
protected:
|
||||
ResourceManager* resources_;
|
||||
const ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -16,18 +16,19 @@ 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 visitArray(const VariantImpl& 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();
|
||||
it->accept(*this);
|
||||
it.move();
|
||||
it->accept(*this, base::resources_);
|
||||
|
||||
it.next(base::resources_);
|
||||
base::write(it.done() ? "\r\n" : ",\r\n");
|
||||
}
|
||||
nesting_--;
|
||||
@@ -39,8 +40,8 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
return this->bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(const VariantImpl& 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_++;
|
||||
@@ -48,8 +49,8 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
while (!it.done()) {
|
||||
if (isKey)
|
||||
indent();
|
||||
it->accept(*this);
|
||||
it.move();
|
||||
it->accept(*this, base::resources_);
|
||||
it.next(base::resources_);
|
||||
if (isKey)
|
||||
base::write(": ");
|
||||
else
|
||||
|
||||
@@ -14,11 +14,20 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
struct VariantData;
|
||||
class VariantData;
|
||||
class VariantWithId;
|
||||
|
||||
class ResourceManager {
|
||||
union SlotData {
|
||||
VariantData variant;
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
VariantExtension extension;
|
||||
#endif
|
||||
};
|
||||
|
||||
public:
|
||||
constexpr static size_t slotSize = sizeof(SlotData);
|
||||
|
||||
ResourceManager(Allocator* allocator = DefaultAllocator::instance())
|
||||
: allocator_(allocator), overflowed_(false) {}
|
||||
|
||||
@@ -26,9 +35,6 @@ class ResourceManager {
|
||||
stringPool_.clear(allocator_);
|
||||
variantPools_.clear(allocator_);
|
||||
staticStringsPools_.clear(allocator_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
eightBytePools_.clear(allocator_);
|
||||
#endif
|
||||
}
|
||||
|
||||
ResourceManager(const ResourceManager&) = delete;
|
||||
@@ -38,9 +44,6 @@ class ResourceManager {
|
||||
swap(a.stringPool_, b.stringPool_);
|
||||
swap(a.variantPools_, b.variantPools_);
|
||||
swap(a.staticStringsPools_, b.staticStringsPools_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
swap(a.eightBytePools_, b.eightBytePools_);
|
||||
#endif
|
||||
swap_(a.allocator_, b.allocator_);
|
||||
swap_(a.overflowed_, b.overflowed_);
|
||||
}
|
||||
@@ -57,42 +60,14 @@ class ResourceManager {
|
||||
return overflowed_;
|
||||
}
|
||||
|
||||
Slot<VariantData> allocVariant() {
|
||||
auto slot = variantPools_.allocSlot(allocator_);
|
||||
if (!slot) {
|
||||
overflowed_ = true;
|
||||
return {};
|
||||
}
|
||||
new (slot.ptr()) VariantData();
|
||||
return slot;
|
||||
}
|
||||
Slot<VariantData> allocVariant();
|
||||
void freeVariant(Slot<VariantData> slot);
|
||||
VariantData* getVariant(SlotId id) const;
|
||||
|
||||
void freeVariant(Slot<VariantData> slot) {
|
||||
variantPools_.freeSlot(slot);
|
||||
}
|
||||
|
||||
VariantData* getVariant(SlotId id) const {
|
||||
return variantPools_.getSlot(id);
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
Slot<EightByteValue> allocEightByte() {
|
||||
auto slot = eightBytePools_.allocSlot(allocator_);
|
||||
if (!slot) {
|
||||
overflowed_ = true;
|
||||
return {};
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
void freeEightByte(SlotId id) {
|
||||
auto p = getEightByte(id);
|
||||
eightBytePools_.freeSlot({p, id});
|
||||
}
|
||||
|
||||
EightByteValue* getEightByte(SlotId id) const {
|
||||
return eightBytePools_.getSlot(id);
|
||||
}
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
Slot<VariantExtension> allocExtension();
|
||||
void freeExtension(SlotId slot);
|
||||
VariantExtension* getExtension(SlotId id) const;
|
||||
#endif
|
||||
|
||||
template <typename TAdaptedString>
|
||||
@@ -134,8 +109,8 @@ class ResourceManager {
|
||||
StringNode::destroy(node, allocator_);
|
||||
}
|
||||
|
||||
void dereferenceString(StringNode* node) {
|
||||
stringPool_.dereference(node, allocator_);
|
||||
void dereferenceString(const char* s) {
|
||||
stringPool_.dereference(s, allocator_);
|
||||
}
|
||||
|
||||
SlotId saveStaticString(const char* s) {
|
||||
@@ -161,28 +136,19 @@ class ResourceManager {
|
||||
variantPools_.clear(allocator_);
|
||||
stringPool_.clear(allocator_);
|
||||
staticStringsPools_.clear(allocator_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
eightBytePools_.clear(allocator_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void shrinkToFit() {
|
||||
variantPools_.shrinkToFit(allocator_);
|
||||
staticStringsPools_.shrinkToFit(allocator_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
eightBytePools_.shrinkToFit(allocator_);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Allocator* allocator_;
|
||||
bool overflowed_;
|
||||
StringPool stringPool_;
|
||||
MemoryPoolList<VariantData> variantPools_;
|
||||
MemoryPoolList<SlotData> variantPools_;
|
||||
MemoryPoolList<const char*> staticStringsPools_;
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
MemoryPoolList<EightByteValue> eightBytePools_;
|
||||
#endif
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
52
src/ArduinoJson/Memory/ResourceManagerImpl.hpp
Normal file
52
src/ArduinoJson/Memory/ResourceManagerImpl.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionData.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Polyfills/alias_cast.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline Slot<VariantData> ResourceManager::allocVariant() {
|
||||
auto p = variantPools_.allocSlot(allocator_);
|
||||
if (!p) {
|
||||
overflowed_ = true;
|
||||
return {};
|
||||
}
|
||||
return {new (&p->variant) VariantData, p.id()};
|
||||
}
|
||||
|
||||
inline void ResourceManager::freeVariant(Slot<VariantData> variant) {
|
||||
variant->clear(this);
|
||||
variantPools_.freeSlot({alias_cast<SlotData*>(variant.ptr()), variant.id()});
|
||||
}
|
||||
|
||||
inline VariantData* ResourceManager::getVariant(SlotId id) const {
|
||||
return reinterpret_cast<VariantData*>(variantPools_.getSlot(id));
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
inline Slot<VariantExtension> ResourceManager::allocExtension() {
|
||||
auto p = variantPools_.allocSlot(allocator_);
|
||||
if (!p) {
|
||||
overflowed_ = true;
|
||||
return {};
|
||||
}
|
||||
return {&p->extension, p.id()};
|
||||
}
|
||||
|
||||
inline void ResourceManager::freeExtension(SlotId id) {
|
||||
auto p = getExtension(id);
|
||||
variantPools_.freeSlot({reinterpret_cast<SlotData*>(p), id});
|
||||
}
|
||||
|
||||
inline VariantExtension* ResourceManager::getExtension(SlotId id) const {
|
||||
return &variantPools_.getSlot(id)->extension;
|
||||
}
|
||||
#endif
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Strings/JsonString.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
|
||||
@@ -78,20 +78,20 @@ class StringPool {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void dereference(StringNode* nodeToRemove, Allocator* allocator) {
|
||||
void dereference(const char* s, Allocator* allocator) {
|
||||
StringNode* prev = nullptr;
|
||||
for (auto current = strings_; current; current = current->next) {
|
||||
if (current == nodeToRemove) {
|
||||
if (--current->references == 0) {
|
||||
for (auto node = strings_; node; node = node->next) {
|
||||
if (node->data == s) {
|
||||
if (--node->references == 0) {
|
||||
if (prev)
|
||||
prev->next = current->next;
|
||||
prev->next = node->next;
|
||||
else
|
||||
strings_ = current->next;
|
||||
StringNode::destroy(current, allocator);
|
||||
strings_ = node->next;
|
||||
StringNode::destroy(node, allocator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = current;
|
||||
prev = node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,51 +24,52 @@ class MsgPackBinary {
|
||||
|
||||
template <>
|
||||
struct Converter<MsgPackBinary> : private detail::VariantAttorney {
|
||||
static bool toJson(MsgPackBinary src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return false;
|
||||
|
||||
if (!src.data())
|
||||
return true;
|
||||
|
||||
size_t headerSize = src.size() >= 0x10000 ? 5 : src.size() >= 0x100 ? 3 : 2;
|
||||
|
||||
auto resources = impl.resources();
|
||||
auto str = resources->createString(src.size() + headerSize);
|
||||
if (!str)
|
||||
return false;
|
||||
resources->saveString(str);
|
||||
|
||||
auto ptr = reinterpret_cast<uint8_t*>(str->data);
|
||||
switch (headerSize) {
|
||||
case 2:
|
||||
ptr[0] = uint8_t(0xc4);
|
||||
ptr[1] = uint8_t(src.size() & 0xff);
|
||||
break;
|
||||
case 3:
|
||||
ptr[0] = uint8_t(0xc5);
|
||||
ptr[1] = uint8_t(src.size() >> 8 & 0xff);
|
||||
ptr[2] = uint8_t(src.size() & 0xff);
|
||||
break;
|
||||
case 5:
|
||||
ptr[0] = uint8_t(0xc6);
|
||||
ptr[1] = uint8_t(src.size() >> 24 & 0xff);
|
||||
ptr[2] = uint8_t(src.size() >> 16 & 0xff);
|
||||
ptr[3] = uint8_t(src.size() >> 8 & 0xff);
|
||||
ptr[4] = uint8_t(src.size() & 0xff);
|
||||
break;
|
||||
default:
|
||||
ARDUINOJSON_ASSERT(false);
|
||||
static void toJson(MsgPackBinary src, JsonVariant dst) {
|
||||
auto data = VariantAttorney::getData(dst);
|
||||
if (!data)
|
||||
return;
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
if (src.data()) {
|
||||
size_t headerSize = src.size() >= 0x10000 ? 5
|
||||
: src.size() >= 0x100 ? 3
|
||||
: 2;
|
||||
auto str = resources->createString(src.size() + headerSize);
|
||||
if (str) {
|
||||
resources->saveString(str);
|
||||
auto ptr = reinterpret_cast<uint8_t*>(str->data);
|
||||
switch (headerSize) {
|
||||
case 2:
|
||||
ptr[0] = uint8_t(0xc4);
|
||||
ptr[1] = uint8_t(src.size() & 0xff);
|
||||
break;
|
||||
case 3:
|
||||
ptr[0] = uint8_t(0xc5);
|
||||
ptr[1] = uint8_t(src.size() >> 8 & 0xff);
|
||||
ptr[2] = uint8_t(src.size() & 0xff);
|
||||
break;
|
||||
case 5:
|
||||
ptr[0] = uint8_t(0xc6);
|
||||
ptr[1] = uint8_t(src.size() >> 24 & 0xff);
|
||||
ptr[2] = uint8_t(src.size() >> 16 & 0xff);
|
||||
ptr[3] = uint8_t(src.size() >> 8 & 0xff);
|
||||
ptr[4] = uint8_t(src.size() & 0xff);
|
||||
break;
|
||||
default:
|
||||
ARDUINOJSON_ASSERT(false);
|
||||
}
|
||||
memcpy(ptr + headerSize, src.data(), src.size());
|
||||
data->setRawString(str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
memcpy(ptr + headerSize, src.data(), src.size());
|
||||
impl.data()->setRawString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MsgPackBinary fromJson(JsonVariantConst src) {
|
||||
auto variant = getImpl(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
|
||||
|
||||
@@ -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();
|
||||
|
||||
VariantImpl array;
|
||||
ArrayData* array;
|
||||
if (allowArray) {
|
||||
ARDUINOJSON_ASSERT(variant != 0);
|
||||
array = VariantImpl(variant->toArray(), resources_);
|
||||
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;
|
||||
|
||||
VariantImpl object;
|
||||
ObjectData* object;
|
||||
if (filter.allowObject()) {
|
||||
ARDUINOJSON_ASSERT(variant != 0);
|
||||
object = VariantImpl(variant->toObject(), resources_);
|
||||
object = &variant->toObject();
|
||||
} else {
|
||||
object = 0;
|
||||
}
|
||||
|
||||
for (; n; --n) {
|
||||
@@ -401,18 +406,13 @@ class MsgPackDeserializer {
|
||||
VariantData* member = 0;
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
auto keySlot = resources_->allocVariant();
|
||||
if (!keySlot)
|
||||
ARDUINOJSON_ASSERT(object != 0);
|
||||
|
||||
auto keyVariant = object->addPair(&member, resources_);
|
||||
if (!keyVariant)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
auto valueSlot = resources_->allocVariant();
|
||||
if (!valueSlot)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
object.addMember(keySlot, valueSlot);
|
||||
|
||||
member = valueSlot.ptr();
|
||||
stringBuffer_.save(keySlot.ptr());
|
||||
stringBuffer_.save(keyVariant);
|
||||
}
|
||||
|
||||
err = parseVariant(member, memberFilter, nestingLimit.decrement());
|
||||
|
||||
@@ -30,62 +30,60 @@ class MsgPackExtension {
|
||||
|
||||
template <>
|
||||
struct Converter<MsgPackExtension> : private detail::VariantAttorney {
|
||||
static bool toJson(MsgPackExtension src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return false;
|
||||
static void toJson(MsgPackExtension src, JsonVariant dst) {
|
||||
auto data = VariantAttorney::getData(dst);
|
||||
if (!data)
|
||||
return;
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
if (src.data()) {
|
||||
uint8_t format, sizeBytes;
|
||||
if (src.size() >= 0x10000) {
|
||||
format = 0xc9; // ext 32
|
||||
sizeBytes = 4;
|
||||
} else if (src.size() >= 0x100) {
|
||||
format = 0xc8; // ext 16
|
||||
sizeBytes = 2;
|
||||
} else if (src.size() == 16) {
|
||||
format = 0xd8; // fixext 16
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 8) {
|
||||
format = 0xd7; // fixext 8
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 4) {
|
||||
format = 0xd6; // fixext 4
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 2) {
|
||||
format = 0xd5; // fixext 2
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 1) {
|
||||
format = 0xd4; // fixext 1
|
||||
sizeBytes = 0;
|
||||
} else {
|
||||
format = 0xc7; // ext 8
|
||||
sizeBytes = 1;
|
||||
}
|
||||
|
||||
impl.clear();
|
||||
|
||||
if (!src.data())
|
||||
return true;
|
||||
|
||||
uint8_t format, sizeBytes;
|
||||
if (src.size() >= 0x10000) {
|
||||
format = 0xc9; // ext 32
|
||||
sizeBytes = 4;
|
||||
} else if (src.size() >= 0x100) {
|
||||
format = 0xc8; // ext 16
|
||||
sizeBytes = 2;
|
||||
} else if (src.size() == 16) {
|
||||
format = 0xd8; // fixext 16
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 8) {
|
||||
format = 0xd7; // fixext 8
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 4) {
|
||||
format = 0xd6; // fixext 4
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 2) {
|
||||
format = 0xd5; // fixext 2
|
||||
sizeBytes = 0;
|
||||
} else if (src.size() == 1) {
|
||||
format = 0xd4; // fixext 1
|
||||
sizeBytes = 0;
|
||||
} else {
|
||||
format = 0xc7; // ext 8
|
||||
sizeBytes = 1;
|
||||
auto str = resources->createString(src.size() + 2 + sizeBytes);
|
||||
if (str) {
|
||||
resources->saveString(str);
|
||||
auto ptr = reinterpret_cast<uint8_t*>(str->data);
|
||||
*ptr++ = uint8_t(format);
|
||||
for (uint8_t i = 0; i < sizeBytes; i++)
|
||||
*ptr++ = uint8_t(src.size() >> (sizeBytes - i - 1) * 8 & 0xff);
|
||||
*ptr++ = uint8_t(src.type());
|
||||
memcpy(ptr, src.data(), src.size());
|
||||
data->setRawString(str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto resources = impl.resources();
|
||||
auto str = resources->createString(src.size() + 2 + sizeBytes);
|
||||
if (!str)
|
||||
return false;
|
||||
resources->saveString(str);
|
||||
|
||||
auto ptr = reinterpret_cast<uint8_t*>(str->data);
|
||||
*ptr++ = uint8_t(format);
|
||||
for (uint8_t i = 0; i < sizeBytes; i++)
|
||||
*ptr++ = uint8_t(src.size() >> (sizeBytes - i - 1) * 8 & 0xff);
|
||||
*ptr++ = uint8_t(src.type());
|
||||
memcpy(ptr, src.data(), src.size());
|
||||
impl.data()->setRawString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MsgPackExtension fromJson(JsonVariantConst src) {
|
||||
auto variant = detail::VariantAttorney::getImpl(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());
|
||||
|
||||
@@ -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 visitArray(const VariantImpl& 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) {
|
||||
@@ -59,14 +59,18 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
writeInteger(uint32_t(n));
|
||||
}
|
||||
|
||||
for (auto it = array.createIterator(); !it.done(); it.move())
|
||||
it->accept(*this);
|
||||
auto slotId = array.head();
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
slot->accept(*this, resources_);
|
||||
slotId = slot->next();
|
||||
}
|
||||
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(const VariantImpl& 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) {
|
||||
@@ -77,8 +81,12 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
writeInteger(uint32_t(n));
|
||||
}
|
||||
|
||||
for (auto it = object.createIterator(); !it.done(); it.move())
|
||||
it->accept(*this);
|
||||
auto slotId = object.head();
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
slot->accept(*this, resources_);
|
||||
slotId = slot->next();
|
||||
}
|
||||
|
||||
return bytesWritten();
|
||||
}
|
||||
@@ -201,7 +209,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
}
|
||||
|
||||
CountingDecorator<TWriter> writer_;
|
||||
ResourceManager* resources_;
|
||||
const ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -20,51 +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(detail::VariantImpl impl) : impl_(impl) {}
|
||||
JsonObject(detail::ObjectData* data, detail::ResourceManager* resource)
|
||||
: data_(data), resources_(resource) {}
|
||||
|
||||
operator JsonVariant() const {
|
||||
return JsonVariant(impl_);
|
||||
void* data = data_; // prevent warning cast-align
|
||||
return JsonVariant(reinterpret_cast<detail::VariantData*>(data),
|
||||
resources_);
|
||||
}
|
||||
|
||||
operator JsonObjectConst() const {
|
||||
return JsonObjectConst(impl_);
|
||||
return JsonObjectConst(data_, resources_);
|
||||
}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(impl_);
|
||||
return JsonVariantConst(collectionToVariant(data_), resources_);
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
// https://arduinojson.org/v7/api/jsonobject/isnull/
|
||||
bool isNull() const {
|
||||
return !operator bool();
|
||||
return data_ == 0;
|
||||
}
|
||||
|
||||
// Returns true if the reference is bound.
|
||||
// https://arduinojson.org/v7/api/jsonobject/isnull/
|
||||
operator bool() const {
|
||||
return impl_.isObject();
|
||||
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());
|
||||
if (!data_)
|
||||
return iterator();
|
||||
return iterator(data_->createIterator(resources_), resources_);
|
||||
}
|
||||
|
||||
// Returns an iterator following the last key-value pair of the object.
|
||||
@@ -76,18 +81,22 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
// Removes all the members of the object.
|
||||
// https://arduinojson.org/v7/api/jsonobject/clear/
|
||||
void clear() const {
|
||||
if (impl_.isObject())
|
||||
impl_.empty();
|
||||
detail::ObjectData::clear(data_, resources_);
|
||||
}
|
||||
|
||||
// Copies an object.
|
||||
// https://arduinojson.org/v7/api/jsonobject/set/
|
||||
bool set(JsonObjectConst src) {
|
||||
if (isNull() ||
|
||||
src.isNull()) // TODO: this check is not consistent with JsonArray
|
||||
if (!data_ || !src.data_)
|
||||
return false;
|
||||
impl_.clear();
|
||||
return impl_.copyObject(detail::VariantAttorney::getImpl(src));
|
||||
|
||||
clear();
|
||||
for (auto kvp : src) {
|
||||
if (!operator[](kvp.key()).set(kvp.value()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets or sets the member with specified key.
|
||||
@@ -122,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_.removeMember(it.iterator_);
|
||||
detail::ObjectData::remove(data_, it.iterator_, resources_);
|
||||
}
|
||||
|
||||
// Removes the member with the specified key.
|
||||
@@ -130,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.
|
||||
@@ -146,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
|
||||
@@ -155,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
|
||||
@@ -166,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
|
||||
@@ -213,15 +226,20 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
}
|
||||
|
||||
private:
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return resources_;
|
||||
}
|
||||
|
||||
const detail::VariantImpl& getOrCreateImpl() const {
|
||||
return impl_;
|
||||
detail::VariantData* getData() const {
|
||||
return detail::collectionToVariant(data_);
|
||||
}
|
||||
|
||||
mutable detail::VariantImpl impl_;
|
||||
detail::VariantData* getOrCreateData() const {
|
||||
return detail::collectionToVariant(data_);
|
||||
}
|
||||
|
||||
detail::ObjectData* data_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -19,43 +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(const detail::VariantImpl& impl) : impl_(impl) {}
|
||||
JsonObjectConst(const detail::ObjectData* data,
|
||||
const detail::ResourceManager* resources)
|
||||
: data_(data), resources_(resources) {}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(impl_);
|
||||
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());
|
||||
if (!data_)
|
||||
return iterator();
|
||||
return iterator(data_->createIterator(resources_), resources_);
|
||||
}
|
||||
|
||||
// Returns an iterator following the last key-value pair of the object.
|
||||
@@ -70,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
|
||||
@@ -78,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
|
||||
@@ -95,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_.resources());
|
||||
return JsonVariantConst(detail::ObjectData::getMember(
|
||||
data_, detail::adaptString(key), resources_),
|
||||
resources_);
|
||||
}
|
||||
|
||||
// Gets the member with specified key.
|
||||
@@ -106,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_.resources());
|
||||
return JsonVariantConst(detail::ObjectData::getMember(
|
||||
data_, detail::adaptString(key), resources_),
|
||||
resources_);
|
||||
}
|
||||
|
||||
// Gets the member with specified key.
|
||||
@@ -128,11 +136,12 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
|
||||
}
|
||||
|
||||
private:
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
const detail::VariantData* getData() const {
|
||||
return collectionToVariant(data_);
|
||||
}
|
||||
|
||||
detail::VariantImpl impl_;
|
||||
const detail::ObjectData* data_;
|
||||
const detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
inline bool operator==(JsonObjectConst lhs, JsonObjectConst rhs) {
|
||||
|
||||
@@ -14,11 +14,12 @@ class JsonObjectIterator {
|
||||
public:
|
||||
JsonObjectIterator() {}
|
||||
|
||||
explicit JsonObjectIterator(const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
explicit JsonObjectIterator(detail::ObjectData::iterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
|
||||
JsonPair operator*() const {
|
||||
return JsonPair(iterator_);
|
||||
return JsonPair(iterator_, resources_);
|
||||
}
|
||||
Ptr<JsonPair> operator->() {
|
||||
return operator*();
|
||||
@@ -33,13 +34,14 @@ class JsonObjectIterator {
|
||||
}
|
||||
|
||||
JsonObjectIterator& operator++() {
|
||||
iterator_.move(); // key
|
||||
iterator_.move(); // value
|
||||
iterator_.next(resources_); // key
|
||||
iterator_.next(resources_); // value
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl::iterator iterator_;
|
||||
detail::ObjectData::iterator iterator_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
class JsonObjectConstIterator {
|
||||
@@ -48,12 +50,12 @@ class JsonObjectConstIterator {
|
||||
public:
|
||||
JsonObjectConstIterator() {}
|
||||
|
||||
explicit JsonObjectConstIterator(
|
||||
const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
explicit JsonObjectConstIterator(detail::ObjectData::iterator iterator,
|
||||
const detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
|
||||
JsonPairConst operator*() const {
|
||||
return JsonPairConst(iterator_);
|
||||
return JsonPairConst(iterator_, resources_);
|
||||
}
|
||||
Ptr<JsonPairConst> operator->() {
|
||||
return operator*();
|
||||
@@ -68,13 +70,14 @@ class JsonObjectConstIterator {
|
||||
}
|
||||
|
||||
JsonObjectConstIterator& operator++() {
|
||||
iterator_.move(); // key
|
||||
iterator_.move(); // value
|
||||
iterator_.next(resources_); // key
|
||||
iterator_.next(resources_); // value
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl::iterator iterator_;
|
||||
detail::ObjectData::iterator iterator_;
|
||||
const detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -15,11 +15,12 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
class JsonPair {
|
||||
public:
|
||||
// INTERNAL USE ONLY
|
||||
JsonPair(detail::VariantImpl::iterator iterator) {
|
||||
JsonPair(detail::ObjectData::iterator iterator,
|
||||
detail::ResourceManager* resources) {
|
||||
if (!iterator.done()) {
|
||||
key_ = iterator->asString();
|
||||
iterator.move();
|
||||
value_ = JsonVariant(*iterator);
|
||||
key_ = iterator->asString(resources);
|
||||
iterator.next(resources);
|
||||
value_ = JsonVariant(iterator.data(), resources);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,11 +43,12 @@ class JsonPair {
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/begin_end/
|
||||
class JsonPairConst {
|
||||
public:
|
||||
JsonPairConst(detail::VariantImpl::iterator iterator) {
|
||||
JsonPairConst(detail::ObjectData::iterator iterator,
|
||||
const detail::ResourceManager* resources) {
|
||||
if (!iterator.done()) {
|
||||
key_ = iterator->asString();
|
||||
iterator.move();
|
||||
value_ = JsonVariantConst(*iterator);
|
||||
key_ = iterator->asString(resources);
|
||||
iterator.next(resources);
|
||||
value_ = JsonVariantConst(iterator.data(), resources);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,15 +51,22 @@ class MemberProxy
|
||||
: upstream_(src.upstream_), key_(src.key_) {}
|
||||
// clang-format on
|
||||
|
||||
VariantImpl getImpl() const {
|
||||
auto impl = VariantAttorney::getImpl(upstream_);
|
||||
return VariantImpl(impl.getMember(key_), impl.resources());
|
||||
ResourceManager* getResourceManager() const {
|
||||
return VariantAttorney::getResourceManager(upstream_);
|
||||
}
|
||||
|
||||
VariantImpl getOrCreateImpl() const {
|
||||
auto impl = VariantAttorney::getOrCreateImpl(upstream_);
|
||||
impl.toObjectIfNull();
|
||||
return VariantImpl(impl.getOrAddMember(key_), impl.resources());
|
||||
VariantData* getData() const {
|
||||
return VariantData::getMember(
|
||||
VariantAttorney::getData(upstream_), key_,
|
||||
VariantAttorney::getResourceManager(upstream_));
|
||||
}
|
||||
|
||||
VariantData* getOrCreateData() const {
|
||||
auto data = VariantAttorney::getOrCreateData(upstream_);
|
||||
if (!data)
|
||||
return nullptr;
|
||||
return data->getOrAddMember(key_,
|
||||
VariantAttorney::getResourceManager(upstream_));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
70
src/ArduinoJson/Object/ObjectData.hpp
Normal file
70
src/ArduinoJson/Object/ObjectData.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class ObjectData : public CollectionData {
|
||||
public:
|
||||
template <typename TAdaptedString>
|
||||
VariantData* addMember(TAdaptedString key, ResourceManager* resources);
|
||||
|
||||
VariantData* addPair(VariantData** value, ResourceManager* resources);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getMember(TAdaptedString key,
|
||||
const ResourceManager* resources) const;
|
||||
|
||||
template <typename TAdaptedString>
|
||||
static VariantData* getMember(const ObjectData* object, TAdaptedString key,
|
||||
const ResourceManager* resources) {
|
||||
if (!object)
|
||||
return nullptr;
|
||||
return object->getMember(key, resources);
|
||||
}
|
||||
|
||||
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 ResourceManager* resources) const;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -4,77 +4,39 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Object/ObjectData.hpp>
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline bool VariantImpl::copyObject(const VariantImpl& src) {
|
||||
ARDUINOJSON_ASSERT(isNull());
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
data_->toObject();
|
||||
|
||||
for (auto it = src.createIterator(); !it.done(); it.move()) {
|
||||
auto keySlot = allocVariant();
|
||||
if (!keySlot)
|
||||
return false;
|
||||
|
||||
auto key = VariantImpl(keySlot.ptr(), resources_);
|
||||
if (!key.copyVariant(*it)) {
|
||||
freeVariant(keySlot);
|
||||
return false;
|
||||
}
|
||||
|
||||
it.move(); // move to value
|
||||
ARDUINOJSON_ASSERT(!it.done());
|
||||
|
||||
auto valueSlot = allocVariant();
|
||||
if (!valueSlot) {
|
||||
freeVariant(keySlot);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we add the pair before copying the value to be keep the old
|
||||
// behavior but this is not consistent with issue #2081
|
||||
addMember(keySlot, valueSlot);
|
||||
|
||||
auto value = VariantImpl(valueSlot.ptr(), resources_);
|
||||
if (!value.copyVariant(*it))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* VariantImpl::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.move();
|
||||
return it->data();
|
||||
it.next(resources);
|
||||
return it.data();
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* VariantImpl::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 VariantImpl::iterator VariantImpl::findKey(TAdaptedString key) const {
|
||||
if (!isObject())
|
||||
return iterator();
|
||||
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.move()) {
|
||||
if (isKey && stringEquals(key, adaptString(it->asString())))
|
||||
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
|
||||
if (isKey && stringEquals(key, adaptString(it->asString(resources))))
|
||||
return it;
|
||||
isKey = !isKey;
|
||||
}
|
||||
@@ -82,42 +44,49 @@ inline VariantImpl::iterator VariantImpl::findKey(TAdaptedString key) const {
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline void VariantImpl::removeMember(TAdaptedString key) {
|
||||
removeMember(findKey(key));
|
||||
}
|
||||
|
||||
inline void VariantImpl::removeMember(iterator it) {
|
||||
removePair(it);
|
||||
inline void ObjectData::removeMember(TAdaptedString key,
|
||||
ResourceManager* resources) {
|
||||
remove(findKey(key, resources), resources);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* VariantImpl::addMember(TAdaptedString key) {
|
||||
if (!isObject())
|
||||
return nullptr;
|
||||
|
||||
if (key.isNull())
|
||||
return nullptr; // Ignore null key
|
||||
|
||||
auto keySlot = allocVariant();
|
||||
inline VariantData* ObjectData::addMember(TAdaptedString key,
|
||||
ResourceManager* resources) {
|
||||
auto keySlot = resources->allocVariant();
|
||||
if (!keySlot)
|
||||
return nullptr;
|
||||
|
||||
auto valueSlot = 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;
|
||||
|
||||
addMember(keySlot, valueSlot);
|
||||
CollectionData::appendPair(keySlot, valueSlot, resources);
|
||||
|
||||
return valueSlot.ptr();
|
||||
}
|
||||
|
||||
inline VariantData* ObjectData::addPair(VariantData** value,
|
||||
ResourceManager* resources) {
|
||||
auto keySlot = resources->allocVariant();
|
||||
if (!keySlot)
|
||||
return nullptr;
|
||||
|
||||
auto valueSlot = resources->allocVariant();
|
||||
if (!valueSlot)
|
||||
return nullptr;
|
||||
*value = valueSlot.ptr();
|
||||
|
||||
CollectionData::appendPair(keySlot, valueSlot, resources);
|
||||
|
||||
return keySlot.ptr();
|
||||
}
|
||||
|
||||
// Returns the size (in bytes) of an object with n members.
|
||||
constexpr size_t sizeofObject(size_t n) {
|
||||
return 2 * n * sizeof(VariantData);
|
||||
return 2 * n * ResourceManager::slotSize;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -11,9 +11,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
template <template <typename> class TSerializer>
|
||||
size_t measure(ArduinoJson::JsonVariantConst source) {
|
||||
DummyWriter dp;
|
||||
auto impl = VariantAttorney::getImpl(source);
|
||||
TSerializer<DummyWriter> serializer(dp, impl.resources());
|
||||
return impl.accept(serializer);
|
||||
auto data = VariantAttorney::getData(source);
|
||||
auto resources = VariantAttorney::getResourceManager(source);
|
||||
TSerializer<DummyWriter> serializer(dp, resources);
|
||||
return VariantData::accept(data, resources, serializer);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -10,9 +10,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <template <typename> class TSerializer, typename TWriter>
|
||||
size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
|
||||
auto impl = VariantAttorney::getImpl(source);
|
||||
TSerializer<TWriter> serializer(writer, impl.resources());
|
||||
return impl.accept(serializer);
|
||||
auto data = VariantAttorney::getData(source);
|
||||
auto resources = VariantAttorney::getResourceManager(source);
|
||||
TSerializer<TWriter> serializer(writer, resources);
|
||||
return VariantData::accept(data, resources, serializer);
|
||||
}
|
||||
|
||||
template <template <typename> class TSerializer, typename TDestination>
|
||||
|
||||
@@ -25,10 +25,9 @@ struct Converter {
|
||||
"type 'char' is not supported, use 'signed char', 'unsigned "
|
||||
"char' or another integer type instead");
|
||||
|
||||
static auto toJson(const T& src, JsonVariant dst)
|
||||
-> decltype(convertToJson(src, dst)) {
|
||||
static void toJson(const T& src, JsonVariant dst) {
|
||||
// clang-format off
|
||||
return convertToJson(src, dst); // Error here? See https://arduinojson.org/v7/unsupported-set/
|
||||
convertToJson(src, dst); // Error here? See https://arduinojson.org/v7/unsupported-set/
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
@@ -61,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 impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.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 getImpl(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 getImpl(src).template isInteger<T>();
|
||||
auto data = getData(src);
|
||||
auto resources = getResourceManager(src);
|
||||
return data && data->template isInteger<T>(resources);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,28 +90,40 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>
|
||||
}
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
return static_cast<T>(getImpl(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 getImpl(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 impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.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 getImpl(src).asBoolean();
|
||||
auto data = getData(src);
|
||||
auto resources = getResourceManager(src);
|
||||
return data ? data->asBoolean(resources) : false;
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getImpl(src).type() == detail::VariantType::Boolean;
|
||||
auto data = getData(src);
|
||||
return data && data->isBoolean();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -113,60 +131,69 @@ 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 impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.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 getImpl(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) {
|
||||
return getImpl(src).isFloat();
|
||||
auto data = getData(src);
|
||||
return data && data->isFloat();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<const char*> : private detail::VariantAttorney {
|
||||
static bool toJson(const char* src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setString(detail::adaptString(src));
|
||||
static void toJson(const char* src, JsonVariant dst) {
|
||||
detail::VariantData::setString(getData(dst), detail::adaptString(src),
|
||||
getResourceManager(dst));
|
||||
}
|
||||
|
||||
static const char* fromJson(JsonVariantConst src) {
|
||||
return getImpl(src).asString().c_str();
|
||||
auto data = getData(src);
|
||||
return data ? data->asString(getResourceManager(src)).c_str() : 0;
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getImpl(src).isString();
|
||||
auto data = getData(src);
|
||||
return data && data->isString();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonString> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonString src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setString(detail::adaptString(src));
|
||||
static void toJson(JsonString src, JsonVariant dst) {
|
||||
detail::VariantData::setString(getData(dst), detail::adaptString(src),
|
||||
getResourceManager(dst));
|
||||
}
|
||||
|
||||
static JsonString fromJson(JsonVariantConst src) {
|
||||
return getImpl(src).asString();
|
||||
auto data = getData(src);
|
||||
return data ? data->asString(getResourceManager(src)) : JsonString();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getImpl(src).isString();
|
||||
auto data = getData(src);
|
||||
return data && data->isString();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline detail::enable_if_t<detail::IsString<T>::value, bool> convertToJson(
|
||||
inline detail::enable_if_t<detail::IsString<T>::value> convertToJson(
|
||||
const T& src, JsonVariant dst) {
|
||||
auto impl = detail::VariantAttorney::getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.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>
|
||||
@@ -174,26 +201,23 @@ inline detail::enable_if_t<detail::IsString<T>::value, bool> convertToJson(
|
||||
// SerializedValue<const __FlashStringHelper*>
|
||||
template <typename T>
|
||||
struct Converter<SerializedValue<T>> : private detail::VariantAttorney {
|
||||
static bool toJson(SerializedValue<T> src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setRawString(detail::adaptString(src.data(), src.size()));
|
||||
static void toJson(SerializedValue<T> src, JsonVariant dst) {
|
||||
detail::VariantData::setRawString(getData(dst), src,
|
||||
getResourceManager(dst));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<detail::nullptr_t> : private detail::VariantAttorney {
|
||||
static bool toJson(detail::nullptr_t, JsonVariant dst) {
|
||||
if (dst.isUnbound())
|
||||
return false;
|
||||
getImpl(dst).clear();
|
||||
return true;
|
||||
static void toJson(detail::nullptr_t, JsonVariant dst) {
|
||||
detail::VariantData::clear(getData(dst), getResourceManager(dst));
|
||||
}
|
||||
static detail::nullptr_t fromJson(JsonVariantConst) {
|
||||
return nullptr;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getImpl(src).isNull();
|
||||
auto data = getData(src);
|
||||
return data == 0 || data->isNull();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -234,17 +258,17 @@ class StringBuilderPrint : public Print {
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
inline bool convertToJson(const ::Printable& src, JsonVariant dst) {
|
||||
auto impl = detail::VariantAttorney::getImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return false;
|
||||
impl.clear();
|
||||
detail::StringBuilderPrint print(impl.resources());
|
||||
inline void convertToJson(const ::Printable& src, JsonVariant dst) {
|
||||
auto resources = detail::VariantAttorney::getResourceManager(dst);
|
||||
auto data = detail::VariantAttorney::getData(dst);
|
||||
if (!resources || !data)
|
||||
return;
|
||||
data->clear(resources);
|
||||
detail::StringBuilderPrint print(resources);
|
||||
src.printTo(print);
|
||||
if (print.overflowed())
|
||||
return false;
|
||||
print.save(impl.data());
|
||||
return true;
|
||||
return;
|
||||
print.save(data);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -297,73 +321,85 @@ inline bool canConvertFromJson(JsonVariantConst src, const std::string_view&) {
|
||||
|
||||
template <>
|
||||
struct Converter<JsonArrayConst> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonArrayConst src, JsonVariant dst) {
|
||||
static void toJson(JsonArrayConst src, JsonVariant dst) {
|
||||
if (src.isNull())
|
||||
return dst.set(nullptr);
|
||||
dst.set(nullptr);
|
||||
else
|
||||
return dst.to<JsonArray>().set(src);
|
||||
dst.to<JsonArray>().set(src);
|
||||
}
|
||||
|
||||
static JsonArrayConst fromJson(JsonVariantConst src) {
|
||||
return JsonArrayConst(getImpl(src));
|
||||
auto data = getData(src);
|
||||
auto array = data ? data->asArray() : nullptr;
|
||||
return JsonArrayConst(array, getResourceManager(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getImpl(src).type() == detail::VariantType::Array;
|
||||
auto data = getData(src);
|
||||
return data && data->isArray();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonArray> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
static void toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
if (src.isNull())
|
||||
return dst.set(nullptr);
|
||||
dst.set(nullptr);
|
||||
else
|
||||
return dst.to<JsonArray>().set(src);
|
||||
dst.to<JsonArray>().set(src);
|
||||
}
|
||||
|
||||
static JsonArray fromJson(JsonVariant src) {
|
||||
return JsonArray(getImpl(src));
|
||||
auto data = getData(src);
|
||||
auto resources = getResourceManager(src);
|
||||
return JsonArray(data != 0 ? data->asArray() : 0, resources);
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariant src) {
|
||||
return getImpl(src).type() == detail::VariantType::Array;
|
||||
auto data = getData(src);
|
||||
return data && data->isArray();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonObjectConst> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
static void toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
if (src.isNull())
|
||||
return dst.set(nullptr);
|
||||
dst.set(nullptr);
|
||||
else
|
||||
return dst.to<JsonObject>().set(src);
|
||||
dst.to<JsonObject>().set(src);
|
||||
}
|
||||
|
||||
static JsonObjectConst fromJson(JsonVariantConst src) {
|
||||
return JsonObjectConst(getImpl(src));
|
||||
auto data = getData(src);
|
||||
auto object = data != 0 ? data->asObject() : nullptr;
|
||||
return JsonObjectConst(object, getResourceManager(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getImpl(src).type() == detail::VariantType::Object;
|
||||
auto data = getData(src);
|
||||
return data && data->isObject();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonObject> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
static void toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
if (src.isNull())
|
||||
return dst.set(nullptr);
|
||||
dst.set(nullptr);
|
||||
else
|
||||
return dst.to<JsonObject>().set(src);
|
||||
dst.to<JsonObject>().set(src);
|
||||
}
|
||||
|
||||
static JsonObject fromJson(JsonVariant src) {
|
||||
return JsonObject(getImpl(src));
|
||||
auto data = getData(src);
|
||||
auto resources = getResourceManager(src);
|
||||
return JsonObject(data != 0 ? data->asObject() : 0, resources);
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariant src) {
|
||||
return getImpl(src).type() == detail::VariantType::Object;
|
||||
auto data = getData(src);
|
||||
return data && data->isObject();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -16,33 +16,37 @@ 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:
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return resources_;
|
||||
}
|
||||
|
||||
const detail::VariantImpl& getOrCreateImpl() const {
|
||||
return impl_;
|
||||
detail::VariantData* getData() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
mutable detail::VariantImpl impl_;
|
||||
detail::VariantData* getOrCreateData() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
detail::VariantData* data_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
bool copyVariant(JsonVariant dst, JsonVariantConst src);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct Converter<JsonVariant> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.copyVariant(getImpl(src));
|
||||
static void toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
copyVariant(dst, src);
|
||||
}
|
||||
|
||||
static JsonVariant fromJson(JsonVariant src) {
|
||||
@@ -50,24 +54,24 @@ struct Converter<JsonVariant> : private detail::VariantAttorney {
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariant src) {
|
||||
return !getImpl(src).isUnbound();
|
||||
auto data = getData(src);
|
||||
return !!data;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonVariantConst> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.copyVariant(getImpl(src));
|
||||
static void toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
copyVariant(dst, src);
|
||||
}
|
||||
|
||||
static JsonVariantConst fromJson(JsonVariantConst src) {
|
||||
return JsonVariantConst(getImpl(src));
|
||||
return JsonVariantConst(getData(src), getResourceManager(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return !getImpl(src).isUnbound();
|
||||
auto data = getData(src);
|
||||
return !!data;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,37 +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) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
explicit JsonVariantConst(detail::VariantImpl impl) : impl_(impl) {}
|
||||
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_.isUnbound();
|
||||
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.
|
||||
@@ -107,7 +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_.resources());
|
||||
return JsonVariantConst(
|
||||
detail::VariantData::getElement(data_, size_t(index), resources_),
|
||||
resources_);
|
||||
}
|
||||
|
||||
// Gets object's member with specified key.
|
||||
@@ -115,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_.resources());
|
||||
return JsonVariantConst(detail::VariantData::getMember(
|
||||
data_, detail::adaptString(key), resources_),
|
||||
resources_);
|
||||
}
|
||||
|
||||
// Gets object's member with specified key.
|
||||
@@ -126,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_.resources());
|
||||
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
|
||||
@@ -148,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
|
||||
@@ -159,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
|
||||
@@ -178,12 +181,17 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
}
|
||||
|
||||
protected:
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
const detail::VariantData* getData() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
const detail::ResourceManager* getResourceManager() const {
|
||||
return resources_;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable detail::VariantImpl impl_;
|
||||
const detail::VariantData* data_;
|
||||
const detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
34
src/ArduinoJson/Variant/JsonVariantCopier.hpp
Normal file
34
src/ArduinoJson/Variant/JsonVariantCopier.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Variant/JsonVariant.hpp>
|
||||
#include <ArduinoJson/Variant/JsonVariantVisitor.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class JsonVariantCopier {
|
||||
public:
|
||||
using result_type = bool;
|
||||
|
||||
JsonVariantCopier(JsonVariant dst) : dst_(dst) {}
|
||||
|
||||
template <typename T>
|
||||
bool visit(T src) {
|
||||
return dst_.set(src);
|
||||
}
|
||||
|
||||
private:
|
||||
JsonVariant dst_;
|
||||
};
|
||||
|
||||
inline bool copyVariant(JsonVariant dst, JsonVariantConst src) {
|
||||
if (dst.isUnbound())
|
||||
return false;
|
||||
JsonVariantCopier copier(dst);
|
||||
return accept(src, copier);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -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 visitArray(const VariantImpl& array) {
|
||||
return visitor_->visit(JsonArrayConst(array));
|
||||
result_type visit(const ArrayData& value) {
|
||||
return visitor_->visit(JsonArrayConst(&value, resources_));
|
||||
}
|
||||
|
||||
result_type visitObject(const VariantImpl& object) {
|
||||
return visitor_->visit(JsonObjectConst(object));
|
||||
result_type visit(const ObjectData& value) {
|
||||
return visitor_->visit(JsonObjectConst(&value, resources_));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -43,14 +44,18 @@ class VisitorAdapter {
|
||||
|
||||
private:
|
||||
TVisitor* visitor_;
|
||||
const ResourceManager* resources_;
|
||||
};
|
||||
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(JsonVariantConst variant,
|
||||
TVisitor& visit) {
|
||||
auto impl = VariantAttorney::getImpl(variant);
|
||||
VisitorAdapter<TVisitor> adapter(visit);
|
||||
return impl.accept(adapter);
|
||||
auto data = VariantAttorney::getData(variant);
|
||||
if (!data)
|
||||
return visit.visit(nullptr);
|
||||
auto resources = VariantAttorney::getResourceManager(variant);
|
||||
VisitorAdapter<TVisitor> adapter(visit, resources);
|
||||
return data->accept(adapter, resources);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -16,13 +16,19 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
class VariantAttorney {
|
||||
public:
|
||||
template <typename TClient>
|
||||
static VariantImpl getImpl(TClient& client) {
|
||||
return client.getImpl();
|
||||
static auto getResourceManager(TClient& client)
|
||||
-> decltype(client.getResourceManager()) {
|
||||
return client.getResourceManager();
|
||||
}
|
||||
|
||||
template <typename TClient>
|
||||
static VariantImpl getOrCreateImpl(TClient& client) {
|
||||
return client.getOrCreateImpl();
|
||||
static auto getData(TClient& client) -> decltype(client.getData()) {
|
||||
return client.getData();
|
||||
}
|
||||
|
||||
template <typename TClient>
|
||||
static VariantData* getOrCreateData(TClient& client) {
|
||||
return client.getOrCreateData();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ struct Comparer<
|
||||
T, enable_if_t<is_convertible<T, ArduinoJson::JsonVariantConst>::value>>
|
||||
: VariantComparer {
|
||||
explicit Comparer(const T& value)
|
||||
: VariantComparer(JsonVariantConst(VariantAttorney::getImpl(value))) {}
|
||||
: VariantComparer(static_cast<JsonVariantConst>(value)) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -6,16 +6,18 @@
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
#include <ArduinoJson/Array/ArrayData.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonInteger.hpp>
|
||||
#include <ArduinoJson/Object/ObjectData.hpp>
|
||||
|
||||
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
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
ExtensionBit = 0x10, // 0001 0000
|
||||
#endif
|
||||
CollectionMask = 0x60,
|
||||
};
|
||||
@@ -45,18 +47,6 @@ inline bool operator&(VariantType type, VariantTypeBits bit) {
|
||||
return (uint8_t(type) & uint8_t(bit)) != 0;
|
||||
}
|
||||
|
||||
struct CollectionData {
|
||||
SlotId head = NULL_SLOT;
|
||||
SlotId tail = NULL_SLOT;
|
||||
|
||||
// Placement new
|
||||
static void* operator new(size_t, void* p) noexcept {
|
||||
return p;
|
||||
}
|
||||
|
||||
static void operator delete(void*, void*) noexcept {}
|
||||
};
|
||||
|
||||
const size_t tinyStringMaxLength = 3;
|
||||
|
||||
union VariantContent {
|
||||
@@ -67,13 +57,15 @@ union VariantContent {
|
||||
uint32_t asUint32;
|
||||
int32_t asInt32;
|
||||
SlotId asSlotId;
|
||||
ArrayData asArray;
|
||||
ObjectData asObject;
|
||||
CollectionData asCollection;
|
||||
struct StringNode* asOwnedString;
|
||||
char asTinyString[tinyStringMaxLength + 1];
|
||||
};
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
union EightByteValue {
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
union VariantExtension {
|
||||
# if ARDUINOJSON_USE_LONG_LONG
|
||||
uint64_t asUint64;
|
||||
int64_t asInt64;
|
||||
@@ -82,9 +74,6 @@ union EightByteValue {
|
||||
double asDouble;
|
||||
# endif
|
||||
};
|
||||
|
||||
static_assert(sizeof(EightByteValue) == 8,
|
||||
"sizeof(EightByteValue) must be 8 bytes");
|
||||
#endif
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -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,70 +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_EXTENSIONS
|
||||
auto extension = getExtension(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(extension->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(extension->asInt64);
|
||||
|
||||
case VariantType::Uint64:
|
||||
return visit.visit(extension->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_EXTENSIONS
|
||||
auto extension = getExtension(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 extension->asDouble != 0;
|
||||
#endif
|
||||
case VariantType::Null:
|
||||
return false;
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case VariantType::Uint64:
|
||||
case VariantType::Int64:
|
||||
return extension->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_EXTENSIONS
|
||||
auto extension = getExtension(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>(extension->asUint64);
|
||||
case VariantType::Int64:
|
||||
return static_cast<T>(extension->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>(extension->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_EXTENSIONS
|
||||
auto extension = getExtension(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>(extension->asUint64);
|
||||
case VariantType::Int64:
|
||||
return convertNumber<T>(extension->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>(extension->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_EXTENSIONS
|
||||
const VariantExtension* getExtension(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 extension = getExtension(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>(extension->asUint64);
|
||||
|
||||
case VariantType::Int64:
|
||||
return canConvertNumber<T>(extension->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 isCollection() const {
|
||||
return type & VariantTypeBits::CollectionMask;
|
||||
static size_t size(const VariantData* var, const ResourceManager* resources) {
|
||||
return var != 0 ? var->size(resources) : 0;
|
||||
}
|
||||
|
||||
bool isFloat() const {
|
||||
return type & VariantTypeBits::NumberBit;
|
||||
ArrayData& toArray() {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
type_ = VariantType::Array;
|
||||
new (&content_.asArray) ArrayData();
|
||||
return content_.asArray;
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return type == VariantType::LinkedString ||
|
||||
type == VariantType::OwnedString || type == VariantType::TinyString;
|
||||
static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
|
||||
if (!var)
|
||||
return 0;
|
||||
var->clear(resources);
|
||||
return &var->toArray();
|
||||
}
|
||||
|
||||
VariantData* toArray() {
|
||||
return toCollection(VariantType::Array);
|
||||
ObjectData& toObject() {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
type_ = VariantType::Object;
|
||||
new (&content_.asObject) ObjectData();
|
||||
return content_.asObject;
|
||||
}
|
||||
|
||||
VariantData* toCollection(VariantType collectionType) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(collectionType & VariantTypeBits::CollectionMask);
|
||||
type = collectionType;
|
||||
new (&content.asCollection) CollectionData();
|
||||
return this;
|
||||
static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
|
||||
if (!var)
|
||||
return 0;
|
||||
var->clear(resources);
|
||||
return &var->toObject();
|
||||
}
|
||||
|
||||
VariantData* toObject() {
|
||||
return toCollection(VariantType::Object);
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Array/ArrayData.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonInteger.hpp>
|
||||
#include <ArduinoJson/Object/ObjectData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -13,14 +15,6 @@ template <typename TResult>
|
||||
struct VariantDataVisitor {
|
||||
using result_type = TResult;
|
||||
|
||||
TResult visitArray(const VariantImpl&) {
|
||||
return TResult();
|
||||
}
|
||||
|
||||
TResult visitObject(const VariantImpl&) {
|
||||
return TResult();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TResult visit(const T&) {
|
||||
return TResult();
|
||||
|
||||
@@ -5,611 +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 CollectionIterator;
|
||||
|
||||
class VariantImpl {
|
||||
VariantData* data_;
|
||||
ResourceManager* resources_;
|
||||
|
||||
public:
|
||||
using iterator = CollectionIterator;
|
||||
|
||||
VariantImpl() : data_(nullptr), resources_(nullptr) {}
|
||||
|
||||
VariantImpl(VariantData* data, ResourceManager* resources)
|
||||
: data_(data), resources_(resources) {}
|
||||
|
||||
VariantData* data() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
ResourceManager* resources() const {
|
||||
return resources_;
|
||||
}
|
||||
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(TVisitor& visit) const {
|
||||
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.visitArray(VariantImpl(data_, resources_));
|
||||
|
||||
case VariantType::Object:
|
||||
return visit.visitObject(VariantImpl(data_, resources_));
|
||||
|
||||
case VariantType::TinyString:
|
||||
return visit.visit(JsonString(data_->content.asTinyString));
|
||||
|
||||
case VariantType::LinkedString:
|
||||
return visit.visit(JsonString(asLinkedString(), true));
|
||||
|
||||
case VariantType::OwnedString: {
|
||||
auto s = asOwnedString();
|
||||
return visit.visit(JsonString(s->data, s->length));
|
||||
}
|
||||
|
||||
case VariantType::RawString: {
|
||||
auto s = asOwnedString();
|
||||
return visit.visit(RawString(s->data, s->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();
|
||||
void addElement(Slot<VariantData> slot);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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 = 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);
|
||||
}
|
||||
|
||||
JsonString asRawString() const {
|
||||
switch (type()) {
|
||||
case VariantType::RawString: {
|
||||
auto s = asOwnedString();
|
||||
return JsonString(s->data, s->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: {
|
||||
auto s = asOwnedString();
|
||||
return JsonString(s->data, s->length);
|
||||
}
|
||||
default:
|
||||
return JsonString();
|
||||
}
|
||||
}
|
||||
|
||||
StringNode* asOwnedString() const {
|
||||
ARDUINOJSON_ASSERT(type() & VariantTypeBits::OwnedStringBit);
|
||||
return data_->content.asOwnedString;
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
const EightByteValue* getEightByte() const {
|
||||
return type() & VariantTypeBits::EightByteBit
|
||||
? resources_->getEightByte(data_->content.asSlotId)
|
||||
: 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
iterator createIterator() const;
|
||||
|
||||
VariantData* getElement(size_t index) const;
|
||||
|
||||
VariantData* getOrAddElement(size_t index);
|
||||
|
||||
void addMember(Slot<VariantData> key, Slot<VariantData> value);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* addMember(TAdaptedString key);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getMember(TAdaptedString key) const;
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getOrAddMember(TAdaptedString key);
|
||||
|
||||
bool isArray() const {
|
||||
return type() == VariantType::Array;
|
||||
}
|
||||
|
||||
bool isBoolean() const {
|
||||
return type() == VariantType::Boolean;
|
||||
}
|
||||
|
||||
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 isUnbound() const {
|
||||
return !data_;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return type() == VariantType::Null;
|
||||
}
|
||||
|
||||
bool isObject() const {
|
||||
return type() == VariantType::Object;
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return data_ && data_->isString();
|
||||
}
|
||||
|
||||
size_t nesting() const;
|
||||
|
||||
void removeElement(iterator it);
|
||||
|
||||
void removeElement(size_t index);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
void removeMember(TAdaptedString key);
|
||||
|
||||
void removeMember(iterator it);
|
||||
|
||||
bool copyVariant(const VariantImpl& src) {
|
||||
switch (src.type()) {
|
||||
case VariantType::Null:
|
||||
return true;
|
||||
|
||||
case VariantType::Array:
|
||||
return copyArray(src);
|
||||
|
||||
case VariantType::Object:
|
||||
return copyObject(src);
|
||||
|
||||
case VariantType::RawString:
|
||||
return setRawString(adaptString(src.asRawString()));
|
||||
|
||||
case VariantType::LinkedString:
|
||||
return setLinkedString(src.asLinkedString());
|
||||
|
||||
case VariantType::OwnedString:
|
||||
return setOwnedString(adaptString(src.asString()));
|
||||
|
||||
default:
|
||||
data_->content = src.data_->content;
|
||||
data_->type = src.data_->type;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool copyArray(const VariantImpl& src);
|
||||
bool copyObject(const VariantImpl& src);
|
||||
|
||||
bool setBoolean(bool value) {
|
||||
if (!data_)
|
||||
return false;
|
||||
data_->setBoolean(value);
|
||||
template <typename T>
|
||||
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)
|
||||
setRawString(dup);
|
||||
}
|
||||
|
||||
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);
|
||||
if (slotId == NULL_SLOT)
|
||||
return false;
|
||||
|
||||
type_ = VariantType::LinkedString;
|
||||
content_.asSlotId = slotId;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
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(), resources);
|
||||
|
||||
if (isTinyString(value, value.size())) {
|
||||
setTinyString(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;
|
||||
auto dup = resources->saveString(value);
|
||||
if (dup) {
|
||||
setOwnedString(dup);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
inline void VariantData::clear(ResourceManager* resources) {
|
||||
if (type_ & VariantTypeBits::OwnedStringBit)
|
||||
resources->dereferenceString(content_.asOwnedString->data);
|
||||
|
||||
float valueAsFloat = static_cast<float>(value);
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
if (type_ & VariantTypeBits::ExtensionBit)
|
||||
resources->freeExtension(content_.asSlotId);
|
||||
#endif
|
||||
|
||||
auto collection = asCollection();
|
||||
if (collection)
|
||||
collection->clear(resources);
|
||||
|
||||
type_ = VariantType::Null;
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
inline const VariantExtension* VariantData::getExtension(
|
||||
const ResourceManager* resources) const {
|
||||
return type_ & VariantTypeBits::ExtensionBit
|
||||
? resources->getExtension(content_.asSlotId)
|
||||
: nullptr;
|
||||
}
|
||||
#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) {
|
||||
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;
|
||||
}
|
||||
if (value == valueAsFloat) {
|
||||
type_ = VariantType::Float;
|
||||
content_.asFloat = valueAsFloat;
|
||||
} else {
|
||||
auto extension = resources->allocExtension();
|
||||
if (!extension)
|
||||
return false;
|
||||
type_ = VariantType::Double;
|
||||
content_.asSlotId = extension.id();
|
||||
extension->asDouble = value;
|
||||
}
|
||||
#else
|
||||
data_->type = VariantType::Float;
|
||||
data_->content.asFloat = valueAsFloat;
|
||||
type_ = VariantType::Float;
|
||||
content_.asFloat = valueAsFloat;
|
||||
#endif
|
||||
return true;
|
||||
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);
|
||||
}
|
||||
|
||||
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_)
|
||||
else {
|
||||
auto extension = resources->allocExtension();
|
||||
if (!extension)
|
||||
return false;
|
||||
type_ = VariantType::Int64;
|
||||
content_.asSlotId = extension.id();
|
||||
extension->asInt64 = value;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
if (canConvertNumber<uint32_t>(value)) {
|
||||
data_->type = VariantType::Uint32;
|
||||
data_->content.asUint32 = static_cast<uint32_t>(value);
|
||||
}
|
||||
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;
|
||||
data_->type = VariantType::Uint64;
|
||||
data_->content.asSlotId = slot.id();
|
||||
slot->asUint64 = value;
|
||||
}
|
||||
else {
|
||||
auto extension = resources->allocExtension();
|
||||
if (!extension)
|
||||
return false;
|
||||
type_ = VariantType::Uint64;
|
||||
content_.asSlotId = extension.id();
|
||||
extension->asUint64 = value;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
bool setRawString(TAdaptedString value) {
|
||||
if (!data_)
|
||||
return false;
|
||||
auto dup = resources_->saveString(value);
|
||||
if (!dup)
|
||||
return false;
|
||||
data_->setRawString(dup);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
bool setString(TAdaptedString value) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
if (value.isNull())
|
||||
return true;
|
||||
|
||||
if (value.isStatic())
|
||||
return setLinkedString(value.data());
|
||||
|
||||
if (isTinyString(value, value.size())) {
|
||||
data_->setTinyString(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return setOwnedString(value);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
bool setOwnedString(TAdaptedString value) {
|
||||
auto dup = resources_->saveString(value);
|
||||
if (!dup)
|
||||
return false;
|
||||
|
||||
data_->setOwnedString(dup);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setLinkedString(const char* s) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
|
||||
auto slotId = resources_->saveStaticString(s);
|
||||
if (slotId == NULL_SLOT)
|
||||
return false;
|
||||
|
||||
data_->type = VariantType::LinkedString;
|
||||
data_->content.asSlotId = slotId;
|
||||
return true;
|
||||
}
|
||||
|
||||
void toArrayIfNull() {
|
||||
if (data_ && data_->type == VariantType::Null)
|
||||
data_->toArray();
|
||||
}
|
||||
|
||||
void toObjectIfNull() {
|
||||
if (data_ && data_->type == VariantType::Null)
|
||||
data_->toObject();
|
||||
}
|
||||
|
||||
void empty() {
|
||||
auto coll = getCollectionData();
|
||||
|
||||
auto next = coll->head;
|
||||
while (next != NULL_SLOT) {
|
||||
auto currId = next;
|
||||
auto slot = getVariant(next);
|
||||
next = slot->next;
|
||||
freeVariant({slot, currId});
|
||||
}
|
||||
|
||||
coll->head = NULL_SLOT;
|
||||
coll->tail = NULL_SLOT;
|
||||
}
|
||||
|
||||
size_t size() const;
|
||||
|
||||
VariantType type() const {
|
||||
return data_ ? data_->type : VariantType::Null;
|
||||
}
|
||||
|
||||
// Release the resources used by this variant and set it to null.
|
||||
void clear() {
|
||||
if (!data_)
|
||||
return;
|
||||
|
||||
if (data_->type & VariantTypeBits::OwnedStringBit)
|
||||
resources_->dereferenceString(asOwnedString());
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
if (data_->type & VariantTypeBits::EightByteBit)
|
||||
resources_->freeEightByte(data_->content.asSlotId);
|
||||
#endif
|
||||
|
||||
if (data_->type & VariantTypeBits::CollectionMask)
|
||||
empty();
|
||||
|
||||
data_->type = VariantType::Null;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename TAdaptedString>
|
||||
iterator findKey(TAdaptedString key) const;
|
||||
|
||||
iterator at(size_t index) const;
|
||||
|
||||
void removeOne(iterator it);
|
||||
void removePair(iterator it);
|
||||
|
||||
VariantData* getVariant(SlotId id) const {
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
return resources_->getVariant(id);
|
||||
}
|
||||
|
||||
void freeVariant(Slot<VariantData> slot) {
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
VariantImpl(slot.ptr(), resources_).clear();
|
||||
resources_->freeVariant(slot);
|
||||
}
|
||||
|
||||
Slot<VariantData> allocVariant() {
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
return resources_->allocVariant();
|
||||
}
|
||||
|
||||
Slot<VariantData> getPreviousSlot(VariantData*) const;
|
||||
|
||||
CollectionData* getCollectionData() const {
|
||||
ARDUINOJSON_ASSERT(data_ != nullptr);
|
||||
ARDUINOJSON_ASSERT(data_->isCollection());
|
||||
return &data_->content.asCollection;
|
||||
}
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -29,18 +29,18 @@ class VariantRefBase : public VariantTag {
|
||||
// Sets the value to null.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/clear/
|
||||
void clear() const {
|
||||
getOrCreateImpl().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 getImpl().isNull();
|
||||
return VariantData::isNull(getData());
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
bool isUnbound() const {
|
||||
return getImpl().isUnbound();
|
||||
return !getData();
|
||||
}
|
||||
|
||||
// Casts the value to the specified type.
|
||||
@@ -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 getImpl().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 getImpl().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 getOrCreateArray().add(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 getOrCreateArray().add(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 {
|
||||
getImpl().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 {
|
||||
getImpl().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 {
|
||||
getImpl().removeMember(adaptString(key));
|
||||
VariantData::removeMember(getData(), adaptString(key),
|
||||
getResourceManager());
|
||||
}
|
||||
|
||||
// Removes a member of the object or an element of the array.
|
||||
@@ -259,20 +263,22 @@ class VariantRefBase : public VariantTag {
|
||||
return static_cast<const TDerived&>(*this);
|
||||
}
|
||||
|
||||
VariantImpl getImpl() const {
|
||||
return VariantAttorney::getImpl(derived());
|
||||
ResourceManager* getResourceManager() const {
|
||||
return VariantAttorney::getResourceManager(derived());
|
||||
}
|
||||
|
||||
VariantImpl getOrCreateImpl() const {
|
||||
return VariantAttorney::getOrCreateImpl(derived());
|
||||
VariantData* getData() const {
|
||||
return VariantAttorney::getData(derived());
|
||||
}
|
||||
|
||||
JsonArray getOrCreateArray() const;
|
||||
VariantData* getOrCreateData() const {
|
||||
return VariantAttorney::getOrCreateData(derived());
|
||||
}
|
||||
|
||||
FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
|
||||
|
||||
FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {
|
||||
return ArduinoJson::JsonVariantConst(getImpl());
|
||||
return ArduinoJson::JsonVariantConst(getData(), getResourceManager());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -300,6 +306,8 @@ class VariantRefBase : public VariantTag {
|
||||
|
||||
template <typename TConverter, typename T>
|
||||
bool doSet(const T& value, true_type) const;
|
||||
|
||||
ArduinoJson::JsonVariant getOrCreateVariant() const;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -61,27 +61,31 @@ inline JsonObject VariantRefBase<TDerived>::createNestedObject(
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
inline bool convertToJson(const VariantRefBase<TDerived>& src,
|
||||
inline void convertToJson(const VariantRefBase<TDerived>& src,
|
||||
JsonVariant dst) {
|
||||
return dst.set(src.template as<JsonVariantConst>());
|
||||
dst.set(src.template as<JsonVariantConst>());
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
|
||||
inline T VariantRefBase<TDerived>::add() const {
|
||||
return getOrCreateArray().template add<T>();
|
||||
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 getImpl().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 getImpl().getMember(adaptString(key)) != 0;
|
||||
return VariantData::getMember(getData(), adaptString(key),
|
||||
getResourceManager()) != 0;
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
@@ -92,7 +96,12 @@ inline bool VariantRefBase<TDerived>::containsKey(const TVariant& key) const {
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonVariant VariantRefBase<TDerived>::getVariant() const {
|
||||
return JsonVariant(getImpl());
|
||||
return JsonVariant(getData(), getResourceManager());
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonVariant VariantRefBase<TDerived>::getOrCreateVariant() const {
|
||||
return JsonVariant(getOrCreateData(), getResourceManager());
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
@@ -127,54 +136,40 @@ VariantRefBase<TDerived>::operator[](const TString& key) const {
|
||||
template <typename TDerived>
|
||||
template <typename TConverter, typename T>
|
||||
inline bool VariantRefBase<TDerived>::doSet(const T& value, false_type) const {
|
||||
auto impl = getOrCreateImpl();
|
||||
TConverter::toJson(value, JsonVariant(impl));
|
||||
auto resources = impl.resources();
|
||||
TConverter::toJson(value, getOrCreateVariant());
|
||||
auto resources = getResourceManager();
|
||||
return resources && !resources->overflowed();
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename TConverter, typename T>
|
||||
inline bool VariantRefBase<TDerived>::doSet(const T& value, true_type) const {
|
||||
auto impl = getOrCreateImpl();
|
||||
return TConverter::toJson(value, JsonVariant(impl));
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonArray VariantRefBase<TDerived>::getOrCreateArray() const {
|
||||
auto impl = getOrCreateImpl();
|
||||
impl.toArrayIfNull();
|
||||
return JsonArray(impl);
|
||||
return TConverter::toJson(value, getOrCreateVariant());
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename T, enable_if_t<is_same<T, JsonArray>::value, int>>
|
||||
inline JsonArray VariantRefBase<TDerived>::to() const {
|
||||
auto impl = getOrCreateImpl();
|
||||
if (impl.isUnbound())
|
||||
return JsonArray();
|
||||
impl.clear();
|
||||
impl.data()->toArray();
|
||||
return JsonArray(impl);
|
||||
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 impl = getOrCreateImpl();
|
||||
if (impl.isUnbound())
|
||||
return JsonObject();
|
||||
impl.clear();
|
||||
impl.data()->toObject();
|
||||
return JsonObject(impl);
|
||||
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 {
|
||||
auto impl = getOrCreateImpl();
|
||||
impl.clear();
|
||||
return JsonVariant(impl);
|
||||
auto data = getOrCreateData();
|
||||
auto resources = getResourceManager();
|
||||
detail::VariantData::clear(data, resources);
|
||||
return JsonVariant(data, resources);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user