Update deps (#482)
This commit is contained in:
@@ -1,6 +1,42 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v7.2.0 (2024-09-18)
|
||||
------
|
||||
|
||||
* Store object members with two slots: one for the key and one for the value
|
||||
* Store 64-bit numbers (`double` and `long long`) in an additional slot
|
||||
* Reduce the slot size (see table below)
|
||||
* Improve message when user forgets third arg of `serializeJson()` et al.
|
||||
* Set `ARDUINOJSON_USE_DOUBLE` to `0` by default on 8-bit architectures
|
||||
* Deprecate `containsKey()` in favor of `doc["key"].is<T>()`
|
||||
* Add support for escape sequence `\'` (issue #2124)
|
||||
|
||||
| Architecture | before | after |
|
||||
|--------------|----------|----------|
|
||||
| 8-bit | 8 bytes | 6 bytes |
|
||||
| 32-bit | 16 bytes | 8 bytes |
|
||||
| 64-bit | 24 bytes | 16 bytes |
|
||||
|
||||
> ### BREAKING CHANGES
|
||||
>
|
||||
> After being on the death row for years, the `containsKey()` method has finally been deprecated.
|
||||
> You should replace `doc.containsKey("key")` with `doc["key"].is<T>()`, which not only checks that the key exists but also that the value is of the expected type.
|
||||
>
|
||||
> ```cpp
|
||||
> // Before
|
||||
> if (doc.containsKey("value")) {
|
||||
> int value = doc["value"];
|
||||
> // ...
|
||||
> }
|
||||
>
|
||||
> // After
|
||||
> if (doc["value"].is<int>()) {
|
||||
> int value = doc["value"];
|
||||
> // ...
|
||||
> }
|
||||
> ```
|
||||
|
||||
v7.1.0 (2024-06-27)
|
||||
------
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ if(ESP_PLATFORM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
project(ArduinoJson VERSION 7.1.0)
|
||||
project(ArduinoJson VERSION 7.2.0)
|
||||
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
include(CTest)
|
||||
|
||||
@@ -148,6 +148,9 @@ ArduinoJson is thankful to its sponsors. Please give them a visit; they deserve
|
||||
<a href="https://github.com/1technophile" rel="sponsored">
|
||||
<img alt="1technophile" src="https://avatars.githubusercontent.com/u/12672732?s=40&v=4">
|
||||
</a>
|
||||
<a href="https://github.com/LArkema" rel="sponsored">
|
||||
<img alt="LArkema" src="https://avatars.githubusercontent.com/u/38381313?s=40&v=4">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
If you run a commercial project that embeds ArduinoJson, think about [sponsoring the library's development](https://github.com/sponsors/bblanchon): it ensures the code that your products rely on stays actively maintained. It can also give your project some exposure to the makers' community.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: 7.1.0.{build}
|
||||
version: 7.2.0.{build}
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
|
||||
@@ -6,12 +6,13 @@ static_assert(ARDUINOJSON_USE_LONG_LONG == 0, "ARDUINOJSON_USE_LONG_LONG");
|
||||
|
||||
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 1, "ARDUINOJSON_SLOT_ID_SIZE");
|
||||
|
||||
static_assert(ARDUINOJSON_POOL_CAPACITY == 16, "ARDUINOJSON_POOL_CAPACITY");
|
||||
|
||||
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
||||
|
||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||
static_assert(ARDUINOJSON_USE_DOUBLE == 0, "ARDUINOJSON_USE_DOUBLE");
|
||||
|
||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 8,
|
||||
"sizeof(VariantSlot)");
|
||||
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 6, "slot size");
|
||||
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
@@ -4,12 +4,13 @@ static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
|
||||
|
||||
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 2, "ARDUINOJSON_SLOT_ID_SIZE");
|
||||
|
||||
static_assert(ARDUINOJSON_POOL_CAPACITY == 128, "ARDUINOJSON_POOL_CAPACITY");
|
||||
|
||||
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
||||
|
||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||
|
||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
|
||||
"sizeof(VariantSlot)");
|
||||
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
|
||||
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
@@ -4,11 +4,13 @@ static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
|
||||
|
||||
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 4, "ARDUINOJSON_SLOT_ID_SIZE");
|
||||
|
||||
static_assert(ARDUINOJSON_POOL_CAPACITY == 256, "ARDUINOJSON_POOL_CAPACITY");
|
||||
|
||||
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
||||
|
||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||
|
||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 24,
|
||||
"sizeof(VariantSlot)");
|
||||
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 16,
|
||||
"slot size");
|
||||
|
||||
int main() {}
|
||||
|
||||
@@ -4,11 +4,12 @@ static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
|
||||
|
||||
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 2, "ARDUINOJSON_SLOT_ID_SIZE");
|
||||
|
||||
static_assert(ARDUINOJSON_POOL_CAPACITY == 128, "ARDUINOJSON_POOL_CAPACITY");
|
||||
|
||||
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
|
||||
|
||||
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
|
||||
|
||||
static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
|
||||
"sizeof(VariantSlot)");
|
||||
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
|
||||
|
||||
int main() {}
|
||||
|
||||
@@ -16,9 +16,10 @@ endif()
|
||||
|
||||
add_executable(DeprecatedTests
|
||||
add.cpp
|
||||
BasicJsonDocument.cpp
|
||||
containsKey.cpp
|
||||
createNestedArray.cpp
|
||||
createNestedObject.cpp
|
||||
BasicJsonDocument.cpp
|
||||
DynamicJsonDocument.cpp
|
||||
macros.cpp
|
||||
memoryUsage.cpp
|
||||
|
||||
210
lib/ArduinoJson/extras/tests/Deprecated/containsKey.cpp
Normal file
210
lib/ArduinoJson/extras/tests/Deprecated/containsKey.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2024, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
TEST_CASE("JsonDocument::containsKey()") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("returns true on object") {
|
||||
doc["hello"] = "world";
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == true);
|
||||
}
|
||||
|
||||
SECTION("returns true when value is null") {
|
||||
doc["hello"] = static_cast<const char*>(0);
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == true);
|
||||
}
|
||||
|
||||
SECTION("returns true when key is a std::string") {
|
||||
doc["hello"] = "world";
|
||||
|
||||
REQUIRE(doc.containsKey("hello"_s) == true);
|
||||
}
|
||||
|
||||
SECTION("returns false on object") {
|
||||
doc["world"] = "hello";
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == false);
|
||||
}
|
||||
|
||||
SECTION("returns false on array") {
|
||||
doc.add("hello");
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == false);
|
||||
}
|
||||
|
||||
SECTION("returns false on null") {
|
||||
REQUIRE(doc.containsKey("hello") == false);
|
||||
}
|
||||
|
||||
SECTION("support JsonVariant") {
|
||||
doc["hello"] = "world";
|
||||
doc["key"] = "hello";
|
||||
|
||||
REQUIRE(doc.containsKey(doc["key"]) == true);
|
||||
REQUIRE(doc.containsKey(doc["foo"]) == false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::containsKey()") {
|
||||
JsonDocument doc;
|
||||
auto mp = doc["hello"];
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key"_s) == true);
|
||||
REQUIRE(mp.containsKey("key"_s) == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::containsKey()") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["hello"] = 42;
|
||||
|
||||
SECTION("returns true only if key is present") {
|
||||
REQUIRE(false == obj.containsKey("world"));
|
||||
REQUIRE(true == obj.containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("returns false after remove()") {
|
||||
obj.remove("hello");
|
||||
|
||||
REQUIRE(false == obj.containsKey("hello"));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("key is a VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(true == obj.containsKey(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("key is a JsonVariant") {
|
||||
doc["key"] = "hello";
|
||||
REQUIRE(true == obj.containsKey(obj["key"]));
|
||||
REQUIRE(false == obj.containsKey(obj["hello"]));
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
REQUIRE(true == obj.containsKey("hello"_s));
|
||||
}
|
||||
|
||||
SECTION("unsigned char[]") {
|
||||
unsigned char key[] = "hello";
|
||||
REQUIRE(true == obj.containsKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObjectConst::containsKey()") {
|
||||
JsonDocument doc;
|
||||
doc["hello"] = 42;
|
||||
auto obj = doc.as<JsonObjectConst>();
|
||||
|
||||
SECTION("supports const char*") {
|
||||
REQUIRE(false == obj.containsKey("world"));
|
||||
REQUIRE(true == obj.containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("supports std::string") {
|
||||
REQUIRE(false == obj.containsKey("world"_s));
|
||||
REQUIRE(true == obj.containsKey("hello"_s));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(true == obj.containsKey(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("supports JsonVariant") {
|
||||
doc["key"] = "hello";
|
||||
REQUIRE(true == obj.containsKey(obj["key"]));
|
||||
REQUIRE(false == obj.containsKey(obj["hello"]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::containsKey()") {
|
||||
JsonDocument doc;
|
||||
JsonVariant var = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("returns false is unbound") {
|
||||
CHECK_FALSE(JsonVariant().containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
var["hello"] = "world";
|
||||
|
||||
REQUIRE(var.containsKey("hello") == true);
|
||||
REQUIRE(var.containsKey("world") == false);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
var["hello"] = "world";
|
||||
|
||||
REQUIRE(var.containsKey("hello"_s) == true);
|
||||
REQUIRE(var.containsKey("world"_s) == false);
|
||||
}
|
||||
|
||||
SECTION("containsKey(JsonVariant)") {
|
||||
var["hello"] = "world";
|
||||
var["key"] = "hello";
|
||||
|
||||
REQUIRE(var.containsKey(doc["key"]) == true);
|
||||
REQUIRE(var.containsKey(doc["foo"]) == false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariantConst::containsKey()") {
|
||||
JsonDocument doc;
|
||||
doc["hello"] = "world";
|
||||
JsonVariantConst var = doc.as<JsonVariant>();
|
||||
|
||||
SECTION("support const char*") {
|
||||
REQUIRE(var.containsKey("hello") == true);
|
||||
REQUIRE(var.containsKey("world") == false);
|
||||
}
|
||||
|
||||
SECTION("support std::string") {
|
||||
REQUIRE(var.containsKey("hello"_s) == true);
|
||||
REQUIRE(var.containsKey("world"_s) == false);
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(true == var.containsKey(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("support JsonVariant") {
|
||||
doc["key"] = "hello";
|
||||
REQUIRE(var.containsKey(var["key"]) == true);
|
||||
REQUIRE(var.containsKey(var["foo"]) == false);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/Allocator.hpp>
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPool.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -265,12 +265,14 @@ class TimebombAllocator : public ArduinoJson::Allocator {
|
||||
} // namespace
|
||||
|
||||
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
|
||||
return sizeof(ArduinoJson::detail::VariantPool) * n;
|
||||
using namespace ArduinoJson::detail;
|
||||
return sizeof(MemoryPool<VariantData>) * n;
|
||||
}
|
||||
|
||||
inline size_t sizeofPool(
|
||||
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
|
||||
return ArduinoJson::detail::VariantPool::slotsToBytes(n);
|
||||
using namespace ArduinoJson::detail;
|
||||
return MemoryPool<VariantData>::slotsToBytes(n);
|
||||
}
|
||||
|
||||
inline size_t sizeofStringBuffer(size_t iteration = 1) {
|
||||
|
||||
@@ -68,6 +68,12 @@ TEST_CASE("JsonArray::remove()") {
|
||||
REQUIRE(array[1] == 2);
|
||||
}
|
||||
|
||||
SECTION("remove end()") {
|
||||
array.remove(array.end());
|
||||
|
||||
REQUIRE(3 == array.size());
|
||||
}
|
||||
|
||||
SECTION("In a loop") {
|
||||
for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) {
|
||||
if (*it == 2)
|
||||
|
||||
@@ -69,14 +69,32 @@ TEST_CASE("deserialize JSON array") {
|
||||
REQUIRE(arr[1] == 84);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
SECTION("Float") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == 4.2);
|
||||
REQUIRE(arr[1] == 1e2);
|
||||
REQUIRE(arr[0].as<float>() == Approx(4.2f));
|
||||
REQUIRE(arr[1] == 1e2f);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2123456,-7E89]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
|
||||
REQUIRE(arr[1] == -7E89);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(4)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Unsigned long") {
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
TEST_CASE("deserializeJson() returns IncompleteInput") {
|
||||
const char* testCases[] = {
|
||||
// strings
|
||||
@@ -118,3 +120,43 @@ TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
|
||||
SECTION("uint32_t should pass") {
|
||||
auto err = deserializeJson(doc, "4294967295");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("uint64_t should fail") {
|
||||
auto err = deserializeJson(doc, "18446744073709551615");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("int32_t should pass") {
|
||||
auto err = deserializeJson(doc, "-2147483648");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("int64_t should fail") {
|
||||
auto err = deserializeJson(doc, "-9223372036854775808");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("float should pass") {
|
||||
auto err = deserializeJson(doc, "3.402823e38");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("double should fail") {
|
||||
auto err = deserializeJson(doc, "1.7976931348623157e308");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,15 +155,27 @@ TEST_CASE("deserialize JSON object") {
|
||||
REQUIRE(obj["key2"] == -42);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
SECTION("Float") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E89}");
|
||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E3}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == 12.345);
|
||||
REQUIRE(obj["key1"].as<float>() == Approx(12.345f));
|
||||
REQUIRE(obj["key2"] == -7E3f);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.3456789,\"key2\":-7E89}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<double>() == Approx(12.3456789));
|
||||
REQUIRE(obj["key2"] == -7E89);
|
||||
}
|
||||
|
||||
@@ -308,12 +320,12 @@ TEST_CASE("deserialize JSON object") {
|
||||
REQUIRE(doc["a"] == 2);
|
||||
}
|
||||
|
||||
SECTION("NUL in keys") { // we don't support NULs in keys
|
||||
SECTION("NUL in keys") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"x\":2}");
|
||||
REQUIRE(doc.as<std::string>() == "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,22 @@ TEST_CASE("Truncated JSON string") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Escape single quote in single quoted string") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "'ab\\\'cd'");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == "ab\'cd");
|
||||
}
|
||||
|
||||
TEST_CASE("Escape double quote in double quoted string") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "'ab\\\"cd'");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == "ab\"cd");
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid JSON string") {
|
||||
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
|
||||
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
|
||||
|
||||
@@ -9,7 +9,6 @@ add_executable(JsonDocumentTests
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
constructor.cpp
|
||||
containsKey.cpp
|
||||
ElementProxy.cpp
|
||||
isNull.cpp
|
||||
issue1120.cpp
|
||||
|
||||
@@ -93,25 +93,6 @@ TEST_CASE("MemberProxy::operator==()") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::containsKey()") {
|
||||
JsonDocument doc;
|
||||
MemberProxy mp = doc["hello"];
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key"_s) == true);
|
||||
REQUIRE(mp.containsKey("key"_s) == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::operator|()") {
|
||||
JsonDocument doc;
|
||||
|
||||
@@ -345,12 +326,12 @@ TEST_CASE("Deduplicate keys") {
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy under memory constraints") {
|
||||
KillswitchAllocator killswitch;
|
||||
SpyingAllocator spy(&killswitch);
|
||||
TimebombAllocator timebomb(1);
|
||||
SpyingAllocator spy(&timebomb);
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("key allocation fails") {
|
||||
killswitch.on();
|
||||
SECTION("key slot allocation fails") {
|
||||
timebomb.setCountdown(0);
|
||||
|
||||
doc["hello"_s] = "world";
|
||||
|
||||
@@ -358,6 +339,38 @@ TEST_CASE("MemberProxy under memory constraints") {
|
||||
REQUIRE(doc.size() == 0);
|
||||
REQUIRE(doc.overflowed() == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("value slot allocation fails") {
|
||||
timebomb.setCountdown(1);
|
||||
|
||||
// fill the pool entirely, but leave one slot for the key
|
||||
doc["foo"][ARDUINOJSON_POOL_CAPACITY - 4] = 1;
|
||||
REQUIRE(doc.overflowed() == false);
|
||||
|
||||
doc["hello"_s] = "world";
|
||||
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(doc.size() == 1);
|
||||
REQUIRE(doc.overflowed() == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
AllocateFail(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("key string allocation fails") {
|
||||
timebomb.setCountdown(1);
|
||||
|
||||
doc["hello"_s] = "world";
|
||||
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(doc.size() == 0);
|
||||
REQUIRE(doc.overflowed() == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
AllocateFail(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ TEST_CASE("JsonDocument assignment") {
|
||||
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -54,8 +54,8 @@ TEST_CASE("JsonDocument assignment") {
|
||||
|
||||
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -72,8 +72,8 @@ TEST_CASE("JsonDocument assignment") {
|
||||
REQUIRE(doc1.as<std::string>() == "null");
|
||||
}
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
Deallocate(sizeofString("hello")),
|
||||
Deallocate(sizeofString("world")),
|
||||
|
||||
@@ -122,8 +122,8 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofString("abcdefg")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("abcdefg")),
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
@@ -178,7 +178,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("abcdefg")),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)),
|
||||
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
add_executable(JsonObjectTests
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
containsKey.cpp
|
||||
equals.cpp
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
|
||||
@@ -52,8 +52,8 @@ TEST_CASE("JsonObject::set()") {
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ TEST_CASE("JsonObject::set()") {
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -81,8 +81,8 @@ TEST_CASE("JsonObject::set()") {
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,13 +34,6 @@ TEST_CASE("std::string") {
|
||||
REQUIRE("value"_s == obj["key"_s]);
|
||||
}
|
||||
|
||||
SECTION("containsKey()") {
|
||||
char json[] = "{\"key\":\"value\"}";
|
||||
deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
REQUIRE(true == obj.containsKey("key"_s));
|
||||
}
|
||||
|
||||
SECTION("remove()") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["key"] = "value";
|
||||
|
||||
@@ -118,16 +118,16 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
SECTION("should duplicate char* key") {
|
||||
obj[const_cast<char*>("hello")] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key&value") {
|
||||
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -143,16 +143,16 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
SECTION("should duplicate std::string key") {
|
||||
obj["hello"_s] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key&value") {
|
||||
obj["hello"_s] = "world"_s;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -160,8 +160,8 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
SECTION("should duplicate a non-static JsonString key") {
|
||||
obj[JsonString("hello", JsonString::Copied)] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
# MIT License
|
||||
|
||||
add_executable(JsonObjectConstTests
|
||||
containsKey.cpp
|
||||
equals.cpp
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
|
||||
@@ -46,6 +46,10 @@ TEST_CASE("serializeJson(JsonVariant)") {
|
||||
check("fifty/fifty"_s, "\"fifty/fifty\"");
|
||||
}
|
||||
|
||||
SECTION("Don't escape single quote") {
|
||||
check("hello'world"_s, "\"hello'world\"");
|
||||
}
|
||||
|
||||
SECTION("Escape backspace") {
|
||||
check("hello\bworld"_s, "\"hello\\bworld\"");
|
||||
}
|
||||
@@ -83,6 +87,11 @@ TEST_CASE("serializeJson(JsonVariant)") {
|
||||
check(3.1415927, "3.1415927");
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
REQUIRE(sizeof(float) == 4);
|
||||
check(3.1415927f, "3.141593");
|
||||
}
|
||||
|
||||
SECTION("Zero") {
|
||||
check(0, "0");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ add_executable(JsonVariantTests
|
||||
as.cpp
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
containsKey.cpp
|
||||
converters.cpp
|
||||
copy.cpp
|
||||
is.cpp
|
||||
|
||||
@@ -31,13 +31,28 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(4.2)") {
|
||||
SECTION("set(float)") {
|
||||
variant.set(4.2f);
|
||||
|
||||
REQUIRE(variant.as<bool>());
|
||||
REQUIRE(0 == variant.as<const char*>());
|
||||
REQUIRE(variant.as<std::string>() == "4.2");
|
||||
REQUIRE(variant.as<long>() == 4L);
|
||||
REQUIRE(variant.as<float>() == 4.2f);
|
||||
REQUIRE(variant.as<unsigned>() == 4U);
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(double)") {
|
||||
variant.set(4.2);
|
||||
|
||||
REQUIRE(variant.as<bool>());
|
||||
REQUIRE(0 == variant.as<const char*>());
|
||||
REQUIRE(variant.as<std::string>() == "4.2");
|
||||
REQUIRE(variant.as<long>() == 4L);
|
||||
REQUIRE(variant.as<double>() == 4.2);
|
||||
REQUIRE(variant.as<unsigned>() == 4U);
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
|
||||
@@ -78,43 +93,62 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(42)") {
|
||||
variant.set(42);
|
||||
SECTION("set(uint32_t)") {
|
||||
variant.set(4294967295U);
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<double>() == 42.0);
|
||||
REQUIRE(variant.as<int>() == 42);
|
||||
REQUIRE(variant.as<unsigned int>() == 42U); // issue #1601
|
||||
REQUIRE(variant.as<std::string>() == "42");
|
||||
REQUIRE(variant.as<double>() == 4294967295.0);
|
||||
REQUIRE(variant.as<int32_t>() == 0);
|
||||
REQUIRE(variant.as<uint32_t>() == 4294967295U);
|
||||
REQUIRE(variant.as<uint64_t>() == 4294967295U);
|
||||
REQUIRE(variant.as<std::string>() == "4294967295");
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(42L)") {
|
||||
variant.set(42L);
|
||||
SECTION("set(int32_t)") {
|
||||
variant.set(-2147483648LL);
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<double>() == 42.0);
|
||||
REQUIRE(variant.as<std::string>() == "42");
|
||||
REQUIRE(variant.as<double>() == -2147483648LL);
|
||||
REQUIRE(variant.as<int32_t>() == -2147483648LL);
|
||||
REQUIRE(variant.as<int64_t>() == -2147483648LL);
|
||||
REQUIRE(variant.as<uint32_t>() == 0);
|
||||
REQUIRE(variant.as<uint64_t>() == 0);
|
||||
REQUIRE(variant.as<std::string>() == "-2147483648");
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(-42L)") {
|
||||
variant.set(-42L);
|
||||
|
||||
REQUIRE(variant.as<double>() == -42.0);
|
||||
REQUIRE(variant.as<std::string>() == "-42");
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
}
|
||||
|
||||
SECTION("set(42UL)") {
|
||||
variant.set(42UL);
|
||||
SECTION("set(uint64_t)") {
|
||||
variant.set(4294967296U);
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<double>() == 42.0);
|
||||
REQUIRE(variant.as<std::string>() == "42");
|
||||
REQUIRE(variant.as<double>() == 4294967296.0);
|
||||
REQUIRE(variant.as<int32_t>() == 0);
|
||||
REQUIRE(variant.as<uint32_t>() == 0);
|
||||
REQUIRE(variant.as<uint64_t>() == 4294967296U);
|
||||
REQUIRE(variant.as<std::string>() == "4294967296");
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(int64_t)") {
|
||||
variant.set(-2147483649LL);
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<double>() == -2147483649LL);
|
||||
REQUIRE(variant.as<int32_t>() == 0);
|
||||
REQUIRE(variant.as<int64_t>() == -2147483649LL);
|
||||
REQUIRE(variant.as<uint32_t>() == 0);
|
||||
REQUIRE(variant.as<uint64_t>() == 0);
|
||||
REQUIRE(variant.as<std::string>() == "-2147483649");
|
||||
REQUIRE(variant.as<JsonString>().isNull());
|
||||
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
|
||||
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("set(0L)") {
|
||||
@@ -169,7 +203,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
|
||||
REQUIRE(variant.as<bool>() == true);
|
||||
REQUIRE(variant.as<long>() == 4L);
|
||||
REQUIRE(variant.as<double>() == 4.2);
|
||||
REQUIRE(variant.as<double>() == Approx(4.2));
|
||||
REQUIRE(variant.as<const char*>() == "4.2"_s);
|
||||
REQUIRE(variant.as<std::string>() == "4.2"_s);
|
||||
REQUIRE(variant.as<JsonString>() == "4.2");
|
||||
|
||||
@@ -13,7 +13,8 @@ using ArduinoJson::detail::sizeofObject;
|
||||
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
|
||||
|
||||
TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
JsonDocument doc;
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("const char*") {
|
||||
@@ -25,6 +26,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "world"); // stores by pointer
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("(const char*)0") {
|
||||
@@ -32,6 +34,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.isNull());
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
@@ -43,6 +46,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("(char*)0") {
|
||||
@@ -50,6 +56,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.isNull());
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("unsigned char*") {
|
||||
@@ -61,6 +68,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("signed char*") {
|
||||
@@ -72,6 +82,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
@@ -85,6 +98,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -97,6 +113,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("static JsonString") {
|
||||
@@ -108,6 +127,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "world"); // stores by pointer
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("non-static JsonString") {
|
||||
@@ -119,6 +139,9 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("enum") {
|
||||
@@ -129,6 +152,89 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<int>() == true);
|
||||
REQUIRE(variant.as<int>() == 10);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("float") {
|
||||
bool result = variant.set(1.2f);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<float>() == true);
|
||||
REQUIRE(variant.as<float>() == 1.2f);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
bool result = variant.set(1.2);
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<double>() == true);
|
||||
REQUIRE(variant.as<double>() == 1.2);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("int32_t") {
|
||||
bool result = variant.set(int32_t(42));
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<int32_t>() == true);
|
||||
REQUIRE(variant.as<int32_t>() == 42);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("int64_t") {
|
||||
bool result = variant.set(int64_t(-2147483649LL));
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<int64_t>() == true);
|
||||
REQUIRE(variant.as<int64_t>() == -2147483649LL);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("uint32_t") {
|
||||
bool result = variant.set(uint32_t(42));
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<uint32_t>() == true);
|
||||
REQUIRE(variant.as<uint32_t>() == 42);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("uint64_t") {
|
||||
bool result = variant.set(uint64_t(4294967296));
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<uint64_t>() == true);
|
||||
REQUIRE(variant.as<uint64_t>() == 4294967296);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("JsonDocument") {
|
||||
JsonDocument doc1;
|
||||
doc1["hello"] = "world";
|
||||
|
||||
// Should copy the doc
|
||||
variant.set(doc1);
|
||||
doc1.clear();
|
||||
|
||||
std::string json;
|
||||
serializeJson(doc, json);
|
||||
REQUIRE(json == "{\"hello\":\"world\"}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,22 +264,48 @@ TEST_CASE("JsonVariant::set() with not enough memory") {
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("float") {
|
||||
bool result = v.set(1.2f);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(v.is<float>());
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::set(JsonDocument)") {
|
||||
JsonDocument doc1;
|
||||
doc1["hello"] = "world";
|
||||
SECTION("double") {
|
||||
bool result = v.set(1.2);
|
||||
|
||||
JsonDocument doc2;
|
||||
JsonVariant v = doc2.to<JsonVariant>();
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
// Should copy the doc
|
||||
v.set(doc1);
|
||||
doc1.clear();
|
||||
SECTION("int32_t") {
|
||||
bool result = v.set(-42);
|
||||
|
||||
std::string json;
|
||||
serializeJson(doc2, json);
|
||||
REQUIRE(json == "{\"hello\":\"world\"}");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(v.is<int32_t>());
|
||||
}
|
||||
|
||||
SECTION("int64_t") {
|
||||
bool result = v.set(-2147483649LL);
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("uint32_t") {
|
||||
bool result = v.set(42);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(v.is<uint32_t>());
|
||||
}
|
||||
|
||||
SECTION("uint64_t") {
|
||||
bool result = v.set(4294967296U);
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::set() releases the previous value") {
|
||||
@@ -220,3 +352,34 @@ TEST_CASE("JsonVariant::set() releases the previous value") {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::set() reuses extension slot") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
variant.set(1.2);
|
||||
doc.shrinkToFit();
|
||||
spy.clearLog();
|
||||
|
||||
SECTION("double") {
|
||||
bool result = variant.set(3.4);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("int64_t") {
|
||||
bool result = variant.set(-2147483649LL);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("uint64_t") {
|
||||
bool result = variant.set(4294967296U);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,10 +73,6 @@ TEST_CASE("Unbound JsonVariant") {
|
||||
CHECK_FALSE(variant["key"_s].set(1));
|
||||
}
|
||||
|
||||
SECTION("containsKey()") {
|
||||
CHECK_FALSE(variant.containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("remove()") {
|
||||
variant.remove(0);
|
||||
variant.remove("hello");
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
add_executable(JsonVariantConstTests
|
||||
as.cpp
|
||||
containsKey.cpp
|
||||
is.cpp
|
||||
isnull.cpp
|
||||
nesting.cpp
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
add_executable(MiscTests
|
||||
arithmeticCompare.cpp
|
||||
conflicts.cpp
|
||||
FloatParts.cpp
|
||||
issue1967.cpp
|
||||
JsonString.cpp
|
||||
NoArduinoHeader.cpp
|
||||
|
||||
@@ -176,15 +176,6 @@ TEST_CASE("unsigned char[]") {
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("containsKey()") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
REQUIRE(true == obj.containsKey(key));
|
||||
}
|
||||
|
||||
SECTION("remove()") {
|
||||
unsigned char key[] = "hello";
|
||||
|
||||
|
||||
@@ -3,7 +3,21 @@
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
namespace my {
|
||||
using ArduinoJson::detail::isinf;
|
||||
} // namespace my
|
||||
|
||||
void checkFloat(const char* input, float expected) {
|
||||
using ArduinoJson::detail::NumberType;
|
||||
using ArduinoJson::detail::parseNumber;
|
||||
CAPTURE(input);
|
||||
auto result = parseNumber(input);
|
||||
REQUIRE(result.type() == NumberType::Float);
|
||||
REQUIRE(result.asFloat() == Approx(expected));
|
||||
}
|
||||
|
||||
TEST_CASE("ARDUINOJSON_USE_DOUBLE == 0") {
|
||||
SECTION("serializeJson()") {
|
||||
JsonDocument doc;
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
|
||||
@@ -15,3 +29,39 @@ TEST_CASE("ARDUINOJSON_USE_DOUBLE == 0") {
|
||||
|
||||
REQUIRE(json == "{\"pi\":3.14,\"e\":2.72}");
|
||||
}
|
||||
|
||||
SECTION("parseNumber()") {
|
||||
using ArduinoJson::detail::NumberType;
|
||||
using ArduinoJson::detail::parseNumber;
|
||||
|
||||
SECTION("Large positive number") {
|
||||
auto result = parseNumber("1e300");
|
||||
REQUIRE(result.type() == NumberType::Float);
|
||||
REQUIRE(result.asFloat() > 0);
|
||||
REQUIRE(my::isinf(result.asFloat()));
|
||||
}
|
||||
|
||||
SECTION("Large negative number") {
|
||||
auto result = parseNumber("-1e300");
|
||||
REQUIRE(result.type() == NumberType::Float);
|
||||
REQUIRE(result.asFloat() < 0);
|
||||
REQUIRE(my::isinf(result.asFloat()));
|
||||
}
|
||||
|
||||
SECTION("Too small to be represented") {
|
||||
auto result = parseNumber("1e-300");
|
||||
REQUIRE(result.type() == NumberType::Float);
|
||||
REQUIRE(result.asFloat() == 0);
|
||||
}
|
||||
|
||||
SECTION("MantissaTooLongToFit") {
|
||||
checkFloat("0.340282346638528861111111111111", 0.34028234663852886f);
|
||||
checkFloat("34028234663852886.11111111111111", 34028234663852886.0f);
|
||||
checkFloat("34028234.66385288611111111111111", 34028234.663852886f);
|
||||
|
||||
checkFloat("-0.340282346638528861111111111111", -0.34028234663852886f);
|
||||
checkFloat("-34028234663852886.11111111111111", -34028234663852886.0f);
|
||||
checkFloat("-34028234.66385288611111111111111", -34028234.663852886f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,7 @@ TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") {
|
||||
deserializeMsgPack(doc, "\xcf\x00\x00\x00\x01\x00\x00\x00\x00"_s);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
|
||||
REQUIRE(doc.as<JsonInteger>() == 0x100000000);
|
||||
#else
|
||||
REQUIRE(doc.isNull());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,3 +199,44 @@ TEST_CASE(
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"deserializeMsgPack() returns NoMemory if extension allocation fails") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
|
||||
SECTION("uint32_t should pass") {
|
||||
auto err = deserializeMsgPack(doc, "\xceXXXX");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("uint64_t should fail") {
|
||||
auto err = deserializeMsgPack(doc, "\xcfXXXXXXXX");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("int32_t should pass") {
|
||||
auto err = deserializeMsgPack(doc, "\xd2XXXX");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("int64_t should fail") {
|
||||
auto err = deserializeMsgPack(doc, "\xd3XXXXXXXX");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("float should pass") {
|
||||
auto err = deserializeMsgPack(doc, "\xcaXXXX");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("double should fail") {
|
||||
auto err = deserializeMsgPack(doc, "\xcbXXXXXXXX");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
add_executable(NumbersTests
|
||||
convertNumber.cpp
|
||||
parseFloat.cpp
|
||||
decomposeFloat.cpp
|
||||
parseDouble.cpp
|
||||
parseFloat.cpp
|
||||
parseInteger.cpp
|
||||
parseNumber.cpp
|
||||
)
|
||||
|
||||
42
lib/ArduinoJson/extras/tests/Numbers/decomposeFloat.cpp
Normal file
42
lib/ArduinoJson/extras/tests/Numbers/decomposeFloat.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2024, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Numbers/FloatParts.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("decomposeFloat()") {
|
||||
SECTION("1.7976931348623157E+308") {
|
||||
auto parts = decomposeFloat(1.7976931348623157E+308, 9);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 797693135);
|
||||
REQUIRE(parts.decimalPlaces == 9);
|
||||
REQUIRE(parts.exponent == 308);
|
||||
}
|
||||
|
||||
SECTION("4.94065645841247e-324") {
|
||||
auto parts = decomposeFloat(4.94065645841247e-324, 9);
|
||||
REQUIRE(parts.integral == 4);
|
||||
REQUIRE(parts.decimal == 940656458);
|
||||
REQUIRE(parts.decimalPlaces == 9);
|
||||
REQUIRE(parts.exponent == -324);
|
||||
}
|
||||
|
||||
SECTION("3.4E+38") {
|
||||
auto parts = decomposeFloat(3.4E+38f, 6);
|
||||
REQUIRE(parts.integral == 3);
|
||||
REQUIRE(parts.decimal == 4);
|
||||
REQUIRE(parts.decimalPlaces == 1);
|
||||
REQUIRE(parts.exponent == 38);
|
||||
}
|
||||
|
||||
SECTION("1.17549435e−38") {
|
||||
auto parts = decomposeFloat(1.17549435e-38f, 6);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 175494);
|
||||
REQUIRE(parts.decimalPlaces == 6);
|
||||
REQUIRE(parts.exponent == -38);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// Copyright © 2014-2024, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_DOUBLE 0
|
||||
#define ARDUINOJSON_ENABLE_NAN 1
|
||||
#define ARDUINOJSON_ENABLE_INFINITY 1
|
||||
|
||||
@@ -13,7 +12,9 @@ using namespace ArduinoJson::detail;
|
||||
|
||||
void checkFloat(const char* input, float expected) {
|
||||
CAPTURE(input);
|
||||
REQUIRE(parseNumber<float>(input) == Approx(expected));
|
||||
auto result = parseNumber(input);
|
||||
REQUIRE(result.type() == NumberType::Float);
|
||||
REQUIRE(result.asFloat() == Approx(expected));
|
||||
}
|
||||
|
||||
void checkFloatNaN(const char* input) {
|
||||
@@ -59,27 +60,15 @@ TEST_CASE("parseNumber<float>()") {
|
||||
|
||||
SECTION("VeryLong") {
|
||||
checkFloat("0.00000000000000000000000000000001", 1e-32f);
|
||||
checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
||||
checkFloat(
|
||||
"100000000000000000000000000000000.00000000000000000000000000000",
|
||||
1e+32f);
|
||||
}
|
||||
|
||||
SECTION("MantissaTooLongToFit") {
|
||||
checkFloat("0.340282346638528861111111111111", 0.34028234663852886f);
|
||||
checkFloat("34028234663852886.11111111111111", 34028234663852886.0f);
|
||||
checkFloat("34028234.66385288611111111111111", 34028234.663852886f);
|
||||
|
||||
checkFloat("-0.340282346638528861111111111111", -0.34028234663852886f);
|
||||
checkFloat("-34028234663852886.11111111111111", -34028234663852886.0f);
|
||||
checkFloat("-34028234.66385288611111111111111", -34028234.663852886f);
|
||||
}
|
||||
|
||||
SECTION("ExponentTooBig") {
|
||||
checkFloatInf("1e39", false);
|
||||
checkFloatInf("-1e39", true);
|
||||
checkFloatInf("1e255", false);
|
||||
checkFloat("1e-255", 0.0f);
|
||||
// The following don't work because they have many digits so parseNumber()
|
||||
// treats them as double. But it's not an issue because JsonVariant will use
|
||||
// a float to store them.
|
||||
//
|
||||
// checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
||||
// checkFloat(
|
||||
// "100000000000000000000000000000000.00000000000000000000000000000",
|
||||
// 1e+32f);
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
@@ -94,8 +83,5 @@ TEST_CASE("parseNumber<float>()") {
|
||||
checkFloatInf("inf", false);
|
||||
checkFloatInf("+inf", false);
|
||||
checkFloatInf("-inf", true);
|
||||
|
||||
checkFloatInf("1e300", false);
|
||||
checkFloatInf("-1e300", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,45 +9,55 @@ using namespace ArduinoJson;
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("Test unsigned integer overflow") {
|
||||
VariantData first, second;
|
||||
Number first, second;
|
||||
|
||||
// Avoids MSVC warning C4127 (conditional expression is constant)
|
||||
size_t integerSize = sizeof(JsonInteger);
|
||||
|
||||
if (integerSize == 8) {
|
||||
parseNumber("18446744073709551615", first);
|
||||
parseNumber("18446744073709551616", second);
|
||||
first = parseNumber("18446744073709551615");
|
||||
second = parseNumber("18446744073709551616");
|
||||
} else {
|
||||
parseNumber("4294967295", first);
|
||||
parseNumber("4294967296", second);
|
||||
first = parseNumber("4294967295");
|
||||
second = parseNumber("4294967296");
|
||||
}
|
||||
|
||||
REQUIRE(first.type() == uint8_t(VALUE_IS_UNSIGNED_INTEGER));
|
||||
REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT));
|
||||
REQUIRE(first.type() == NumberType::UnsignedInteger);
|
||||
REQUIRE(second.type() == NumberType::Double);
|
||||
}
|
||||
|
||||
TEST_CASE("Test signed integer overflow") {
|
||||
VariantData first, second;
|
||||
Number first, second;
|
||||
|
||||
// Avoids MSVC warning C4127 (conditional expression is constant)
|
||||
size_t integerSize = sizeof(JsonInteger);
|
||||
|
||||
if (integerSize == 8) {
|
||||
parseNumber("-9223372036854775808", first);
|
||||
parseNumber("-9223372036854775809", second);
|
||||
first = parseNumber("-9223372036854775808");
|
||||
second = parseNumber("-9223372036854775809");
|
||||
} else {
|
||||
parseNumber("-2147483648", first);
|
||||
parseNumber("-2147483649", second);
|
||||
first = parseNumber("-2147483648");
|
||||
second = parseNumber("-2147483649");
|
||||
}
|
||||
|
||||
REQUIRE(first.type() == uint8_t(VALUE_IS_SIGNED_INTEGER));
|
||||
REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT));
|
||||
REQUIRE(first.type() == NumberType::SignedInteger);
|
||||
REQUIRE(second.type() == NumberType::Double);
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid value") {
|
||||
VariantData result;
|
||||
auto result = parseNumber("6a3");
|
||||
|
||||
parseNumber("6a3", result);
|
||||
|
||||
REQUIRE(result.type() == uint8_t(VALUE_IS_NULL));
|
||||
REQUIRE(result.type() == NumberType::Invalid);
|
||||
}
|
||||
|
||||
TEST_CASE("float") {
|
||||
auto result = parseNumber("3.402823e38");
|
||||
|
||||
REQUIRE(result.type() == NumberType::Float);
|
||||
}
|
||||
|
||||
TEST_CASE("double") {
|
||||
auto result = parseNumber("1.7976931348623157e308");
|
||||
|
||||
REQUIRE(result.type() == NumberType::Double);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -9,28 +9,28 @@
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("ResourceManager::allocSlot()") {
|
||||
TEST_CASE("ResourceManager::allocVariant()") {
|
||||
SECTION("Returns different pointer") {
|
||||
ResourceManager resources;
|
||||
|
||||
VariantSlot* s1 = resources.allocSlot();
|
||||
REQUIRE(s1 != 0);
|
||||
VariantSlot* s2 = resources.allocSlot();
|
||||
REQUIRE(s2 != 0);
|
||||
auto s1 = resources.allocVariant();
|
||||
REQUIRE(s1.ptr() != nullptr);
|
||||
auto s2 = resources.allocVariant();
|
||||
REQUIRE(s2.ptr() != nullptr);
|
||||
|
||||
REQUIRE(s1 != s2);
|
||||
REQUIRE(s1.ptr() != s2.ptr());
|
||||
}
|
||||
|
||||
SECTION("Returns the same slot after calling freeSlot()") {
|
||||
SECTION("Returns the same slot after calling freeVariant()") {
|
||||
ResourceManager resources;
|
||||
|
||||
auto s1 = resources.allocSlot();
|
||||
auto s2 = resources.allocSlot();
|
||||
resources.freeSlot(s1);
|
||||
resources.freeSlot(s2);
|
||||
auto s3 = resources.allocSlot();
|
||||
auto s4 = resources.allocSlot();
|
||||
auto s5 = resources.allocSlot();
|
||||
auto s1 = resources.allocVariant();
|
||||
auto s2 = resources.allocVariant();
|
||||
resources.freeVariant(s1);
|
||||
resources.freeVariant(s2);
|
||||
auto s3 = resources.allocVariant();
|
||||
auto s4 = resources.allocVariant();
|
||||
auto s5 = resources.allocVariant();
|
||||
|
||||
REQUIRE(s2.id() != s1.id());
|
||||
REQUIRE(s3.id() == s2.id());
|
||||
@@ -42,26 +42,26 @@ TEST_CASE("ResourceManager::allocSlot()") {
|
||||
SECTION("Returns aligned pointers") {
|
||||
ResourceManager resources;
|
||||
|
||||
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
|
||||
REQUIRE(isAligned(resources.allocSlot().operator VariantSlot*()));
|
||||
REQUIRE(isAligned(resources.allocVariant().ptr()));
|
||||
REQUIRE(isAligned(resources.allocVariant().ptr()));
|
||||
}
|
||||
|
||||
SECTION("Returns null if pool list allocation fails") {
|
||||
ResourceManager resources(FailingAllocator::instance());
|
||||
|
||||
auto variant = resources.allocSlot();
|
||||
auto variant = resources.allocVariant();
|
||||
REQUIRE(variant.id() == NULL_SLOT);
|
||||
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
|
||||
REQUIRE(variant.ptr() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("Returns null if pool allocation fails") {
|
||||
ResourceManager resources(FailingAllocator::instance());
|
||||
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
|
||||
auto variant = resources.allocSlot();
|
||||
auto variant = resources.allocVariant();
|
||||
REQUIRE(variant.id() == NULL_SLOT);
|
||||
REQUIRE(static_cast<VariantSlot*>(variant) == nullptr);
|
||||
REQUIRE(variant.ptr() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("Try overflow pool counter") {
|
||||
@@ -73,18 +73,18 @@ TEST_CASE("ResourceManager::allocSlot()") {
|
||||
|
||||
// fill all the pools
|
||||
for (SlotId i = 0; i < NULL_SLOT; i++) {
|
||||
auto slot = resources.allocSlot();
|
||||
auto slot = resources.allocVariant();
|
||||
REQUIRE(slot.id() == i); // or != NULL_SLOT
|
||||
REQUIRE(static_cast<VariantSlot*>(slot) != nullptr);
|
||||
REQUIRE(slot.ptr() != nullptr);
|
||||
}
|
||||
|
||||
REQUIRE(resources.overflowed() == false);
|
||||
|
||||
// now all allocations should fail
|
||||
for (int i = 0; i < 10; i++) {
|
||||
auto slot = resources.allocSlot();
|
||||
auto slot = resources.allocVariant();
|
||||
REQUIRE(slot.id() == NULL_SLOT);
|
||||
REQUIRE(static_cast<VariantSlot*>(slot) == nullptr);
|
||||
REQUIRE(slot.ptr() == nullptr);
|
||||
}
|
||||
|
||||
REQUIRE(resources.overflowed() == true);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
@@ -13,7 +13,7 @@ TEST_CASE("ResourceManager::clear()") {
|
||||
ResourceManager resources;
|
||||
|
||||
SECTION("Discards allocated variants") {
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
|
||||
resources.clear();
|
||||
REQUIRE(resources.size() == 0);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
@@ -21,14 +21,14 @@ TEST_CASE("ResourceManager::shrinkToFit()") {
|
||||
}
|
||||
|
||||
SECTION("only one pool") {
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
|
||||
resources.shrinkToFit();
|
||||
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeof(VariantSlot)),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ TEST_CASE("ResourceManager::shrinkToFit()") {
|
||||
for (size_t i = 0;
|
||||
i < ARDUINOJSON_POOL_CAPACITY * ARDUINOJSON_INITIAL_POOL_COUNT + 1;
|
||||
i++)
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()) * ARDUINOJSON_INITIAL_POOL_COUNT,
|
||||
@@ -49,7 +49,7 @@ TEST_CASE("ResourceManager::shrinkToFit()") {
|
||||
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Reallocate(sizeofPool(), sizeof(VariantSlot)),
|
||||
Reallocate(sizeofPool(), sizeofPool(1)),
|
||||
Reallocate(sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT * 2),
|
||||
sizeofPoolList(ARDUINOJSON_INITIAL_POOL_COUNT + 1)),
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
@@ -21,10 +21,10 @@ TEST_CASE("ResourceManager::size()") {
|
||||
SECTION("Doesn't grow when allocation of second pool fails") {
|
||||
timebomb.setCountdown(1);
|
||||
for (size_t i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
size_t size = resources.size();
|
||||
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
|
||||
REQUIRE(size == resources.size());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManagerImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
@@ -14,7 +14,7 @@ using namespace ArduinoJson::detail;
|
||||
static void fullPreallocatedPools(ResourceManager& resources) {
|
||||
for (int i = 0;
|
||||
i < ARDUINOJSON_INITIAL_POOL_COUNT * ARDUINOJSON_POOL_CAPACITY; i++)
|
||||
resources.allocSlot();
|
||||
resources.allocVariant();
|
||||
}
|
||||
|
||||
TEST_CASE("ResourceManager::swap()") {
|
||||
@@ -23,13 +23,13 @@ TEST_CASE("ResourceManager::swap()") {
|
||||
ResourceManager a(&spy);
|
||||
ResourceManager b(&spy);
|
||||
|
||||
auto a1 = a.allocSlot();
|
||||
auto b1 = b.allocSlot();
|
||||
auto a1 = a.allocVariant();
|
||||
auto b1 = b.allocVariant();
|
||||
|
||||
swap(a, b);
|
||||
|
||||
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
|
||||
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
|
||||
REQUIRE(a1.ptr() == b.getVariant(a1.id()));
|
||||
REQUIRE(b1.ptr() == a.getVariant(b1.id()));
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()) * 2,
|
||||
@@ -42,12 +42,12 @@ TEST_CASE("ResourceManager::swap()") {
|
||||
ResourceManager b(&spy);
|
||||
fullPreallocatedPools(b);
|
||||
|
||||
auto a1 = a.allocSlot();
|
||||
auto b1 = b.allocSlot();
|
||||
auto a1 = a.allocVariant();
|
||||
auto b1 = b.allocVariant();
|
||||
swap(a, b);
|
||||
|
||||
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
|
||||
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
|
||||
REQUIRE(a1.ptr() == b.getVariant(a1.id()));
|
||||
REQUIRE(b1.ptr() == a.getVariant(b1.id()));
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
@@ -63,12 +63,12 @@ TEST_CASE("ResourceManager::swap()") {
|
||||
fullPreallocatedPools(a);
|
||||
ResourceManager b(&spy);
|
||||
|
||||
auto a1 = a.allocSlot();
|
||||
auto b1 = b.allocSlot();
|
||||
auto a1 = a.allocVariant();
|
||||
auto b1 = b.allocVariant();
|
||||
swap(a, b);
|
||||
|
||||
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
|
||||
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
|
||||
REQUIRE(a1.ptr() == b.getVariant(a1.id()));
|
||||
REQUIRE(b1.ptr() == a.getVariant(b1.id()));
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
@@ -85,12 +85,12 @@ TEST_CASE("ResourceManager::swap()") {
|
||||
ResourceManager b(&spy);
|
||||
fullPreallocatedPools(b);
|
||||
|
||||
auto a1 = a.allocSlot();
|
||||
auto b1 = b.allocSlot();
|
||||
auto a1 = a.allocVariant();
|
||||
auto b1 = b.allocVariant();
|
||||
|
||||
swap(a, b);
|
||||
|
||||
REQUIRE(a1->data() == b.getSlot(a1.id())->data());
|
||||
REQUIRE(b1->data() == a.getSlot(b1.id())->data());
|
||||
REQUIRE(a1.ptr() == b.getVariant(a1.id()));
|
||||
REQUIRE(b1.ptr() == a.getVariant(b1.id()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "7.1.0"
|
||||
version: "7.2.0"
|
||||
description: >-
|
||||
A simple and efficient JSON library for embedded C++.
|
||||
⭐ 6624 stars on GitHub!
|
||||
⭐ 6690 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++. ⭐ 6624 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++. ⭐ 6690 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.1.0",
|
||||
"version": "7.2.0",
|
||||
"authors": {
|
||||
"name": "Benoit Blanchon",
|
||||
"url": "https://blog.benoitblanchon.fr"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
name=ArduinoJson
|
||||
version=7.1.0
|
||||
version=7.2.0
|
||||
author=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
sentence=A simple and efficient JSON library for embedded C++.
|
||||
paragraph=⭐ 6624 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.
|
||||
paragraph=⭐ 6690 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=*
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
#include "ArduinoJson/Array/Utilities.hpp"
|
||||
#include "ArduinoJson/Collection/CollectionImpl.hpp"
|
||||
#include "ArduinoJson/Memory/ResourceManagerImpl.hpp"
|
||||
#include "ArduinoJson/Memory/VariantPoolImpl.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"
|
||||
|
||||
@@ -10,9 +10,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class ArrayData : public CollectionData {
|
||||
public:
|
||||
VariantData* addElement(ResourceManager* resources) {
|
||||
return addSlot(resources).data();
|
||||
}
|
||||
VariantData* addElement(ResourceManager* resources);
|
||||
|
||||
static VariantData* addElement(ArrayData* array, ResourceManager* resources) {
|
||||
if (!array)
|
||||
@@ -51,14 +49,14 @@ class ArrayData : public CollectionData {
|
||||
array->removeElement(index, resources);
|
||||
}
|
||||
|
||||
bool copyFrom(const ArrayData& src, ResourceManager* resources);
|
||||
void remove(iterator it, ResourceManager* resources) {
|
||||
CollectionData::removeOne(it, resources);
|
||||
}
|
||||
|
||||
static bool copy(ArrayData* dst, const ArrayData* src,
|
||||
static void remove(ArrayData* array, iterator it,
|
||||
ResourceManager* resources) {
|
||||
if (!dst || !src)
|
||||
return false;
|
||||
|
||||
return dst->copyFrom(*src, resources);
|
||||
if (array)
|
||||
return array->remove(it, resources);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <ArduinoJson/Array/ArrayData.hpp>
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -19,6 +20,14 @@ inline ArrayData::iterator ArrayData::at(
|
||||
return it;
|
||||
}
|
||||
|
||||
inline VariantData* ArrayData::addElement(ResourceManager* resources) {
|
||||
auto slot = resources->allocVariant();
|
||||
if (!slot)
|
||||
return nullptr;
|
||||
CollectionData::appendOne(slot, resources);
|
||||
return slot.ptr();
|
||||
}
|
||||
|
||||
inline VariantData* ArrayData::getOrAddElement(size_t index,
|
||||
ResourceManager* resources) {
|
||||
auto it = createIterator(resources);
|
||||
@@ -50,16 +59,21 @@ inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
|
||||
template <typename T>
|
||||
inline bool ArrayData::addValue(T&& value, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
auto slot = resources->allocSlot();
|
||||
auto slot = resources->allocVariant();
|
||||
if (!slot)
|
||||
return false;
|
||||
JsonVariant variant(slot->data(), resources);
|
||||
JsonVariant variant(slot.ptr(), resources);
|
||||
if (!variant.set(detail::forward<T>(value))) {
|
||||
resources->freeSlot(slot);
|
||||
resources->freeVariant(slot);
|
||||
return false;
|
||||
}
|
||||
addSlot(slot, resources);
|
||||
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 * ResourceManager::slotSize;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
|
||||
@@ -12,7 +13,7 @@
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class VariantData;
|
||||
class VariantSlot;
|
||||
class ResourceManager;
|
||||
|
||||
class CollectionIterator {
|
||||
friend class CollectionData;
|
||||
@@ -49,12 +50,6 @@ class CollectionIterator {
|
||||
return *data();
|
||||
}
|
||||
|
||||
const char* key() const;
|
||||
bool ownsKey() const;
|
||||
|
||||
void setKey(StringNode*);
|
||||
void setKey(const char*);
|
||||
|
||||
VariantData* data() {
|
||||
return reinterpret_cast<VariantData*>(slot_);
|
||||
}
|
||||
@@ -64,9 +59,9 @@ class CollectionIterator {
|
||||
}
|
||||
|
||||
private:
|
||||
CollectionIterator(VariantSlot* slot, SlotId slotId);
|
||||
CollectionIterator(VariantData* slot, SlotId slotId);
|
||||
|
||||
VariantSlot* slot_;
|
||||
VariantData* slot_;
|
||||
SlotId currentId_, nextId_;
|
||||
};
|
||||
|
||||
@@ -84,9 +79,7 @@ class CollectionData {
|
||||
|
||||
using iterator = CollectionIterator;
|
||||
|
||||
iterator createIterator(const ResourceManager* resources) const {
|
||||
return iterator(resources->getSlot(head_), head_);
|
||||
}
|
||||
iterator createIterator(const ResourceManager* resources) const;
|
||||
|
||||
size_t size(const ResourceManager*) const;
|
||||
size_t nesting(const ResourceManager*) const;
|
||||
@@ -99,25 +92,20 @@ class CollectionData {
|
||||
collection->clear(resources);
|
||||
}
|
||||
|
||||
void remove(iterator it, ResourceManager* resources);
|
||||
|
||||
static void remove(CollectionData* collection, iterator it,
|
||||
ResourceManager* resources) {
|
||||
if (collection)
|
||||
return collection->remove(it, resources);
|
||||
}
|
||||
|
||||
SlotId head() const {
|
||||
return head_;
|
||||
}
|
||||
|
||||
void addSlot(SlotWithId slot, ResourceManager* resources);
|
||||
|
||||
protected:
|
||||
iterator addSlot(ResourceManager*);
|
||||
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:
|
||||
SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const;
|
||||
Slot<VariantData> getPreviousSlot(VariantData*, const ResourceManager*) const;
|
||||
};
|
||||
|
||||
inline const VariantData* collectionToVariant(
|
||||
|
||||
@@ -12,61 +12,28 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline CollectionIterator::CollectionIterator(VariantSlot* slot, SlotId slotId)
|
||||
inline CollectionIterator::CollectionIterator(VariantData* slot, SlotId slotId)
|
||||
: slot_(slot), currentId_(slotId) {
|
||||
nextId_ = slot_ ? slot_->next() : NULL_SLOT;
|
||||
}
|
||||
|
||||
inline const char* CollectionIterator::key() const {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return slot_->key();
|
||||
}
|
||||
|
||||
inline void CollectionIterator::setKey(const char* s) {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
ARDUINOJSON_ASSERT(s != nullptr);
|
||||
return slot_->setKey(s);
|
||||
}
|
||||
|
||||
inline void CollectionIterator::setKey(StringNode* s) {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
ARDUINOJSON_ASSERT(s != nullptr);
|
||||
return slot_->setKey(s);
|
||||
}
|
||||
|
||||
inline bool CollectionIterator::ownsKey() const {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return slot_->ownsKey();
|
||||
}
|
||||
|
||||
inline void CollectionIterator::next(const ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(currentId_ != NULL_SLOT);
|
||||
slot_ = resources->getSlot(nextId_);
|
||||
slot_ = resources->getVariant(nextId_);
|
||||
currentId_ = nextId_;
|
||||
if (slot_)
|
||||
nextId_ = slot_->next();
|
||||
}
|
||||
|
||||
inline CollectionData::iterator CollectionData::addSlot(
|
||||
ResourceManager* resources) {
|
||||
auto slot = resources->allocSlot();
|
||||
if (!slot)
|
||||
return {};
|
||||
if (tail_ != NULL_SLOT) {
|
||||
auto tail = resources->getSlot(tail_);
|
||||
tail->setNext(slot.id());
|
||||
tail_ = slot.id();
|
||||
} else {
|
||||
head_ = slot.id();
|
||||
tail_ = slot.id();
|
||||
}
|
||||
return iterator(slot, slot.id());
|
||||
inline CollectionData::iterator CollectionData::createIterator(
|
||||
const ResourceManager* resources) const {
|
||||
return iterator(resources->getVariant(head_), head_);
|
||||
}
|
||||
|
||||
inline void CollectionData::addSlot(SlotWithId slot,
|
||||
ResourceManager* resources) {
|
||||
inline void CollectionData::appendOne(Slot<VariantData> slot,
|
||||
const ResourceManager* resources) {
|
||||
if (tail_ != NULL_SLOT) {
|
||||
auto tail = resources->getSlot(tail_);
|
||||
auto tail = resources->getVariant(tail_);
|
||||
tail->setNext(slot.id());
|
||||
tail_ = slot.id();
|
||||
} else {
|
||||
@@ -75,34 +42,49 @@ inline void CollectionData::addSlot(SlotWithId slot,
|
||||
}
|
||||
}
|
||||
|
||||
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->getSlot(next);
|
||||
auto slot = resources->getVariant(next);
|
||||
next = slot->next();
|
||||
resources->freeSlot(SlotWithId(slot, currId));
|
||||
resources->freeVariant({slot, currId});
|
||||
}
|
||||
|
||||
head_ = NULL_SLOT;
|
||||
tail_ = NULL_SLOT;
|
||||
}
|
||||
|
||||
inline SlotWithId CollectionData::getPreviousSlot(
|
||||
VariantSlot* target, const ResourceManager* resources) const {
|
||||
auto prev = SlotWithId();
|
||||
inline Slot<VariantData> CollectionData::getPreviousSlot(
|
||||
VariantData* target, const ResourceManager* resources) const {
|
||||
auto prev = Slot<VariantData>();
|
||||
auto currentId = head_;
|
||||
while (currentId != NULL_SLOT) {
|
||||
auto currentSlot = resources->getSlot(currentId);
|
||||
auto currentSlot = resources->getVariant(currentId);
|
||||
if (currentSlot == target)
|
||||
return prev;
|
||||
prev = SlotWithId(currentSlot, currentId);
|
||||
break;
|
||||
prev = Slot<VariantData>(currentSlot, currentId);
|
||||
currentId = currentSlot->next();
|
||||
}
|
||||
return SlotWithId();
|
||||
return prev;
|
||||
}
|
||||
|
||||
inline void CollectionData::remove(iterator it, ResourceManager* resources) {
|
||||
inline void CollectionData::removeOne(iterator it, ResourceManager* resources) {
|
||||
if (it.done())
|
||||
return;
|
||||
auto curr = it.slot_;
|
||||
@@ -114,7 +96,25 @@ inline void CollectionData::remove(iterator it, ResourceManager* resources) {
|
||||
head_ = next;
|
||||
if (next == NULL_SLOT)
|
||||
tail_ = prev.id();
|
||||
resources->freeSlot({it.slot_, it.currentId_});
|
||||
resources->freeVariant({it.slot_, it.currentId_});
|
||||
}
|
||||
|
||||
inline void CollectionData::removePair(ObjectData::iterator it,
|
||||
ResourceManager* resources) {
|
||||
if (it.done())
|
||||
return;
|
||||
|
||||
auto keySlot = it.slot_;
|
||||
|
||||
auto valueId = it.nextId_;
|
||||
auto valueSlot = resources->getVariant(valueId);
|
||||
|
||||
// remove value slot
|
||||
keySlot->setNext(valueSlot->next());
|
||||
resources->freeVariant({valueSlot, valueId});
|
||||
|
||||
// remove key slot
|
||||
removeOne(it, resources);
|
||||
}
|
||||
|
||||
inline size_t CollectionData::nesting(const ResourceManager* resources) const {
|
||||
|
||||
@@ -56,12 +56,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Store floating-point values with float (0) or double (1)
|
||||
// https://arduinojson.org/v7/config/use_double/
|
||||
#ifndef ARDUINOJSON_USE_DOUBLE
|
||||
# define ARDUINOJSON_USE_DOUBLE 1
|
||||
#endif
|
||||
|
||||
// Pointer size: a heuristic to set sensible defaults
|
||||
#ifndef ARDUINOJSON_SIZEOF_POINTER
|
||||
# if defined(__SIZEOF_POINTER__)
|
||||
@@ -73,6 +67,16 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Store floating-point values with float (0) or double (1)
|
||||
// https://arduinojson.org/v7/config/use_double/
|
||||
#ifndef ARDUINOJSON_USE_DOUBLE
|
||||
# if ARDUINOJSON_SIZEOF_POINTER >= 4 // 32 & 64 bits systems
|
||||
# define ARDUINOJSON_USE_DOUBLE 1
|
||||
# else
|
||||
# define ARDUINOJSON_USE_DOUBLE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Store integral values with long (0) or long long (1)
|
||||
// https://arduinojson.org/v7/config/use_long_long/
|
||||
#ifndef ARDUINOJSON_USE_LONG_LONG
|
||||
@@ -106,12 +110,12 @@
|
||||
|
||||
// Capacity of each variant pool (in slots)
|
||||
#ifndef ARDUINOJSON_POOL_CAPACITY
|
||||
# if ARDUINOJSON_SIZEOF_POINTER <= 2
|
||||
# define ARDUINOJSON_POOL_CAPACITY 16 // 128 bytes
|
||||
# elif ARDUINOJSON_SIZEOF_POINTER == 4
|
||||
# define ARDUINOJSON_POOL_CAPACITY 64 // 1024 bytes
|
||||
# if ARDUINOJSON_SLOT_ID_SIZE == 1
|
||||
# define ARDUINOJSON_POOL_CAPACITY 16 // 96 bytes
|
||||
# elif ARDUINOJSON_SLOT_ID_SIZE == 2
|
||||
# define ARDUINOJSON_POOL_CAPACITY 128 // 1024 bytes
|
||||
# else
|
||||
# define ARDUINOJSON_POOL_CAPACITY 128 // 3072 bytes
|
||||
# define ARDUINOJSON_POOL_CAPACITY 256 // 4096 bytes
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -269,6 +273,12 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_DOUBLE
|
||||
# define ARDUINOJSON_USE_EXTENSIONS 1
|
||||
#else
|
||||
# define ARDUINOJSON_USE_EXTENSIONS 0
|
||||
#endif
|
||||
|
||||
#if defined(nullptr)
|
||||
# error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr
|
||||
// See https://github.com/bblanchon/ArduinoJson/issues/1355
|
||||
|
||||
@@ -150,24 +150,27 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
return getVariant().template to<T>();
|
||||
}
|
||||
|
||||
// Returns true if the root object contains the specified key.
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsondocument/containskey/
|
||||
template <typename TChar>
|
||||
ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
|
||||
bool containsKey(TChar* key) const {
|
||||
return data_.getMember(detail::adaptString(key), &resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if the root object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsondocument/containskey/
|
||||
template <typename TString>
|
||||
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
|
||||
const TString& key) const {
|
||||
return data_.getMember(detail::adaptString(key), &resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if the root object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsondocument/containskey/
|
||||
template <typename TVariant>
|
||||
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
|
||||
const TVariant& key) const {
|
||||
return containsKey(key.template as<const char*>());
|
||||
|
||||
@@ -32,8 +32,8 @@ class EscapeSequence {
|
||||
}
|
||||
|
||||
private:
|
||||
static const char* escapeTable(bool excludeSolidus) {
|
||||
return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0];
|
||||
static const char* escapeTable(bool isSerializing) {
|
||||
return &"//''\"\"\\\\b\bf\fn\nr\rt\t"[isSerializing ? 4 : 0];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ class JsonDeserializer {
|
||||
if (!member)
|
||||
return DeserializationError::NoMemory;
|
||||
} else {
|
||||
member->setNull(resources_);
|
||||
member->clear(resources_);
|
||||
}
|
||||
|
||||
// Parse value
|
||||
@@ -517,10 +517,37 @@ class JsonDeserializer {
|
||||
}
|
||||
buffer_[n] = 0;
|
||||
|
||||
if (!parseNumber(buffer_, result))
|
||||
return DeserializationError::InvalidInput;
|
||||
|
||||
auto number = parseNumber(buffer_);
|
||||
switch (number.type()) {
|
||||
case NumberType::UnsignedInteger:
|
||||
if (result.setInteger(number.asUnsignedInteger(), resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
case NumberType::SignedInteger:
|
||||
if (result.setInteger(number.asSignedInteger(), resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
case NumberType::Float:
|
||||
if (result.setFloat(number.asFloat(), resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case NumberType::Double:
|
||||
if (result.setFloat(number.asDouble(), resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return DeserializationError::InvalidInput;
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError::Code skipNumericValue() {
|
||||
|
||||
@@ -25,9 +25,9 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
||||
auto slotId = array.head();
|
||||
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getSlot(slotId);
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
|
||||
slot->data()->accept(*this);
|
||||
slot->accept(*this, resources_);
|
||||
|
||||
slotId = slot->next();
|
||||
|
||||
@@ -44,24 +44,26 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
||||
|
||||
auto slotId = object.head();
|
||||
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getSlot(slotId);
|
||||
bool isKey = true;
|
||||
|
||||
formatter_.writeString(slot->key());
|
||||
write(':');
|
||||
slot->data()->accept(*this);
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
slot->accept(*this, resources_);
|
||||
|
||||
slotId = slot->next();
|
||||
|
||||
if (slotId != NULL_SLOT)
|
||||
write(',');
|
||||
write(isKey ? ':' : ',');
|
||||
|
||||
isKey = !isKey;
|
||||
}
|
||||
|
||||
write('}');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visit(JsonFloat value) {
|
||||
template <typename T>
|
||||
enable_if_t<is_floating_point<T>::value, size_t> visit(T value) {
|
||||
formatter_.writeFloat(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
@@ -128,7 +130,8 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
// Produces a minified JSON document.
|
||||
// https://arduinojson.org/v7/api/json/serializejson/
|
||||
template <typename TDestination>
|
||||
size_t serializeJson(JsonVariantConst source, TDestination& destination) {
|
||||
detail::enable_if_t<!detail::is_pointer<TDestination>::value, size_t>
|
||||
serializeJson(JsonVariantConst source, TDestination& destination) {
|
||||
using namespace detail;
|
||||
return serialize<JsonSerializer>(source, destination);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
nesting_++;
|
||||
while (!it.done()) {
|
||||
indent();
|
||||
it->accept(*this);
|
||||
it->accept(*this, base::resources_);
|
||||
|
||||
it.next(base::resources_);
|
||||
base::write(it.done() ? "\r\n" : ",\r\n");
|
||||
@@ -45,14 +45,17 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
if (!it.done()) {
|
||||
base::write("{\r\n");
|
||||
nesting_++;
|
||||
bool isKey = true;
|
||||
while (!it.done()) {
|
||||
if (isKey)
|
||||
indent();
|
||||
base::visit(it.key());
|
||||
base::write(": ");
|
||||
it->accept(*this);
|
||||
|
||||
it->accept(*this, base::resources_);
|
||||
it.next(base::resources_);
|
||||
if (isKey)
|
||||
base::write(": ");
|
||||
else
|
||||
base::write(it.done() ? "\r\n" : ",\r\n");
|
||||
isKey = !isKey;
|
||||
}
|
||||
nesting_--;
|
||||
indent();
|
||||
@@ -81,7 +84,8 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
// Produces JsonDocument to create a prettified JSON document.
|
||||
// https://arduinojson.org/v7/api/json/serializejsonpretty/
|
||||
template <typename TDestination>
|
||||
size_t serializeJsonPretty(JsonVariantConst source, TDestination& destination) {
|
||||
detail::enable_if_t<!detail::is_pointer<TDestination>::value, size_t>
|
||||
serializeJsonPretty(JsonVariantConst source, TDestination& destination) {
|
||||
using namespace ArduinoJson::detail;
|
||||
return serialize<PrettyJsonSerializer>(source, destination);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ class TextFormatter {
|
||||
|
||||
template <typename T>
|
||||
void writeFloat(T value) {
|
||||
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 6);
|
||||
}
|
||||
|
||||
void writeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||
if (isnan(value))
|
||||
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
|
||||
|
||||
@@ -87,7 +91,7 @@ class TextFormatter {
|
||||
}
|
||||
#endif
|
||||
|
||||
FloatParts<T> parts(value);
|
||||
auto parts = decomposeFloat(value, decimalPlaces);
|
||||
|
||||
writeInteger(parts.integral);
|
||||
if (parts.decimalPlaces)
|
||||
|
||||
110
lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp
Normal file
110
lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2024, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/Allocator.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/integer.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>;
|
||||
using SlotCount = SlotId;
|
||||
const SlotId NULL_SLOT = SlotId(-1);
|
||||
|
||||
template <typename T>
|
||||
class Slot {
|
||||
public:
|
||||
Slot() : ptr_(nullptr), id_(NULL_SLOT) {}
|
||||
Slot(T* p, SlotId id) : ptr_(p), id_(id) {
|
||||
ARDUINOJSON_ASSERT((p == nullptr) == (id == NULL_SLOT));
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return ptr_ != nullptr;
|
||||
}
|
||||
|
||||
SlotId id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
T* ptr() const {
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
ARDUINOJSON_ASSERT(ptr_ != nullptr);
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
SlotId id_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class MemoryPool {
|
||||
public:
|
||||
void create(SlotCount cap, Allocator* allocator) {
|
||||
ARDUINOJSON_ASSERT(cap > 0);
|
||||
slots_ = reinterpret_cast<T*>(allocator->allocate(slotsToBytes(cap)));
|
||||
capacity_ = slots_ ? cap : 0;
|
||||
usage_ = 0;
|
||||
}
|
||||
|
||||
void destroy(Allocator* allocator) {
|
||||
if (slots_)
|
||||
allocator->deallocate(slots_);
|
||||
slots_ = nullptr;
|
||||
capacity_ = 0;
|
||||
usage_ = 0;
|
||||
}
|
||||
|
||||
Slot<T> allocSlot() {
|
||||
if (!slots_)
|
||||
return {};
|
||||
if (usage_ >= capacity_)
|
||||
return {};
|
||||
auto index = usage_++;
|
||||
return {slots_ + index, SlotId(index)};
|
||||
}
|
||||
|
||||
T* getSlot(SlotId id) const {
|
||||
ARDUINOJSON_ASSERT(id < usage_);
|
||||
return slots_ + id;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
usage_ = 0;
|
||||
}
|
||||
|
||||
void shrinkToFit(Allocator* allocator) {
|
||||
auto newSlots = reinterpret_cast<T*>(
|
||||
allocator->reallocate(slots_, slotsToBytes(usage_)));
|
||||
if (newSlots) {
|
||||
slots_ = newSlots;
|
||||
capacity_ = usage_;
|
||||
}
|
||||
}
|
||||
|
||||
SlotCount usage() const {
|
||||
return usage_;
|
||||
}
|
||||
|
||||
static SlotCount bytesToSlots(size_t n) {
|
||||
return static_cast<SlotCount>(n / sizeof(T));
|
||||
}
|
||||
|
||||
static size_t slotsToBytes(SlotCount n) {
|
||||
return n * sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
SlotCount capacity_;
|
||||
SlotCount usage_;
|
||||
T* slots_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
214
lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPoolList.hpp
Normal file
214
lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPoolList.hpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2024, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/utility.hpp>
|
||||
|
||||
#include <string.h> // memcpy
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
using PoolCount = SlotId;
|
||||
|
||||
template <typename T>
|
||||
class MemoryPoolList {
|
||||
struct FreeSlot {
|
||||
SlotId next;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FreeSlot) <= sizeof(T), "T is too small");
|
||||
|
||||
public:
|
||||
using Pool = MemoryPool<T>;
|
||||
|
||||
MemoryPoolList() = default;
|
||||
|
||||
~MemoryPoolList() {
|
||||
ARDUINOJSON_ASSERT(count_ == 0);
|
||||
}
|
||||
|
||||
friend void swap(MemoryPoolList& a, MemoryPoolList& b) {
|
||||
bool aUsedPreallocated = a.pools_ == a.preallocatedPools_;
|
||||
bool bUsedPreallocated = b.pools_ == b.preallocatedPools_;
|
||||
|
||||
// Who is using preallocated pools?
|
||||
if (aUsedPreallocated && bUsedPreallocated) {
|
||||
// both of us => swap preallocated pools
|
||||
for (PoolCount i = 0; i < ARDUINOJSON_INITIAL_POOL_COUNT; i++)
|
||||
swap_(a.preallocatedPools_[i], b.preallocatedPools_[i]);
|
||||
} else if (bUsedPreallocated) {
|
||||
// only b => copy b's preallocated pools and give him a's pointer
|
||||
for (PoolCount i = 0; i < b.count_; i++)
|
||||
a.preallocatedPools_[i] = b.preallocatedPools_[i];
|
||||
b.pools_ = a.pools_;
|
||||
a.pools_ = a.preallocatedPools_;
|
||||
} else if (aUsedPreallocated) {
|
||||
// only a => copy a's preallocated pools and give him b's pointer
|
||||
for (PoolCount i = 0; i < a.count_; i++)
|
||||
b.preallocatedPools_[i] = a.preallocatedPools_[i];
|
||||
a.pools_ = b.pools_;
|
||||
b.pools_ = b.preallocatedPools_;
|
||||
} else {
|
||||
// neither => swap pointers
|
||||
swap_(a.pools_, b.pools_);
|
||||
}
|
||||
|
||||
swap_(a.count_, b.count_);
|
||||
swap_(a.capacity_, b.capacity_);
|
||||
swap_(a.freeList_, b.freeList_);
|
||||
}
|
||||
|
||||
MemoryPoolList& operator=(MemoryPoolList&& src) {
|
||||
ARDUINOJSON_ASSERT(count_ == 0);
|
||||
if (src.pools_ == src.preallocatedPools_) {
|
||||
memcpy(preallocatedPools_, src.preallocatedPools_,
|
||||
sizeof(preallocatedPools_));
|
||||
pools_ = preallocatedPools_;
|
||||
} else {
|
||||
pools_ = src.pools_;
|
||||
src.pools_ = nullptr;
|
||||
}
|
||||
count_ = src.count_;
|
||||
capacity_ = src.capacity_;
|
||||
src.count_ = 0;
|
||||
src.capacity_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Slot<T> allocSlot(Allocator* allocator) {
|
||||
// try to allocate from free list
|
||||
if (freeList_ != NULL_SLOT) {
|
||||
return allocFromFreeList();
|
||||
}
|
||||
|
||||
// try to allocate from last pool (other pools are full)
|
||||
if (count_) {
|
||||
auto slot = allocFromLastPool();
|
||||
if (slot)
|
||||
return slot;
|
||||
}
|
||||
|
||||
// create a new pool and try again
|
||||
auto pool = addPool(allocator);
|
||||
if (!pool)
|
||||
return {};
|
||||
|
||||
return allocFromLastPool();
|
||||
}
|
||||
|
||||
void freeSlot(Slot<T> slot) {
|
||||
reinterpret_cast<FreeSlot*>(slot.ptr())->next = freeList_;
|
||||
freeList_ = slot.id();
|
||||
}
|
||||
|
||||
T* getSlot(SlotId id) const {
|
||||
if (id == NULL_SLOT)
|
||||
return nullptr;
|
||||
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
|
||||
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
|
||||
ARDUINOJSON_ASSERT(poolIndex < count_);
|
||||
return pools_[poolIndex].getSlot(indexInPool);
|
||||
}
|
||||
|
||||
void clear(Allocator* allocator) {
|
||||
for (PoolCount i = 0; i < count_; i++)
|
||||
pools_[i].destroy(allocator);
|
||||
count_ = 0;
|
||||
freeList_ = NULL_SLOT;
|
||||
if (pools_ != preallocatedPools_) {
|
||||
allocator->deallocate(pools_);
|
||||
pools_ = preallocatedPools_;
|
||||
capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
SlotCount usage() const {
|
||||
SlotCount total = 0;
|
||||
for (PoolCount i = 0; i < count_; i++)
|
||||
total = SlotCount(total + pools_[i].usage());
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return Pool::slotsToBytes(usage());
|
||||
}
|
||||
|
||||
void shrinkToFit(Allocator* allocator) {
|
||||
if (count_ > 0)
|
||||
pools_[count_ - 1].shrinkToFit(allocator);
|
||||
if (pools_ != preallocatedPools_ && count_ != capacity_) {
|
||||
pools_ = static_cast<Pool*>(
|
||||
allocator->reallocate(pools_, count_ * sizeof(Pool)));
|
||||
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
|
||||
capacity_ = count_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Slot<T> allocFromFreeList() {
|
||||
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
|
||||
auto id = freeList_;
|
||||
auto slot = getSlot(freeList_);
|
||||
freeList_ = reinterpret_cast<FreeSlot*>(slot)->next;
|
||||
return {slot, id};
|
||||
}
|
||||
|
||||
Slot<T> allocFromLastPool() {
|
||||
ARDUINOJSON_ASSERT(count_ > 0);
|
||||
auto poolIndex = SlotId(count_ - 1);
|
||||
auto slot = pools_[poolIndex].allocSlot();
|
||||
if (!slot)
|
||||
return {};
|
||||
return {slot.ptr(),
|
||||
SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
|
||||
}
|
||||
|
||||
Pool* addPool(Allocator* allocator) {
|
||||
if (count_ == capacity_ && !increaseCapacity(allocator))
|
||||
return nullptr;
|
||||
auto pool = &pools_[count_++];
|
||||
SlotCount poolCapacity = ARDUINOJSON_POOL_CAPACITY;
|
||||
if (count_ == maxPools) // last pool is smaller because of NULL_SLOT
|
||||
poolCapacity--;
|
||||
pool->create(poolCapacity, allocator);
|
||||
return pool;
|
||||
}
|
||||
|
||||
bool increaseCapacity(Allocator* allocator) {
|
||||
if (capacity_ == maxPools)
|
||||
return false;
|
||||
void* newPools;
|
||||
auto newCapacity = PoolCount(capacity_ * 2);
|
||||
|
||||
if (pools_ == preallocatedPools_) {
|
||||
newPools = allocator->allocate(newCapacity * sizeof(Pool));
|
||||
if (!newPools)
|
||||
return false;
|
||||
memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_));
|
||||
} else {
|
||||
newPools = allocator->reallocate(pools_, newCapacity * sizeof(Pool));
|
||||
if (!newPools)
|
||||
return false;
|
||||
}
|
||||
|
||||
pools_ = static_cast<Pool*>(newPools);
|
||||
capacity_ = newCapacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
Pool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
|
||||
Pool* pools_ = preallocatedPools_;
|
||||
PoolCount count_ = 0;
|
||||
PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
|
||||
SlotId freeList_ = NULL_SLOT;
|
||||
|
||||
public:
|
||||
static const PoolCount maxPools =
|
||||
PoolCount(NULL_SLOT / ARDUINOJSON_POOL_CAPACITY + 1);
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -5,19 +5,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/Allocator.hpp>
|
||||
#include <ArduinoJson/Memory/MemoryPoolList.hpp>
|
||||
#include <ArduinoJson/Memory/StringPool.hpp>
|
||||
#include <ArduinoJson/Memory/VariantPoolList.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/utility.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class VariantSlot;
|
||||
class VariantPool;
|
||||
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) {}
|
||||
|
||||
@@ -41,26 +51,22 @@ class ResourceManager {
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return VariantPool::slotsToBytes(variantPools_.usage()) +
|
||||
stringPool_.size();
|
||||
return variantPools_.size() + stringPool_.size();
|
||||
}
|
||||
|
||||
bool overflowed() const {
|
||||
return overflowed_;
|
||||
}
|
||||
|
||||
SlotWithId allocSlot() {
|
||||
auto p = variantPools_.allocSlot(allocator_);
|
||||
if (!p)
|
||||
overflowed_ = true;
|
||||
return p;
|
||||
}
|
||||
Slot<VariantData> allocVariant();
|
||||
void freeVariant(Slot<VariantData> slot);
|
||||
VariantData* getVariant(SlotId id) const;
|
||||
|
||||
void freeSlot(SlotWithId slot);
|
||||
|
||||
VariantSlot* getSlot(SlotId id) const {
|
||||
return variantPools_.getSlot(id);
|
||||
}
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
Slot<VariantExtension> allocExtension();
|
||||
void freeExtension(SlotId slot);
|
||||
VariantExtension* getExtension(SlotId id) const;
|
||||
#endif
|
||||
|
||||
template <typename TAdaptedString>
|
||||
StringNode* saveString(TAdaptedString str) {
|
||||
@@ -119,7 +125,7 @@ class ResourceManager {
|
||||
Allocator* allocator_;
|
||||
bool overflowed_;
|
||||
StringPool stringPool_;
|
||||
VariantPoolList variantPools_;
|
||||
MemoryPoolList<SlotData> variantPools_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -6,15 +6,47 @@
|
||||
|
||||
#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 void ResourceManager::freeSlot(SlotWithId slot) {
|
||||
if (slot->ownsKey())
|
||||
dereferenceString(slot->key());
|
||||
slot->data()->setNull(this);
|
||||
variantPools_.freeSlot(slot);
|
||||
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
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class VariantSlot;
|
||||
class VariantPool;
|
||||
|
||||
class StringPool {
|
||||
public:
|
||||
StringPool() = default;
|
||||
|
||||
@@ -29,6 +29,7 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
|
||||
if (!data)
|
||||
return;
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
if (src.data()) {
|
||||
size_t headerSize = src.size() >= 0x10000 ? 5
|
||||
: src.size() >= 0x100 ? 3
|
||||
@@ -62,7 +63,6 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
|
||||
return;
|
||||
}
|
||||
}
|
||||
data->setNull();
|
||||
}
|
||||
|
||||
static MsgPackBinary fromJson(JsonVariantConst src) {
|
||||
|
||||
@@ -91,7 +91,7 @@ class MsgPackDeserializer {
|
||||
|
||||
if (code <= 0x7f || code >= 0xe0) { // fixint
|
||||
if (allowValue)
|
||||
variant->setInteger(static_cast<int8_t>(code));
|
||||
variant->setInteger(static_cast<int8_t>(code), resources_);
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
@@ -230,13 +230,16 @@ class MsgPackDeserializer {
|
||||
|
||||
if (isSigned) {
|
||||
auto truncatedValue = static_cast<JsonInteger>(signedValue);
|
||||
if (truncatedValue == signedValue)
|
||||
variant->setInteger(truncatedValue);
|
||||
if (truncatedValue == signedValue) {
|
||||
if (!variant->setInteger(truncatedValue, resources_))
|
||||
return DeserializationError::NoMemory;
|
||||
}
|
||||
// else set null on overflow
|
||||
} else {
|
||||
auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
|
||||
if (truncatedValue == unsignedValue)
|
||||
variant->setInteger(truncatedValue);
|
||||
if (!variant->setInteger(truncatedValue, resources_))
|
||||
return DeserializationError::NoMemory;
|
||||
// else set null on overflow
|
||||
}
|
||||
|
||||
@@ -254,7 +257,7 @@ class MsgPackDeserializer {
|
||||
return err;
|
||||
|
||||
fixEndianness(value);
|
||||
variant->setFloat(value);
|
||||
variant->setFloat(value, resources_);
|
||||
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
@@ -270,9 +273,10 @@ class MsgPackDeserializer {
|
||||
return err;
|
||||
|
||||
fixEndianness(value);
|
||||
variant->setFloat(value);
|
||||
|
||||
if (variant->setFloat(value, resources_))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -289,7 +293,7 @@ class MsgPackDeserializer {
|
||||
|
||||
doubleToFloat(i, o);
|
||||
fixEndianness(value);
|
||||
variant->setFloat(value);
|
||||
variant->setFloat(value, resources_);
|
||||
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ struct Converter<MsgPackExtension> : private detail::VariantAttorney {
|
||||
if (!data)
|
||||
return;
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
if (src.data()) {
|
||||
uint8_t format, sizeBytes;
|
||||
if (src.size() >= 0x10000) {
|
||||
@@ -76,7 +77,6 @@ struct Converter<MsgPackExtension> : private detail::VariantAttorney {
|
||||
return;
|
||||
}
|
||||
}
|
||||
data->setNull();
|
||||
}
|
||||
|
||||
static MsgPackExtension fromJson(JsonVariantConst src) {
|
||||
|
||||
@@ -61,8 +61,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
|
||||
auto slotId = array.head();
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getSlot(slotId);
|
||||
slot->data()->accept(*this);
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
slot->accept(*this, resources_);
|
||||
slotId = slot->next();
|
||||
}
|
||||
|
||||
@@ -83,9 +83,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
|
||||
auto slotId = object.head();
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getSlot(slotId);
|
||||
visit(slot->key());
|
||||
slot->data()->accept(*this);
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
slot->accept(*this, resources_);
|
||||
slotId = slot->next();
|
||||
}
|
||||
|
||||
@@ -220,7 +219,8 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
// Produces a MessagePack document.
|
||||
// https://arduinojson.org/v7/api/msgpack/serializemsgpack/
|
||||
template <typename TDestination>
|
||||
inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) {
|
||||
detail::enable_if_t<!detail::is_pointer<TDestination>::value, size_t>
|
||||
serializeMsgPack(JsonVariantConst source, TDestination& output) {
|
||||
using namespace ArduinoJson::detail;
|
||||
return serialize<MsgPackSerializer>(source, output);
|
||||
}
|
||||
|
||||
@@ -6,55 +6,20 @@
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <typename TFloat>
|
||||
struct FloatParts {
|
||||
uint32_t integral;
|
||||
uint32_t decimal;
|
||||
int16_t exponent;
|
||||
int8_t decimalPlaces;
|
||||
};
|
||||
|
||||
FloatParts(TFloat value) {
|
||||
uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000;
|
||||
decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6;
|
||||
|
||||
exponent = normalize(value);
|
||||
|
||||
integral = uint32_t(value);
|
||||
// reduce number of decimal places by the number of integral places
|
||||
for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) {
|
||||
maxDecimalPart /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
|
||||
TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart);
|
||||
|
||||
decimal = uint32_t(remainder);
|
||||
remainder = remainder - TFloat(decimal);
|
||||
|
||||
// rounding:
|
||||
// increment by 1 if remainder >= 0.5
|
||||
decimal += uint32_t(remainder * 2);
|
||||
if (decimal >= maxDecimalPart) {
|
||||
decimal = 0;
|
||||
integral++;
|
||||
if (exponent && integral >= 10) {
|
||||
exponent++;
|
||||
integral = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// remove trailing zeros
|
||||
while (decimal % 10 == 0 && decimalPlaces > 0) {
|
||||
decimal /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
}
|
||||
|
||||
static int16_t normalize(TFloat& value) {
|
||||
template <typename TFloat>
|
||||
inline int16_t normalize(TFloat& value) {
|
||||
typedef FloatTraits<TFloat> traits;
|
||||
int16_t powersOf10 = 0;
|
||||
|
||||
@@ -83,6 +48,48 @@ struct FloatParts {
|
||||
|
||||
return powersOf10;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr uint32_t pow10(int exponent) {
|
||||
return (exponent == 0) ? 1 : 10 * pow10(exponent - 1);
|
||||
}
|
||||
|
||||
inline FloatParts decomposeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||
uint32_t maxDecimalPart = pow10(decimalPlaces);
|
||||
|
||||
int16_t exponent = normalize(value);
|
||||
|
||||
uint32_t integral = uint32_t(value);
|
||||
// reduce number of decimal places by the number of integral places
|
||||
for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) {
|
||||
maxDecimalPart /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
|
||||
JsonFloat remainder =
|
||||
(value - JsonFloat(integral)) * JsonFloat(maxDecimalPart);
|
||||
|
||||
uint32_t decimal = uint32_t(remainder);
|
||||
remainder = remainder - JsonFloat(decimal);
|
||||
|
||||
// rounding:
|
||||
// increment by 1 if remainder >= 0.5
|
||||
decimal += uint32_t(remainder * 2);
|
||||
if (decimal >= maxDecimalPart) {
|
||||
decimal = 0;
|
||||
integral++;
|
||||
if (exponent && integral >= 10) {
|
||||
exponent++;
|
||||
integral = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// remove trailing zeros
|
||||
while (decimal % 10 == 0 && decimalPlaces > 0) {
|
||||
decimal /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
|
||||
return {integral, decimal, exponent, decimalPlaces};
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -121,10 +121,21 @@ canConvertNumber(TIn value) {
|
||||
value <= FloatTraits<TIn>::template highest_for<TOut>();
|
||||
}
|
||||
|
||||
// float32 -> float32
|
||||
// float64 -> float64
|
||||
// float64 -> float32
|
||||
template <typename TOut, typename TIn>
|
||||
enable_if_t<is_floating_point<TIn>::value && is_floating_point<TOut>::value,
|
||||
bool>
|
||||
canConvertNumber(TIn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TOut, typename TIn>
|
||||
TOut convertNumber(TIn value) {
|
||||
return canConvertNumber<TOut>(value) ? TOut(value) : 0;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
@@ -5,20 +5,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/ctype.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Variant/Converter.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <typename A, typename B>
|
||||
using largest_type = conditional_t<(sizeof(A) > sizeof(B)), A, B>;
|
||||
|
||||
inline bool parseNumber(const char* s, VariantData& result) {
|
||||
enum class NumberType : uint8_t {
|
||||
Invalid,
|
||||
Float,
|
||||
SignedInteger,
|
||||
UnsignedInteger,
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
Double,
|
||||
#endif
|
||||
};
|
||||
|
||||
union NumberValue {
|
||||
NumberValue() {}
|
||||
NumberValue(float x) : asFloat(x) {}
|
||||
NumberValue(JsonInteger x) : asSignedInteger(x) {}
|
||||
NumberValue(JsonUInt x) : asUnsignedInteger(x) {}
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
NumberValue(double x) : asDouble(x) {}
|
||||
#endif
|
||||
|
||||
JsonInteger asSignedInteger;
|
||||
JsonUInt asUnsignedInteger;
|
||||
float asFloat;
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
double asDouble;
|
||||
#endif
|
||||
};
|
||||
|
||||
class Number {
|
||||
NumberType type_;
|
||||
NumberValue value_;
|
||||
|
||||
public:
|
||||
Number() : type_(NumberType::Invalid) {}
|
||||
Number(float value) : type_(NumberType::Float), value_(value) {}
|
||||
Number(JsonInteger value) : type_(NumberType::SignedInteger), value_(value) {}
|
||||
Number(JsonUInt value) : type_(NumberType::UnsignedInteger), value_(value) {}
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
Number(double value) : type_(NumberType::Double), value_(value) {}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
T convertTo() const {
|
||||
switch (type_) {
|
||||
case NumberType::Float:
|
||||
return convertNumber<T>(value_.asFloat);
|
||||
case NumberType::SignedInteger:
|
||||
return convertNumber<T>(value_.asSignedInteger);
|
||||
case NumberType::UnsignedInteger:
|
||||
return convertNumber<T>(value_.asUnsignedInteger);
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case NumberType::Double:
|
||||
return convertNumber<T>(value_.asDouble);
|
||||
#endif
|
||||
default:
|
||||
return T();
|
||||
}
|
||||
}
|
||||
|
||||
NumberType type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
JsonInteger asSignedInteger() const {
|
||||
ARDUINOJSON_ASSERT(type_ == NumberType::SignedInteger);
|
||||
return value_.asSignedInteger;
|
||||
}
|
||||
|
||||
JsonUInt asUnsignedInteger() const {
|
||||
ARDUINOJSON_ASSERT(type_ == NumberType::UnsignedInteger);
|
||||
return value_.asUnsignedInteger;
|
||||
}
|
||||
|
||||
float asFloat() const {
|
||||
ARDUINOJSON_ASSERT(type_ == NumberType::Float);
|
||||
return value_.asFloat;
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
double asDouble() const {
|
||||
ARDUINOJSON_ASSERT(type_ == NumberType::Double);
|
||||
return value_.asDouble;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
inline Number parseNumber(const char* s) {
|
||||
typedef FloatTraits<JsonFloat> traits;
|
||||
typedef largest_type<traits::mantissa_type, JsonUInt> mantissa_t;
|
||||
typedef traits::exponent_type exponent_t;
|
||||
@@ -38,20 +122,18 @@ inline bool parseNumber(const char* s, VariantData& result) {
|
||||
|
||||
#if ARDUINOJSON_ENABLE_NAN
|
||||
if (*s == 'n' || *s == 'N') {
|
||||
result.setFloat(traits::nan());
|
||||
return true;
|
||||
return Number(traits::nan());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_INFINITY
|
||||
if (*s == 'i' || *s == 'I') {
|
||||
result.setFloat(is_negative ? -traits::inf() : traits::inf());
|
||||
return true;
|
||||
return Number(is_negative ? -traits::inf() : traits::inf());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!isdigit(*s) && *s != '.')
|
||||
return false;
|
||||
return Number();
|
||||
|
||||
mantissa_t mantissa = 0;
|
||||
exponent_t exponent_offset = 0;
|
||||
@@ -73,12 +155,10 @@ inline bool parseNumber(const char* s, VariantData& result) {
|
||||
const mantissa_t sintMantissaMax = mantissa_t(1)
|
||||
<< (sizeof(JsonInteger) * 8 - 1);
|
||||
if (mantissa <= sintMantissaMax) {
|
||||
result.setInteger(JsonInteger(~mantissa + 1));
|
||||
return true;
|
||||
return Number(JsonInteger(~mantissa + 1));
|
||||
}
|
||||
} else {
|
||||
result.setInteger(JsonUInt(mantissa));
|
||||
return true;
|
||||
return Number(JsonUInt(mantissa));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,10 +200,9 @@ inline bool parseNumber(const char* s, VariantData& result) {
|
||||
exponent = exponent * 10 + (*s - '0');
|
||||
if (exponent + exponent_offset > traits::exponent_max) {
|
||||
if (negative_exponent)
|
||||
result.setFloat(is_negative ? -0.0f : 0.0f);
|
||||
return Number(is_negative ? -0.0f : 0.0f);
|
||||
else
|
||||
result.setFloat(is_negative ? -traits::inf() : traits::inf());
|
||||
return true;
|
||||
return Number(is_negative ? -traits::inf() : traits::inf());
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@@ -134,19 +213,26 @@ inline bool parseNumber(const char* s, VariantData& result) {
|
||||
|
||||
// we should be at the end of the string, otherwise it's an error
|
||||
if (*s != '\0')
|
||||
return false;
|
||||
return Number();
|
||||
|
||||
JsonFloat final_result =
|
||||
make_float(static_cast<JsonFloat>(mantissa), exponent);
|
||||
|
||||
result.setFloat(is_negative ? -final_result : final_result);
|
||||
return true;
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
bool isDouble = exponent < -FloatTraits<float>::exponent_max ||
|
||||
exponent > FloatTraits<float>::exponent_max ||
|
||||
mantissa > FloatTraits<float>::mantissa_max;
|
||||
if (isDouble) {
|
||||
auto final_result = make_float(double(mantissa), exponent);
|
||||
return Number(is_negative ? -final_result : final_result);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
auto final_result = make_float(float(mantissa), exponent);
|
||||
return Number(is_negative ? -final_result : final_result);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T parseNumber(const char* s) {
|
||||
VariantData value;
|
||||
parseNumber(s, value);
|
||||
return Converter<T>::fromJson(JsonVariantConst(&value, nullptr));
|
||||
return parseNumber(s).convertTo<T>();
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -161,27 +161,30 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
resources_);
|
||||
}
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobject/containskey/
|
||||
template <typename TString>
|
||||
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
|
||||
const TString& key) const {
|
||||
return detail::ObjectData::getMember(data_, detail::adaptString(key),
|
||||
resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobject/containskey/
|
||||
template <typename TChar>
|
||||
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, bool> containsKey(
|
||||
TChar* key) const {
|
||||
return detail::ObjectData::getMember(data_, detail::adaptString(key),
|
||||
resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobject/containskey/
|
||||
template <typename TVariant>
|
||||
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
|
||||
const TVariant& key) const {
|
||||
return containsKey(key.template as<const char*>());
|
||||
|
||||
@@ -68,26 +68,29 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
|
||||
return iterator();
|
||||
}
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/
|
||||
template <typename TString>
|
||||
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
|
||||
const TString& key) const {
|
||||
return detail::ObjectData::getMember(data_, detail::adaptString(key),
|
||||
resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/
|
||||
template <typename TChar>
|
||||
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
|
||||
bool containsKey(TChar* key) const {
|
||||
return detail::ObjectData::getMember(data_, detail::adaptString(key),
|
||||
resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/
|
||||
template <typename TVariant>
|
||||
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
|
||||
const TVariant& key) const {
|
||||
return containsKey(key.template as<const char*>());
|
||||
|
||||
@@ -34,7 +34,8 @@ class JsonObjectIterator {
|
||||
}
|
||||
|
||||
JsonObjectIterator& operator++() {
|
||||
iterator_.next(resources_);
|
||||
iterator_.next(resources_); // key
|
||||
iterator_.next(resources_); // value
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -69,7 +70,8 @@ class JsonObjectConstIterator {
|
||||
}
|
||||
|
||||
JsonObjectConstIterator& operator++() {
|
||||
iterator_.next(resources_);
|
||||
iterator_.next(resources_); // key
|
||||
iterator_.next(resources_); // value
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,27 +16,27 @@ class JsonPair {
|
||||
public:
|
||||
// INTERNAL USE ONLY
|
||||
JsonPair(detail::ObjectData::iterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
detail::ResourceManager* resources) {
|
||||
if (!iterator.done()) {
|
||||
key_ = iterator->asString();
|
||||
iterator.next(resources);
|
||||
value_ = JsonVariant(iterator.data(), resources);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the key.
|
||||
JsonString key() const {
|
||||
if (!iterator_.done())
|
||||
return JsonString(iterator_.key(), iterator_.ownsKey()
|
||||
? JsonString::Copied
|
||||
: JsonString::Linked);
|
||||
else
|
||||
return JsonString();
|
||||
return key_;
|
||||
}
|
||||
|
||||
// Returns the value.
|
||||
JsonVariant value() {
|
||||
return JsonVariant(iterator_.data(), resources_);
|
||||
return value_;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::ObjectData::iterator iterator_;
|
||||
detail::ResourceManager* resources_;
|
||||
JsonString key_;
|
||||
JsonVariant value_;
|
||||
};
|
||||
|
||||
// A read-only key-value pair.
|
||||
@@ -44,27 +44,27 @@ class JsonPair {
|
||||
class JsonPairConst {
|
||||
public:
|
||||
JsonPairConst(detail::ObjectData::iterator iterator,
|
||||
const detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
const detail::ResourceManager* resources) {
|
||||
if (!iterator.done()) {
|
||||
key_ = iterator->asString();
|
||||
iterator.next(resources);
|
||||
value_ = JsonVariantConst(iterator.data(), resources);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the key.
|
||||
JsonString key() const {
|
||||
if (!iterator_.done())
|
||||
return JsonString(iterator_.key(), iterator_.ownsKey()
|
||||
? JsonString::Copied
|
||||
: JsonString::Linked);
|
||||
else
|
||||
return JsonString();
|
||||
return key_;
|
||||
}
|
||||
|
||||
// Returns the value.
|
||||
JsonVariantConst value() const {
|
||||
return JsonVariantConst(iterator_.data(), resources_);
|
||||
return value_;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::ObjectData::iterator iterator_;
|
||||
const detail::ResourceManager* resources_;
|
||||
JsonString key_;
|
||||
JsonVariantConst value_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -10,34 +10,8 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class ObjectData : public CollectionData {
|
||||
public:
|
||||
VariantData* addMember(StringNode* key, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(key != nullptr);
|
||||
auto it = addSlot(resources);
|
||||
if (it.done())
|
||||
return nullptr;
|
||||
|
||||
it.setKey(key);
|
||||
return it.data();
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* addMember(TAdaptedString key, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(!key.isNull());
|
||||
if (key.isLinked()) {
|
||||
auto it = addSlot(resources);
|
||||
if (!it.done())
|
||||
it.setKey(key.data());
|
||||
return it.data();
|
||||
} else {
|
||||
auto storedKey = resources->saveString(key);
|
||||
if (!storedKey)
|
||||
return nullptr;
|
||||
auto it = addSlot(resources);
|
||||
if (!it.done())
|
||||
it.setKey(storedKey);
|
||||
return it.data();
|
||||
}
|
||||
}
|
||||
template <typename TAdaptedString> // also works with StringNode*
|
||||
VariantData* addMember(TAdaptedString key, ResourceManager* resources);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getOrAddMember(TAdaptedString key, ResourceManager* resources);
|
||||
@@ -65,6 +39,27 @@ class ObjectData : public CollectionData {
|
||||
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;
|
||||
|
||||
@@ -6,21 +6,26 @@
|
||||
|
||||
#include <ArduinoJson/Object/ObjectData.hpp>
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* ObjectData::getMember(
|
||||
TAdaptedString key, const ResourceManager* resources) const {
|
||||
return findKey(key, resources).data();
|
||||
auto it = findKey(key, resources);
|
||||
if (it.done())
|
||||
return nullptr;
|
||||
it.next(resources);
|
||||
return it.data();
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* ObjectData::getOrAddMember(TAdaptedString key,
|
||||
ResourceManager* resources) {
|
||||
auto it = findKey(key, resources);
|
||||
if (!it.done())
|
||||
return it.data();
|
||||
auto data = getMember(key, resources);
|
||||
if (data)
|
||||
return data;
|
||||
return addMember(key, resources);
|
||||
}
|
||||
|
||||
@@ -29,9 +34,11 @@ inline ObjectData::iterator ObjectData::findKey(
|
||||
TAdaptedString key, const ResourceManager* resources) const {
|
||||
if (key.isNull())
|
||||
return iterator();
|
||||
bool isKey = true;
|
||||
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
|
||||
if (stringEquals(key, adaptString(it.key())))
|
||||
if (isKey && stringEquals(key, adaptString(it->asString())))
|
||||
return it;
|
||||
isKey = !isKey;
|
||||
}
|
||||
return iterator();
|
||||
}
|
||||
@@ -42,4 +49,28 @@ inline void ObjectData::removeMember(TAdaptedString key,
|
||||
remove(findKey(key, resources), resources);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* ObjectData::addMember(TAdaptedString key,
|
||||
ResourceManager* resources) {
|
||||
auto keySlot = resources->allocVariant();
|
||||
if (!keySlot)
|
||||
return nullptr;
|
||||
|
||||
auto valueSlot = resources->allocVariant();
|
||||
if (!valueSlot)
|
||||
return nullptr;
|
||||
|
||||
if (!keySlot->setString(key, resources))
|
||||
return nullptr;
|
||||
|
||||
CollectionData::appendPair(keySlot, valueSlot, resources);
|
||||
|
||||
return valueSlot.ptr();
|
||||
}
|
||||
|
||||
// Returns the size (in bytes) of an object with n members.
|
||||
constexpr size_t sizeofObject(size_t n) {
|
||||
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;
|
||||
TSerializer<DummyWriter> serializer(
|
||||
dp, VariantAttorney::getResourceManager(source));
|
||||
return VariantData::accept(VariantAttorney::getData(source), serializer);
|
||||
auto data = VariantAttorney::getData(source);
|
||||
auto resources = VariantAttorney::getResourceManager(source);
|
||||
TSerializer<DummyWriter> serializer(dp, resources);
|
||||
return VariantData::accept(data, resources, serializer);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -10,9 +10,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <template <typename> class TSerializer, typename TWriter>
|
||||
size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
|
||||
TSerializer<TWriter> serializer(writer,
|
||||
VariantAttorney::getResourceManager(source));
|
||||
return VariantData::accept(VariantAttorney::getData(source), serializer);
|
||||
auto data = VariantAttorney::getData(source);
|
||||
auto resources = VariantAttorney::getResourceManager(source);
|
||||
TSerializer<TWriter> serializer(writer, resources);
|
||||
return VariantData::accept(data, resources, serializer);
|
||||
}
|
||||
|
||||
template <template <typename> class TSerializer, typename TDestination>
|
||||
|
||||
@@ -63,19 +63,22 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
|
||||
auto data = getData(dst);
|
||||
if (!data)
|
||||
return false;
|
||||
data->setInteger(src, getResourceManager(dst));
|
||||
return true;
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
return data->setInteger(src, resources);
|
||||
}
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
|
||||
auto data = getData(src);
|
||||
return data ? data->template asIntegral<T>() : T();
|
||||
auto resources = getResourceManager(src);
|
||||
return data ? data->template asIntegral<T>(resources) : T();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->template isInteger<T>();
|
||||
auto resources = getResourceManager(src);
|
||||
return data && data->template isInteger<T>(resources);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,12 +91,15 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data ? static_cast<T>(data->template asIntegral<int>()) : T();
|
||||
auto resources = getResourceManager(src);
|
||||
return data ? static_cast<T>(data->template asIntegral<int>(resources))
|
||||
: T();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->template isInteger<int>();
|
||||
auto resources = getResourceManager(src);
|
||||
return data && data->template isInteger<int>(resources);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,13 +109,16 @@ struct Converter<bool> : private detail::VariantAttorney {
|
||||
auto data = getData(dst);
|
||||
if (!data)
|
||||
return false;
|
||||
data->setBoolean(src, getResourceManager(dst));
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
data->setBoolean(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data ? data->asBoolean() : false;
|
||||
auto resources = getResourceManager(src);
|
||||
return data ? data->asBoolean(resources) : false;
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
@@ -125,13 +134,15 @@ struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
|
||||
auto data = getData(dst);
|
||||
if (!data)
|
||||
return false;
|
||||
data->setFloat(static_cast<JsonFloat>(src), getResourceManager(dst));
|
||||
return true;
|
||||
auto resources = getResourceManager(dst);
|
||||
data->clear(resources);
|
||||
return data->setFloat(src, resources);
|
||||
}
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data ? data->template asFloat<T>() : 0;
|
||||
auto resources = getResourceManager(src);
|
||||
return data ? data->template asFloat<T>(resources) : 0;
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
@@ -199,7 +210,7 @@ struct Converter<SerializedValue<T>> : private detail::VariantAttorney {
|
||||
template <>
|
||||
struct Converter<detail::nullptr_t> : private detail::VariantAttorney {
|
||||
static void toJson(detail::nullptr_t, JsonVariant dst) {
|
||||
detail::VariantData::setNull(getData(dst), getResourceManager(dst));
|
||||
detail::VariantData::clear(getData(dst), getResourceManager(dst));
|
||||
}
|
||||
static detail::nullptr_t fromJson(JsonVariantConst) {
|
||||
return nullptr;
|
||||
@@ -252,12 +263,11 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
|
||||
auto data = detail::VariantAttorney::getData(dst);
|
||||
if (!resources || !data)
|
||||
return;
|
||||
data->clear(resources);
|
||||
detail::StringBuilderPrint print(resources);
|
||||
src.printTo(print);
|
||||
if (print.overflowed()) {
|
||||
data->setNull();
|
||||
if (print.overflowed())
|
||||
return;
|
||||
}
|
||||
data->setOwnedString(print.save());
|
||||
}
|
||||
|
||||
|
||||
@@ -139,25 +139,30 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
return operator[](key.template as<const char*>());
|
||||
}
|
||||
|
||||
// Returns true if tge object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
|
||||
template <typename TString>
|
||||
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
|
||||
const TString& key) const {
|
||||
return detail::VariantData::getMember(getData(), detail::adaptString(key),
|
||||
resources_) != 0;
|
||||
}
|
||||
|
||||
// Returns true if tge object contains the specified key.
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
|
||||
template <typename TChar>
|
||||
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, bool> containsKey(
|
||||
TChar* key) const {
|
||||
return detail::VariantData::getMember(getData(), detail::adaptString(key),
|
||||
resources_) != 0;
|
||||
}
|
||||
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
|
||||
template <typename TVariant>
|
||||
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
|
||||
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
|
||||
const TVariant& key) const {
|
||||
return containsKey(key.template as<const char*>());
|
||||
|
||||
@@ -55,7 +55,7 @@ typename TVisitor::result_type accept(JsonVariantConst variant,
|
||||
return visit.visit(nullptr);
|
||||
auto resources = VariantAttorney::getResourceManager(variant);
|
||||
VisitorAdapter<TVisitor> adapter(visit, resources);
|
||||
return data->accept(adapter);
|
||||
return data->accept(adapter, resources);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -52,23 +52,19 @@ struct Comparer<
|
||||
|
||||
explicit Comparer(T value) : rhs(value) {}
|
||||
|
||||
CompareResult visit(JsonFloat lhs) {
|
||||
template <typename U>
|
||||
enable_if_t<is_floating_point<U>::value || is_integral<U>::value,
|
||||
CompareResult>
|
||||
visit(const U& lhs) {
|
||||
return arithmeticCompare(lhs, rhs);
|
||||
}
|
||||
|
||||
CompareResult visit(JsonInteger lhs) {
|
||||
return arithmeticCompare(lhs, rhs);
|
||||
template <typename U>
|
||||
enable_if_t<!is_floating_point<U>::value && !is_integral<U>::value,
|
||||
CompareResult>
|
||||
visit(const U& lhs) {
|
||||
return ComparerBase::visit(lhs);
|
||||
}
|
||||
|
||||
CompareResult visit(JsonUInt lhs) {
|
||||
return arithmeticCompare(lhs, rhs);
|
||||
}
|
||||
|
||||
CompareResult visit(bool lhs) {
|
||||
return visit(static_cast<JsonUInt>(lhs));
|
||||
}
|
||||
|
||||
using ComparerBase::visit;
|
||||
};
|
||||
|
||||
struct NullComparer : ComparerBase {
|
||||
|
||||
@@ -13,38 +13,49 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
enum {
|
||||
VALUE_MASK = 0x7F,
|
||||
|
||||
OWNED_VALUE_BIT = 0x01,
|
||||
VALUE_IS_NULL = 0,
|
||||
VALUE_IS_RAW_STRING = 0x03,
|
||||
VALUE_IS_LINKED_STRING = 0x04,
|
||||
VALUE_IS_OWNED_STRING = 0x05,
|
||||
|
||||
// CAUTION: no OWNED_VALUE_BIT below
|
||||
|
||||
VALUE_IS_BOOLEAN = 0x06,
|
||||
|
||||
NUMBER_BIT = 0x08,
|
||||
VALUE_IS_UNSIGNED_INTEGER = 0x08,
|
||||
VALUE_IS_SIGNED_INTEGER = 0x0A,
|
||||
VALUE_IS_FLOAT = 0x0C,
|
||||
|
||||
COLLECTION_MASK = 0x60,
|
||||
VALUE_IS_OBJECT = 0x20,
|
||||
VALUE_IS_ARRAY = 0x40,
|
||||
|
||||
OWNED_KEY_BIT = 0x80
|
||||
enum class VariantTypeBits : uint8_t {
|
||||
OwnedStringBit = 0x01, // 0000 0001
|
||||
NumberBit = 0x08, // 0000 1000
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
ExtensionBit = 0x10, // 0001 0000
|
||||
#endif
|
||||
CollectionMask = 0x60,
|
||||
};
|
||||
|
||||
enum class VariantType : uint8_t {
|
||||
Null = 0, // 0000 0000
|
||||
RawString = 0x03, // 0000 0011
|
||||
LinkedString = 0x04, // 0000 0100
|
||||
OwnedString = 0x05, // 0000 0101
|
||||
Boolean = 0x06, // 0000 0110
|
||||
Uint32 = 0x0A, // 0000 1010
|
||||
Int32 = 0x0C, // 0000 1100
|
||||
Float = 0x0E, // 0000 1110
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
Uint64 = 0x1A, // 0001 1010
|
||||
Int64 = 0x1C, // 0001 1100
|
||||
#endif
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
Double = 0x1E, // 0001 1110
|
||||
#endif
|
||||
Object = 0x20,
|
||||
Array = 0x40,
|
||||
};
|
||||
|
||||
inline bool operator&(VariantType type, VariantTypeBits bit) {
|
||||
return (uint8_t(type) & uint8_t(bit)) != 0;
|
||||
}
|
||||
|
||||
union VariantContent {
|
||||
VariantContent() {}
|
||||
|
||||
JsonFloat asFloat;
|
||||
float asFloat;
|
||||
bool asBoolean;
|
||||
JsonUInt asUnsignedInteger;
|
||||
JsonInteger asSignedInteger;
|
||||
uint32_t asUint32;
|
||||
int32_t asInt32;
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
SlotId asSlotId;
|
||||
#endif
|
||||
ArrayData asArray;
|
||||
ObjectData asObject;
|
||||
CollectionData asCollection;
|
||||
@@ -52,4 +63,16 @@ union VariantContent {
|
||||
struct StringNode* asOwnedString;
|
||||
};
|
||||
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
union VariantExtension {
|
||||
# if ARDUINOJSON_USE_LONG_LONG
|
||||
uint64_t asUint64;
|
||||
int64_t asInt64;
|
||||
# endif
|
||||
# if ARDUINOJSON_USE_DOUBLE
|
||||
double asDouble;
|
||||
# endif
|
||||
};
|
||||
#endif
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
#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>
|
||||
#include <ArduinoJson/Variant/VariantSlot.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -19,42 +19,77 @@ T parseNumber(const char* s);
|
||||
|
||||
class VariantData {
|
||||
VariantContent content_; // must be first to allow cast from array to variant
|
||||
uint8_t flags_;
|
||||
VariantType type_;
|
||||
SlotId next_;
|
||||
|
||||
public:
|
||||
VariantData() : flags_(VALUE_IS_NULL) {}
|
||||
// Placement new
|
||||
static void* operator new(size_t, void* p) noexcept {
|
||||
return p;
|
||||
}
|
||||
|
||||
static void operator delete(void*, void*) noexcept {}
|
||||
|
||||
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 {
|
||||
switch (type()) {
|
||||
case VALUE_IS_FLOAT:
|
||||
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);
|
||||
|
||||
case VALUE_IS_ARRAY:
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
return visit.visit(extension->asDouble);
|
||||
#endif
|
||||
|
||||
case VariantType::Array:
|
||||
return visit.visit(content_.asArray);
|
||||
|
||||
case VALUE_IS_OBJECT:
|
||||
case VariantType::Object:
|
||||
return visit.visit(content_.asObject);
|
||||
|
||||
case VALUE_IS_LINKED_STRING:
|
||||
case VariantType::LinkedString:
|
||||
return visit.visit(JsonString(content_.asLinkedString));
|
||||
|
||||
case VALUE_IS_OWNED_STRING:
|
||||
case VariantType::OwnedString:
|
||||
return visit.visit(JsonString(content_.asOwnedString->data,
|
||||
content_.asOwnedString->length,
|
||||
JsonString::Copied));
|
||||
|
||||
case VALUE_IS_RAW_STRING:
|
||||
case VariantType::RawString:
|
||||
return visit.visit(RawString(content_.asOwnedString->data,
|
||||
content_.asOwnedString->length));
|
||||
|
||||
case VALUE_IS_SIGNED_INTEGER:
|
||||
return visit.visit(content_.asSignedInteger);
|
||||
case VariantType::Int32:
|
||||
return visit.visit(static_cast<JsonInteger>(content_.asInt32));
|
||||
|
||||
case VALUE_IS_UNSIGNED_INTEGER:
|
||||
return visit.visit(content_.asUnsignedInteger);
|
||||
case VariantType::Uint32:
|
||||
return visit.visit(static_cast<JsonUInt>(content_.asUint32));
|
||||
|
||||
case VALUE_IS_BOOLEAN:
|
||||
#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:
|
||||
@@ -64,9 +99,10 @@ class VariantData {
|
||||
|
||||
template <typename TVisitor>
|
||||
static typename TVisitor::result_type accept(const VariantData* var,
|
||||
const ResourceManager* resources,
|
||||
TVisitor& visit) {
|
||||
if (var != 0)
|
||||
return var->accept(visit);
|
||||
return var->accept(visit, resources);
|
||||
else
|
||||
return visit.visit(nullptr);
|
||||
}
|
||||
@@ -97,17 +133,31 @@ class VariantData {
|
||||
return var->addValue(value, resources);
|
||||
}
|
||||
|
||||
bool asBoolean() const {
|
||||
switch (type()) {
|
||||
case VALUE_IS_BOOLEAN:
|
||||
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 VALUE_IS_SIGNED_INTEGER:
|
||||
case VALUE_IS_UNSIGNED_INTEGER:
|
||||
return content_.asUnsignedInteger != 0;
|
||||
case VALUE_IS_FLOAT:
|
||||
case VariantType::Uint32:
|
||||
case VariantType::Int32:
|
||||
return content_.asUint32 != 0;
|
||||
case VariantType::Float:
|
||||
return content_.asFloat != 0;
|
||||
case VALUE_IS_NULL:
|
||||
#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;
|
||||
}
|
||||
@@ -130,41 +180,71 @@ class VariantData {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T asFloat() const {
|
||||
T asFloat(const ResourceManager* resources) const {
|
||||
static_assert(is_floating_point<T>::value, "T must be a floating point");
|
||||
switch (type()) {
|
||||
case VALUE_IS_BOOLEAN:
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
auto extension = getExtension(resources);
|
||||
#else
|
||||
(void)resources; // silence warning
|
||||
#endif
|
||||
switch (type_) {
|
||||
case VariantType::Boolean:
|
||||
return static_cast<T>(content_.asBoolean);
|
||||
case VALUE_IS_UNSIGNED_INTEGER:
|
||||
return static_cast<T>(content_.asUnsignedInteger);
|
||||
case VALUE_IS_SIGNED_INTEGER:
|
||||
return static_cast<T>(content_.asSignedInteger);
|
||||
case VALUE_IS_LINKED_STRING:
|
||||
case VALUE_IS_OWNED_STRING:
|
||||
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::LinkedString:
|
||||
case VariantType::OwnedString:
|
||||
return parseNumber<T>(content_.asOwnedString->data);
|
||||
case VALUE_IS_FLOAT:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T asIntegral() const {
|
||||
T asIntegral(const ResourceManager* resources) const {
|
||||
static_assert(is_integral<T>::value, "T must be an integral type");
|
||||
switch (type()) {
|
||||
case VALUE_IS_BOOLEAN:
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
auto extension = getExtension(resources);
|
||||
#else
|
||||
(void)resources; // silence warning
|
||||
#endif
|
||||
switch (type_) {
|
||||
case VariantType::Boolean:
|
||||
return content_.asBoolean;
|
||||
case VALUE_IS_UNSIGNED_INTEGER:
|
||||
return convertNumber<T>(content_.asUnsignedInteger);
|
||||
case VALUE_IS_SIGNED_INTEGER:
|
||||
return convertNumber<T>(content_.asSignedInteger);
|
||||
case VALUE_IS_LINKED_STRING:
|
||||
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::LinkedString:
|
||||
return parseNumber<T>(content_.asLinkedString);
|
||||
case VALUE_IS_OWNED_STRING:
|
||||
case VariantType::OwnedString:
|
||||
return parseNumber<T>(content_.asOwnedString->data);
|
||||
case VALUE_IS_FLOAT:
|
||||
case VariantType::Float:
|
||||
return convertNumber<T>(content_.asFloat);
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
return convertNumber<T>(extension->asDouble);
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -179,8 +259,8 @@ class VariantData {
|
||||
}
|
||||
|
||||
JsonString asRawString() const {
|
||||
switch (type()) {
|
||||
case VALUE_IS_RAW_STRING:
|
||||
switch (type_) {
|
||||
case VariantType::RawString:
|
||||
return JsonString(content_.asOwnedString->data,
|
||||
content_.asOwnedString->length, JsonString::Copied);
|
||||
default:
|
||||
@@ -189,10 +269,10 @@ class VariantData {
|
||||
}
|
||||
|
||||
JsonString asString() const {
|
||||
switch (type()) {
|
||||
case VALUE_IS_LINKED_STRING:
|
||||
switch (type_) {
|
||||
case VariantType::LinkedString:
|
||||
return JsonString(content_.asLinkedString, JsonString::Linked);
|
||||
case VALUE_IS_OWNED_STRING:
|
||||
case VariantType::OwnedString:
|
||||
return JsonString(content_.asOwnedString->data,
|
||||
content_.asOwnedString->length, JsonString::Copied);
|
||||
default:
|
||||
@@ -200,6 +280,10 @@ class VariantData {
|
||||
}
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
const VariantExtension* getExtension(const ResourceManager* resources) const;
|
||||
#endif
|
||||
|
||||
VariantData* getElement(size_t index,
|
||||
const ResourceManager* resources) const {
|
||||
return ArrayData::getElement(asArray(), index, resources);
|
||||
@@ -242,29 +326,42 @@ class VariantData {
|
||||
}
|
||||
|
||||
bool isArray() const {
|
||||
return (flags_ & VALUE_IS_ARRAY) != 0;
|
||||
return type_ == VariantType::Array;
|
||||
}
|
||||
|
||||
bool isBoolean() const {
|
||||
return type() == VALUE_IS_BOOLEAN;
|
||||
return type_ == VariantType::Boolean;
|
||||
}
|
||||
|
||||
bool isCollection() const {
|
||||
return (flags_ & COLLECTION_MASK) != 0;
|
||||
return type_ & VariantTypeBits::CollectionMask;
|
||||
}
|
||||
|
||||
bool isFloat() const {
|
||||
return (flags_ & NUMBER_BIT) != 0;
|
||||
return type_ & VariantTypeBits::NumberBit;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isInteger() const {
|
||||
switch (type()) {
|
||||
case VALUE_IS_UNSIGNED_INTEGER:
|
||||
return canConvertNumber<T>(content_.asUnsignedInteger);
|
||||
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 VALUE_IS_SIGNED_INTEGER:
|
||||
return canConvertNumber<T>(content_.asSignedInteger);
|
||||
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;
|
||||
@@ -272,7 +369,7 @@ class VariantData {
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return type() == VALUE_IS_NULL;
|
||||
return type_ == VariantType::Null;
|
||||
}
|
||||
|
||||
static bool isNull(const VariantData* var) {
|
||||
@@ -282,11 +379,12 @@ class VariantData {
|
||||
}
|
||||
|
||||
bool isObject() const {
|
||||
return (flags_ & VALUE_IS_OBJECT) != 0;
|
||||
return type_ == VariantType::Object;
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return type() == VALUE_IS_LINKED_STRING || type() == VALUE_IS_OWNED_STRING;
|
||||
return type_ == VariantType::LinkedString ||
|
||||
type_ == VariantType::OwnedString;
|
||||
}
|
||||
|
||||
size_t nesting(const ResourceManager* resources) const {
|
||||
@@ -328,102 +426,60 @@ class VariantData {
|
||||
var->removeMember(key, resources);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
flags_ = VALUE_IS_NULL;
|
||||
void reset() { // TODO: remove
|
||||
type_ = VariantType::Null;
|
||||
}
|
||||
|
||||
void setBoolean(bool value) {
|
||||
setType(VALUE_IS_BOOLEAN);
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
type_ = VariantType::Boolean;
|
||||
content_.asBoolean = value;
|
||||
}
|
||||
|
||||
void setBoolean(bool value, ResourceManager* resources) {
|
||||
release(resources);
|
||||
setBoolean(value);
|
||||
}
|
||||
|
||||
void setFloat(JsonFloat value) {
|
||||
setType(VALUE_IS_FLOAT);
|
||||
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;
|
||||
}
|
||||
|
||||
void setFloat(JsonFloat value, ResourceManager* resources) {
|
||||
release(resources);
|
||||
setFloat(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
enable_if_t<is_signed<T>::value> setInteger(T value) {
|
||||
setType(VALUE_IS_SIGNED_INTEGER);
|
||||
content_.asSignedInteger = value;
|
||||
}
|
||||
enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
|
||||
|
||||
template <typename T>
|
||||
enable_if_t<is_unsigned<T>::value> setInteger(T value) {
|
||||
setType(VALUE_IS_UNSIGNED_INTEGER);
|
||||
content_.asUnsignedInteger = static_cast<JsonUInt>(value);
|
||||
}
|
||||
enable_if_t<is_signed<T>::value, bool> setInteger(T value,
|
||||
ResourceManager* resources);
|
||||
|
||||
template <typename T>
|
||||
void setInteger(T value, ResourceManager* resources) {
|
||||
release(resources);
|
||||
setInteger(value);
|
||||
}
|
||||
|
||||
void setNull() {
|
||||
setType(VALUE_IS_NULL);
|
||||
}
|
||||
|
||||
void setNull(ResourceManager* resources) {
|
||||
release(resources);
|
||||
setNull();
|
||||
}
|
||||
|
||||
static void setNull(VariantData* var, ResourceManager* resources) {
|
||||
if (!var)
|
||||
return;
|
||||
var->setNull(resources);
|
||||
}
|
||||
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);
|
||||
setType(VALUE_IS_RAW_STRING);
|
||||
type_ = VariantType::RawString;
|
||||
content_.asOwnedString = s;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setRawString(SerializedValue<T> value, ResourceManager* resources) {
|
||||
release(resources);
|
||||
auto dup = resources->saveString(adaptString(value.data(), value.size()));
|
||||
if (dup)
|
||||
setRawString(dup);
|
||||
else
|
||||
setNull();
|
||||
}
|
||||
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>
|
||||
void setString(TAdaptedString value, ResourceManager* resources) {
|
||||
setNull(resources);
|
||||
bool setString(TAdaptedString value, ResourceManager* resources);
|
||||
|
||||
if (value.isNull())
|
||||
return;
|
||||
|
||||
if (value.isLinked()) {
|
||||
setLinkedString(value.data());
|
||||
return;
|
||||
}
|
||||
|
||||
auto dup = resources->saveString(value);
|
||||
if (dup)
|
||||
setOwnedString(dup);
|
||||
bool setString(StringNode* s, ResourceManager*) {
|
||||
setOwnedString(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
@@ -431,23 +487,32 @@ class VariantData {
|
||||
ResourceManager* resources) {
|
||||
if (!var)
|
||||
return;
|
||||
var->clear(resources);
|
||||
var->setString(value, resources);
|
||||
}
|
||||
|
||||
void setLinkedString(const char* s) {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
setType(VALUE_IS_LINKED_STRING);
|
||||
type_ = VariantType::LinkedString;
|
||||
content_.asLinkedString = s;
|
||||
}
|
||||
|
||||
void setOwnedString(StringNode* s) {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
setType(VALUE_IS_OWNED_STRING);
|
||||
type_ = VariantType::OwnedString;
|
||||
content_.asOwnedString = s;
|
||||
}
|
||||
|
||||
size_t size(const ResourceManager* resources) const {
|
||||
return isCollection() ? content_.asCollection.size(resources) : 0;
|
||||
if (isObject())
|
||||
return content_.asObject.size(resources);
|
||||
|
||||
if (isArray())
|
||||
return content_.asArray.size(resources);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t size(const VariantData* var, const ResourceManager* resources) {
|
||||
@@ -455,56 +520,44 @@ class VariantData {
|
||||
}
|
||||
|
||||
ArrayData& toArray() {
|
||||
setType(VALUE_IS_ARRAY);
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
type_ = VariantType::Array;
|
||||
new (&content_.asArray) ArrayData();
|
||||
return content_.asArray;
|
||||
}
|
||||
|
||||
ArrayData& toArray(ResourceManager* resources) {
|
||||
release(resources);
|
||||
return toArray();
|
||||
}
|
||||
|
||||
static ArrayData* toArray(VariantData* var, ResourceManager* resources) {
|
||||
if (!var)
|
||||
return 0;
|
||||
return &var->toArray(resources);
|
||||
var->clear(resources);
|
||||
return &var->toArray();
|
||||
}
|
||||
|
||||
ObjectData& toObject() {
|
||||
setType(VALUE_IS_OBJECT);
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
type_ = VariantType::Object;
|
||||
new (&content_.asObject) ObjectData();
|
||||
return content_.asObject;
|
||||
}
|
||||
|
||||
ObjectData& toObject(ResourceManager* resources) {
|
||||
release(resources);
|
||||
return toObject();
|
||||
}
|
||||
|
||||
static ObjectData* toObject(VariantData* var, ResourceManager* resources) {
|
||||
if (!var)
|
||||
return 0;
|
||||
return &var->toObject(resources);
|
||||
var->clear(resources);
|
||||
return &var->toObject();
|
||||
}
|
||||
|
||||
uint8_t type() const {
|
||||
return flags_ & VALUE_MASK;
|
||||
VariantType type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
private:
|
||||
void release(ResourceManager* resources) {
|
||||
if (flags_ & OWNED_VALUE_BIT)
|
||||
resources->dereferenceString(content_.asOwnedString->data);
|
||||
// Release the resources used by this variant and set it to null.
|
||||
void clear(ResourceManager* resources);
|
||||
|
||||
auto collection = asCollection();
|
||||
if (collection)
|
||||
collection->clear(resources);
|
||||
}
|
||||
|
||||
void setType(uint8_t t) {
|
||||
flags_ &= OWNED_KEY_BIT;
|
||||
flags_ |= t;
|
||||
static void clear(VariantData* var, ResourceManager* resources) {
|
||||
if (!var)
|
||||
return;
|
||||
var->clear(resources);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
141
lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp
Normal file
141
lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2024, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.isLinked()) {
|
||||
setLinkedString(value.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto dup = resources->saveString(value);
|
||||
if (dup) {
|
||||
setOwnedString(dup);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void VariantData::clear(ResourceManager* resources) {
|
||||
if (type_ & VariantTypeBits::OwnedStringBit)
|
||||
resources->dereferenceString(content_.asOwnedString->data);
|
||||
|
||||
#if ARDUINOJSON_USE_EXTENSIONS
|
||||
if (type_ & VariantTypeBits::ExtensionBit)
|
||||
resources->freeExtension(content_.asSlotId);
|
||||
#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
|
||||
|
||||
template <typename T>
|
||||
enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
|
||||
T value, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
(void)resources; // silence warning
|
||||
|
||||
float valueAsFloat = static_cast<float>(value);
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
if (value == valueAsFloat) {
|
||||
type_ = VariantType::Float;
|
||||
content_.asFloat = valueAsFloat;
|
||||
} else {
|
||||
auto extension = resources->allocExtension();
|
||||
if (!extension)
|
||||
return false;
|
||||
type_ = VariantType::Double;
|
||||
content_.asSlotId = extension.id();
|
||||
extension->asDouble = value;
|
||||
}
|
||||
#else
|
||||
type_ = VariantType::Float;
|
||||
content_.asFloat = valueAsFloat;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
|
||||
T value, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
(void)resources; // silence warning
|
||||
|
||||
if (canConvertNumber<int32_t>(value)) {
|
||||
type_ = VariantType::Int32;
|
||||
content_.asInt32 = static_cast<int32_t>(value);
|
||||
}
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
else {
|
||||
auto extension = resources->allocExtension();
|
||||
if (!extension)
|
||||
return false;
|
||||
type_ = VariantType::Int64;
|
||||
content_.asSlotId = extension.id();
|
||||
extension->asInt64 = value;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
enable_if_t<is_unsigned<T>::value, bool> VariantData::setInteger(
|
||||
T value, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
|
||||
(void)resources; // silence warning
|
||||
|
||||
if (canConvertNumber<uint32_t>(value)) {
|
||||
type_ = VariantType::Uint32;
|
||||
content_.asUint32 = static_cast<uint32_t>(value);
|
||||
}
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
else {
|
||||
auto extension = resources->allocExtension();
|
||||
if (!extension)
|
||||
return false;
|
||||
type_ = VariantType::Uint64;
|
||||
content_.asSlotId = extension.id();
|
||||
extension->asUint64 = value;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -29,7 +29,7 @@ class VariantRefBase : public VariantTag {
|
||||
// Sets the value to null.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/clear/
|
||||
void clear() const {
|
||||
VariantData::setNull(getOrCreateData(), getResourceManager());
|
||||
VariantData::clear(getOrCreateData(), getResourceManager());
|
||||
}
|
||||
|
||||
// Returns true if the value is null or the reference is unbound.
|
||||
@@ -165,20 +165,23 @@ class VariantRefBase : public VariantTag {
|
||||
// https://arduinojson.org/v7/api/jsonvariant/subscript/
|
||||
ElementProxy<TDerived> operator[](size_t index) const;
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariant/containskey/
|
||||
template <typename TString>
|
||||
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
|
||||
enable_if_t<IsString<TString>::value, bool> containsKey(
|
||||
const TString& key) const;
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariant/containskey/
|
||||
template <typename TChar>
|
||||
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
|
||||
enable_if_t<IsString<TChar*>::value, bool> containsKey(TChar* key) const;
|
||||
|
||||
// Returns true if the object contains the specified key.
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariant/containskey/
|
||||
template <typename TVariant>
|
||||
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
|
||||
enable_if_t<IsVariant<TVariant>::value, bool> containsKey(
|
||||
const TVariant& key) const;
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ enable_if_t<is_same<T, JsonVariant>::value, JsonVariant>
|
||||
VariantRefBase<TDerived>::to() const {
|
||||
auto data = getOrCreateData();
|
||||
auto resources = getResourceManager();
|
||||
detail::VariantData::setNull(data, resources);
|
||||
detail::VariantData::clear(data, resources);
|
||||
return JsonVariant(data, resources);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ARDUINOJSON_VERSION "7.1.0"
|
||||
#define ARDUINOJSON_VERSION "7.2.0"
|
||||
#define ARDUINOJSON_VERSION_MAJOR 7
|
||||
#define ARDUINOJSON_VERSION_MINOR 1
|
||||
#define ARDUINOJSON_VERSION_MINOR 2
|
||||
#define ARDUINOJSON_VERSION_REVISION 0
|
||||
#define ARDUINOJSON_VERSION_MACRO V710
|
||||
#define ARDUINOJSON_VERSION_MACRO V720
|
||||
|
||||
@@ -13,7 +13,7 @@ default_envs = esp32
|
||||
boards_dir = boards
|
||||
|
||||
[env]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
platform_packages =
|
||||
framework = arduino, espidf
|
||||
board_build.embed_txtfiles =
|
||||
|
||||
@@ -4,7 +4,7 @@ dependencies:
|
||||
|
||||
esp-nimble-cpp:
|
||||
git: https://github.com/h2zero/esp-nimble-cpp.git
|
||||
version: 2d3ff4922e38f13559b5151078c914c643c44ff6
|
||||
version: 758c4d0471cca8a00c8652aac2e16940ecb30cb3
|
||||
|
||||
espressif/libsodium: "^1.0.20~1"
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ default_envs = updater_esp32
|
||||
boards_dir = ../boards
|
||||
|
||||
[env]
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip
|
||||
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
|
||||
platform_packages =
|
||||
framework = arduino, espidf
|
||||
board_build.embed_txtfiles =
|
||||
|
||||
Reference in New Issue
Block a user