From 3f0ef34c9e223416f88d1587f5ccc84e1a646bc3 Mon Sep 17 00:00:00 2001 From: iranl Date: Wed, 9 Oct 2024 15:06:50 +0200 Subject: [PATCH] Update deps (#482) --- lib/ArduinoJson/CHANGELOG.md | 36 ++ lib/ArduinoJson/CMakeLists.txt | 2 +- lib/ArduinoJson/README.md | 3 + lib/ArduinoJson/appveyor.yml | 2 +- lib/ArduinoJson/extras/conf_test/avr.cpp | 7 +- lib/ArduinoJson/extras/conf_test/esp8266.cpp | 5 +- lib/ArduinoJson/extras/conf_test/x64.cpp | 6 +- lib/ArduinoJson/extras/conf_test/x86.cpp | 5 +- .../extras/tests/Deprecated/CMakeLists.txt | 3 +- .../extras/tests/Deprecated/containsKey.cpp | 210 ++++++++++ .../extras/tests/Helpers/Allocators.hpp | 8 +- .../extras/tests/JsonArray/remove.cpp | 6 + .../extras/tests/JsonDeserializer/array.cpp | 24 +- .../extras/tests/JsonDeserializer/errors.cpp | 42 ++ .../extras/tests/JsonDeserializer/object.cpp | 22 +- .../extras/tests/JsonDeserializer/string.cpp | 16 + .../extras/tests/JsonDocument/CMakeLists.txt | 1 - .../extras/tests/JsonDocument/MemberProxy.cpp | 59 +-- .../extras/tests/JsonDocument/assignment.cpp | 6 +- .../extras/tests/JsonDocument/shrinkToFit.cpp | 4 +- .../extras/tests/JsonObject/CMakeLists.txt | 1 - .../extras/tests/JsonObject/set.cpp | 6 +- .../extras/tests/JsonObject/std_string.cpp | 7 - .../extras/tests/JsonObject/subscript.cpp | 10 +- .../tests/JsonObjectConst/CMakeLists.txt | 1 - .../tests/JsonSerializer/JsonVariant.cpp | 9 + .../extras/tests/JsonVariant/CMakeLists.txt | 1 - .../extras/tests/JsonVariant/as.cpp | 82 ++-- .../extras/tests/JsonVariant/set.cpp | 189 ++++++++- .../extras/tests/JsonVariant/unbound.cpp | 4 - .../tests/JsonVariantConst/CMakeLists.txt | 1 - .../extras/tests/Misc/CMakeLists.txt | 1 - .../extras/tests/Misc/unsigned_char.cpp | 9 - .../tests/MixedConfiguration/use_double_0.cpp | 70 +++- .../MixedConfiguration/use_long_long_0.cpp | 4 - .../tests/MsgPackDeserializer/errors.cpp | 41 ++ .../extras/tests/Numbers/CMakeLists.txt | 3 +- .../extras/tests/Numbers/decomposeFloat.cpp | 42 ++ .../extras/tests/Numbers/parseFloat.cpp | 36 +- .../extras/tests/Numbers/parseNumber.cpp | 46 ++- .../tests/ResourceManager/StringBuilder.cpp | 1 - .../tests/ResourceManager/allocVariant.cpp | 50 +-- .../extras/tests/ResourceManager/clear.cpp | 4 +- .../tests/ResourceManager/saveString.cpp | 1 - .../tests/ResourceManager/shrinkToFit.cpp | 10 +- .../extras/tests/ResourceManager/size.cpp | 6 +- .../extras/tests/ResourceManager/swap.cpp | 36 +- lib/ArduinoJson/idf_component.yml | 4 +- lib/ArduinoJson/library.json | 4 +- lib/ArduinoJson/library.properties | 4 +- lib/ArduinoJson/src/ArduinoJson.hpp | 2 +- .../src/ArduinoJson/Array/ArrayData.hpp | 18 +- .../src/ArduinoJson/Array/ArrayImpl.hpp | 22 +- .../ArduinoJson/Collection/CollectionData.hpp | 36 +- .../ArduinoJson/Collection/CollectionImpl.hpp | 98 ++--- .../src/ArduinoJson/Configuration.hpp | 32 +- .../src/ArduinoJson/Document/JsonDocument.hpp | 9 +- .../src/ArduinoJson/Json/EscapeSequence.hpp | 4 +- .../src/ArduinoJson/Json/JsonDeserializer.hpp | 35 +- .../src/ArduinoJson/Json/JsonSerializer.hpp | 23 +- .../ArduinoJson/Json/PrettyJsonSerializer.hpp | 20 +- .../src/ArduinoJson/Json/TextFormatter.hpp | 6 +- .../src/ArduinoJson/Memory/MemoryPool.hpp | 110 ++++++ .../src/ArduinoJson/Memory/MemoryPoolList.hpp | 214 ++++++++++ .../ArduinoJson/Memory/ResourceManager.hpp | 40 +- .../Memory/ResourceManagerImpl.hpp | 42 +- .../src/ArduinoJson/Memory/StringPool.hpp | 3 - .../src/ArduinoJson/MsgPack/MsgPackBinary.hpp | 2 +- .../MsgPack/MsgPackDeserializer.hpp | 22 +- .../ArduinoJson/MsgPack/MsgPackExtension.hpp | 2 +- .../ArduinoJson/MsgPack/MsgPackSerializer.hpp | 12 +- .../src/ArduinoJson/Numbers/FloatParts.hpp | 143 +++---- .../src/ArduinoJson/Numbers/convertNumber.hpp | 11 + .../src/ArduinoJson/Numbers/parseNumber.hpp | 134 +++++-- .../src/ArduinoJson/Object/JsonObject.hpp | 9 +- .../ArduinoJson/Object/JsonObjectConst.hpp | 9 +- .../ArduinoJson/Object/JsonObjectIterator.hpp | 6 +- .../src/ArduinoJson/Object/JsonPair.hpp | 44 +-- .../src/ArduinoJson/Object/ObjectData.hpp | 51 ++- .../src/ArduinoJson/Object/ObjectImpl.hpp | 41 +- .../src/ArduinoJson/Serialization/measure.hpp | 7 +- .../ArduinoJson/Serialization/serialize.hpp | 7 +- .../src/ArduinoJson/Variant/ConverterImpl.hpp | 40 +- .../ArduinoJson/Variant/JsonVariantConst.hpp | 9 +- .../Variant/JsonVariantVisitor.hpp | 2 +- .../ArduinoJson/Variant/VariantCompare.hpp | 22 +- .../ArduinoJson/Variant/VariantContent.hpp | 75 ++-- .../src/ArduinoJson/Variant/VariantData.hpp | 369 ++++++++++-------- .../src/ArduinoJson/Variant/VariantImpl.hpp | 141 +++++++ .../ArduinoJson/Variant/VariantRefBase.hpp | 11 +- .../Variant/VariantRefBaseImpl.hpp | 2 +- lib/ArduinoJson/src/ArduinoJson/version.hpp | 6 +- platformio.ini | 2 +- src/idf_component.yml | 2 +- updater/platformio.ini | 2 +- 95 files changed, 2250 insertions(+), 797 deletions(-) create mode 100644 lib/ArduinoJson/extras/tests/Deprecated/containsKey.cpp create mode 100644 lib/ArduinoJson/extras/tests/Numbers/decomposeFloat.cpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPoolList.hpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp diff --git a/lib/ArduinoJson/CHANGELOG.md b/lib/ArduinoJson/CHANGELOG.md index b348d56..aaec586 100644 --- a/lib/ArduinoJson/CHANGELOG.md +++ b/lib/ArduinoJson/CHANGELOG.md @@ -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()` +* 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()`, 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 value = doc["value"]; +> // ... +> } +> ``` + v7.1.0 (2024-06-27) ------ diff --git a/lib/ArduinoJson/CMakeLists.txt b/lib/ArduinoJson/CMakeLists.txt index d76b837..2fa0593 100644 --- a/lib/ArduinoJson/CMakeLists.txt +++ b/lib/ArduinoJson/CMakeLists.txt @@ -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) diff --git a/lib/ArduinoJson/README.md b/lib/ArduinoJson/README.md index 2900240..4632534 100644 --- a/lib/ArduinoJson/README.md +++ b/lib/ArduinoJson/README.md @@ -148,6 +148,9 @@ ArduinoJson is thankful to its sponsors. Please give them a visit; they deserve 1technophile + + LArkema +

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. diff --git a/lib/ArduinoJson/appveyor.yml b/lib/ArduinoJson/appveyor.yml index edcdb4b..c928a56 100644 --- a/lib/ArduinoJson/appveyor.yml +++ b/lib/ArduinoJson/appveyor.yml @@ -1,4 +1,4 @@ -version: 7.1.0.{build} +version: 7.2.0.{build} environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 diff --git a/lib/ArduinoJson/extras/conf_test/avr.cpp b/lib/ArduinoJson/extras/conf_test/avr.cpp index e0061ea..769317a 100644 --- a/lib/ArduinoJson/extras/conf_test/avr.cpp +++ b/lib/ArduinoJson/extras/conf_test/avr.cpp @@ -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() {} diff --git a/lib/ArduinoJson/extras/conf_test/esp8266.cpp b/lib/ArduinoJson/extras/conf_test/esp8266.cpp index b2e6d06..b37bfd6 100644 --- a/lib/ArduinoJson/extras/conf_test/esp8266.cpp +++ b/lib/ArduinoJson/extras/conf_test/esp8266.cpp @@ -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() {} diff --git a/lib/ArduinoJson/extras/conf_test/x64.cpp b/lib/ArduinoJson/extras/conf_test/x64.cpp index 2b533bf..df6b044 100644 --- a/lib/ArduinoJson/extras/conf_test/x64.cpp +++ b/lib/ArduinoJson/extras/conf_test/x64.cpp @@ -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() {} diff --git a/lib/ArduinoJson/extras/conf_test/x86.cpp b/lib/ArduinoJson/extras/conf_test/x86.cpp index a9874ef..9dff8f1 100644 --- a/lib/ArduinoJson/extras/conf_test/x86.cpp +++ b/lib/ArduinoJson/extras/conf_test/x86.cpp @@ -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() {} diff --git a/lib/ArduinoJson/extras/tests/Deprecated/CMakeLists.txt b/lib/ArduinoJson/extras/tests/Deprecated/CMakeLists.txt index 9cefe82..c69fc44 100644 --- a/lib/ArduinoJson/extras/tests/Deprecated/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/Deprecated/CMakeLists.txt @@ -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 diff --git a/lib/ArduinoJson/extras/tests/Deprecated/containsKey.cpp b/lib/ArduinoJson/extras/tests/Deprecated/containsKey.cpp new file mode 100644 index 0000000..3a429de --- /dev/null +++ b/lib/ArduinoJson/extras/tests/Deprecated/containsKey.cpp @@ -0,0 +1,210 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#include +#include + +#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(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(); + 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(); + + 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(); + + 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(); + + 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); + } +} diff --git a/lib/ArduinoJson/extras/tests/Helpers/Allocators.hpp b/lib/ArduinoJson/extras/tests/Helpers/Allocators.hpp index 095d726..a551c31 100644 --- a/lib/ArduinoJson/extras/tests/Helpers/Allocators.hpp +++ b/lib/ArduinoJson/extras/tests/Helpers/Allocators.hpp @@ -5,8 +5,8 @@ #pragma once #include +#include #include -#include #include @@ -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) * n; } inline size_t sizeofPool( ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) { - return ArduinoJson::detail::VariantPool::slotsToBytes(n); + using namespace ArduinoJson::detail; + return MemoryPool::slotsToBytes(n); } inline size_t sizeofStringBuffer(size_t iteration = 1) { diff --git a/lib/ArduinoJson/extras/tests/JsonArray/remove.cpp b/lib/ArduinoJson/extras/tests/JsonArray/remove.cpp index 9cbd90b..82e3bab 100644 --- a/lib/ArduinoJson/extras/tests/JsonArray/remove.cpp +++ b/lib/ArduinoJson/extras/tests/JsonArray/remove.cpp @@ -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) diff --git a/lib/ArduinoJson/extras/tests/JsonDeserializer/array.cpp b/lib/ArduinoJson/extras/tests/JsonDeserializer/array.cpp index 71a567d..f61083d 100644 --- a/lib/ArduinoJson/extras/tests/JsonDeserializer/array.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDeserializer/array.cpp @@ -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(); REQUIRE(err == DeserializationError::Ok); REQUIRE(2 == arr.size()); - REQUIRE(arr[0] == 4.2); - REQUIRE(arr[1] == 1e2); + REQUIRE(arr[0].as() == 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(); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(2 == arr.size()); + REQUIRE(arr[0].as() == Approx(4.2123456)); + REQUIRE(arr[1] == -7E89); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), + Reallocate(sizeofPool(), sizeofPool(4)), + }); } SECTION("Unsigned long") { diff --git a/lib/ArduinoJson/extras/tests/JsonDeserializer/errors.cpp b/lib/ArduinoJson/extras/tests/JsonDeserializer/errors.cpp index 35b4de2..0c2444e 100644 --- a/lib/ArduinoJson/extras/tests/JsonDeserializer/errors.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDeserializer/errors.cpp @@ -6,6 +6,8 @@ #include #include +#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); + } +} diff --git a/lib/ArduinoJson/extras/tests/JsonDeserializer/object.cpp b/lib/ArduinoJson/extras/tests/JsonDeserializer/object.cpp index 501e87a..ee62ae4 100644 --- a/lib/ArduinoJson/extras/tests/JsonDeserializer/object.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDeserializer/object.cpp @@ -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(); REQUIRE(err == DeserializationError::Ok); REQUIRE(doc.is()); REQUIRE(obj.size() == 2); - REQUIRE(obj["key1"] == 12.345); + REQUIRE(obj["key1"].as() == Approx(12.345f)); + REQUIRE(obj["key2"] == -7E3f); + } + + SECTION("Double") { + DeserializationError err = + deserializeJson(doc, "{\"key1\":12.3456789,\"key2\":-7E89}"); + JsonObject obj = doc.as(); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.is()); + REQUIRE(obj.size() == 2); + REQUIRE(obj["key1"].as() == 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() == "{\"x\":2}"); + REQUIRE(doc.as() == "{\"x\\u0000a\":1,\"x\\u0000b\":2}"); } } diff --git a/lib/ArduinoJson/extras/tests/JsonDeserializer/string.cpp b/lib/ArduinoJson/extras/tests/JsonDeserializer/string.cpp index a47ab76..a760ae6 100644 --- a/lib/ArduinoJson/extras/tests/JsonDeserializer/string.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDeserializer/string.cpp @@ -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() == "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() == "ab\"cd"); +} + TEST_CASE("Invalid JSON string") { const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'", "'\\u000G'", "'\\u000/'", "'\\x1234'"}; diff --git a/lib/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt b/lib/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt index e6b6f1d..0caa7b9 100644 --- a/lib/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt @@ -9,7 +9,6 @@ add_executable(JsonDocumentTests clear.cpp compare.cpp constructor.cpp - containsKey.cpp ElementProxy.cpp isNull.cpp issue1120.cpp diff --git a/lib/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp b/lib/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp index bb6acdf..b48eda1 100644 --- a/lib/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDocument/MemberProxy.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()); + 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()); + REQUIRE(doc.size() == 0); + REQUIRE(doc.overflowed() == true); + REQUIRE(spy.log() == AllocatorLog{ + Allocate(sizeofPool()), AllocateFail(sizeofString("hello")), }); } diff --git a/lib/ArduinoJson/extras/tests/JsonDocument/assignment.cpp b/lib/ArduinoJson/extras/tests/JsonDocument/assignment.cpp index 42cef3e..58a8391 100644 --- a/lib/ArduinoJson/extras/tests/JsonDocument/assignment.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDocument/assignment.cpp @@ -22,8 +22,8 @@ TEST_CASE("JsonDocument assignment") { REQUIRE(doc2.as() == "{\"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() == "{\"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() == "null"); } REQUIRE(spyingAllocator.log() == AllocatorLog{ - Allocate(sizeofString("hello")), Allocate(sizeofPool()), + Allocate(sizeofString("hello")), Allocate(sizeofString("world")), Deallocate(sizeofString("hello")), Deallocate(sizeofString("world")), diff --git a/lib/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp b/lib/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp index 1d43140..91b9d91 100644 --- a/lib/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp +++ b/lib/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp @@ -122,8 +122,8 @@ TEST_CASE("JsonDocument::shrinkToFit()") { REQUIRE(doc.as() == "{\"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)), }); } } diff --git a/lib/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt b/lib/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt index dd2dfaa..60ba1fe 100644 --- a/lib/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt @@ -5,7 +5,6 @@ add_executable(JsonObjectTests clear.cpp compare.cpp - containsKey.cpp equals.cpp isNull.cpp iterator.cpp diff --git a/lib/ArduinoJson/extras/tests/JsonObject/set.cpp b/lib/ArduinoJson/extras/tests/JsonObject/set.cpp index f80f8a8..bad0674 100644 --- a/lib/ArduinoJson/extras/tests/JsonObject/set.cpp +++ b/lib/ArduinoJson/extras/tests/JsonObject/set.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")), }); } diff --git a/lib/ArduinoJson/extras/tests/JsonObject/std_string.cpp b/lib/ArduinoJson/extras/tests/JsonObject/std_string.cpp index 613e0bd..85ca300 100644 --- a/lib/ArduinoJson/extras/tests/JsonObject/std_string.cpp +++ b/lib/ArduinoJson/extras/tests/JsonObject/std_string.cpp @@ -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(); - REQUIRE(true == obj.containsKey("key"_s)); - } - SECTION("remove()") { JsonObject obj = doc.to(); obj["key"] = "value"; diff --git a/lib/ArduinoJson/extras/tests/JsonObject/subscript.cpp b/lib/ArduinoJson/extras/tests/JsonObject/subscript.cpp index 775d149..1a5ceb7 100644 --- a/lib/ArduinoJson/extras/tests/JsonObject/subscript.cpp +++ b/lib/ArduinoJson/extras/tests/JsonObject/subscript.cpp @@ -118,16 +118,16 @@ TEST_CASE("JsonObject::operator[]") { SECTION("should duplicate char* key") { obj[const_cast("hello")] = "world"; REQUIRE(spy.log() == AllocatorLog{ - Allocate(sizeofString("hello")), Allocate(sizeofPool()), + Allocate(sizeofString("hello")), }); } SECTION("should duplicate char* key&value") { obj[const_cast("hello")] = const_cast("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")), }); } diff --git a/lib/ArduinoJson/extras/tests/JsonObjectConst/CMakeLists.txt b/lib/ArduinoJson/extras/tests/JsonObjectConst/CMakeLists.txt index dadb910..447e239 100644 --- a/lib/ArduinoJson/extras/tests/JsonObjectConst/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/JsonObjectConst/CMakeLists.txt @@ -3,7 +3,6 @@ # MIT License add_executable(JsonObjectConstTests - containsKey.cpp equals.cpp isNull.cpp iterator.cpp diff --git a/lib/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp b/lib/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp index 800c3a9..1ddaea6 100644 --- a/lib/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp +++ b/lib/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.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"); } diff --git a/lib/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt b/lib/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt index e14e548..282e6ce 100644 --- a/lib/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt @@ -7,7 +7,6 @@ add_executable(JsonVariantTests as.cpp clear.cpp compare.cpp - containsKey.cpp converters.cpp copy.cpp is.cpp diff --git a/lib/ArduinoJson/extras/tests/JsonVariant/as.cpp b/lib/ArduinoJson/extras/tests/JsonVariant/as.cpp index fa76b0e..2c6f54b 100644 --- a/lib/ArduinoJson/extras/tests/JsonVariant/as.cpp +++ b/lib/ArduinoJson/extras/tests/JsonVariant/as.cpp @@ -31,13 +31,28 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as().data() == nullptr); } - SECTION("set(4.2)") { + SECTION("set(float)") { + variant.set(4.2f); + + REQUIRE(variant.as()); + REQUIRE(0 == variant.as()); + REQUIRE(variant.as() == "4.2"); + REQUIRE(variant.as() == 4L); + REQUIRE(variant.as() == 4.2f); + REQUIRE(variant.as() == 4U); + REQUIRE(variant.as().isNull()); + REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); + } + + SECTION("set(double)") { variant.set(4.2); REQUIRE(variant.as()); REQUIRE(0 == variant.as()); REQUIRE(variant.as() == "4.2"); REQUIRE(variant.as() == 4L); + REQUIRE(variant.as() == 4.2); REQUIRE(variant.as() == 4U); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); @@ -78,43 +93,62 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as().data() == nullptr); } - SECTION("set(42)") { - variant.set(42); + SECTION("set(uint32_t)") { + variant.set(4294967295U); REQUIRE(variant.as() == true); - REQUIRE(variant.as() == 42.0); - REQUIRE(variant.as() == 42); - REQUIRE(variant.as() == 42U); // issue #1601 - REQUIRE(variant.as() == "42"); + REQUIRE(variant.as() == 4294967295.0); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == 4294967295U); + REQUIRE(variant.as() == 4294967295U); + REQUIRE(variant.as() == "4294967295"); REQUIRE(variant.as().isNull()); REQUIRE(variant.as().data() == nullptr); REQUIRE(variant.as().data() == nullptr); } - SECTION("set(42L)") { - variant.set(42L); + SECTION("set(int32_t)") { + variant.set(-2147483648LL); REQUIRE(variant.as() == true); - REQUIRE(variant.as() == 42.0); - REQUIRE(variant.as() == "42"); + REQUIRE(variant.as() == -2147483648LL); + REQUIRE(variant.as() == -2147483648LL); + REQUIRE(variant.as() == -2147483648LL); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == "-2147483648"); REQUIRE(variant.as().isNull()); + REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } - SECTION("set(-42L)") { - variant.set(-42L); - - REQUIRE(variant.as() == -42.0); - REQUIRE(variant.as() == "-42"); - REQUIRE(variant.as().isNull()); - } - - SECTION("set(42UL)") { - variant.set(42UL); + SECTION("set(uint64_t)") { + variant.set(4294967296U); REQUIRE(variant.as() == true); - REQUIRE(variant.as() == 42.0); - REQUIRE(variant.as() == "42"); + REQUIRE(variant.as() == 4294967296.0); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == 4294967296U); + REQUIRE(variant.as() == "4294967296"); REQUIRE(variant.as().isNull()); + REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); + } + + SECTION("set(int64_t)") { + variant.set(-2147483649LL); + + REQUIRE(variant.as() == true); + REQUIRE(variant.as() == -2147483649LL); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == -2147483649LL); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == 0); + REQUIRE(variant.as() == "-2147483649"); + REQUIRE(variant.as().isNull()); + REQUIRE(variant.as().data() == nullptr); + REQUIRE(variant.as().data() == nullptr); } SECTION("set(0L)") { @@ -169,7 +203,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == true); REQUIRE(variant.as() == 4L); - REQUIRE(variant.as() == 4.2); + REQUIRE(variant.as() == Approx(4.2)); REQUIRE(variant.as() == "4.2"_s); REQUIRE(variant.as() == "4.2"_s); REQUIRE(variant.as() == "4.2"); diff --git a/lib/ArduinoJson/extras/tests/JsonVariant/set.cpp b/lib/ArduinoJson/extras/tests/JsonVariant/set.cpp index e47c2b4..b10f950 100644 --- a/lib/ArduinoJson/extras/tests/JsonVariant/set.cpp +++ b/lib/ArduinoJson/extras/tests/JsonVariant/set.cpp @@ -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(); 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() == true); REQUIRE(variant.as() == 10); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("float") { + bool result = variant.set(1.2f); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 1.2f); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("double") { + bool result = variant.set(1.2); + doc.shrinkToFit(); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 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() == true); + REQUIRE(variant.as() == 42); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("int64_t") { + bool result = variant.set(int64_t(-2147483649LL)); + doc.shrinkToFit(); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == -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() == true); + REQUIRE(variant.as() == 42); + REQUIRE(spy.log() == AllocatorLog{}); + } + + SECTION("uint64_t") { + bool result = variant.set(uint64_t(4294967296)); + doc.shrinkToFit(); + + REQUIRE(result == true); + REQUIRE(variant.is() == true); + REQUIRE(variant.as() == 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()); } -} -TEST_CASE("JsonVariant::set(JsonDocument)") { - JsonDocument doc1; - doc1["hello"] = "world"; + SECTION("float") { + bool result = v.set(1.2f); - JsonDocument doc2; - JsonVariant v = doc2.to(); + REQUIRE(result == true); + REQUIRE(v.is()); + } - // Should copy the doc - v.set(doc1); - doc1.clear(); + SECTION("double") { + bool result = v.set(1.2); - std::string json; - serializeJson(doc2, json); - REQUIRE(json == "{\"hello\":\"world\"}"); + REQUIRE(result == false); + REQUIRE(v.isNull()); + } + + SECTION("int32_t") { + bool result = v.set(-42); + + REQUIRE(result == true); + REQUIRE(v.is()); + } + + 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()); + } + + 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(); + + 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{}); + } +} diff --git a/lib/ArduinoJson/extras/tests/JsonVariant/unbound.cpp b/lib/ArduinoJson/extras/tests/JsonVariant/unbound.cpp index ff93625..0f1740b 100644 --- a/lib/ArduinoJson/extras/tests/JsonVariant/unbound.cpp +++ b/lib/ArduinoJson/extras/tests/JsonVariant/unbound.cpp @@ -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"); diff --git a/lib/ArduinoJson/extras/tests/JsonVariantConst/CMakeLists.txt b/lib/ArduinoJson/extras/tests/JsonVariantConst/CMakeLists.txt index d0dc65e..b943f16 100644 --- a/lib/ArduinoJson/extras/tests/JsonVariantConst/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/JsonVariantConst/CMakeLists.txt @@ -4,7 +4,6 @@ add_executable(JsonVariantConstTests as.cpp - containsKey.cpp is.cpp isnull.cpp nesting.cpp diff --git a/lib/ArduinoJson/extras/tests/Misc/CMakeLists.txt b/lib/ArduinoJson/extras/tests/Misc/CMakeLists.txt index 2fdc613..82f1a0c 100644 --- a/lib/ArduinoJson/extras/tests/Misc/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/Misc/CMakeLists.txt @@ -5,7 +5,6 @@ add_executable(MiscTests arithmeticCompare.cpp conflicts.cpp - FloatParts.cpp issue1967.cpp JsonString.cpp NoArduinoHeader.cpp diff --git a/lib/ArduinoJson/extras/tests/Misc/unsigned_char.cpp b/lib/ArduinoJson/extras/tests/Misc/unsigned_char.cpp index fd780d6..a1bb155 100644 --- a/lib/ArduinoJson/extras/tests/Misc/unsigned_char.cpp +++ b/lib/ArduinoJson/extras/tests/Misc/unsigned_char.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(); - REQUIRE(true == obj.containsKey(key)); - } - SECTION("remove()") { unsigned char key[] = "hello"; diff --git a/lib/ArduinoJson/extras/tests/MixedConfiguration/use_double_0.cpp b/lib/ArduinoJson/extras/tests/MixedConfiguration/use_double_0.cpp index 8752890..6958bc0 100644 --- a/lib/ArduinoJson/extras/tests/MixedConfiguration/use_double_0.cpp +++ b/lib/ArduinoJson/extras/tests/MixedConfiguration/use_double_0.cpp @@ -3,15 +3,65 @@ #include -TEST_CASE("ARDUINOJSON_USE_DOUBLE == 0") { - JsonDocument doc; - JsonObject root = doc.to(); +namespace my { +using ArduinoJson::detail::isinf; +} // namespace my - root["pi"] = 3.14; - root["e"] = 2.72; - - std::string json; - serializeJson(doc, json); - - REQUIRE(json == "{\"pi\":3.14,\"e\":2.72}"); +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(); + + root["pi"] = 3.14; + root["e"] = 2.72; + + std::string json; + serializeJson(doc, json); + + 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); + } + } } diff --git a/lib/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_0.cpp b/lib/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_0.cpp index 2781a76..16c07db 100644 --- a/lib/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_0.cpp +++ b/lib/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_0.cpp @@ -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() == 0x100000000); -#else REQUIRE(doc.isNull()); -#endif } } } diff --git a/lib/ArduinoJson/extras/tests/MsgPackDeserializer/errors.cpp b/lib/ArduinoJson/extras/tests/MsgPackDeserializer/errors.cpp index 01e761a..dff712a 100644 --- a/lib/ArduinoJson/extras/tests/MsgPackDeserializer/errors.cpp +++ b/lib/ArduinoJson/extras/tests/MsgPackDeserializer/errors.cpp @@ -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); + } +} diff --git a/lib/ArduinoJson/extras/tests/Numbers/CMakeLists.txt b/lib/ArduinoJson/extras/tests/Numbers/CMakeLists.txt index c3d6dc0..e4ef2dd 100644 --- a/lib/ArduinoJson/extras/tests/Numbers/CMakeLists.txt +++ b/lib/ArduinoJson/extras/tests/Numbers/CMakeLists.txt @@ -4,8 +4,9 @@ add_executable(NumbersTests convertNumber.cpp - parseFloat.cpp + decomposeFloat.cpp parseDouble.cpp + parseFloat.cpp parseInteger.cpp parseNumber.cpp ) diff --git a/lib/ArduinoJson/extras/tests/Numbers/decomposeFloat.cpp b/lib/ArduinoJson/extras/tests/Numbers/decomposeFloat.cpp new file mode 100644 index 0000000..73de571 --- /dev/null +++ b/lib/ArduinoJson/extras/tests/Numbers/decomposeFloat.cpp @@ -0,0 +1,42 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#include +#include + +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); + } +} diff --git a/lib/ArduinoJson/extras/tests/Numbers/parseFloat.cpp b/lib/ArduinoJson/extras/tests/Numbers/parseFloat.cpp index 4a42f44..f81428f 100644 --- a/lib/ArduinoJson/extras/tests/Numbers/parseFloat.cpp +++ b/lib/ArduinoJson/extras/tests/Numbers/parseFloat.cpp @@ -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(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()") { 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()") { checkFloatInf("inf", false); checkFloatInf("+inf", false); checkFloatInf("-inf", true); - - checkFloatInf("1e300", false); - checkFloatInf("-1e300", true); } } diff --git a/lib/ArduinoJson/extras/tests/Numbers/parseNumber.cpp b/lib/ArduinoJson/extras/tests/Numbers/parseNumber.cpp index ba9412f..bc5d3b3 100644 --- a/lib/ArduinoJson/extras/tests/Numbers/parseNumber.cpp +++ b/lib/ArduinoJson/extras/tests/Numbers/parseNumber.cpp @@ -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); } diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/StringBuilder.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/StringBuilder.cpp index f839858..b649afe 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/StringBuilder.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/StringBuilder.cpp @@ -3,7 +3,6 @@ // MIT License #include -#include #include #include "Allocators.hpp" diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/allocVariant.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/allocVariant.cpp index 4a3685d..3cfadf3 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/allocVariant.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/allocVariant.cpp @@ -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(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(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(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(slot) == nullptr); + REQUIRE(slot.ptr() == nullptr); } REQUIRE(resources.overflowed() == true); diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/clear.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/clear.cpp index 30afd90..a773603 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/clear.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/clear.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #include #include @@ -13,7 +13,7 @@ TEST_CASE("ResourceManager::clear()") { ResourceManager resources; SECTION("Discards allocated variants") { - resources.allocSlot(); + resources.allocVariant(); resources.clear(); REQUIRE(resources.size() == 0); diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/saveString.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/saveString.cpp index f19ac19..88c242f 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/saveString.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/saveString.cpp @@ -3,7 +3,6 @@ // MIT License #include -#include #include #include diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/shrinkToFit.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/shrinkToFit.cpp index 7419770..d35ee34 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/shrinkToFit.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/shrinkToFit.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #include #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)), }); diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/size.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/size.cpp index 6b09b17..8d4b799 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/size.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/size.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #include #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()); } diff --git a/lib/ArduinoJson/extras/tests/ResourceManager/swap.cpp b/lib/ArduinoJson/extras/tests/ResourceManager/swap.cpp index 4fdb45c..a2b1fe9 100644 --- a/lib/ArduinoJson/extras/tests/ResourceManager/swap.cpp +++ b/lib/ArduinoJson/extras/tests/ResourceManager/swap.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #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())); } } diff --git a/lib/ArduinoJson/idf_component.yml b/lib/ArduinoJson/idf_component.yml index 2f504e4..bdf8d36 100644 --- a/lib/ArduinoJson/idf_component.yml +++ b/lib/ArduinoJson/idf_component.yml @@ -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/ diff --git a/lib/ArduinoJson/library.json b/lib/ArduinoJson/library.json index e1c712a..30da9fc 100644 --- a/lib/ArduinoJson/library.json +++ b/lib/ArduinoJson/library.json @@ -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" diff --git a/lib/ArduinoJson/library.properties b/lib/ArduinoJson/library.properties index 9a59308..86866c0 100644 --- a/lib/ArduinoJson/library.properties +++ b/lib/ArduinoJson/library.properties @@ -1,9 +1,9 @@ name=ArduinoJson -version=7.1.0 +version=7.2.0 author=Benoit Blanchon maintainer=Benoit Blanchon 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=* diff --git a/lib/ArduinoJson/src/ArduinoJson.hpp b/lib/ArduinoJson/src/ArduinoJson.hpp index 085507a..a14f2b1 100644 --- a/lib/ArduinoJson/src/ArduinoJson.hpp +++ b/lib/ArduinoJson/src/ArduinoJson.hpp @@ -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" diff --git a/lib/ArduinoJson/src/ArduinoJson/Array/ArrayData.hpp b/lib/ArduinoJson/src/ArduinoJson/Array/ArrayData.hpp index 7e15ab1..c2ae1fb 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Array/ArrayData.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Array/ArrayData.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, - ResourceManager* resources) { - if (!dst || !src) - return false; - - return dst->copyFrom(*src, resources); + static void remove(ArrayData* array, iterator it, + ResourceManager* resources) { + if (array) + return array->remove(it, resources); } private: diff --git a/lib/ArduinoJson/src/ArduinoJson/Array/ArrayImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Array/ArrayImpl.hpp index 9b96138..9599a5e 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Array/ArrayImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Array/ArrayImpl.hpp @@ -6,6 +6,7 @@ #include #include +#include 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 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(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 diff --git a/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp b/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp index 07e6982..5817c7a 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -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(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 slot, const ResourceManager* resources); + void appendPair(Slot key, Slot value, + const ResourceManager* resources); + + void removeOne(iterator it, ResourceManager* resources); + void removePair(iterator it, ResourceManager* resources); private: - SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; + Slot getPreviousSlot(VariantData*, const ResourceManager*) const; }; inline const VariantData* collectionToVariant( diff --git a/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp index c7e23a3..c0a650c 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -12,66 +12,48 @@ 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 {}; +inline CollectionData::iterator CollectionData::createIterator( + const ResourceManager* resources) const { + return iterator(resources->getVariant(head_), head_); +} + +inline void CollectionData::appendOne(Slot 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 { head_ = slot.id(); tail_ = slot.id(); } - return iterator(slot, slot.id()); } -inline void CollectionData::addSlot(SlotWithId slot, - ResourceManager* resources) { +inline void CollectionData::appendPair(Slot key, + Slot value, + const ResourceManager* resources) { + key->setNext(value.id()); + if (tail_ != NULL_SLOT) { - auto tail = resources->getSlot(tail_); - tail->setNext(slot.id()); - tail_ = slot.id(); + auto tail = resources->getVariant(tail_); + tail->setNext(key.id()); + tail_ = value.id(); } else { - head_ = slot.id(); - tail_ = slot.id(); + head_ = key.id(); + tail_ = value.id(); } } @@ -79,30 +61,30 @@ 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 CollectionData::getPreviousSlot( + VariantData* target, const ResourceManager* resources) const { + auto prev = Slot(); 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(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 { diff --git a/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp b/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp index ae60890..cf6fb38 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp @@ -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 diff --git a/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp b/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp index acad2b2..5ba62f4 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp @@ -150,24 +150,27 @@ class JsonDocument : public detail::VariantOperators { return getVariant().template to(); } - // Returns true if the root object contains the specified key. + // DEPRECATED: use obj["key"].is() instead // https://arduinojson.org/v7/api/jsondocument/containskey/ template + ARDUINOJSON_DEPRECATED("use doc[\"key\"].is() 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() instead // https://arduinojson.org/v7/api/jsondocument/containskey/ template + ARDUINOJSON_DEPRECATED("use doc[key].is() instead") detail::enable_if_t::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() instead // https://arduinojson.org/v7/api/jsondocument/containskey/ template + ARDUINOJSON_DEPRECATED("use doc[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp index e1b7199..35a99d6 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp @@ -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]; } }; diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp index 33de891..459f78a 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -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; - return DeserializationError::Ok; + 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() { diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp index 189ae33..f9cfa59 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp @@ -25,9 +25,9 @@ class JsonSerializer : public VariantDataVisitor { 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 { 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 + enable_if_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 -size_t serializeJson(JsonVariantConst source, TDestination& destination) { +detail::enable_if_t::value, size_t> +serializeJson(JsonVariantConst source, TDestination& destination) { using namespace detail; return serialize(source, destination); } diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp index ab049ad..9ae0367 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp @@ -26,7 +26,7 @@ class PrettyJsonSerializer : public JsonSerializer { 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 { if (!it.done()) { base::write("{\r\n"); nesting_++; + bool isKey = true; while (!it.done()) { - indent(); - base::visit(it.key()); - base::write(": "); - it->accept(*this); - + if (isKey) + indent(); + it->accept(*this, base::resources_); it.next(base::resources_); - base::write(it.done() ? "\r\n" : ",\r\n"); + 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 -size_t serializeJsonPretty(JsonVariantConst source, TDestination& destination) { +detail::enable_if_t::value, size_t> +serializeJsonPretty(JsonVariantConst source, TDestination& destination) { using namespace ArduinoJson::detail; return serialize(source, destination); } diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp index 5b13222..5b94b5e 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp @@ -66,6 +66,10 @@ class TextFormatter { template 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 parts(value); + auto parts = decomposeFloat(value, decimalPlaces); writeInteger(parts.integral); if (parts.decimalPlaces) diff --git a/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp b/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp new file mode 100644 index 0000000..baa988f --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp @@ -0,0 +1,110 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include +#include +#include + +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE + +using SlotId = uint_t; +using SlotCount = SlotId; +const SlotId NULL_SLOT = SlotId(-1); + +template +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 +class MemoryPool { + public: + void create(SlotCount cap, Allocator* allocator) { + ARDUINOJSON_ASSERT(cap > 0); + slots_ = reinterpret_cast(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 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( + 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(n / sizeof(T)); + } + + static size_t slotsToBytes(SlotCount n) { + return n * sizeof(T); + } + + private: + SlotCount capacity_; + SlotCount usage_; + T* slots_; +}; + +ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPoolList.hpp b/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPoolList.hpp new file mode 100644 index 0000000..dc6503c --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPoolList.hpp @@ -0,0 +1,214 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include +#include +#include + +#include // memcpy + +ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE + +using PoolCount = SlotId; + +template +class MemoryPoolList { + struct FreeSlot { + SlotId next; + }; + + static_assert(sizeof(FreeSlot) <= sizeof(T), "T is too small"); + + public: + using Pool = MemoryPool; + + 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 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 slot) { + reinterpret_cast(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( + allocator->reallocate(pools_, count_ * sizeof(Pool))); + ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail + capacity_ = count_; + } + } + + private: + Slot allocFromFreeList() { + ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT); + auto id = freeList_; + auto slot = getSlot(freeList_); + freeList_ = reinterpret_cast(slot)->next; + return {slot, id}; + } + + Slot 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(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 diff --git a/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManager.hpp b/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManager.hpp index 2270e4f..c8e0d5b 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManager.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManager.hpp @@ -5,19 +5,29 @@ #pragma once #include +#include #include -#include #include #include #include +#include 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 allocVariant(); + void freeVariant(Slot slot); + VariantData* getVariant(SlotId id) const; - void freeSlot(SlotWithId slot); - - VariantSlot* getSlot(SlotId id) const { - return variantPools_.getSlot(id); - } +#if ARDUINOJSON_USE_EXTENSIONS + Slot allocExtension(); + void freeExtension(SlotId slot); + VariantExtension* getExtension(SlotId id) const; +#endif template StringNode* saveString(TAdaptedString str) { @@ -119,7 +125,7 @@ class ResourceManager { Allocator* allocator_; bool overflowed_; StringPool stringPool_; - VariantPoolList variantPools_; + MemoryPoolList variantPools_; }; ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManagerImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManagerImpl.hpp index 136dc2d..427c9ad 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManagerImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Memory/ResourceManagerImpl.hpp @@ -6,15 +6,47 @@ #include #include +#include #include 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 ResourceManager::allocVariant() { + auto p = variantPools_.allocSlot(allocator_); + if (!p) { + overflowed_ = true; + return {}; + } + return {new (&p->variant) VariantData, p.id()}; } +inline void ResourceManager::freeVariant(Slot variant) { + variant->clear(this); + variantPools_.freeSlot({alias_cast(variant.ptr()), variant.id()}); +} + +inline VariantData* ResourceManager::getVariant(SlotId id) const { + return reinterpret_cast(variantPools_.getSlot(id)); +} + +#if ARDUINOJSON_USE_EXTENSIONS +inline Slot 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(p), id}); +} + +inline VariantExtension* ResourceManager::getExtension(SlotId id) const { + return &variantPools_.getSlot(id)->extension; +} +#endif + ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Memory/StringPool.hpp b/lib/ArduinoJson/src/ArduinoJson/Memory/StringPool.hpp index dcdc98b..6121b29 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Memory/StringPool.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Memory/StringPool.hpp @@ -12,9 +12,6 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -class VariantSlot; -class VariantPool; - class StringPool { public: StringPool() = default; diff --git a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackBinary.hpp b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackBinary.hpp index bd275d4..2c0f6b7 100644 --- a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackBinary.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackBinary.hpp @@ -29,6 +29,7 @@ struct Converter : 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 : private detail::VariantAttorney { return; } } - data->setNull(); } static MsgPackBinary fromJson(JsonVariantConst src) { diff --git a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 0c80dde..0c32f58 100644 --- a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -91,7 +91,7 @@ class MsgPackDeserializer { if (code <= 0x7f || code >= 0xe0) { // fixint if (allowValue) - variant->setInteger(static_cast(code)); + variant->setInteger(static_cast(code), resources_); return DeserializationError::Ok; } @@ -230,13 +230,16 @@ class MsgPackDeserializer { if (isSigned) { auto truncatedValue = static_cast(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(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); - - return DeserializationError::Ok; + if (variant->setFloat(value, resources_)) + return DeserializationError::Ok; + else + return DeserializationError::NoMemory; } template @@ -289,7 +293,7 @@ class MsgPackDeserializer { doubleToFloat(i, o); fixEndianness(value); - variant->setFloat(value); + variant->setFloat(value, resources_); return DeserializationError::Ok; } diff --git a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackExtension.hpp b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackExtension.hpp index 5be4e50..b51eb30 100644 --- a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackExtension.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackExtension.hpp @@ -35,6 +35,7 @@ struct Converter : 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 : private detail::VariantAttorney { return; } } - data->setNull(); } static MsgPackExtension fromJson(JsonVariantConst src) { diff --git a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index ce5f66a..3058ad8 100644 --- a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -61,8 +61,8 @@ class MsgPackSerializer : public VariantDataVisitor { 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 { 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 -inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) { +detail::enable_if_t::value, size_t> +serializeMsgPack(JsonVariantConst source, TDestination& output) { using namespace ArduinoJson::detail; return serialize(source, output); } diff --git a/lib/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp b/lib/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp index 0a39a7c..0affe06 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp @@ -6,83 +6,90 @@ #include #include +#include #include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE -template 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) { - typedef FloatTraits traits; - int16_t powersOf10 = 0; - - int8_t index = sizeof(TFloat) == 8 ? 8 : 5; - int bit = 1 << index; - - if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { - for (; index >= 0; index--) { - if (value >= traits::positiveBinaryPowersOfTen()[index]) { - value *= traits::negativeBinaryPowersOfTen()[index]; - powersOf10 = int16_t(powersOf10 + bit); - } - bit >>= 1; - } - } - - if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { - for (; index >= 0; index--) { - if (value < traits::negativeBinaryPowersOfTen()[index] * 10) { - value *= traits::positiveBinaryPowersOfTen()[index]; - powersOf10 = int16_t(powersOf10 - bit); - } - bit >>= 1; - } - } - - return powersOf10; - } }; +template +inline int16_t normalize(TFloat& value) { + typedef FloatTraits traits; + int16_t powersOf10 = 0; + + int8_t index = sizeof(TFloat) == 8 ? 8 : 5; + int bit = 1 << index; + + if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value >= traits::positiveBinaryPowersOfTen()[index]) { + value *= traits::negativeBinaryPowersOfTen()[index]; + powersOf10 = int16_t(powersOf10 + bit); + } + bit >>= 1; + } + } + + if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { + for (; index >= 0; index--) { + if (value < traits::negativeBinaryPowersOfTen()[index] * 10) { + value *= traits::positiveBinaryPowersOfTen()[index]; + powersOf10 = int16_t(powersOf10 - bit); + } + bit >>= 1; + } + } + + 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 diff --git a/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp b/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp index 0136278..0b6ffdc 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp @@ -121,10 +121,21 @@ canConvertNumber(TIn value) { value <= FloatTraits::template highest_for(); } +// float32 -> float32 +// float64 -> float64 +// float64 -> float32 +template +enable_if_t::value && is_floating_point::value, + bool> +canConvertNumber(TIn) { + return true; +} + template TOut convertNumber(TIn value) { return canConvertNumber(value) ? TOut(value) : 0; } + ARDUINOJSON_END_PRIVATE_NAMESPACE #if defined(__clang__) diff --git a/lib/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp b/lib/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp index 7fb3cba..2df8cdc 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp @@ -5,20 +5,104 @@ #pragma once #include +#include #include #include #include #include #include -#include -#include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template 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 + T convertTo() const { + switch (type_) { + case NumberType::Float: + return convertNumber(value_.asFloat); + case NumberType::SignedInteger: + return convertNumber(value_.asSignedInteger); + case NumberType::UnsignedInteger: + return convertNumber(value_.asUnsignedInteger); +#if ARDUINOJSON_USE_DOUBLE + case NumberType::Double: + return convertNumber(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 traits; typedef largest_type 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(mantissa), exponent); - - result.setFloat(is_negative ? -final_result : final_result); - return true; +#if ARDUINOJSON_USE_DOUBLE + bool isDouble = exponent < -FloatTraits::exponent_max || + exponent > FloatTraits::exponent_max || + mantissa > FloatTraits::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 inline T parseNumber(const char* s) { - VariantData value; - parseNumber(s, value); - return Converter::fromJson(JsonVariantConst(&value, nullptr)); + return parseNumber(s).convertTo(); } + ARDUINOJSON_END_PRIVATE_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp index a11af74..2eeeed3 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp @@ -161,27 +161,30 @@ class JsonObject : public detail::VariantOperators { resources_); } - // Returns true if the object contains the specified key. + // DEPRECATED: use obj[key].is() instead // https://arduinojson.org/v7/api/jsonobject/containskey/ template + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::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() instead // https://arduinojson.org/v7/api/jsonobject/containskey/ template + ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() instead") detail::enable_if_t::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() instead // https://arduinojson.org/v7/api/jsonobject/containskey/ template + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp index 81132e2..3af7d12 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp @@ -68,26 +68,29 @@ class JsonObjectConst : public detail::VariantOperators { return iterator(); } - // Returns true if the object contains the specified key. + // DEPRECATED: use obj[key].is() instead // https://arduinojson.org/v7/api/jsonobjectconst/containskey/ template + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::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() instead // https://arduinojson.org/v7/api/jsonobjectconst/containskey/ template + ARDUINOJSON_DEPRECATED("use obj[\"key\"].is() 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() instead // https://arduinojson.org/v7/api/jsonobjectconst/containskey/ template + ARDUINOJSON_DEPRECATED("use obj[key].is() instead") detail::enable_if_t::value, bool> containsKey( const TVariant& key) const { return containsKey(key.template as()); diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp index b849e62..74f75bf 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp @@ -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; } diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp index 4d621a1..923bd43 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp @@ -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 diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/ObjectData.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/ObjectData.hpp index 40e743e..7290e93 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/ObjectData.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/ObjectData.hpp @@ -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 - 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 // also works with StringNode* + VariantData* addMember(TAdaptedString key, ResourceManager* resources); template 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 iterator findKey(TAdaptedString key, const ResourceManager* resources) const; diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/ObjectImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/ObjectImpl.hpp index eed39ca..024d772 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/ObjectImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/ObjectImpl.hpp @@ -6,21 +6,26 @@ #include #include +#include ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template 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 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 +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 diff --git a/lib/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp b/lib/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp index fa2c664..fd2acf2 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp @@ -11,9 +11,10 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE template