From 702f8c2e2f6b317dbc4894e2b480ddf44c5cf207 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sun, 6 Feb 2022 11:14:23 +0100 Subject: [PATCH 001/202] Fix `cannot convert 'pgm_p' to 'const void*'` (fixes #1707) --- CHANGELOG.md | 5 +++++ extras/tests/MixedConfiguration/CMakeLists.txt | 1 + extras/tests/MixedConfiguration/issue1707.cpp | 17 +++++++++++++++++ src/ArduinoJson/Polyfills/pgmspace.hpp | 4 ++-- 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 extras/tests/MixedConfiguration/issue1707.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 47dff0bf0..337ca255e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Fix `cannot convert 'pgm_p' to 'const void*'` (issue #1707) + v6.19.1 (2022-01-14) ------- diff --git a/extras/tests/MixedConfiguration/CMakeLists.txt b/extras/tests/MixedConfiguration/CMakeLists.txt index a0b10b7c5..01d1216e3 100644 --- a/extras/tests/MixedConfiguration/CMakeLists.txt +++ b/extras/tests/MixedConfiguration/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(MixedConfigurationTests enable_progmem_1.cpp enable_string_deduplication_0.cpp enable_string_deduplication_1.cpp + issue1707.cpp use_double_0.cpp use_double_1.cpp ) diff --git a/extras/tests/MixedConfiguration/issue1707.cpp b/extras/tests/MixedConfiguration/issue1707.cpp new file mode 100644 index 000000000..befdee536 --- /dev/null +++ b/extras/tests/MixedConfiguration/issue1707.cpp @@ -0,0 +1,17 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2022, Benoit BLANCHON +// MIT License + +#define ARDUINO +#define memcpy_P(dest, src, n) memcpy((dest), (src), (n)) + +#include + +#include + +TEST_CASE("Issue1707") { + StaticJsonDocument<128> doc; + + DeserializationError err = deserializeJson(doc, F("{\"hello\":12}")); + REQUIRE(err == DeserializationError::Ok); +} diff --git a/src/ArduinoJson/Polyfills/pgmspace.hpp b/src/ArduinoJson/Polyfills/pgmspace.hpp index 189251705..afef4ce9d 100644 --- a/src/ArduinoJson/Polyfills/pgmspace.hpp +++ b/src/ArduinoJson/Polyfills/pgmspace.hpp @@ -99,7 +99,7 @@ inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) { #ifndef pgm_read_dword inline uint32_t pgm_read_dword(ARDUINOJSON_NAMESPACE::pgm_p p) { uint32_t result; - memcpy_P(&result, p, 4); + memcpy_P(&result, p.address, 4); return result; } #endif @@ -107,7 +107,7 @@ inline uint32_t pgm_read_dword(ARDUINOJSON_NAMESPACE::pgm_p p) { #ifndef pgm_read_ptr inline void* pgm_read_ptr(ARDUINOJSON_NAMESPACE::pgm_p p) { void* result; - memcpy_P(&result, p, sizeof(result)); + memcpy_P(&result, p.address, sizeof(result)); return result; } #endif From ef8379df1ba717e437a35a512d2e0eb1595910b7 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 14 Feb 2022 09:06:06 +0100 Subject: [PATCH 002/202] Set version to 6.19.2 --- CHANGELOG.md | 4 ++-- CMakeLists.txt | 2 +- README.md | 4 ++-- appveyor.yml | 2 +- library.json | 2 +- library.properties | 2 +- src/ArduinoJson/version.hpp | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 337ca255e..f49c949de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ ArduinoJson: change log ======================= -HEAD ----- +v6.19.2 (2022-02-14) +------- * Fix `cannot convert 'pgm_p' to 'const void*'` (issue #1707) diff --git a/CMakeLists.txt b/CMakeLists.txt index 72efc5d8c..d5a32a9f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(ESP_PLATFORM) return() endif() -project(ArduinoJson VERSION 6.19.1) +project(ArduinoJson VERSION 6.19.2) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) diff --git a/README.md b/README.md index 78436e28d..bf1ba1118 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) [![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/bblanchon/ArduinoJson?label=quality&logo=lgtm)](https://lgtm.com/projects/g/bblanchon/ArduinoJson/) [![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x) -[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.19.1&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.19.1) -[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.19.1)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.19.1) +[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.19.2&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.19.2) +[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.19.2)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.19.2) [![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github)](https://github.com/bblanchon/ArduinoJson/stargazers) [![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github)](https://github.com/sponsors/bblanchon) diff --git a/appveyor.yml b/appveyor.yml index d5cd90203..a4c35eef1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 6.19.1.{build} +version: 6.19.2.{build} environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 diff --git a/library.json b/library.json index c94d54f49..a982484a7 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/bblanchon/ArduinoJson.git" }, - "version": "6.19.1", + "version": "6.19.2", "authors": { "name": "Benoit Blanchon", "url": "https://blog.benoitblanchon.fr" diff --git a/library.properties b/library.properties index 93435203b..fa4790ce8 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ArduinoJson -version=6.19.1 +version=6.19.2 author=Benoit Blanchon maintainer=Benoit Blanchon sentence=A simple and efficient JSON library for embedded C++. diff --git a/src/ArduinoJson/version.hpp b/src/ArduinoJson/version.hpp index 0b4b5febf..5beae8aff 100644 --- a/src/ArduinoJson/version.hpp +++ b/src/ArduinoJson/version.hpp @@ -4,7 +4,7 @@ #pragma once -#define ARDUINOJSON_VERSION "6.19.1" +#define ARDUINOJSON_VERSION "6.19.2" #define ARDUINOJSON_VERSION_MAJOR 6 #define ARDUINOJSON_VERSION_MINOR 19 -#define ARDUINOJSON_VERSION_REVISION 1 +#define ARDUINOJSON_VERSION_REVISION 2 From a880614a7501cd00871b71750a65b1c3da165047 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 17 Feb 2022 10:47:42 +0100 Subject: [PATCH 003/202] Fix `call of overloaded 'String(const char*, int)' is ambiguous` --- CHANGELOG.md | 5 +++++ extras/tests/JsonObject/subscript.cpp | 4 ++-- extras/tests/JsonVariant/as.cpp | 4 ++-- extras/tests/JsonVariant/set.cpp | 4 ++-- extras/tests/Misc/JsonString.cpp | 9 ++++++++- src/ArduinoJson/Collection/CollectionImpl.hpp | 2 +- src/ArduinoJson/Object/Pair.hpp | 6 ++++-- .../StringStorage/StringCopier.hpp | 4 ++-- src/ArduinoJson/StringStorage/StringMover.hpp | 2 +- src/ArduinoJson/Strings/StoragePolicy.hpp | 4 ++-- src/ArduinoJson/Strings/String.hpp | 20 +++++++++---------- src/ArduinoJson/Variant/ConverterImpl.hpp | 2 +- src/ArduinoJson/Variant/VariantData.hpp | 2 +- src/ArduinoJson/Variant/VariantImpl.hpp | 8 +++++--- src/ArduinoJson/Variant/VariantSlot.hpp | 2 +- 15 files changed, 47 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f49c949de..d67a779e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Fix `call of overloaded 'String(const char*, int)' is ambiguous` + v6.19.2 (2022-02-14) ------- diff --git a/extras/tests/JsonObject/subscript.cpp b/extras/tests/JsonObject/subscript.cpp index b57924910..aa7dde358 100644 --- a/extras/tests/JsonObject/subscript.cpp +++ b/extras/tests/JsonObject/subscript.cpp @@ -141,13 +141,13 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("should duplicate a non-static JsonString key") { - obj[JsonString("hello", false)] = "world"; + obj[JsonString("hello", JsonString::Copied)] = "world"; const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5); REQUIRE(expectedSize == doc.memoryUsage()); } SECTION("should not duplicate a static JsonString key") { - obj[JsonString("hello", true)] = "world"; + obj[JsonString("hello", JsonString::Linked)] = "world"; const size_t expectedSize = JSON_OBJECT_SIZE(1); REQUIRE(expectedSize == doc.memoryUsage()); } diff --git a/extras/tests/JsonVariant/as.cpp b/extras/tests/JsonVariant/as.cpp index 455b8d81a..c9956778b 100644 --- a/extras/tests/JsonVariant/as.cpp +++ b/extras/tests/JsonVariant/as.cpp @@ -136,7 +136,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == 42L); REQUIRE(variant.as() == "42"); - REQUIRE(variant.as().isStatic() == true); + REQUIRE(variant.as().isLinked() == true); } SECTION("set(\"hello\")") { @@ -159,7 +159,7 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == std::string("4.2")); REQUIRE(variant.as() == std::string("4.2")); REQUIRE(variant.as() == "4.2"); - REQUIRE(variant.as().isStatic() == false); + REQUIRE(variant.as().isLinked() == false); } SECTION("set(\"true\")") { diff --git a/extras/tests/JsonVariant/set.cpp b/extras/tests/JsonVariant/set.cpp index f0bfba398..eba15d435 100644 --- a/extras/tests/JsonVariant/set.cpp +++ b/extras/tests/JsonVariant/set.cpp @@ -98,7 +98,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { char str[16]; strcpy(str, "hello"); - bool result = variant.set(JsonString(str, true)); + bool result = variant.set(JsonString(str, JsonString::Linked)); strcpy(str, "world"); REQUIRE(result == true); @@ -109,7 +109,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { char str[16]; strcpy(str, "hello"); - bool result = variant.set(JsonString(str, false)); + bool result = variant.set(JsonString(str, JsonString::Copied)); strcpy(str, "world"); REQUIRE(result == true); diff --git a/extras/tests/Misc/JsonString.cpp b/extras/tests/Misc/JsonString.cpp index 10d08d366..b7d606fdb 100644 --- a/extras/tests/Misc/JsonString.cpp +++ b/extras/tests/Misc/JsonString.cpp @@ -13,7 +13,7 @@ TEST_CASE("JsonString") { CHECK(s.isNull() == true); CHECK(s.c_str() == 0); - CHECK(s.isStatic() == true); + CHECK(s.isLinked() == true); } SECTION("Compare null with boolean") { @@ -82,4 +82,11 @@ TEST_CASE("JsonString") { ss << JsonString("hello world!"); CHECK(ss.str() == "hello world!"); } + + SECTION("Construct with a size") { + JsonString s("hello world", 5); + + CHECK(s.size() == 5); + CHECK(s.isLinked() == true); + } } diff --git a/src/ArduinoJson/Collection/CollectionImpl.hpp b/src/ArduinoJson/Collection/CollectionImpl.hpp index 60f5cb11b..35678eebf 100644 --- a/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -64,7 +64,7 @@ inline bool CollectionData::copyFrom(const CollectionData& src, for (VariantSlot* s = src._head; s; s = s->next()) { VariantData* var; if (s->key() != 0) { - String key(s->key(), !s->ownsKey()); + String key(s->key(), s->ownsKey() ? String::Copied : String::Linked); var = addMember(adaptString(key), pool, getStringStoragePolicy(key)); } else { var = addElement(pool); diff --git a/src/ArduinoJson/Object/Pair.hpp b/src/ArduinoJson/Object/Pair.hpp index e9e71eb63..7e3ca32c4 100644 --- a/src/ArduinoJson/Object/Pair.hpp +++ b/src/ArduinoJson/Object/Pair.hpp @@ -13,7 +13,8 @@ class Pair { public: Pair(MemoryPool* pool, VariantSlot* slot) { if (slot) { - _key = String(slot->key(), !slot->ownsKey()); + _key = String(slot->key(), + slot->ownsKey() ? String::Copied : String::Linked); _value = VariantRef(pool, slot->data()); } } @@ -35,7 +36,8 @@ class PairConst { public: PairConst(const VariantSlot* slot) { if (slot) { - _key = String(slot->key(), !slot->ownsKey()); + _key = String(slot->key(), + slot->ownsKey() ? String::Copied : String::Linked); _value = VariantConstRef(slot->data()); } } diff --git a/src/ArduinoJson/StringStorage/StringCopier.hpp b/src/ArduinoJson/StringStorage/StringCopier.hpp index 455a155a1..f743e4ac1 100644 --- a/src/ArduinoJson/StringStorage/StringCopier.hpp +++ b/src/ArduinoJson/StringStorage/StringCopier.hpp @@ -22,7 +22,7 @@ class StringCopier { String save() { ARDUINOJSON_ASSERT(_ptr); ARDUINOJSON_ASSERT(_size < _capacity); // needs room for the terminator - return String(_pool->saveStringFromFreeZone(_size), _size, false); + return String(_pool->saveStringFromFreeZone(_size), _size, String::Copied); } void append(const char* s) { @@ -52,7 +52,7 @@ class StringCopier { ARDUINOJSON_ASSERT(_ptr); ARDUINOJSON_ASSERT(_size < _capacity); _ptr[_size] = 0; - return String(_ptr, _size, false); + return String(_ptr, _size, String::Copied); } private: diff --git a/src/ArduinoJson/StringStorage/StringMover.hpp b/src/ArduinoJson/StringStorage/StringMover.hpp index 4ed7d92e1..f4cd1baef 100644 --- a/src/ArduinoJson/StringStorage/StringMover.hpp +++ b/src/ArduinoJson/StringStorage/StringMover.hpp @@ -33,7 +33,7 @@ class StringMover { String str() const { _writePtr[0] = 0; // terminator - return String(_startPtr, size(), true); + return String(_startPtr, size(), String::Linked); } size_t size() const { diff --git a/src/ArduinoJson/Strings/StoragePolicy.hpp b/src/ArduinoJson/Strings/StoragePolicy.hpp index 2a01f0d5a..d1e138c48 100644 --- a/src/ArduinoJson/Strings/StoragePolicy.hpp +++ b/src/ArduinoJson/Strings/StoragePolicy.hpp @@ -12,7 +12,7 @@ namespace ARDUINOJSON_NAMESPACE { struct LinkStringStoragePolicy { template bool store(TAdaptedString str, MemoryPool *, TCallback callback) { - String storedString(str.data(), str.size(), true); + String storedString(str.data(), str.size(), String::Linked); callback(storedString); return !str.isNull(); } @@ -50,7 +50,7 @@ inline LinkStringStoragePolicy getStringStoragePolicy(const char *) { } inline LinkOrCopyStringStoragePolicy getStringStoragePolicy(const String &s) { - return LinkOrCopyStringStoragePolicy(s.isStatic()); + return LinkOrCopyStringStoragePolicy(s.isLinked()); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Strings/String.hpp b/src/ArduinoJson/Strings/String.hpp index 1c71c67ae..08956f051 100644 --- a/src/ArduinoJson/Strings/String.hpp +++ b/src/ArduinoJson/Strings/String.hpp @@ -14,15 +14,15 @@ namespace ARDUINOJSON_NAMESPACE { class String : public SafeBoolIdom { public: - String() : _data(0), _size(0), _isStatic(true) {} + enum Ownership { Copied, Linked }; - String(const char* data, bool isStaticData = true) - : _data(data), - _size(data ? ::strlen(data) : 0), - _isStatic(isStaticData) {} + String() : _data(0), _size(0), _ownership(Linked) {} - String(const char* data, size_t sz, bool isStaticData = true) - : _data(data), _size(sz), _isStatic(isStaticData) {} + String(const char* data, Ownership ownership = Linked) + : _data(data), _size(data ? ::strlen(data) : 0), _ownership(ownership) {} + + String(const char* data, size_t sz, Ownership ownership = Linked) + : _data(data), _size(sz), _ownership(ownership) {} const char* c_str() const { return _data; @@ -32,8 +32,8 @@ class String : public SafeBoolIdom { return !_data; } - bool isStatic() const { - return _isStatic; + bool isLinked() const { + return _ownership == Linked; } size_t size() const { @@ -75,7 +75,7 @@ class String : public SafeBoolIdom { private: const char* _data; size_t _size; - bool _isStatic; + Ownership _ownership; }; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/ConverterImpl.hpp b/src/ArduinoJson/Variant/ConverterImpl.hpp index a825760c9..baf1187ae 100644 --- a/src/ArduinoJson/Variant/ConverterImpl.hpp +++ b/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -208,7 +208,7 @@ class MemoryPoolPrint : public Print { String str() { ARDUINOJSON_ASSERT(_size < _capacity); - return String(_pool->saveStringFromFreeZone(_size), _size, false); + return String(_pool->saveStringFromFreeZone(_size), _size, String::Copied); } size_t write(uint8_t c) { diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 6f8076323..27128706f 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -210,7 +210,7 @@ class VariantData { void setString(String s) { ARDUINOJSON_ASSERT(s); - if (s.isStatic()) + if (s.isLinked()) setType(VALUE_IS_LINKED_STRING); else setType(VALUE_IS_OWNED_STRING); diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index bd3ce07b7..b5a620873 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -73,9 +73,11 @@ inline T VariantData::asFloat() const { inline String VariantData::asString() const { switch (type()) { case VALUE_IS_LINKED_STRING: - return String(_content.asString.data, _content.asString.size, true); + return String(_content.asString.data, _content.asString.size, + String::Linked); case VALUE_IS_OWNED_STRING: - return String(_content.asString.data, _content.asString.size, false); + return String(_content.asString.data, _content.asString.size, + String::Copied); default: return String(); } @@ -174,7 +176,7 @@ template bool CopyStringStoragePolicy::store(TAdaptedString str, MemoryPool *pool, TCallback callback) { const char *copy = pool->saveString(str); - String storedString(copy, str.size(), false); + String storedString(copy, str.size(), String::Copied); callback(storedString); return copy != 0; } diff --git a/src/ArduinoJson/Variant/VariantSlot.hpp b/src/ArduinoJson/Variant/VariantSlot.hpp index 8f4169cc7..83ff1d78b 100644 --- a/src/ArduinoJson/Variant/VariantSlot.hpp +++ b/src/ArduinoJson/Variant/VariantSlot.hpp @@ -78,7 +78,7 @@ class VariantSlot { void setKey(String k) { ARDUINOJSON_ASSERT(k); - if (k.isStatic()) + if (k.isLinked()) _flags &= VALUE_MASK; else _flags |= OWNED_KEY_BIT; From f831ed395d275dc56418df9f2c9403c4f56e16d1 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 17 Feb 2022 11:16:05 +0100 Subject: [PATCH 004/202] Fix `JsonString` operator `==` and `!=` for non-zero-terminated string --- CHANGELOG.md | 1 + extras/tests/Misc/JsonString.cpp | 39 +++++++++++++++++++----------- src/ArduinoJson/Strings/String.hpp | 12 +++------ 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d67a779e5..4b7a9ef6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ HEAD ---- * Fix `call of overloaded 'String(const char*, int)' is ambiguous` +* Fix `JsonString` operator `==` and `!=` for non-zero-terminated string v6.19.2 (2022-02-14) ------- diff --git a/extras/tests/Misc/JsonString.cpp b/extras/tests/Misc/JsonString.cpp index b7d606fdb..557f99366 100644 --- a/extras/tests/Misc/JsonString.cpp +++ b/extras/tests/Misc/JsonString.cpp @@ -14,44 +14,53 @@ TEST_CASE("JsonString") { CHECK(s.isNull() == true); CHECK(s.c_str() == 0); CHECK(s.isLinked() == true); + CHECK(s == JsonString()); + CHECK(s != ""); } - SECTION("Compare null with boolean") { + SECTION("Null converts to false") { JsonString s; CHECK(bool(s) == false); - CHECK(false == bool(s)); - CHECK(bool(s) != true); - CHECK(true != bool(s)); } - SECTION("Compare non-null with boolean") { - JsonString s("hello"); + SECTION("Empty string converts to true") { + JsonString s(""); + CHECK(bool(s) == true); - CHECK(true == bool(s)); - CHECK(bool(s) != false); - CHECK(false != bool(s)); } - SECTION("Compare null with null") { + SECTION("Non-empty string converts to true") { + JsonString s(""); + + CHECK(bool(s) == true); + } + + SECTION("Null strings equals each others") { JsonString a, b; CHECK(a == b); CHECK_FALSE(a != b); } - SECTION("Compare null with non-null") { - JsonString a(0), b("hello"); + SECTION("Null and empty strings differ") { + JsonString a, b(""); CHECK_FALSE(a == b); CHECK(a != b); + + CHECK_FALSE(b == a); + CHECK(b != a); } - SECTION("Compare non-null with null") { - JsonString a("hello"), b(0); + SECTION("Null and non-empty strings differ") { + JsonString a, b("hello"); CHECK_FALSE(a == b); CHECK(a != b); + + CHECK_FALSE(b == a); + CHECK(b != a); } SECTION("Compare different strings") { @@ -88,5 +97,7 @@ TEST_CASE("JsonString") { CHECK(s.size() == 5); CHECK(s.isLinked() == true); + CHECK(s == "hello"); + CHECK(s != "hello world"); } } diff --git a/src/ArduinoJson/Strings/String.hpp b/src/ArduinoJson/Strings/String.hpp index 08956f051..1371114ec 100644 --- a/src/ArduinoJson/Strings/String.hpp +++ b/src/ArduinoJson/Strings/String.hpp @@ -46,23 +46,19 @@ class String : public SafeBoolIdom { } friend bool operator==(String lhs, String rhs) { + if (lhs._size != rhs._size) + return false; if (lhs._data == rhs._data) return true; if (!lhs._data) return false; if (!rhs._data) return false; - return strcmp(lhs._data, rhs._data) == 0; + return memcmp(lhs._data, rhs._data, lhs._size) == 0; } friend bool operator!=(String lhs, String rhs) { - if (lhs._data == rhs._data) - return false; - if (!lhs._data) - return true; - if (!rhs._data) - return true; - return strcmp(lhs._data, rhs._data) != 0; + return !(lhs == rhs); } #if ARDUINOJSON_ENABLE_STD_STREAM From 986f77fa159c960c83addd5e656a98fd046f20e7 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Fri, 25 Feb 2022 10:28:22 +0100 Subject: [PATCH 005/202] Tests: link with `-static` on MinGW --- extras/CompileOptions.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extras/CompileOptions.cmake b/extras/CompileOptions.cmake index 3421ba060..e083ff9ec 100644 --- a/extras/CompileOptions.cmake +++ b/extras/CompileOptions.cmake @@ -92,3 +92,9 @@ if(MSVC) ) endif() endif() + +if(MINGW) + # Static link on MinGW to avoid linking with the wrong DLLs when multiple + # versions are installed. + add_link_options(-static) +endif() From 89ed54362bd0be9f9343e25d4ae947584f0b648a Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Fri, 25 Feb 2022 09:23:51 +0100 Subject: [PATCH 006/202] Fix `-Wsign-conversion` on GCC 8 (fixes #1715) --- CHANGELOG.md | 1 + extras/CompileOptions.cmake | 1 + extras/tests/JsonArray/add.cpp | 2 +- extras/tests/JsonArray/subscript.cpp | 4 ++-- extras/tests/JsonDeserializer/filter.cpp | 6 +++--- extras/tests/JsonDeserializer/input_types.cpp | 2 +- extras/tests/JsonDocument/ElementProxy.cpp | 2 +- extras/tests/JsonDocument/MemberProxy.cpp | 2 +- extras/tests/JsonDocument/remove.cpp | 2 +- extras/tests/JsonObject/containsKey.cpp | 2 +- extras/tests/JsonObject/createNestedArray.cpp | 2 +- extras/tests/JsonObject/createNestedObject.cpp | 2 +- extras/tests/JsonObject/remove.cpp | 2 +- extras/tests/JsonObject/subscript.cpp | 10 +++++----- extras/tests/JsonVariant/set.cpp | 2 +- extras/tests/JsonVariant/subscript.cpp | 4 ++-- extras/tests/MsgPackDeserializer/filter.cpp | 6 +++--- extras/tests/MsgPackDeserializer/input_types.cpp | 2 +- src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp | 2 +- src/ArduinoJson/Strings/Adapters/RamString.hpp | 6 +++--- 20 files changed, 32 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7a9ef6f..29749eed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ HEAD * Fix `call of overloaded 'String(const char*, int)' is ambiguous` * Fix `JsonString` operator `==` and `!=` for non-zero-terminated string +* Fix `-Wsign-conversion` on GCC 8 (issue #1715) v6.19.2 (2022-02-14) ------- diff --git a/extras/CompileOptions.cmake b/extras/CompileOptions.cmake index e083ff9ec..82a750a12 100644 --- a/extras/CompileOptions.cmake +++ b/extras/CompileOptions.cmake @@ -18,6 +18,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") -Wparentheses -Wredundant-decls -Wshadow + -Wsign-conversion -Wsign-promo -Wstrict-aliasing -Wundef diff --git a/extras/tests/JsonArray/add.cpp b/extras/tests/JsonArray/add.cpp index c6b1b9a75..369bcf2da 100644 --- a/extras/tests/JsonArray/add.cpp +++ b/extras/tests/JsonArray/add.cpp @@ -40,7 +40,7 @@ TEST_CASE("JsonArray::add()") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("vla") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "world"); diff --git a/extras/tests/JsonArray/subscript.cpp b/extras/tests/JsonArray/subscript.cpp index 1d5a6a532..57bbe140d 100644 --- a/extras/tests/JsonArray/subscript.cpp +++ b/extras/tests/JsonArray/subscript.cpp @@ -135,7 +135,7 @@ TEST_CASE("JsonArray::operator[]") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("set(VLA)") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "world"); @@ -146,7 +146,7 @@ TEST_CASE("JsonArray::operator[]") { } SECTION("operator=(VLA)") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "world"); diff --git a/extras/tests/JsonDeserializer/filter.cpp b/extras/tests/JsonDeserializer/filter.cpp index e950f7683..f72968eeb 100644 --- a/extras/tests/JsonDeserializer/filter.cpp +++ b/extras/tests/JsonDeserializer/filter.cpp @@ -708,7 +708,7 @@ TEST_CASE("Overloads") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("char[n], Filter") { - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "{}"); deserializeJson(doc, vla, Filter(filter)); @@ -736,7 +736,7 @@ TEST_CASE("Overloads") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("char[n], Filter, NestingLimit") { - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "{}"); deserializeJson(doc, vla, Filter(filter), NestingLimit(5)); @@ -764,7 +764,7 @@ TEST_CASE("Overloads") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("char[n], NestingLimit, Filter") { - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "{}"); deserializeJson(doc, vla, NestingLimit(5), Filter(filter)); diff --git a/extras/tests/JsonDeserializer/input_types.cpp b/extras/tests/JsonDeserializer/input_types.cpp index 7f9f00acb..04054c6a7 100644 --- a/extras/tests/JsonDeserializer/input_types.cpp +++ b/extras/tests/JsonDeserializer/input_types.cpp @@ -121,7 +121,7 @@ TEST_CASE("deserializeJson(std::istream&)") { #ifdef HAS_VARIABLE_LENGTH_ARRAY TEST_CASE("deserializeJson(VLA)") { - int i = 9; + size_t i = 9; char vla[i]; strcpy(vla, "{\"a\":42}"); diff --git a/extras/tests/JsonDocument/ElementProxy.cpp b/extras/tests/JsonDocument/ElementProxy.cpp index 9e4c7a963..1912d5f9f 100644 --- a/extras/tests/JsonDocument/ElementProxy.cpp +++ b/extras/tests/JsonDocument/ElementProxy.cpp @@ -131,7 +131,7 @@ TEST_CASE("ElementProxy::remove()") { ep["a"] = 1; ep["b"] = 2; - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "b"); ep.remove(vla); diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index 813152d1d..ea1dc44e0 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -171,7 +171,7 @@ TEST_CASE("MemberProxy::remove()") { mp["a"] = 1; mp["b"] = 2; - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "b"); mp.remove(vla); diff --git a/extras/tests/JsonDocument/remove.cpp b/extras/tests/JsonDocument/remove.cpp index 2fb8cad34..b77707339 100644 --- a/extras/tests/JsonDocument/remove.cpp +++ b/extras/tests/JsonDocument/remove.cpp @@ -41,7 +41,7 @@ TEST_CASE("JsonDocument::remove()") { doc["a"] = 1; doc["b"] = 2; - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "b"); doc.remove(vla); diff --git a/extras/tests/JsonObject/containsKey.cpp b/extras/tests/JsonObject/containsKey.cpp index 75991acbb..4f37f7e64 100644 --- a/extras/tests/JsonObject/containsKey.cpp +++ b/extras/tests/JsonObject/containsKey.cpp @@ -29,7 +29,7 @@ TEST_CASE("JsonObject::containsKey()") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("key is a VLA") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); diff --git a/extras/tests/JsonObject/createNestedArray.cpp b/extras/tests/JsonObject/createNestedArray.cpp index 6871794e5..4d43038fa 100644 --- a/extras/tests/JsonObject/createNestedArray.cpp +++ b/extras/tests/JsonObject/createNestedArray.cpp @@ -16,7 +16,7 @@ TEST_CASE("JsonObject::createNestedArray()") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("key is a VLA") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); diff --git a/extras/tests/JsonObject/createNestedObject.cpp b/extras/tests/JsonObject/createNestedObject.cpp index 47d76b27f..29f4d39df 100644 --- a/extras/tests/JsonObject/createNestedObject.cpp +++ b/extras/tests/JsonObject/createNestedObject.cpp @@ -15,7 +15,7 @@ TEST_CASE("JsonObject::createNestedObject()") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("key is a VLA") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); diff --git a/extras/tests/JsonObject/remove.cpp b/extras/tests/JsonObject/remove.cpp index 6a2efdb1d..f1cb20cb7 100644 --- a/extras/tests/JsonObject/remove.cpp +++ b/extras/tests/JsonObject/remove.cpp @@ -60,7 +60,7 @@ TEST_CASE("JsonObject::remove()") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("key is a vla") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "b"); obj.remove(vla); diff --git a/extras/tests/JsonObject/subscript.cpp b/extras/tests/JsonObject/subscript.cpp index aa7dde358..e8242f632 100644 --- a/extras/tests/JsonObject/subscript.cpp +++ b/extras/tests/JsonObject/subscript.cpp @@ -172,7 +172,7 @@ TEST_CASE("JsonObject::operator[]") { #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \ !defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR) SECTION("obj[VLA] = str") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); @@ -182,7 +182,7 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("obj[str] = VLA") { // issue #416 - int i = 32; + size_t i = 32; char vla[i]; strcpy(vla, "world"); @@ -192,7 +192,7 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("obj.set(VLA, str)") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); @@ -202,7 +202,7 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("obj.set(str, VLA)") { - int i = 32; + size_t i = 32; char vla[i]; strcpy(vla, "world"); @@ -212,7 +212,7 @@ TEST_CASE("JsonObject::operator[]") { } SECTION("obj[VLA]") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); diff --git a/extras/tests/JsonVariant/set.cpp b/extras/tests/JsonVariant/set.cpp index eba15d435..899ef6a55 100644 --- a/extras/tests/JsonVariant/set.cpp +++ b/extras/tests/JsonVariant/set.cpp @@ -71,7 +71,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("VLA") { - int n = 16; + size_t n = 16; char str[n]; strcpy(str, "hello"); diff --git a/extras/tests/JsonVariant/subscript.cpp b/extras/tests/JsonVariant/subscript.cpp index 9662555ec..f466cd275 100644 --- a/extras/tests/JsonVariant/subscript.cpp +++ b/extras/tests/JsonVariant/subscript.cpp @@ -108,7 +108,7 @@ TEST_CASE("JsonVariant::operator[]") { #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \ !defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR) SECTION("key is a VLA") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); @@ -119,7 +119,7 @@ TEST_CASE("JsonVariant::operator[]") { } SECTION("key is a VLA, const JsonVariant") { - int i = 16; + size_t i = 16; char vla[i]; strcpy(vla, "hello"); diff --git a/extras/tests/MsgPackDeserializer/filter.cpp b/extras/tests/MsgPackDeserializer/filter.cpp index 583c42f81..57dd7bea9 100644 --- a/extras/tests/MsgPackDeserializer/filter.cpp +++ b/extras/tests/MsgPackDeserializer/filter.cpp @@ -1068,7 +1068,7 @@ TEST_CASE("Overloads") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("char[n], Filter") { - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "{}"); deserializeMsgPack(doc, vla, Filter(filter)); @@ -1096,7 +1096,7 @@ TEST_CASE("Overloads") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("char[n], Filter, NestingLimit") { - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "{}"); deserializeMsgPack(doc, vla, Filter(filter), NestingLimit(5)); @@ -1124,7 +1124,7 @@ TEST_CASE("Overloads") { #ifdef HAS_VARIABLE_LENGTH_ARRAY SECTION("char[n], NestingLimit, Filter") { - int i = 4; + size_t i = 4; char vla[i]; strcpy(vla, "{}"); deserializeMsgPack(doc, vla, NestingLimit(5), Filter(filter)); diff --git a/extras/tests/MsgPackDeserializer/input_types.cpp b/extras/tests/MsgPackDeserializer/input_types.cpp index af9b693a3..f46d4691e 100644 --- a/extras/tests/MsgPackDeserializer/input_types.cpp +++ b/extras/tests/MsgPackDeserializer/input_types.cpp @@ -72,7 +72,7 @@ TEST_CASE("deserializeMsgPack(std::istream&)") { #ifdef HAS_VARIABLE_LENGTH_ARRAY TEST_CASE("deserializeMsgPack(VLA)") { - int i = 16; + size_t i = 16; char vla[i]; memcpy(vla, "\xDE\x00\x01\xA5Hello\xA5world", 15); diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 41da6f068..00b717a6c 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -481,7 +481,7 @@ class MsgPackDeserializer { T size; if (!readInteger(size)) return false; - return skipBytes(size + 1); + return skipBytes(size + 1U); } MemoryPool *_pool; diff --git a/src/ArduinoJson/Strings/Adapters/RamString.hpp b/src/ArduinoJson/Strings/Adapters/RamString.hpp index 1d3991615..5a55552a3 100644 --- a/src/ArduinoJson/Strings/Adapters/RamString.hpp +++ b/src/ArduinoJson/Strings/Adapters/RamString.hpp @@ -106,13 +106,13 @@ inline SizedRamString adaptString(const char* s, size_t n) { return SizedRamString(s, n); } -template +template struct IsString : true_type {}; -template +template struct IsString : true_type {}; -template +template inline SizedRamString adaptString(char s[N]) { return SizedRamString(s, strlen(s)); } From 421ecec0ddae1a7195b3be17367fdbeb2de31bd4 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Fri, 25 Feb 2022 09:28:26 +0100 Subject: [PATCH 007/202] Tests: trim trailing white spaces --- extras/tests/JsonDeserializer/filter.cpp | 114 +++++++++++------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/extras/tests/JsonDeserializer/filter.cpp b/extras/tests/JsonDeserializer/filter.cpp index f72968eeb..9cf85d27a 100644 --- a/extras/tests/JsonDeserializer/filter.cpp +++ b/extras/tests/JsonDeserializer/filter.cpp @@ -366,7 +366,7 @@ TEST_CASE("Filtering") { 0 }, { - // bubble up element error even if array is skipped + // bubble up element error even if array is skipped "[1,'2,3]", "false", 10, @@ -375,7 +375,7 @@ TEST_CASE("Filtering") { 0 }, { - // bubble up member error even if object is skipped + // bubble up member error even if object is skipped "{'hello':'worl}", "false", 10, @@ -384,7 +384,7 @@ TEST_CASE("Filtering") { 0 }, { - // bubble up colon error even if object is skipped + // bubble up colon error even if object is skipped "{'hello','world'}", "false", 10, @@ -393,7 +393,7 @@ TEST_CASE("Filtering") { 0 }, { - // bubble up key error even if object is skipped + // bubble up key error even if object is skipped "{'hello:1}", "false", 10, @@ -404,7 +404,7 @@ TEST_CASE("Filtering") { { // detect invalid value in skipped object "{'hello':!}", - "false", + "false", 10, DeserializationError::InvalidInput, "null", @@ -413,235 +413,235 @@ TEST_CASE("Filtering") { { // ignore invalid value in skipped object "{'hello':\\}", - "false", + "false", 10, DeserializationError::InvalidInput, - "null", + "null", 0 }, { // check nesting limit even for ignored objects "{}", - "false", + "false", 0, DeserializationError::TooDeep, - "null", + "null", 0 }, { // check nesting limit even for ignored objects "{'hello':{}}", - "false", + "false", 1, DeserializationError::TooDeep, - "null", + "null", 0 }, { // check nesting limit even for ignored values in objects "{'hello':{}}", - "{}", + "{}", 1, DeserializationError::TooDeep, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // check nesting limit even for ignored arrays "[]", - "false", + "false", 0, DeserializationError::TooDeep, - "null", + "null", 0 }, { // check nesting limit even for ignored arrays "[[]]", - "false", + "false", 1, DeserializationError::TooDeep, - "null", + "null", 0 }, { // check nesting limit even for ignored values in arrays "[[]]", - "[]", + "[]", 1, DeserializationError::TooDeep, - "[]", + "[]", JSON_ARRAY_SIZE(0) }, { // supports back-slash at the end of skipped string "\"hell\\", - "false", + "false", 1, DeserializationError::IncompleteInput, - "null", + "null", 0 }, { // invalid comment at after an element in a skipped array "[1/]", - "false", + "false", 10, DeserializationError::InvalidInput, - "null", + "null", 0 }, { // incomplete comment at after an element in a skipped array "[1/*]", - "false", + "false", 10, DeserializationError::IncompleteInput, - "null", + "null", 0 }, { // missing comma in a skipped array "[1 2]", - "false", + "false", 10, DeserializationError::InvalidInput, - "null", + "null", 0 }, { // invalid comment at the beginning of array "[/1]", - "[false]", + "[false]", 10, DeserializationError::InvalidInput, - "[]", + "[]", JSON_ARRAY_SIZE(0) }, { // incomplete comment at the begining of an array "[/*]", - "[false]", + "[false]", 10, DeserializationError::IncompleteInput, - "[]", + "[]", JSON_ARRAY_SIZE(0) }, { // invalid comment before key "{/1:2}", - "{}", + "{}", 10, DeserializationError::InvalidInput, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // incomplete comment before key "{/*:2}", - "{}", + "{}", 10, DeserializationError::IncompleteInput, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // invalid comment after key "{\"example\"/1:2}", - "{}", + "{}", 10, DeserializationError::InvalidInput, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // incomplete comment after key "{\"example\"/*:2}", - "{}", + "{}", 10, DeserializationError::IncompleteInput, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // invalid comment after colon "{\"example\":/12}", - "{}", + "{}", 10, DeserializationError::InvalidInput, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // incomplete comment after colon "{\"example\":/*2}", - "{}", + "{}", 10, DeserializationError::IncompleteInput, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // comment next to an integer "{\"ignore\":1//,\"example\":2\n}", - "{\"example\":true}", + "{\"example\":true}", 10, DeserializationError::Ok, - "{}", + "{}", JSON_OBJECT_SIZE(0) }, { // invalid comment after opening brace of a skipped object "{/1:2}", - "false", + "false", 10, DeserializationError::InvalidInput, - "null", + "null", 0 }, { // incomplete after opening brace of a skipped object "{/*:2}", - "false", + "false", 10, DeserializationError::IncompleteInput, - "null", + "null", 0 }, { // invalid comment after key of a skipped object "{\"example\"/:2}", - "false", + "false", 10, DeserializationError::InvalidInput, - "null", + "null", 0 }, { // incomplete after after key of a skipped object "{\"example\"/*:2}", - "false", + "false", 10, DeserializationError::IncompleteInput, - "null", + "null", 0 }, { // invalid comment after value in a skipped object "{\"example\":2/}", - "false", + "false", 10, DeserializationError::InvalidInput, - "null", + "null", 0 }, { // incomplete after after value of a skipped object "{\"example\":2/*}", - "false", + "false", 10, DeserializationError::IncompleteInput, - "null", + "null", 0 }, }; // clang-format on From c1278797f28d2cda6eb264ee8edd1995c37753fd Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 28 Feb 2022 09:50:32 +0100 Subject: [PATCH 008/202] CI: use `-funsigned-char` in one GCC build (issue #1715) --- .github/workflows/ci.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 522dc790c..e1808e2ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,7 @@ jobs: - gcc: "9" cxxflags: -fsanitize=address -fno-sanitize-recover=all - gcc: "10" + cxxflags: -funsigned-char # Issue #1715 - gcc: "11" steps: - name: Install @@ -47,10 +48,10 @@ jobs: sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ trusty main' sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ trusty universe' sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ xenial main' - sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ xenial universe' + sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ xenial universe' sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ bionic main' - sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ bionic universe' - sudo add-apt-repository -yn 'deb http://mirrors.kernel.org/ubuntu hirsute main universe' + sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ bionic universe' + sudo add-apt-repository -yn 'deb http://mirrors.kernel.org/ubuntu hirsute main universe' sudo apt-get update sudo apt-get install -y gcc-${{ matrix.gcc }} g++-${{ matrix.gcc }} - name: Checkout @@ -103,9 +104,9 @@ jobs: sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ trusty main' sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ trusty universe' sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ xenial main' - sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ xenial universe' + sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ xenial universe' sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ bionic main' - sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ bionic universe' + sudo add-apt-repository -yn 'deb http://archive.ubuntu.com/ubuntu/ bionic universe' sudo apt-get update sudo apt-get install -y clang-${{ matrix.clang }} - name: Checkout From ff06292d745c9c5928e55a8a435c8a43ab901ce0 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 28 Feb 2022 10:00:12 +0100 Subject: [PATCH 009/202] Fix `-Wsign-conversion -funsigned-char` (fixes #1715) --- src/ArduinoJson/Variant/VariantImpl.hpp | 2 +- src/ArduinoJson/Variant/VariantRef.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index b5a620873..48c45fb8f 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -168,7 +168,7 @@ inline VariantConstRef operator|(VariantConstRef preferedValue, // Out of class definition to avoid #1560 inline bool VariantRef::set(char value) const { - return set(value); + return set(static_cast(value)); } // TODO: move somewhere else diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 0865d6935..31fd02198 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -119,7 +119,7 @@ class VariantRef : public VariantRefBase, ARDUINOJSON_DEPRECATED( "Support for char is deprecated, use int8_t or uint8_t instead") as() const { - return as(); + return static_cast(as()); } template @@ -265,7 +265,7 @@ class VariantConstRef : public VariantRefBase, ARDUINOJSON_DEPRECATED( "Support for char is deprecated, use int8_t or uint8_t instead") as() const { - return as(); + return static_cast(as()); } template From e3e375f5cdd9e99411bc1bc4bae087a920e909b1 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 28 Feb 2022 18:00:43 +0100 Subject: [PATCH 010/202] MessagePack: serialize round floats as integers (fixes #1718) --- CHANGELOG.md | 1 + .../tests/MsgPackSerializer/serializeVariant.cpp | 14 ++++++++++++++ src/ArduinoJson/MsgPack/MsgPackSerializer.hpp | 16 +++++++++------- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29749eed2..3ffb91112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ HEAD * Fix `call of overloaded 'String(const char*, int)' is ambiguous` * Fix `JsonString` operator `==` and `!=` for non-zero-terminated string * Fix `-Wsign-conversion` on GCC 8 (issue #1715) +* MessagePack: serialize round floats as integers (issue #1718) v6.19.2 (2022-02-14) ------- diff --git a/extras/tests/MsgPackSerializer/serializeVariant.cpp b/extras/tests/MsgPackSerializer/serializeVariant.cpp index 7817946eb..1fd8ba180 100644 --- a/extras/tests/MsgPackSerializer/serializeVariant.cpp +++ b/extras/tests/MsgPackSerializer/serializeVariant.cpp @@ -144,4 +144,18 @@ TEST_CASE("serialize MsgPack value") { checkVariant(serialized("\xDA\xFF\xFF"), "\xDA\xFF\xFF"); checkVariant(serialized("\xDB\x00\x01\x00\x00", 5), "\xDB\x00\x01\x00\x00"); } + + SECTION("serialize round double as integer") { // Issue #1718 + checkVariant(-32768.0, "\xD1\x80\x00"); + checkVariant(-129.0, "\xD1\xFF\x7F"); + checkVariant(-128.0, "\xD0\x80"); + checkVariant(-33.0, "\xD0\xDF"); + checkVariant(-32.0, "\xE0"); + checkVariant(-1.0, "\xFF"); + checkVariant(0.0, "\x00"); + checkVariant(127.0, "\x7F"); + checkVariant(128.0, "\xCC\x80"); + checkVariant(255.0, "\xCC\xFF"); + checkVariant(256.0, "\xCD\x01\x00"); + } } diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index 51e929bec..a1053f0a4 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -23,6 +23,11 @@ class MsgPackSerializer : public Visitor { template typename enable_if::type visitFloat(T value32) { + if (canConvertNumber(value32)) { + Integer truncatedValue = Integer(value32); + if (value32 == T(truncatedValue)) + return visitSignedInteger(truncatedValue); + } writeByte(0xCA); writeInteger(value32); return bytesWritten(); @@ -32,13 +37,10 @@ class MsgPackSerializer : public Visitor { ARDUINOJSON_NO_SANITIZE("float-cast-overflow") typename enable_if::type visitFloat(T value64) { float value32 = float(value64); - if (value32 == value64) { - writeByte(0xCA); - writeInteger(value32); - } else { - writeByte(0xCB); - writeInteger(value64); - } + if (value32 == value64) + return visitFloat(value32); + writeByte(0xCB); + writeInteger(value64); return bytesWritten(); } From 7abf875071a79e3e506cf8334773d647a5caad98 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 8 Mar 2022 17:24:14 +0100 Subject: [PATCH 011/202] Set version to 6.19.3 --- CHANGELOG.md | 4 ++-- CMakeLists.txt | 2 +- README.md | 4 ++-- appveyor.yml | 2 +- library.json | 2 +- library.properties | 2 +- src/ArduinoJson/version.hpp | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ffb91112..6a2e8a8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ ArduinoJson: change log ======================= -HEAD ----- +v6.19.3 (2022-03-08) +------- * Fix `call of overloaded 'String(const char*, int)' is ambiguous` * Fix `JsonString` operator `==` and `!=` for non-zero-terminated string diff --git a/CMakeLists.txt b/CMakeLists.txt index d5a32a9f1..cdc848b75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(ESP_PLATFORM) return() endif() -project(ArduinoJson VERSION 6.19.2) +project(ArduinoJson VERSION 6.19.3) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) diff --git a/README.md b/README.md index bf1ba1118..a4c95cb97 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) [![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/bblanchon/ArduinoJson?label=quality&logo=lgtm)](https://lgtm.com/projects/g/bblanchon/ArduinoJson/) [![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x) -[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.19.2&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.19.2) -[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.19.2)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.19.2) +[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.19.3&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.19.3) +[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.19.3)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.19.3) [![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github)](https://github.com/bblanchon/ArduinoJson/stargazers) [![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github)](https://github.com/sponsors/bblanchon) diff --git a/appveyor.yml b/appveyor.yml index a4c35eef1..c713ff4b7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 6.19.2.{build} +version: 6.19.3.{build} environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 diff --git a/library.json b/library.json index a982484a7..e0e6d4f05 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/bblanchon/ArduinoJson.git" }, - "version": "6.19.2", + "version": "6.19.3", "authors": { "name": "Benoit Blanchon", "url": "https://blog.benoitblanchon.fr" diff --git a/library.properties b/library.properties index fa4790ce8..72695a177 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ArduinoJson -version=6.19.2 +version=6.19.3 author=Benoit Blanchon maintainer=Benoit Blanchon sentence=A simple and efficient JSON library for embedded C++. diff --git a/src/ArduinoJson/version.hpp b/src/ArduinoJson/version.hpp index 5beae8aff..d6a23d4c8 100644 --- a/src/ArduinoJson/version.hpp +++ b/src/ArduinoJson/version.hpp @@ -4,7 +4,7 @@ #pragma once -#define ARDUINOJSON_VERSION "6.19.2" +#define ARDUINOJSON_VERSION "6.19.3" #define ARDUINOJSON_VERSION_MAJOR 6 #define ARDUINOJSON_VERSION_MINOR 19 -#define ARDUINOJSON_VERSION_REVISION 2 +#define ARDUINOJSON_VERSION_REVISION 3 From 27c924746b18de7f753faae273265919f43549c1 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 8 Mar 2022 19:09:56 +0100 Subject: [PATCH 012/202] CI: run `apt-get update` before installing valgrind --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1808e2ef..c59b01afa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -425,7 +425,9 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Install - run: sudo apt-get install -y valgrind ninja-build + run: | + sudo apt-get update + sudo apt-get install -y valgrind ninja-build - name: Checkout uses: actions/checkout@v2 - name: Configure From 47f90b02c38af71897c599bbf93348d3e3fdf8c9 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 9 Mar 2022 08:33:26 +0100 Subject: [PATCH 013/202] CI: run `apt-get update` before installing `g++-multilib` --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c59b01afa..aacbff90c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,9 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Install - run: sudo apt-get install -y g++-multilib + run: | + sudo apt-get update + sudo apt-get install -y g++-multilib - name: Checkout uses: actions/checkout@v2 - name: GCC 32-bit From 3dc67c5663c5b3488a783990241eab4cd3afb521 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sat, 19 Mar 2022 12:11:40 +0100 Subject: [PATCH 014/202] Add `memoryUsage()` to `ElementProxy` and `MemberProxy` (fixes #1730) --- CHANGELOG.md | 6 +++++ .../JsonDocument/DynamicJsonDocument.cpp | 22 +++++++++++++++++++ extras/tests/JsonDocument/ElementProxy.cpp | 15 +++++++++++++ extras/tests/JsonDocument/MemberProxy.cpp | 14 ++++++++++++ src/ArduinoJson/Array/ElementProxy.hpp | 4 ++++ src/ArduinoJson/Object/MemberProxy.hpp | 4 ++++ 6 files changed, 65 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a2e8a8d7..0d60ad556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Add `ElementProxy::memoryUsage()` +* Add `MemberProxy::memoryUsage()` (issue #1730) + v6.19.3 (2022-03-08) ------- diff --git a/extras/tests/JsonDocument/DynamicJsonDocument.cpp b/extras/tests/JsonDocument/DynamicJsonDocument.cpp index 8a5c7bb69..ce050f17b 100644 --- a/extras/tests/JsonDocument/DynamicJsonDocument.cpp +++ b/extras/tests/JsonDocument/DynamicJsonDocument.cpp @@ -205,4 +205,26 @@ TEST_CASE("DynamicJsonDocument assignment") { REQUIRE_JSON(doc2, "42"); REQUIRE(doc2.capacity() == 4096); } + + SECTION("Assign from MemberProxy") { + StaticJsonDocument<200> doc1; + doc1["value"] = 42; + + DynamicJsonDocument doc2(4096); + doc2 = doc1["value"]; + + REQUIRE_JSON(doc2, "42"); + REQUIRE(doc2.capacity() == 4096); + } + + SECTION("Assign from ElementProxy") { + StaticJsonDocument<200> doc1; + doc1[0] = 42; + + DynamicJsonDocument doc2(4096); + doc2 = doc1[0]; + + REQUIRE_JSON(doc2, "42"); + REQUIRE(doc2.capacity() == 4096); + } } diff --git a/extras/tests/JsonDocument/ElementProxy.cpp b/extras/tests/JsonDocument/ElementProxy.cpp index 1912d5f9f..f16f4f959 100644 --- a/extras/tests/JsonDocument/ElementProxy.cpp +++ b/extras/tests/JsonDocument/ElementProxy.cpp @@ -188,6 +188,21 @@ TEST_CASE("ElementProxy::size()") { } } +TEST_CASE("ElementProxy::memoryUsage()") { + DynamicJsonDocument doc(4096); + doc.addElement(); + ElementProxy ep = doc[0]; + + SECTION("returns 0 for null") { + REQUIRE(ep.memoryUsage() == 0); + } + + SECTION("returns size for string") { + ep.set(std::string("hello")); + REQUIRE(ep.memoryUsage() == 6); + } +} + TEST_CASE("ElementProxy::operator[]") { DynamicJsonDocument doc(4096); ElementProxy ep = doc[1]; diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index ea1dc44e0..93622ad87 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -229,6 +229,20 @@ TEST_CASE("MemberProxy::size()") { } } +TEST_CASE("MemberProxy::memoryUsage()") { + DynamicJsonDocument doc(4096); + MemberProxy mp = doc["hello"]; + + SECTION("returns 0 when null") { + REQUIRE(mp.memoryUsage() == 0); + } + + SECTION("return the size for a string") { + mp.set(std::string("hello")); + REQUIRE(mp.memoryUsage() == 6); + } +} + TEST_CASE("MemberProxy::operator[]") { DynamicJsonDocument doc(4096); MemberProxy mp = doc["hello"]; diff --git a/src/ArduinoJson/Array/ElementProxy.hpp b/src/ArduinoJson/Array/ElementProxy.hpp index d54de4947..821cfba3c 100644 --- a/src/ArduinoJson/Array/ElementProxy.hpp +++ b/src/ArduinoJson/Array/ElementProxy.hpp @@ -118,6 +118,10 @@ class ElementProxy : public VariantOperators >, return getUpstreamElement().size(); } + FORCE_INLINE size_t memoryUsage() const { + return getUpstreamElement().memoryUsage(); + } + template VariantRef getMember(TNestedKey* key) const { return getUpstreamElement().getMember(key); diff --git a/src/ArduinoJson/Object/MemberProxy.hpp b/src/ArduinoJson/Object/MemberProxy.hpp index e8fff4def..9a1bc83fd 100644 --- a/src/ArduinoJson/Object/MemberProxy.hpp +++ b/src/ArduinoJson/Object/MemberProxy.hpp @@ -94,6 +94,10 @@ class MemberProxy : public VariantOperators >, return getUpstreamMember().size(); } + FORCE_INLINE size_t memoryUsage() const { + return getUpstreamMember().memoryUsage(); + } + FORCE_INLINE void remove(size_t index) const { getUpstreamMember().remove(index); } From f4379f97aed4f4583423dcae0f2e9a73bb51053b Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 23 Mar 2022 17:17:09 +0100 Subject: [PATCH 015/202] Format code with clang-format --- extras/tests/JsonDocument/ElementProxy.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/extras/tests/JsonDocument/ElementProxy.cpp b/extras/tests/JsonDocument/ElementProxy.cpp index f16f4f959..6eb06256f 100644 --- a/extras/tests/JsonDocument/ElementProxy.cpp +++ b/extras/tests/JsonDocument/ElementProxy.cpp @@ -10,7 +10,7 @@ using namespace ARDUINOJSON_NAMESPACE; TEST_CASE("ElementProxy::add()") { DynamicJsonDocument doc(4096); doc.addElement(); - ElementProxy ep = doc[0]; + ElementProxy ep = doc[0]; SECTION("add(int)") { ep.add(42); @@ -36,7 +36,7 @@ TEST_CASE("ElementProxy::add()") { TEST_CASE("ElementProxy::clear()") { DynamicJsonDocument doc(4096); doc.addElement(); - ElementProxy ep = doc[0]; + ElementProxy ep = doc[0]; SECTION("size goes back to zero") { ep.add(42); @@ -96,7 +96,7 @@ TEST_CASE("ElementProxy::operator==()") { TEST_CASE("ElementProxy::remove()") { DynamicJsonDocument doc(4096); doc.addElement(); - ElementProxy ep = doc[0]; + ElementProxy ep = doc[0]; SECTION("remove(int)") { ep.add(1); @@ -143,7 +143,7 @@ TEST_CASE("ElementProxy::remove()") { TEST_CASE("ElementProxy::set()") { DynamicJsonDocument doc(4096); - ElementProxy ep = doc[0]; + ElementProxy ep = doc[0]; SECTION("set(int)") { ep.set(42); @@ -169,7 +169,7 @@ TEST_CASE("ElementProxy::set()") { TEST_CASE("ElementProxy::size()") { DynamicJsonDocument doc(4096); doc.addElement(); - ElementProxy ep = doc[0]; + ElementProxy ep = doc[0]; SECTION("returns 0") { REQUIRE(ep.size() == 0); @@ -191,7 +191,7 @@ TEST_CASE("ElementProxy::size()") { TEST_CASE("ElementProxy::memoryUsage()") { DynamicJsonDocument doc(4096); doc.addElement(); - ElementProxy ep = doc[0]; + ElementProxy ep = doc[0]; SECTION("returns 0 for null") { REQUIRE(ep.memoryUsage() == 0); @@ -205,7 +205,7 @@ TEST_CASE("ElementProxy::memoryUsage()") { TEST_CASE("ElementProxy::operator[]") { DynamicJsonDocument doc(4096); - ElementProxy ep = doc[1]; + ElementProxy ep = doc[1]; SECTION("set member") { ep["world"] = 42; From bf5d0c790c3418fa5c155fb9e946d5629b2f2801 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 23 Mar 2022 16:00:24 +0100 Subject: [PATCH 016/202] Add implicit conversion from `JsonDocument` to `JsonVariant` --- CHANGELOG.md | 1 + extras/tests/JsonDocument/CMakeLists.txt | 1 + extras/tests/JsonDocument/ElementProxy.cpp | 26 ++++++++++++++++++++++ extras/tests/JsonDocument/MemberProxy.cpp | 26 ++++++++++++++++++++++ extras/tests/JsonDocument/cast.cpp | 18 +++++++++++++++ src/ArduinoJson/Document/JsonDocument.hpp | 15 +++++-------- 6 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 extras/tests/JsonDocument/cast.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d60ad556..1e88d08a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ HEAD * Add `ElementProxy::memoryUsage()` * Add `MemberProxy::memoryUsage()` (issue #1730) +* Add implicit conversion from `JsonDocument` to `JsonVariant` v6.19.3 (2022-03-08) ------- diff --git a/extras/tests/JsonDocument/CMakeLists.txt b/extras/tests/JsonDocument/CMakeLists.txt index e44689ce2..535678033 100644 --- a/extras/tests/JsonDocument/CMakeLists.txt +++ b/extras/tests/JsonDocument/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(JsonDocumentTests add.cpp BasicJsonDocument.cpp + cast.cpp compare.cpp containsKey.cpp createNested.cpp diff --git a/extras/tests/JsonDocument/ElementProxy.cpp b/extras/tests/JsonDocument/ElementProxy.cpp index 6eb06256f..0fe3c4713 100644 --- a/extras/tests/JsonDocument/ElementProxy.cpp +++ b/extras/tests/JsonDocument/ElementProxy.cpp @@ -219,3 +219,29 @@ TEST_CASE("ElementProxy::operator[]") { REQUIRE(doc.as() == "[null,[null,null,42]]"); } } + +TEST_CASE("ElementProxy cast to JsonVariantConst") { + DynamicJsonDocument doc(4096); + doc[0] = "world"; + + const ElementProxy ep = doc[0]; + + JsonVariantConst var = ep; + + CHECK(var.as() == "world"); +} + +TEST_CASE("ElementProxy cast to JsonVariant") { + DynamicJsonDocument doc(4096); + doc[0] = "world"; + + ElementProxy ep = doc[0]; + + JsonVariant var = ep; + + CHECK(var.as() == "world"); + + var.set("toto"); + + CHECK(doc.as() == "[\"toto\"]"); +} diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index 93622ad87..64cd06a05 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -259,3 +259,29 @@ TEST_CASE("MemberProxy::operator[]") { REQUIRE(doc.as() == "{\"hello\":[null,null,42]}"); } } + +TEST_CASE("MemberProxy cast to JsonVariantConst") { + DynamicJsonDocument doc(4096); + doc["hello"] = "world"; + + const MemberProxy mp = doc["hello"]; + + JsonVariantConst var = mp; + + CHECK(var.as() == "world"); +} + +TEST_CASE("MemberProxy cast to JsonVariant") { + DynamicJsonDocument doc(4096); + doc["hello"] = "world"; + + MemberProxy mp = doc["hello"]; + + JsonVariant var = mp; + + CHECK(var.as() == "world"); + + var.set("toto"); + + CHECK(doc.as() == "{\"hello\":\"toto\"}"); +} diff --git a/extras/tests/JsonDocument/cast.cpp b/extras/tests/JsonDocument/cast.cpp new file mode 100644 index 000000000..e538f4bbf --- /dev/null +++ b/extras/tests/JsonDocument/cast.cpp @@ -0,0 +1,18 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2022, Benoit BLANCHON +// MIT License + +#include +#include + +#include + +TEST_CASE("Implicit cast to JsonVariant") { + StaticJsonDocument<128> doc; + + doc["hello"] = "world"; + + JsonVariant var = doc; + + CHECK(var.as() == "{\"hello\":\"world\"}"); +} diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index 90e22e798..274cce714 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/src/ArduinoJson/Document/JsonDocument.hpp @@ -14,7 +14,8 @@ namespace ARDUINOJSON_NAMESPACE { -class JsonDocument : public Visitable { +class JsonDocument : public Visitable, + public VariantOperators { public: template typename TVisitor::result_type accept(TVisitor& visitor) const { @@ -295,16 +296,12 @@ class JsonDocument : public Visitable { _data.remove(adaptString(key)); } - FORCE_INLINE operator VariantConstRef() const { - return VariantConstRef(&_data); + FORCE_INLINE operator VariantRef() { + return getVariant(); } - bool operator==(VariantConstRef rhs) const { - return getVariant() == rhs; - } - - bool operator!=(VariantConstRef rhs) const { - return getVariant() != rhs; + FORCE_INLINE operator VariantConstRef() const { + return getVariant(); } protected: From 8d9504239b70084fac2d833f8ffa69b4554b48ce Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 5 Apr 2022 10:12:17 +0200 Subject: [PATCH 017/202] Fix comparisons operators with `const JsonDocument&` --- CHANGELOG.md | 1 + extras/tests/JsonDocument/compare.cpp | 26 ++++++++++++++++++++++ src/ArduinoJson/Variant/VariantCompare.hpp | 6 ++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e88d08a3..0c44e4c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ HEAD * Add `ElementProxy::memoryUsage()` * Add `MemberProxy::memoryUsage()` (issue #1730) * Add implicit conversion from `JsonDocument` to `JsonVariant` +* Fix comparisons operators with `const JsonDocument&` v6.19.3 (2022-03-08) ------- diff --git a/extras/tests/JsonDocument/compare.cpp b/extras/tests/JsonDocument/compare.cpp index 24d27bc58..7e9b95008 100644 --- a/extras/tests/JsonDocument/compare.cpp +++ b/extras/tests/JsonDocument/compare.cpp @@ -75,3 +75,29 @@ TEST_CASE("StaticJsonDocument::operator==(const DynamicJsonDocument&)") { REQUIRE(doc1 != doc2); } } + +TEST_CASE("JsonDocument::operator==(const JsonDocument&)") { + StaticJsonDocument<256> doc1; + StaticJsonDocument<256> doc2; + const JsonDocument& ref1 = doc1; + const JsonDocument& ref2 = doc2; + + SECTION("Empty") { + REQUIRE(ref1 == ref2); + REQUIRE_FALSE(ref1 != ref2); + } + + SECTION("With same object") { + doc1["hello"] = "world"; + doc2["hello"] = "world"; + REQUIRE(ref1 == ref2); + REQUIRE_FALSE(ref1 != ref2); + } + + SECTION("With different object") { + doc1["hello"] = "world"; + doc2["world"] = "hello"; + REQUIRE_FALSE(ref1 == ref2); + REQUIRE(ref1 != ref2); + } +} diff --git a/src/ArduinoJson/Variant/VariantCompare.hpp b/src/ArduinoJson/Variant/VariantCompare.hpp index a71f3e0d0..2f636156b 100644 --- a/src/ArduinoJson/Variant/VariantCompare.hpp +++ b/src/ArduinoJson/Variant/VariantCompare.hpp @@ -131,9 +131,9 @@ struct RawComparer : ComparerBase { template struct Comparer::value>::type> : ComparerBase { - T rhs; + const T *rhs; // TODO: should be a VariantConstRef - explicit Comparer(T value) : rhs(value) {} + explicit Comparer(const T &value) : rhs(&value) {} CompareResult visitArray(const CollectionData &lhs) { ArrayComparer comparer(lhs); @@ -183,7 +183,7 @@ struct Comparer::value>::type> private: template CompareResult accept(TComparer &comparer) { - CompareResult reversedResult = rhs.accept(comparer); + CompareResult reversedResult = rhs->accept(comparer); switch (reversedResult) { case COMPARE_RESULT_GREATER: return COMPARE_RESULT_LESS; From 67b6797b6d19e944b01213926872f955f4b9d54d Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 5 Apr 2022 10:53:11 +0200 Subject: [PATCH 018/202] Set version to 6.19.4 --- CHANGELOG.md | 4 ++-- CMakeLists.txt | 2 +- README.md | 4 ++-- appveyor.yml | 2 +- library.json | 2 +- library.properties | 2 +- src/ArduinoJson/version.hpp | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c44e4c1d..78cd12ac3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ ArduinoJson: change log ======================= -HEAD ----- +v6.19.4 (2022-04-05) +------- * Add `ElementProxy::memoryUsage()` * Add `MemberProxy::memoryUsage()` (issue #1730) diff --git a/CMakeLists.txt b/CMakeLists.txt index cdc848b75..f809a5893 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ if(ESP_PLATFORM) return() endif() -project(ArduinoJson VERSION 6.19.3) +project(ArduinoJson VERSION 6.19.4) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) diff --git a/README.md b/README.md index a4c95cb97..ddf35475a 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) [![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/bblanchon/ArduinoJson?label=quality&logo=lgtm)](https://lgtm.com/projects/g/bblanchon/ArduinoJson/) [![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x) -[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.19.3&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.19.3) -[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.19.3)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.19.3) +[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.19.4&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.19.4) +[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.19.4)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.19.4) [![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github)](https://github.com/bblanchon/ArduinoJson/stargazers) [![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github)](https://github.com/sponsors/bblanchon) diff --git a/appveyor.yml b/appveyor.yml index c713ff4b7..6e3e458f1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 6.19.3.{build} +version: 6.19.4.{build} environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 diff --git a/library.json b/library.json index e0e6d4f05..24a9794b2 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/bblanchon/ArduinoJson.git" }, - "version": "6.19.3", + "version": "6.19.4", "authors": { "name": "Benoit Blanchon", "url": "https://blog.benoitblanchon.fr" diff --git a/library.properties b/library.properties index 72695a177..aa20cfc9a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ArduinoJson -version=6.19.3 +version=6.19.4 author=Benoit Blanchon maintainer=Benoit Blanchon sentence=A simple and efficient JSON library for embedded C++. diff --git a/src/ArduinoJson/version.hpp b/src/ArduinoJson/version.hpp index d6a23d4c8..d2de577d5 100644 --- a/src/ArduinoJson/version.hpp +++ b/src/ArduinoJson/version.hpp @@ -4,7 +4,7 @@ #pragma once -#define ARDUINOJSON_VERSION "6.19.3" +#define ARDUINOJSON_VERSION "6.19.4" #define ARDUINOJSON_VERSION_MAJOR 6 #define ARDUINOJSON_VERSION_MINOR 19 -#define ARDUINOJSON_VERSION_REVISION 3 +#define ARDUINOJSON_VERSION_REVISION 4 From c9fbc5e40ab25c944c2a959067ba7eeeb51d4b5d Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 7 Apr 2022 18:49:39 +0200 Subject: [PATCH 019/202] README: remove utm_source/utm_medium to make links more readable (#1735) --- README.md | 68 +++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index ddf35475a..410b35133 100644 --- a/README.md +++ b/README.md @@ -16,32 +16,32 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). ## Features -* [JSON deserialization](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme) - * [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/?utm_source=github&utm_medium=readme) - * [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme) - * [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/?utm_source=github&utm_medium=readme) - * [Optionally filters the input to keep only desired values](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#filtering) +* [JSON deserialization](https://arduinojson.org/v6/api/json/deserializejson/) + * [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/) + * [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/) + * [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/) + * [Optionally filters the input to keep only desired values](https://arduinojson.org/v6/api/json/deserializejson/#filtering) * Supports single quotes as a string delimiter * Compatible with [NDJSON](http://ndjson.org/) and [JSON Lines](https://jsonlines.org/) -* [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme) - * [Can write to a buffer or a stream](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme) - * [Optionally indents the document (prettified JSON)](https://arduinojson.org/v6/api/json/serializejsonpretty/?utm_source=github&utm_medium=readme) -* [MessagePack serialization](https://arduinojson.org/v6/api/msgpack/serializemsgpack/?utm_source=github&utm_medium=readme) -* [MessagePack deserialization](https://arduinojson.org/v6/api/msgpack/deserializemsgpack/?utm_source=github&utm_medium=readme) +* [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/) + * [Can write to a buffer or a stream](https://arduinojson.org/v6/api/json/serializejson/) + * [Optionally indents the document (prettified JSON)](https://arduinojson.org/v6/api/json/serializejsonpretty/) +* [MessagePack serialization](https://arduinojson.org/v6/api/msgpack/serializemsgpack/) +* [MessagePack deserialization](https://arduinojson.org/v6/api/msgpack/deserializemsgpack/) * Efficient - * [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme) - * [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme) - * [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme) - * [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/?utm_source=github&utm_medium=readme) - * [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme) - * [Deduplicates strings](https://arduinojson.org/news/2020/08/01/version-6-16-0/?utm_source=github&utm_medium=readme) + * [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/) + * [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/) + * [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/) + * [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/) + * [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/) + * [Deduplicates strings](https://arduinojson.org/news/2020/08/01/version-6-16-0/) * Versatile - * Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme) - * Supports [`String`](https://arduinojson.org/v6/api/config/enable_arduino_string/?utm_source=github&utm_medium=readme), [`std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme), and [`std::string_view`](https://arduinojson.org/v6/api/config/enable_string_view/?utm_source=github&utm_medium=readme) - * Supports [`Stream`](https://arduinojson.org/v6/api/config/enable_arduino_stream/?utm_source=github&utm_medium=readme) and [`std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme) - * Supports [Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme) - * Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer) - * Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/?utm_source=github&utm_medium=readme) + * Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/) + * Supports [`String`](https://arduinojson.org/v6/api/config/enable_arduino_string/), [`std::string`](https://arduinojson.org/v6/api/config/enable_std_string/), and [`std::string_view`](https://arduinojson.org/v6/api/config/enable_string_view/) + * Supports [`Stream`](https://arduinojson.org/v6/api/config/enable_arduino_stream/) and [`std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/) + * Supports [Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/) + * Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/#custom-writer) + * Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/) * Portable * Usable on any C++ project (not limited to Arduino) * Compatible with C++98, C++11, C++14 and C++17 @@ -69,15 +69,15 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). * [Visual Micro](http://www.visualmicro.com/) * [Visual Studio](https://www.visualstudio.com/) * [Even works with online compilers like wandbox.org](https://wandbox.org/permlink/RlZSKy17DjJ6HcdN) - * [CMake friendly](https://arduinojson.org/v6/how-to/use-arduinojson-with-cmake/?utm_source=github&utm_medium=readme) + * [CMake friendly](https://arduinojson.org/v6/how-to/use-arduinojson-with-cmake/) * Well designed - * [Elegant API](http://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme) + * [Elegant API](http://arduinojson.org/v6/example/) * [Thread-safe](https://en.wikipedia.org/wiki/Thread_safety) * Self-contained (no external dependency) * `const` friendly - * [`for` friendly](https://arduinojson.org/v6/api/jsonobject/begin_end/?utm_source=github&utm_medium=readme) + * [`for` friendly](https://arduinojson.org/v6/api/jsonobject/begin_end/) * [TMP friendly](https://en.wikipedia.org/wiki/Template_metaprogramming) - * Handles [integer overflows](https://arduinojson.org/v6/api/jsonvariant/as/?utm_source=github&utm_medium=readme#integer-overflows) + * Handles [integer overflows](https://arduinojson.org/v6/api/jsonvariant/as/#integer-overflows) * Well tested * [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x) * Continuously tested on @@ -87,12 +87,12 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). * [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) * Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/) * Well documented - * [Tutorials](https://arduinojson.org/v6/doc/deserialization/?utm_source=github&utm_medium=readme) - * [Examples](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme) - * [How-tos](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme) - * [FAQ](https://arduinojson.org/v6/faq/?utm_source=github&utm_medium=readme) - * [Troubleshooter](https://arduinojson.org/v6/troubleshooter/?utm_source=github&utm_medium=readme) - * [Book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme) + * [Tutorials](https://arduinojson.org/v6/doc/deserialization/) + * [Examples](https://arduinojson.org/v6/example/) + * [How-tos](https://arduinojson.org/v6/example/) + * [FAQ](https://arduinojson.org/v6/faq/) + * [Troubleshooter](https://arduinojson.org/v6/troubleshooter/) + * [Book](https://arduinojson.org/book/) * [Changelog](CHANGELOG.md) * Vibrant user community * Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories) @@ -118,7 +118,7 @@ double latitude = doc["data"][0]; double longitude = doc["data"][1]; ``` -See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme) +See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/) ### Serialization @@ -137,7 +137,7 @@ serializeJson(doc, Serial); // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} ``` -See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme) +See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/) ## Sponsors From ccfbb5fd1dbf37d99a14ba984262b9ca7891c5c2 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 7 Apr 2022 20:58:54 +0200 Subject: [PATCH 020/202] Fix `9.22337e+18 outside range of representable values of type 'long'` --- CHANGELOG.md | 5 ++ .../MsgPackSerializer/serializeVariant.cpp | 1 + extras/tests/Numbers/CMakeLists.txt | 5 +- extras/tests/Numbers/convertNumber.cpp | 58 +++++++++++++++++++ src/ArduinoJson/Numbers/FloatTraits.hpp | 49 ++++++++++++++++ src/ArduinoJson/Numbers/convertNumber.hpp | 26 +++++++-- 6 files changed, 139 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78cd12ac3..f7a3fca97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Fix `9.22337e+18 is outside the range of representable values of type 'long'` + v6.19.4 (2022-04-05) ------- diff --git a/extras/tests/MsgPackSerializer/serializeVariant.cpp b/extras/tests/MsgPackSerializer/serializeVariant.cpp index 1fd8ba180..c09195901 100644 --- a/extras/tests/MsgPackSerializer/serializeVariant.cpp +++ b/extras/tests/MsgPackSerializer/serializeVariant.cpp @@ -109,6 +109,7 @@ TEST_CASE("serialize MsgPack value") { SECTION("float 32") { checkVariant(1.25, "\xCA\x3F\xA0\x00\x00"); + checkVariant(9.22337204e+18f, "\xca\x5f\x00\x00\x00"); } SECTION("float 64") { diff --git a/extras/tests/Numbers/CMakeLists.txt b/extras/tests/Numbers/CMakeLists.txt index 9bcf33413..05e1e8949 100644 --- a/extras/tests/Numbers/CMakeLists.txt +++ b/extras/tests/Numbers/CMakeLists.txt @@ -2,7 +2,10 @@ # Copyright © 2014-2022, Benoit BLANCHON # MIT License -add_executable(NumbersTests +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED OFF) + +add_executable(NumbersTests convertNumber.cpp parseFloat.cpp parseDouble.cpp diff --git a/extras/tests/Numbers/convertNumber.cpp b/extras/tests/Numbers/convertNumber.cpp index 1c36aa068..8c193dc30 100644 --- a/extras/tests/Numbers/convertNumber.cpp +++ b/extras/tests/Numbers/convertNumber.cpp @@ -75,4 +75,62 @@ TEST_CASE("canConvertNumber()") { CHECK((canConvertNumber(128)) == true); CHECK((canConvertNumber(255)) == true); } + + SECTION("float -> int32_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-2.147483904e9f)) == false); + CHECK((canConvertNumber(-2.147483648e+9f)) == true); + CHECK((canConvertNumber(2.14748352e+9f)) == true); + CHECK((canConvertNumber(2.14748365e+9f)) == false); + } + + SECTION("double -> int32_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-2.147483649e+9)) == false); + CHECK((canConvertNumber(-2.147483648e+9)) == true); + CHECK((canConvertNumber(2.147483647e+9)) == true); + CHECK((canConvertNumber(2.147483648e+9)) == false); + } + + SECTION("float -> uint32_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-1.401298e-45f)) == false); + CHECK((canConvertNumber(4.29496704e+9f)) == true); + CHECK((canConvertNumber(4.294967296e+9f)) == false); + } + +#if ARDUINOJSON_HAS_LONG_LONG + SECTION("float -> int64_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-9.22337204e+18f)) == true); + CHECK((canConvertNumber(9.22337149e+18f)) == true); + CHECK((canConvertNumber(9.22337204e+18f)) == false); + } + + SECTION("double -> int64_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-9.2233720368547758e+18)) == true); + CHECK((canConvertNumber(9.2233720368547748e+18)) == true); + CHECK((canConvertNumber(9.2233720368547758e+18)) == false); + } + + SECTION("float -> uint64_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-1.401298e-45f)) == false); + CHECK((canConvertNumber(1.844674297419792384e+19f)) == + true); + CHECK((canConvertNumber(1.8446744073709551616e+19f)) == + false); + } + + SECTION("double -> uint64_t") { + CHECK((canConvertNumber(0)) == true); + CHECK((canConvertNumber(-4.94065645841247e-324)) == + false); + CHECK((canConvertNumber(1.8446744073709549568e+19)) == + true); + CHECK((canConvertNumber(1.8446744073709551616e+19)) == + false); + } +#endif } diff --git a/src/ArduinoJson/Numbers/FloatTraits.hpp b/src/ArduinoJson/Numbers/FloatTraits.hpp index 0af77f3b0..4d5782b7a 100644 --- a/src/ArduinoJson/Numbers/FloatTraits.hpp +++ b/src/ArduinoJson/Numbers/FloatTraits.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace ARDUINOJSON_NAMESPACE { @@ -116,6 +117,22 @@ struct FloatTraits { return forge(0x7FEFFFFF, 0xFFFFFFFF); } + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 8, + signed>::type* = 0) { + return forge(0x43DFFFFF, 0xFFFFFFFF); // 9.2233720368547748e+18 + } + + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>::type* = 0) { + return forge(0x43EFFFFF, 0xFFFFFFFF); // 1.8446744073709549568e+19 + } + static T lowest() { return forge(0xFFEFFFFF, 0xFFFFFFFF); } @@ -212,6 +229,38 @@ struct FloatTraits { return forge(0x7f7fffff); } + template // int32_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 4, + signed>::type* = 0) { + return forge(0x4EFFFFFF); // 2.14748352E9 + } + + template // uint32_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 4, + unsigned>::type* = 0) { + return forge(0x4F7FFFFF); // 4.29496704E9 + } + + template // int64_t + static T highest_for( + typename enable_if::value && is_signed::value && + sizeof(TOut) == 8, + signed>::type* = 0) { + return forge(0x5EFFFFFF); // 9.22337148709896192E18 + } + + template // uint64_t + static T highest_for( + typename enable_if::value && is_unsigned::value && + sizeof(TOut) == 8, + unsigned>::type* = 0) { + return forge(0x5F7FFFFF); // 1.844674297419792384E19 + } + static T lowest() { return forge(0xFf7fffff); } diff --git a/src/ArduinoJson/Numbers/convertNumber.hpp b/src/ArduinoJson/Numbers/convertNumber.hpp index 87547f5b4..6dd400edd 100644 --- a/src/ArduinoJson/Numbers/convertNumber.hpp +++ b/src/ArduinoJson/Numbers/convertNumber.hpp @@ -15,6 +15,7 @@ #endif #include +#include #include #include @@ -95,17 +96,34 @@ canConvertNumber(TIn value) { return value <= TIn(numeric_limits::highest()); } -// float -> int32 -// float -> int64 +// float32 -> int16 +// float64 -> int32 template -typename enable_if::value && - !is_floating_point::value, +typename enable_if::value && is_integral::value && + sizeof(TOut) < sizeof(TIn), bool>::type canConvertNumber(TIn value) { return value >= numeric_limits::lowest() && value <= numeric_limits::highest(); } +// float32 -> int32 +// float32 -> uint32 +// float32 -> int64 +// float32 -> uint64 +// float64 -> int64 +// float64 -> uint64 +template +typename enable_if::value && is_integral::value && + sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + // Avoid error "9.22337e+18 is outside the range of representable values of + // type 'long'" + return value >= numeric_limits::lowest() && + value <= FloatTraits::template highest_for(); +} + template TOut convertNumber(TIn value) { return canConvertNumber(value) ? TOut(value) : 0; From fc9d8aa31e1326e3420327cc511d788eae185e69 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 29 Mar 2022 13:03:07 +0200 Subject: [PATCH 021/202] Implement `nesting()` out of `VariantData` --- src/ArduinoJson/Array/ArrayRef.hpp | 10 +++++++--- src/ArduinoJson/Collection/CollectionData.hpp | 1 - src/ArduinoJson/Collection/CollectionImpl.hpp | 10 ---------- src/ArduinoJson/Document/JsonDocument.hpp | 2 +- src/ArduinoJson/Object/ObjectRef.hpp | 10 +++++++--- src/ArduinoJson/Variant/VariantData.hpp | 8 ++++---- src/ArduinoJson/Variant/VariantFunctions.hpp | 18 ++++++++++++++++++ src/ArduinoJson/Variant/VariantRef.hpp | 2 +- 8 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/ArduinoJson/Array/ArrayRef.hpp b/src/ArduinoJson/Array/ArrayRef.hpp index a5df7abcd..2c1d6ec63 100644 --- a/src/ArduinoJson/Array/ArrayRef.hpp +++ b/src/ArduinoJson/Array/ArrayRef.hpp @@ -23,8 +23,7 @@ template class ArrayRefBase { public: operator VariantConstRef() const { - const void* data = _data; // prevent warning cast-align - return VariantConstRef(reinterpret_cast(data)); + return VariantConstRef(getVariantData()); } template @@ -45,7 +44,7 @@ class ArrayRefBase { } FORCE_INLINE size_t nesting() const { - return _data ? _data->nesting() : 0; + return variantNesting(getVariantData()); } FORCE_INLINE size_t size() const { @@ -53,6 +52,11 @@ class ArrayRefBase { } protected: + const VariantData* getVariantData() const { + const void* data = _data; // prevent warning cast-align + return reinterpret_cast(data); + } + ArrayRefBase(TData* data) : _data(data) {} TData* _data; }; diff --git a/src/ArduinoJson/Collection/CollectionData.hpp b/src/ArduinoJson/Collection/CollectionData.hpp index 74b605466..36ffbf728 100644 --- a/src/ArduinoJson/Collection/CollectionData.hpp +++ b/src/ArduinoJson/Collection/CollectionData.hpp @@ -64,7 +64,6 @@ class CollectionData { void clear(); size_t memoryUsage() const; - size_t nesting() const; size_t size() const; VariantSlot *addSlot(MemoryPool *); diff --git a/src/ArduinoJson/Collection/CollectionImpl.hpp b/src/ArduinoJson/Collection/CollectionImpl.hpp index 35678eebf..a82917d7b 100644 --- a/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -203,16 +203,6 @@ inline size_t CollectionData::memoryUsage() const { return total; } -inline size_t CollectionData::nesting() const { - size_t maxChildNesting = 0; - for (VariantSlot* s = _head; s; s = s->next()) { - size_t childNesting = s->data()->nesting(); - if (childNesting > maxChildNesting) - maxChildNesting = childNesting; - } - return maxChildNesting + 1; -} - inline size_t CollectionData::size() const { return slotSize(_head); } diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index 274cce714..d9c4eb5bc 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/src/ArduinoJson/Document/JsonDocument.hpp @@ -60,7 +60,7 @@ class JsonDocument : public Visitable, } size_t nesting() const { - return _data.nesting(); + return variantNesting(&_data); } size_t capacity() const { diff --git a/src/ArduinoJson/Object/ObjectRef.hpp b/src/ArduinoJson/Object/ObjectRef.hpp index 6cf9c14d1..ba7ce89a1 100644 --- a/src/ArduinoJson/Object/ObjectRef.hpp +++ b/src/ArduinoJson/Object/ObjectRef.hpp @@ -18,8 +18,7 @@ template class ObjectRefBase { public: operator VariantConstRef() const { - const void* data = _data; // prevent warning cast-align - return VariantConstRef(reinterpret_cast(data)); + return VariantConstRef(getVariantData()); } template @@ -40,7 +39,7 @@ class ObjectRefBase { } FORCE_INLINE size_t nesting() const { - return _data ? _data->nesting() : 0; + return variantNesting(getVariantData()); } FORCE_INLINE size_t size() const { @@ -48,6 +47,11 @@ class ObjectRefBase { } protected: + const VariantData* getVariantData() const { + const void* data = _data; // prevent warning cast-align + return reinterpret_cast(data); + } + ObjectRefBase(TData* data) : _data(data) {} TData* _data; }; diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 27128706f..162eab0c1 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -91,6 +91,10 @@ class VariantData { return const_cast(this)->asArray(); } + const CollectionData *asCollection() const { + return isCollection() ? &_content.asCollection : 0; + } + CollectionData *asObject() { return isObject() ? &_content.asCollection : 0; } @@ -245,10 +249,6 @@ class VariantData { } } - size_t nesting() const { - return isCollection() ? _content.asCollection.nesting() : 0; - } - size_t size() const { return isCollection() ? _content.asCollection.size() : 0; } diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index 8890597e5..e2820ce06 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/src/ArduinoJson/Variant/VariantFunctions.hpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace ARDUINOJSON_NAMESPACE { @@ -106,4 +107,21 @@ inline bool variantIsNull(const VariantData *var) { return var == 0 || var->isNull(); } +inline size_t variantNesting(const VariantData *var) { + if (!var) + return 0; + + const CollectionData *collection = var->asCollection(); + if (!collection) + return 0; + + size_t maxChildNesting = 0; + for (const VariantSlot *s = collection->head(); s; s = s->next()) { + size_t childNesting = variantNesting(s->data()); + if (childNesting > maxChildNesting) + maxChildNesting = childNesting; + } + return maxChildNesting + 1; +} + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 31fd02198..e26ec2d55 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -41,7 +41,7 @@ class VariantRefBase : public VariantTag { } FORCE_INLINE size_t nesting() const { - return _data ? _data->nesting() : 0; + return variantNesting(_data); } size_t size() const { From 3760a643cbec74e3c791ce220477fa9bc68ce3ec Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 7 Apr 2022 21:37:00 +0200 Subject: [PATCH 022/202] Implement comparison out of `VariantData` --- src/ArduinoJson/Array/ArrayFunctions.hpp | 8 ----- src/ArduinoJson/Array/ArrayRef.hpp | 23 ++++++++++++-- src/ArduinoJson/Collection/CollectionData.hpp | 4 --- src/ArduinoJson/Collection/CollectionImpl.hpp | 31 ------------------- src/ArduinoJson/Object/ObjectFunctions.hpp | 8 ----- src/ArduinoJson/Object/ObjectRef.hpp | 16 ++++++++-- src/ArduinoJson/Variant/VariantCompare.hpp | 8 ++--- src/ArduinoJson/Variant/VariantFunctions.hpp | 2 -- 8 files changed, 37 insertions(+), 63 deletions(-) diff --git a/src/ArduinoJson/Array/ArrayFunctions.hpp b/src/ArduinoJson/Array/ArrayFunctions.hpp index 0b4d342f2..f08f56a1f 100644 --- a/src/ArduinoJson/Array/ArrayFunctions.hpp +++ b/src/ArduinoJson/Array/ArrayFunctions.hpp @@ -20,12 +20,4 @@ inline typename TVisitor::result_type arrayAccept(const CollectionData *arr, else return visitor.visitNull(); } - -inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) { - if (lhs == rhs) - return true; - if (!lhs || !rhs) - return false; - return lhs->equalsArray(*rhs); -} } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Array/ArrayRef.hpp b/src/ArduinoJson/Array/ArrayRef.hpp index 2c1d6ec63..2737c571a 100644 --- a/src/ArduinoJson/Array/ArrayRef.hpp +++ b/src/ArduinoJson/Array/ArrayRef.hpp @@ -83,7 +83,26 @@ class ArrayConstRef : public ArrayRefBase, FORCE_INLINE ArrayConstRef(const CollectionData* data) : base_type(data) {} FORCE_INLINE bool operator==(ArrayConstRef rhs) const { - return arrayEquals(_data, rhs._data); + if (_data == rhs._data) + return true; + if (!_data || !rhs._data) + return false; + + iterator it1 = begin(); + iterator it2 = rhs.begin(); + + for (;;) { + bool end1 = it1 == end(); + bool end2 = it2 == rhs.end(); + if (end1 && end2) + return true; + if (end1 || end2) + return false; + if (*it1 != *it2) + return false; + ++it1; + ++it2; + } } FORCE_INLINE VariantConstRef operator[](size_t index) const { @@ -138,7 +157,7 @@ class ArrayRef : public ArrayRefBase, } FORCE_INLINE bool operator==(ArrayRef rhs) const { - return arrayEquals(_data, rhs._data); + return ArrayConstRef(_data) == ArrayConstRef(rhs._data); } // Internal use diff --git a/src/ArduinoJson/Collection/CollectionData.hpp b/src/ArduinoJson/Collection/CollectionData.hpp index 36ffbf728..0483227e3 100644 --- a/src/ArduinoJson/Collection/CollectionData.hpp +++ b/src/ArduinoJson/Collection/CollectionData.hpp @@ -36,8 +36,6 @@ class CollectionData { void removeElement(size_t index); - bool equalsArray(const CollectionData &other) const; - // Object only template @@ -58,8 +56,6 @@ class CollectionData { template bool containsKey(const TAdaptedString &key) const; - bool equalsObject(const CollectionData &other) const; - // Generic void clear(); diff --git a/src/ArduinoJson/Collection/CollectionImpl.hpp b/src/ArduinoJson/Collection/CollectionImpl.hpp index a82917d7b..8f963baa2 100644 --- a/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -11,10 +11,6 @@ namespace ARDUINOJSON_NAMESPACE { -inline bool variantEquals(const VariantData* a, const VariantData* b) { - return variantCompare(a, b) == COMPARE_RESULT_EQUAL; -} - inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { VariantSlot* slot = pool->allocVariant(); if (!slot) @@ -77,33 +73,6 @@ inline bool CollectionData::copyFrom(const CollectionData& src, return true; } -inline bool CollectionData::equalsObject(const CollectionData& other) const { - size_t count = 0; - for (VariantSlot* slot = _head; slot; slot = slot->next()) { - VariantData* v1 = slot->data(); - VariantData* v2 = other.getMember(adaptString(slot->key())); - if (!variantEquals(v1, v2)) - return false; - count++; - } - return count == other.size(); -} - -inline bool CollectionData::equalsArray(const CollectionData& other) const { - VariantSlot* s1 = _head; - VariantSlot* s2 = other._head; - for (;;) { - if (s1 == s2) - return true; - if (!s1 || !s2) - return false; - if (!variantEquals(s1->data(), s2->data())) - return false; - s1 = s1->next(); - s2 = s2->next(); - } -} - template inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { if (key.isNull()) diff --git a/src/ArduinoJson/Object/ObjectFunctions.hpp b/src/ArduinoJson/Object/ObjectFunctions.hpp index 0f0b7ebff..df84d24d4 100644 --- a/src/ArduinoJson/Object/ObjectFunctions.hpp +++ b/src/ArduinoJson/Object/ObjectFunctions.hpp @@ -17,14 +17,6 @@ typename TVisitor::result_type objectAccept(const CollectionData *obj, return visitor.visitNull(); } -inline bool objectEquals(const CollectionData *lhs, const CollectionData *rhs) { - if (lhs == rhs) - return true; - if (!lhs || !rhs) - return false; - return lhs->equalsObject(*rhs); -} - template inline VariantData *objectGetMember(const CollectionData *obj, TAdaptedString key) { diff --git a/src/ArduinoJson/Object/ObjectRef.hpp b/src/ArduinoJson/Object/ObjectRef.hpp index ba7ce89a1..4b368a8ac 100644 --- a/src/ArduinoJson/Object/ObjectRef.hpp +++ b/src/ArduinoJson/Object/ObjectRef.hpp @@ -127,7 +127,19 @@ class ObjectConstRef : public ObjectRefBase, } FORCE_INLINE bool operator==(ObjectConstRef rhs) const { - return objectEquals(_data, rhs._data); + if (_data == rhs._data) + return true; + + if (!_data || !rhs._data) + return false; + + size_t count = 0; + for (iterator it = begin(); it != end(); ++it) { + if (it->value() != rhs[it->key()]) + return false; + count++; + } + return count == rhs.size(); } private: @@ -215,7 +227,7 @@ class ObjectRef : public ObjectRefBase, } FORCE_INLINE bool operator==(ObjectRef rhs) const { - return objectEquals(_data, rhs._data); + return ObjectConstRef(_data) == ObjectConstRef(rhs._data); } FORCE_INLINE void remove(iterator it) const { diff --git a/src/ArduinoJson/Variant/VariantCompare.hpp b/src/ArduinoJson/Variant/VariantCompare.hpp index 2f636156b..50967788b 100644 --- a/src/ArduinoJson/Variant/VariantCompare.hpp +++ b/src/ArduinoJson/Variant/VariantCompare.hpp @@ -89,7 +89,7 @@ struct ArrayComparer : ComparerBase { explicit ArrayComparer(const CollectionData &rhs) : _rhs(&rhs) {} CompareResult visitArray(const CollectionData &lhs) { - if (lhs.equalsArray(*_rhs)) + if (ArrayConstRef(&lhs) == ArrayConstRef(_rhs)) return COMPARE_RESULT_EQUAL; else return COMPARE_RESULT_DIFFER; @@ -102,7 +102,7 @@ struct ObjectComparer : ComparerBase { explicit ObjectComparer(const CollectionData &rhs) : _rhs(&rhs) {} CompareResult visitObject(const CollectionData &lhs) { - if (lhs.equalsObject(*_rhs)) + if (ObjectConstRef(&lhs) == ObjectConstRef(_rhs)) return COMPARE_RESULT_EQUAL; else return COMPARE_RESULT_DIFFER; @@ -201,8 +201,4 @@ CompareResult compare(const T1 &lhs, const T2 &rhs) { return lhs.accept(comparer); } -inline int variantCompare(const VariantData *a, const VariantData *b) { - return compare(VariantConstRef(a), VariantConstRef(b)); -} - } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index e2820ce06..51ffd64d8 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/src/ArduinoJson/Variant/VariantFunctions.hpp @@ -43,8 +43,6 @@ inline bool variantCopyFrom(VariantData *dst, const VariantData *src, return dst->copyFrom(*src, pool); } -inline int variantCompare(const VariantData *a, const VariantData *b); - inline void variantSetNull(VariantData *var) { if (!var) return; From d0e3808dd0c79fe874243de31942d235780d4630 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 13 Apr 2022 18:13:28 +0200 Subject: [PATCH 023/202] Move declaration of `VariantConstRef` above `VariantRef` --- src/ArduinoJson/Variant/VariantRef.hpp | 225 +++++++++++++------------ 1 file changed, 113 insertions(+), 112 deletions(-) diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index e26ec2d55..d01ca7517 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -57,6 +57,115 @@ class VariantRefBase : public VariantTag { } }; +class VariantConstRef : public VariantRefBase, + public VariantOperators, + public VariantShortcuts, + public Visitable { + typedef VariantRefBase base_type; + + public: + VariantConstRef() : base_type(0) {} + explicit VariantConstRef(const VariantData *data) : base_type(data) {} + + template + typename TVisitor::result_type accept(TVisitor &visitor) const { + return variantAccept(_data, visitor); + } + + template + FORCE_INLINE + typename enable_if::value && !is_same::value, + T>::type + as() const { + return Converter::fromJson(*this); + } + + template + FORCE_INLINE typename enable_if::value, const char *>::type + ARDUINOJSON_DEPRECATED("Replace as() with as()") + as() const { + return as(); + } + + template + FORCE_INLINE typename enable_if::value, char>::type + ARDUINOJSON_DEPRECATED( + "Support for char is deprecated, use int8_t or uint8_t instead") + as() const { + return static_cast(as()); + } + + template + FORCE_INLINE + typename enable_if::value && !is_same::value, + bool>::type + is() const { + return Converter::checkJson(*this); + } + + template + FORCE_INLINE typename enable_if::value, bool>::type + ARDUINOJSON_DEPRECATED("Replace is() with is()") + is() const { + return is(); + } + + template + FORCE_INLINE typename enable_if::value, bool>::type + ARDUINOJSON_DEPRECATED( + "Support for char is deprecated, use int8_t or uint8_t instead") + is() const { + return is(); + } + + template + FORCE_INLINE operator T() const { + return as(); + } + + FORCE_INLINE VariantConstRef getElement(size_t) const; + + FORCE_INLINE VariantConstRef operator[](size_t index) const { + return getElement(index); + } + + // getMember(const std::string&) const + // getMember(const String&) const + template + FORCE_INLINE VariantConstRef getMember(const TString &key) const { + return VariantConstRef( + objectGetMember(variantAsObject(_data), adaptString(key))); + } + + // getMember(char*) const + // getMember(const char*) const + // getMember(const __FlashStringHelper*) const + template + FORCE_INLINE VariantConstRef getMember(TChar *key) const { + const CollectionData *obj = variantAsObject(_data); + return VariantConstRef(obj ? obj->getMember(adaptString(key)) : 0); + } + + // operator[](const std::string&) const + // operator[](const String&) const + template + FORCE_INLINE + typename enable_if::value, VariantConstRef>::type + operator[](const TString &key) const { + return getMember(key); + } + + // operator[](char*) const + // operator[](const char*) const + // operator[](const __FlashStringHelper*) const + template + FORCE_INLINE + typename enable_if::value, VariantConstRef>::type + operator[](TChar *key) const { + return getMember(key); + } +}; + // A variant that can be a any value serializable to a JSON value. // // It can be set to: @@ -69,7 +178,6 @@ class VariantRef : public VariantRefBase, public VariantShortcuts, public Visitable { typedef VariantRefBase base_type; - friend class VariantConstRef; public: // Intenal use only @@ -150,6 +258,10 @@ class VariantRef : public VariantRefBase, return as(); } + FORCE_INLINE operator VariantConstRef() const { + return VariantConstRef(_data); + } + template typename TVisitor::result_type accept(TVisitor &visitor) const { return variantAccept(_data, visitor); @@ -228,117 +340,6 @@ class VariantRef : public VariantRefBase, } }; -class VariantConstRef : public VariantRefBase, - public VariantOperators, - public VariantShortcuts, - public Visitable { - typedef VariantRefBase base_type; - friend class VariantRef; - - public: - VariantConstRef() : base_type(0) {} - VariantConstRef(const VariantData *data) : base_type(data) {} - VariantConstRef(VariantRef var) : base_type(var._data) {} - - template - typename TVisitor::result_type accept(TVisitor &visitor) const { - return variantAccept(_data, visitor); - } - - template - FORCE_INLINE - typename enable_if::value && !is_same::value, - T>::type - as() const { - return Converter::fromJson(*this); - } - - template - FORCE_INLINE typename enable_if::value, const char *>::type - ARDUINOJSON_DEPRECATED("Replace as() with as()") - as() const { - return as(); - } - - template - FORCE_INLINE typename enable_if::value, char>::type - ARDUINOJSON_DEPRECATED( - "Support for char is deprecated, use int8_t or uint8_t instead") - as() const { - return static_cast(as()); - } - - template - FORCE_INLINE - typename enable_if::value && !is_same::value, - bool>::type - is() const { - return Converter::checkJson(*this); - } - - template - FORCE_INLINE typename enable_if::value, bool>::type - ARDUINOJSON_DEPRECATED("Replace is() with is()") - is() const { - return is(); - } - - template - FORCE_INLINE typename enable_if::value, bool>::type - ARDUINOJSON_DEPRECATED( - "Support for char is deprecated, use int8_t or uint8_t instead") - is() const { - return is(); - } - - template - FORCE_INLINE operator T() const { - return as(); - } - - FORCE_INLINE VariantConstRef getElement(size_t) const; - - FORCE_INLINE VariantConstRef operator[](size_t index) const { - return getElement(index); - } - - // getMember(const std::string&) const - // getMember(const String&) const - template - FORCE_INLINE VariantConstRef getMember(const TString &key) const { - return VariantConstRef( - objectGetMember(variantAsObject(_data), adaptString(key))); - } - - // getMember(char*) const - // getMember(const char*) const - // getMember(const __FlashStringHelper*) const - template - FORCE_INLINE VariantConstRef getMember(TChar *key) const { - const CollectionData *obj = variantAsObject(_data); - return VariantConstRef(obj ? obj->getMember(adaptString(key)) : 0); - } - - // operator[](const std::string&) const - // operator[](const String&) const - template - FORCE_INLINE - typename enable_if::value, VariantConstRef>::type - operator[](const TString &key) const { - return getMember(key); - } - - // operator[](char*) const - // operator[](const char*) const - // operator[](const __FlashStringHelper*) const - template - FORCE_INLINE - typename enable_if::value, VariantConstRef>::type - operator[](TChar *key) const { - return getMember(key); - } -}; - template <> struct Converter { static void toJson(VariantRef src, VariantRef dst) { From 98037e5742e6fad41d472117764466a7f89f7acf Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 14 Apr 2022 14:05:33 +0200 Subject: [PATCH 024/202] Move several functions from `VariantImpl.hpp` to `VariantRef.hpp` --- src/ArduinoJson/Variant/VariantImpl.hpp | 37 ------------------------- src/ArduinoJson/Variant/VariantRef.hpp | 34 +++++++++++++++++------ 2 files changed, 26 insertions(+), 45 deletions(-) diff --git a/src/ArduinoJson/Variant/VariantImpl.hpp b/src/ArduinoJson/Variant/VariantImpl.hpp index 48c45fb8f..c74cbde7e 100644 --- a/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/src/ArduinoJson/Variant/VariantImpl.hpp @@ -124,43 +124,6 @@ VariantRef::to() const { return *this; } -inline VariantConstRef VariantConstRef::getElement(size_t index) const { - return ArrayConstRef(_data != 0 ? _data->asArray() : 0)[index]; -} - -inline VariantRef VariantRef::addElement() const { - return VariantRef(_pool, variantAddElement(_data, _pool)); -} - -inline VariantRef VariantRef::getElement(size_t index) const { - return VariantRef(_pool, _data != 0 ? _data->getElement(index) : 0); -} - -inline VariantRef VariantRef::getOrAddElement(size_t index) const { - return VariantRef(_pool, variantGetOrAddElement(_data, index, _pool)); -} - -template -inline VariantRef VariantRef::getMember(TChar *key) const { - return VariantRef(_pool, _data != 0 ? _data->getMember(adaptString(key)) : 0); -} - -template -inline typename enable_if::value, VariantRef>::type -VariantRef::getMember(const TString &key) const { - return VariantRef(_pool, _data != 0 ? _data->getMember(adaptString(key)) : 0); -} - -template -inline VariantRef VariantRef::getOrAddMember(TChar *key) const { - return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool)); -} - -template -inline VariantRef VariantRef::getOrAddMember(const TString &key) const { - return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool)); -} - inline VariantConstRef operator|(VariantConstRef preferedValue, VariantConstRef defaultValue) { return preferedValue ? preferedValue : defaultValue; diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index d01ca7517..fb80de232 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -123,7 +123,9 @@ class VariantConstRef : public VariantRefBase, return as(); } - FORCE_INLINE VariantConstRef getElement(size_t) const; + FORCE_INLINE VariantConstRef getElement(size_t index) const { + return VariantConstRef(_data != 0 ? _data->getElement(index) : 0); + } FORCE_INLINE VariantConstRef operator[](size_t index) const { return getElement(index); @@ -282,33 +284,49 @@ class VariantRef : public VariantRefBase, typename enable_if::value, VariantRef>::type to() const; - VariantRef addElement() const; + VariantRef addElement() const { + return VariantRef(_pool, variantAddElement(_data, _pool)); + } - FORCE_INLINE VariantRef getElement(size_t) const; + FORCE_INLINE VariantRef getElement(size_t index) const { + return VariantRef(_pool, _data != 0 ? _data->getElement(index) : 0); + } - FORCE_INLINE VariantRef getOrAddElement(size_t) const; + FORCE_INLINE VariantRef getOrAddElement(size_t index) const { + return VariantRef(_pool, variantGetOrAddElement(_data, index, _pool)); + } // getMember(const char*) const // getMember(const __FlashStringHelper*) const template - FORCE_INLINE VariantRef getMember(TChar *) const; + FORCE_INLINE VariantRef getMember(TChar *key) const { + return VariantRef(_pool, + _data != 0 ? _data->getMember(adaptString(key)) : 0); + } // getMember(const std::string&) const // getMember(const String&) const template FORCE_INLINE typename enable_if::value, VariantRef>::type - getMember(const TString &) const; + getMember(const TString &key) const { + return VariantRef(_pool, + _data != 0 ? _data->getMember(adaptString(key)) : 0); + } // getOrAddMember(char*) const // getOrAddMember(const char*) const // getOrAddMember(const __FlashStringHelper*) const template - FORCE_INLINE VariantRef getOrAddMember(TChar *) const; + FORCE_INLINE VariantRef getOrAddMember(TChar *key) const { + return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool)); + } // getOrAddMember(const std::string&) const // getOrAddMember(const String&) const template - FORCE_INLINE VariantRef getOrAddMember(const TString &) const; + FORCE_INLINE VariantRef getOrAddMember(const TString &key) const { + return VariantRef(_pool, variantGetOrAddMember(_data, key, _pool)); + } FORCE_INLINE void remove(size_t index) const { if (_data) From ac1d29fac0faa546c6a6464b586a62ebb935147d Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 31 Mar 2022 11:23:24 +0200 Subject: [PATCH 025/202] Add meta function `ConverterNeedsWriteableRef` --- extras/tests/JsonVariant/converters.cpp | 12 ++++++++++++ src/ArduinoJson/Variant/ConverterImpl.hpp | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/extras/tests/JsonVariant/converters.cpp b/extras/tests/JsonVariant/converters.cpp index 3c4ad6d3e..785289e17 100644 --- a/extras/tests/JsonVariant/converters.cpp +++ b/extras/tests/JsonVariant/converters.cpp @@ -140,3 +140,15 @@ TEST_CASE("Custom converter with specialization") { REQUIRE(doc["value"]["imag"] == 3); } } + +TEST_CASE("ConverterNeedsWriteableRef") { + using namespace ARDUINOJSON_NAMESPACE; + CHECK(ConverterNeedsWriteableRef::value == false); + CHECK(ConverterNeedsWriteableRef::value == false); + CHECK(ConverterNeedsWriteableRef::value == true); + CHECK(ConverterNeedsWriteableRef::value == false); + CHECK(ConverterNeedsWriteableRef::value == true); + CHECK(ConverterNeedsWriteableRef::value == false); + CHECK(ConverterNeedsWriteableRef::value == true); + CHECK(ConverterNeedsWriteableRef::value == false); +} diff --git a/src/ArduinoJson/Variant/ConverterImpl.hpp b/src/ArduinoJson/Variant/ConverterImpl.hpp index baf1187ae..e02225921 100644 --- a/src/ArduinoJson/Variant/ConverterImpl.hpp +++ b/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -303,4 +303,18 @@ inline bool canConvertFromJson(VariantConstRef src, const std::string_view&) { #endif +template +struct ConverterNeedsWriteableRef { + protected: // <- to avoid GCC's "all member functions in class are private" + typedef char Yes[1]; + typedef char No[2]; + + static Yes& probe(T (*f)(VariantRef)); + static No& probe(T (*f)(VariantConstRef)); + + public: + static const bool value = + sizeof(probe(Converter::fromJson)) == sizeof(Yes); +}; + } // namespace ARDUINOJSON_NAMESPACE From 7b19a4b6e7af3fca3ba50bc4463a87f1f9824577 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 31 Mar 2022 11:59:00 +0200 Subject: [PATCH 026/202] Add `getMemberConst()` and `getElementConst()` --- src/ArduinoJson/Array/ArrayRef.hpp | 9 +++- src/ArduinoJson/Array/ElementProxy.hpp | 53 ++++++++++++++---- src/ArduinoJson/Document/JsonDocument.hpp | 26 ++++----- src/ArduinoJson/Object/MemberProxy.hpp | 65 ++++++++++++++++++----- src/ArduinoJson/Object/ObjectImpl.hpp | 4 +- src/ArduinoJson/Object/ObjectRef.hpp | 45 ++++++++++------ src/ArduinoJson/Variant/Converter.hpp | 3 ++ src/ArduinoJson/Variant/VariantRef.hpp | 42 +++++++++++---- 8 files changed, 181 insertions(+), 66 deletions(-) diff --git a/src/ArduinoJson/Array/ArrayRef.hpp b/src/ArduinoJson/Array/ArrayRef.hpp index 2737c571a..e890bc95d 100644 --- a/src/ArduinoJson/Array/ArrayRef.hpp +++ b/src/ArduinoJson/Array/ArrayRef.hpp @@ -106,10 +106,10 @@ class ArrayConstRef : public ArrayRefBase, } FORCE_INLINE VariantConstRef operator[](size_t index) const { - return getElement(index); + return getElementConst(index); } - FORCE_INLINE VariantConstRef getElement(size_t index) const { + FORCE_INLINE VariantConstRef getElementConst(size_t index) const { return VariantConstRef(_data ? _data->getElement(index) : 0); } }; @@ -170,6 +170,11 @@ class ArrayRef : public ArrayRefBase, return VariantRef(_pool, _data ? _data->getElement(index) : 0); } + // Gets the value at the specified index. + FORCE_INLINE VariantConstRef getElementConst(size_t index) const { + return VariantConstRef(_data ? _data->getElement(index) : 0); + } + // Removes element at specified position. FORCE_INLINE void remove(iterator it) const { if (!_data) diff --git a/src/ArduinoJson/Array/ElementProxy.hpp b/src/ArduinoJson/Array/ElementProxy.hpp index 821cfba3c..fe2d71cca 100644 --- a/src/ArduinoJson/Array/ElementProxy.hpp +++ b/src/ArduinoJson/Array/ElementProxy.hpp @@ -61,12 +61,20 @@ class ElementProxy : public VariantOperators >, } FORCE_INLINE bool isNull() const { - return getUpstreamElement().isNull(); + return getUpstreamElementConst().isNull(); } template - FORCE_INLINE typename enable_if::value, T>::type as() - const { + FORCE_INLINE typename enable_if::value && + !ConverterNeedsWriteableRef::value, + T>::type + as() const { + return getUpstreamElementConst().template as(); + } + + template + FORCE_INLINE typename enable_if::value, T>::type + as() const { return getUpstreamElement().template as(); } @@ -79,14 +87,23 @@ class ElementProxy : public VariantOperators >, template FORCE_INLINE operator T() const { - return getUpstreamElement(); + return as(); } template - FORCE_INLINE bool is() const { + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { return getUpstreamElement().template is(); } + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { + return getUpstreamElementConst().template is(); + } + template FORCE_INLINE typename VariantTo::type to() const { return getOrAddUpstreamElement().template to(); @@ -111,15 +128,15 @@ class ElementProxy : public VariantOperators >, template typename TVisitor::result_type accept(TVisitor& visitor) const { - return getUpstreamElement().accept(visitor); + return getUpstreamElementConst().accept(visitor); } FORCE_INLINE size_t size() const { - return getUpstreamElement().size(); + return getUpstreamElementConst().size(); } FORCE_INLINE size_t memoryUsage() const { - return getUpstreamElement().memoryUsage(); + return getUpstreamElementConst().memoryUsage(); } template @@ -132,6 +149,16 @@ class ElementProxy : public VariantOperators >, return getUpstreamElement().getMember(key); } + template + VariantConstRef getMemberConst(TNestedKey* key) const { + return getUpstreamElementConst().getMemberConst(key); + } + + template + VariantConstRef getMemberConst(const TNestedKey& key) const { + return getUpstreamElementConst().getMemberConst(key); + } + template VariantRef getOrAddMember(TNestedKey* key) const { return getOrAddUpstreamElement().getOrAddMember(key); @@ -150,6 +177,10 @@ class ElementProxy : public VariantOperators >, return getOrAddUpstreamElement().getElement(index); } + VariantConstRef getElementConst(size_t index) const { + return getUpstreamElementConst().getElementConst(index); + } + VariantRef getOrAddElement(size_t index) const { return getOrAddUpstreamElement().getOrAddElement(index); } @@ -178,12 +209,16 @@ class ElementProxy : public VariantOperators >, return _array.getElement(_index); } + FORCE_INLINE VariantConstRef getUpstreamElementConst() const { + return _array.getElementConst(_index); + } + FORCE_INLINE VariantRef getOrAddUpstreamElement() const { return _array.getOrAddElement(_index); } friend void convertToJson(const this_type& src, VariantRef dst) { - dst.set(src.getUpstreamElement()); + dst.set(src.getUpstreamElementConst()); } TArray _array; diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index d9c4eb5bc..d1d7cb63a 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/src/ArduinoJson/Document/JsonDocument.hpp @@ -140,14 +140,14 @@ class JsonDocument : public Visitable, // containsKey(const __FlashStringHelper*) const template bool containsKey(TChar* key) const { - return !getMember(key).isUnbound(); + return !getMemberConst(key).isUnbound(); } // containsKey(const std::string&) const // containsKey(const String&) const template bool containsKey(const TString& key) const { - return !getMember(key).isUnbound(); + return !getMemberConst(key).isUnbound(); } // operator[](const std::string&) @@ -175,7 +175,7 @@ class JsonDocument : public Visitable, FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](const TString& key) const { - return getMember(key); + return getMemberConst(key); } // operator[](char*) const @@ -185,7 +185,7 @@ class JsonDocument : public Visitable, FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](TChar* key) const { - return getMember(key); + return getMemberConst(key); } FORCE_INLINE ElementProxy operator[](size_t index) { @@ -193,14 +193,14 @@ class JsonDocument : public Visitable, } FORCE_INLINE VariantConstRef operator[](size_t index) const { - return getElement(index); + return getElementConst(index); } FORCE_INLINE VariantRef getElement(size_t index) { return VariantRef(&_pool, _data.getElement(index)); } - FORCE_INLINE VariantConstRef getElement(size_t index) const { + FORCE_INLINE VariantConstRef getElementConst(size_t index) const { return VariantConstRef(_data.getElement(index)); } @@ -208,20 +208,20 @@ class JsonDocument : public Visitable, return VariantRef(&_pool, _data.getOrAddElement(index, &_pool)); } - // JsonVariantConst getMember(char*) const - // JsonVariantConst getMember(const char*) const - // JsonVariantConst getMember(const __FlashStringHelper*) const + // JsonVariantConst getMemberConst(char*) const + // JsonVariantConst getMemberConst(const char*) const + // JsonVariantConst getMemberConst(const __FlashStringHelper*) const template - FORCE_INLINE VariantConstRef getMember(TChar* key) const { + FORCE_INLINE VariantConstRef getMemberConst(TChar* key) const { return VariantConstRef(_data.getMember(adaptString(key))); } - // JsonVariantConst getMember(const std::string&) const - // JsonVariantConst getMember(const String&) const + // JsonVariantConst getMemberConst(const std::string&) const + // JsonVariantConst getMemberConst(const String&) const template FORCE_INLINE typename enable_if::value, VariantConstRef>::type - getMember(const TString& key) const { + getMemberConst(const TString& key) const { return VariantConstRef(_data.getMember(adaptString(key))); } diff --git a/src/ArduinoJson/Object/MemberProxy.hpp b/src/ArduinoJson/Object/MemberProxy.hpp index 9a1bc83fd..b0cbd9d28 100644 --- a/src/ArduinoJson/Object/MemberProxy.hpp +++ b/src/ArduinoJson/Object/MemberProxy.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,7 @@ class MemberProxy : public VariantOperators >, : _object(src._object), _key(src._key) {} FORCE_INLINE operator VariantConstRef() const { - return getUpstreamMember(); + return getUpstreamMemberConst(); } FORCE_INLINE this_type &operator=(const this_type &src) { @@ -64,12 +65,20 @@ class MemberProxy : public VariantOperators >, } FORCE_INLINE bool isNull() const { - return getUpstreamMember().isNull(); + return getUpstreamMemberConst().isNull(); } template - FORCE_INLINE typename enable_if::value, T>::type as() - const { + FORCE_INLINE typename enable_if::value && + !ConverterNeedsWriteableRef::value, + T>::type + as() const { + return getUpstreamMemberConst().template as(); + } + + template + FORCE_INLINE typename enable_if::value, T>::type + as() const { return getUpstreamMember().template as(); } @@ -82,20 +91,29 @@ class MemberProxy : public VariantOperators >, template FORCE_INLINE operator T() const { - return getUpstreamMember(); + return as(); } - template - FORCE_INLINE bool is() const { - return getUpstreamMember().template is(); + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { + return getUpstreamMember().template is(); + } + + template + FORCE_INLINE + typename enable_if::value, bool>::type + is() const { + return getUpstreamMemberConst().template is(); } FORCE_INLINE size_t size() const { - return getUpstreamMember().size(); + return getUpstreamMemberConst().size(); } FORCE_INLINE size_t memoryUsage() const { - return getUpstreamMember().memoryUsage(); + return getUpstreamMemberConst().memoryUsage(); } FORCE_INLINE void remove(size_t index) const { @@ -137,7 +155,7 @@ class MemberProxy : public VariantOperators >, template typename TVisitor::result_type accept(TVisitor &visitor) const { - return getUpstreamMember().accept(visitor); + return getUpstreamMemberConst().accept(visitor); } FORCE_INLINE VariantRef addElement() const { @@ -148,6 +166,10 @@ class MemberProxy : public VariantOperators >, return getUpstreamMember().getElement(index); } + FORCE_INLINE VariantConstRef getElementConst(size_t index) const { + return getUpstreamMemberConst().getElementConst(index); + } + FORCE_INLINE VariantRef getOrAddElement(size_t index) const { return getOrAddUpstreamMember().getOrAddElement(index); } @@ -167,6 +189,21 @@ class MemberProxy : public VariantOperators >, return getUpstreamMember().getMember(key); } + // getMemberConst(char*) const + // getMemberConst(const char*) const + // getMemberConst(const __FlashStringHelper*) const + template + FORCE_INLINE VariantConstRef getMemberConst(TChar *key) const { + return getUpstreamMemberConst().getMemberConst(key); + } + + // getMemberConst(const std::string&) const + // getMemberConst(const String&) const + template + FORCE_INLINE VariantConstRef getMemberConst(const TString &key) const { + return getUpstreamMemberConst().getMemberConst(key); + } + // getOrAddMember(char*) const // getOrAddMember(const char*) const // getOrAddMember(const __FlashStringHelper*) const @@ -187,12 +224,16 @@ class MemberProxy : public VariantOperators >, return _object.getMember(_key); } + FORCE_INLINE VariantConstRef getUpstreamMemberConst() const { + return _object.getMemberConst(_key); + } + FORCE_INLINE VariantRef getOrAddUpstreamMember() const { return _object.getOrAddMember(_key); } friend void convertToJson(const this_type &src, VariantRef dst) { - dst.set(src.getUpstreamMember()); + dst.set(src.getUpstreamMemberConst()); } TObject _object; diff --git a/src/ArduinoJson/Object/ObjectImpl.hpp b/src/ArduinoJson/Object/ObjectImpl.hpp index 66793a772..ba3276281 100644 --- a/src/ArduinoJson/Object/ObjectImpl.hpp +++ b/src/ArduinoJson/Object/ObjectImpl.hpp @@ -40,14 +40,14 @@ template template inline typename enable_if::value, bool>::type ObjectShortcuts::containsKey(const TString& key) const { - return !impl()->getMember(key).isUnbound(); + return !impl()->getMemberConst(key).isUnbound(); } template template inline typename enable_if::value, bool>::type ObjectShortcuts::containsKey(TChar* key) const { - return !impl()->getMember(key).isUnbound(); + return !impl()->getMemberConst(key).isUnbound(); } template diff --git a/src/ArduinoJson/Object/ObjectRef.hpp b/src/ArduinoJson/Object/ObjectRef.hpp index 4b368a8ac..934452c15 100644 --- a/src/ArduinoJson/Object/ObjectRef.hpp +++ b/src/ArduinoJson/Object/ObjectRef.hpp @@ -81,7 +81,7 @@ class ObjectConstRef : public ObjectRefBase, // containsKey(const String&) const template FORCE_INLINE bool containsKey(const TString& key) const { - return !getMember(key).isUnbound(); + return !getMemberConst(key).isUnbound(); } // containsKey(char*) const @@ -89,22 +89,22 @@ class ObjectConstRef : public ObjectRefBase, // containsKey(const __FlashStringHelper*) const template FORCE_INLINE bool containsKey(TChar* key) const { - return !getMember(key).isUnbound(); + return !getMemberConst(key).isUnbound(); } - // getMember(const std::string&) const - // getMember(const String&) const + // getMemberConst(const std::string&) const + // getMemberConst(const String&) const template - FORCE_INLINE VariantConstRef getMember(const TString& key) const { - return get_impl(adaptString(key)); + FORCE_INLINE VariantConstRef getMemberConst(const TString& key) const { + return VariantConstRef(objectGetMember(_data, adaptString(key))); } - // getMember(char*) const - // getMember(const char*) const - // getMember(const __FlashStringHelper*) const + // getMemberConst(char*) const + // getMemberConst(const char*) const + // getMemberConst(const __FlashStringHelper*) const template - FORCE_INLINE VariantConstRef getMember(TChar* key) const { - return get_impl(adaptString(key)); + FORCE_INLINE VariantConstRef getMemberConst(TChar* key) const { + return VariantConstRef(objectGetMember(_data, adaptString(key))); } // operator[](const std::string&) const @@ -113,7 +113,7 @@ class ObjectConstRef : public ObjectRefBase, FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](const TString& key) const { - return get_impl(adaptString(key)); + return getMemberConst(key); } // operator[](char*) const @@ -123,7 +123,7 @@ class ObjectConstRef : public ObjectRefBase, FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](TChar* key) const { - return get_impl(adaptString(key)); + return getMemberConst(key); } FORCE_INLINE bool operator==(ObjectConstRef rhs) const { @@ -143,10 +143,6 @@ class ObjectConstRef : public ObjectRefBase, } private: - template - FORCE_INLINE VariantConstRef get_impl(TAdaptedString key) const { - return VariantConstRef(objectGetMember(_data, key)); - } }; class ObjectRef : public ObjectRefBase, @@ -207,6 +203,21 @@ class ObjectRef : public ObjectRefBase, return VariantRef(_pool, objectGetMember(_data, adaptString(key))); } + // getMemberConst(const std::string&) const + // getMemberConst(const String&) const + template + FORCE_INLINE VariantConstRef getMemberConst(const TString& key) const { + return VariantConstRef(objectGetMember(_data, adaptString(key))); + } + + // getMemberConst(char*) const + // getMemberConst(const char*) const + // getMemberConst(const __FlashStringHelper*) const + template + FORCE_INLINE VariantConstRef getMemberConst(TChar* key) const { + return VariantConstRef(objectGetMember(_data, adaptString(key))); + } + // getOrAddMember(const std::string&) const // getOrAddMember(const String&) const template diff --git a/src/ArduinoJson/Variant/Converter.hpp b/src/ArduinoJson/Variant/Converter.hpp index f9ce9ab7a..72f9dfd40 100644 --- a/src/ArduinoJson/Variant/Converter.hpp +++ b/src/ArduinoJson/Variant/Converter.hpp @@ -14,4 +14,7 @@ template class InvalidConversion; // Error here? See https://arduinojson.org/v6/invalid-conversion/ // clang-format on +template +struct ConverterNeedsWriteableRef; + } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index fb80de232..a26e62a30 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -123,27 +123,27 @@ class VariantConstRef : public VariantRefBase, return as(); } - FORCE_INLINE VariantConstRef getElement(size_t index) const { + FORCE_INLINE VariantConstRef getElementConst(size_t index) const { return VariantConstRef(_data != 0 ? _data->getElement(index) : 0); } FORCE_INLINE VariantConstRef operator[](size_t index) const { - return getElement(index); + return getElementConst(index); } - // getMember(const std::string&) const - // getMember(const String&) const + // getMemberConst(const std::string&) const + // getMemberConst(const String&) const template - FORCE_INLINE VariantConstRef getMember(const TString &key) const { + FORCE_INLINE VariantConstRef getMemberConst(const TString &key) const { return VariantConstRef( objectGetMember(variantAsObject(_data), adaptString(key))); } - // getMember(char*) const - // getMember(const char*) const - // getMember(const __FlashStringHelper*) const + // getMemberConst(char*) const + // getMemberConst(const char*) const + // getMemberConst(const __FlashStringHelper*) const template - FORCE_INLINE VariantConstRef getMember(TChar *key) const { + FORCE_INLINE VariantConstRef getMemberConst(TChar *key) const { const CollectionData *obj = variantAsObject(_data); return VariantConstRef(obj ? obj->getMember(adaptString(key)) : 0); } @@ -154,7 +154,7 @@ class VariantConstRef : public VariantRefBase, FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](const TString &key) const { - return getMember(key); + return getMemberConst(key); } // operator[](char*) const @@ -164,7 +164,7 @@ class VariantConstRef : public VariantRefBase, FORCE_INLINE typename enable_if::value, VariantConstRef>::type operator[](TChar *key) const { - return getMember(key); + return getMemberConst(key); } }; @@ -292,6 +292,10 @@ class VariantRef : public VariantRefBase, return VariantRef(_pool, _data != 0 ? _data->getElement(index) : 0); } + FORCE_INLINE VariantConstRef getElementConst(size_t index) const { + return VariantConstRef(_data != 0 ? _data->getElement(index) : 0); + } + FORCE_INLINE VariantRef getOrAddElement(size_t index) const { return VariantRef(_pool, variantGetOrAddElement(_data, index, _pool)); } @@ -313,6 +317,22 @@ class VariantRef : public VariantRefBase, _data != 0 ? _data->getMember(adaptString(key)) : 0); } + // getMemberConst(const char*) const + // getMemberConst(const __FlashStringHelper*) const + template + FORCE_INLINE VariantConstRef getMemberConst(TChar *key) const { + return VariantConstRef(_data ? _data->getMember(adaptString(key)) : 0); + } + + // getMemberConst(const std::string&) const + // getMemberConst(const String&) const + template + FORCE_INLINE + typename enable_if::value, VariantConstRef>::type + getMemberConst(const TString &key) const { + return VariantConstRef(_data ? _data->getMember(adaptString(key)) : 0); + } + // getOrAddMember(char*) const // getOrAddMember(const char*) const // getOrAddMember(const __FlashStringHelper*) const From 1d103a15284daf1b380c4423be962ace2fb818f8 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 27 Apr 2022 14:58:37 +0200 Subject: [PATCH 027/202] MemberProxy: move tests for `createNestedArray()` and `createNestedObject()` --- extras/tests/JsonDocument/MemberProxy.cpp | 33 +++++++++++++++++++++++ extras/tests/JsonVariant/createNested.cpp | 28 ------------------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index 64cd06a05..087f53928 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -285,3 +285,36 @@ TEST_CASE("MemberProxy cast to JsonVariant") { CHECK(doc.as() == "{\"hello\":\"toto\"}"); } + +TEST_CASE("MemberProxy::createNestedArray()") { + StaticJsonDocument<1024> doc; + JsonArray arr = doc["items"].createNestedArray(); + arr.add(42); + + CHECK(doc["items"][0][0] == 42); +} + +TEST_CASE("MemberProxy::createNestedArray(key)") { + StaticJsonDocument<1024> doc; + JsonArray arr = doc["weather"].createNestedArray("temp"); + arr.add(42); + + CHECK(doc["weather"]["temp"][0] == 42); +} + +TEST_CASE("MemberProxy::createNestedObject()") { + StaticJsonDocument<1024> doc; + JsonObject obj = doc["items"].createNestedObject(); + obj["value"] = 42; + + CHECK(doc["items"][0]["value"] == 42); +} + +TEST_CASE("MemberProxy::createNestedObject(key)") { + StaticJsonDocument<1024> doc; + JsonObject obj = doc["status"].createNestedObject("weather"); + obj["temp"] = 42; + + CHECK(doc["status"]["weather"]["temp"] == 42); +} + diff --git a/extras/tests/JsonVariant/createNested.cpp b/extras/tests/JsonVariant/createNested.cpp index 34c74c470..efd750158 100644 --- a/extras/tests/JsonVariant/createNested.cpp +++ b/extras/tests/JsonVariant/createNested.cpp @@ -18,13 +18,6 @@ TEST_CASE("JsonVariant::createNestedObject()") { REQUIRE(variant[0]["value"] == 42); REQUIRE(obj.isNull() == false); } - - SECTION("works on MemberProxy") { - JsonObject obj = variant["items"].createNestedObject(); - obj["value"] = 42; - - REQUIRE(variant["items"][0]["value"] == 42); - } } TEST_CASE("JsonVariant::createNestedArray()") { @@ -37,13 +30,6 @@ TEST_CASE("JsonVariant::createNestedArray()") { REQUIRE(variant.is() == true); REQUIRE(arr.isNull() == false); } - - SECTION("works on MemberProxy") { - JsonArray arr = variant["items"].createNestedArray(); - arr.add(42); - - REQUIRE(variant["items"][0][0] == 42); - } } TEST_CASE("JsonVariant::createNestedObject(key)") { @@ -57,13 +43,6 @@ TEST_CASE("JsonVariant::createNestedObject(key)") { REQUIRE(variant.is() == true); REQUIRE(variant["weather"]["temp"] == 42); } - - SECTION("works on MemberProxy") { - JsonObject obj = variant["status"].createNestedObject("weather"); - obj["temp"] = 42; - - REQUIRE(variant["status"]["weather"]["temp"] == 42); - } } TEST_CASE("JsonVariant::createNestedArray(key)") { @@ -76,11 +55,4 @@ TEST_CASE("JsonVariant::createNestedArray(key)") { REQUIRE(variant.is() == true); REQUIRE(arr.isNull() == false); } - - SECTION("works on MemberProxy") { - JsonArray arr = variant["weather"].createNestedArray("temp"); - arr.add(42); - - REQUIRE(variant["weather"]["temp"][0] == 42); - } } From 5577d18377452ed84c49f9df865d1ee663bb84da Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 27 Apr 2022 15:00:27 +0200 Subject: [PATCH 028/202] JsonVariant: add tests for `size()` --- extras/tests/JsonVariant/CMakeLists.txt | 3 +- extras/tests/JsonVariant/size.cpp | 44 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 extras/tests/JsonVariant/size.cpp diff --git a/extras/tests/JsonVariant/CMakeLists.txt b/extras/tests/JsonVariant/CMakeLists.txt index 134c67511..78f810e12 100644 --- a/extras/tests/JsonVariant/CMakeLists.txt +++ b/extras/tests/JsonVariant/CMakeLists.txt @@ -8,8 +8,8 @@ add_executable(JsonVariantTests clear.cpp compare.cpp containsKey.cpp - copy.cpp converters.cpp + copy.cpp createNested.cpp is.cpp isnull.cpp @@ -20,6 +20,7 @@ add_executable(JsonVariantTests overflow.cpp remove.cpp set.cpp + size.cpp subscript.cpp types.cpp unbound.cpp diff --git a/extras/tests/JsonVariant/size.cpp b/extras/tests/JsonVariant/size.cpp new file mode 100644 index 000000000..a08aaead0 --- /dev/null +++ b/extras/tests/JsonVariant/size.cpp @@ -0,0 +1,44 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2022, Benoit BLANCHON +// MIT License + +#include +#include + +TEST_CASE("JsonVariant::size()") { + DynamicJsonDocument doc(4096); + JsonVariant variant = doc.to(); + + SECTION("unbound reference") { + JsonVariant unbound; + + CHECK(unbound.size() == 0); + } + + SECTION("int") { + variant.set(42); + + CHECK(variant.size() == 0); + } + + SECTION("string") { + variant.set("hello"); + + CHECK(variant.size() == 0); + } + + SECTION("object") { + variant["a"] = 1; + variant["b"] = 2; + + CHECK(variant.size() == 2); + } + + SECTION("linked object") { + StaticJsonDocument<1024> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + + CHECK(variant.size() == 1); + } +} From 3d6c328a4fc06704830c271feb9b4c123fe23eb0 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Wed, 27 Apr 2022 15:06:58 +0200 Subject: [PATCH 029/202] Add `JsonVariant::link()` (resolves #1343) --- CHANGELOG.md | 1 + extras/tests/Cpp11/nullptr.cpp | 8 ++ extras/tests/JsonDocument/ElementProxy.cpp | 8 ++ extras/tests/JsonDocument/MemberProxy.cpp | 7 ++ extras/tests/JsonDocument/size.cpp | 17 +++ extras/tests/JsonVariant/CMakeLists.txt | 1 + extras/tests/JsonVariant/add.cpp | 10 ++ extras/tests/JsonVariant/as.cpp | 93 +++++++++++++++ extras/tests/JsonVariant/clear.cpp | 11 ++ extras/tests/JsonVariant/compare.cpp | 29 +++++ extras/tests/JsonVariant/containsKey.cpp | 13 ++- extras/tests/JsonVariant/copy.cpp | 10 ++ extras/tests/JsonVariant/createNested.cpp | 44 +++++++ extras/tests/JsonVariant/is.cpp | 110 ++++++++++++++++++ extras/tests/JsonVariant/isnull.cpp | 35 ++++-- extras/tests/JsonVariant/link.cpp | 77 ++++++++++++ extras/tests/JsonVariant/memoryUsage.cpp | 8 ++ extras/tests/JsonVariant/nesting.cpp | 8 ++ extras/tests/JsonVariant/or.cpp | 8 ++ extras/tests/JsonVariant/remove.cpp | 20 ++++ extras/tests/JsonVariant/size.cpp | 9 ++ extras/tests/JsonVariant/subscript.cpp | 54 +++++++++ src/ArduinoJson/Array/ArrayRef.hpp | 2 +- src/ArduinoJson/Array/ElementProxy.hpp | 4 + src/ArduinoJson/Document/JsonDocument.hpp | 2 +- src/ArduinoJson/Json/JsonSerializer.hpp | 4 +- src/ArduinoJson/Json/PrettyJsonSerializer.hpp | 4 +- src/ArduinoJson/MsgPack/MsgPackSerializer.hpp | 4 +- src/ArduinoJson/Object/MemberProxy.hpp | 4 + src/ArduinoJson/Object/ObjectRef.hpp | 2 +- src/ArduinoJson/Variant/ConverterImpl.hpp | 26 ++--- src/ArduinoJson/Variant/VariantContent.hpp | 3 + src/ArduinoJson/Variant/VariantData.hpp | 22 +++- src/ArduinoJson/Variant/VariantFunctions.hpp | 12 +- src/ArduinoJson/Variant/VariantRef.hpp | 30 +++-- 35 files changed, 649 insertions(+), 51 deletions(-) create mode 100644 extras/tests/JsonVariant/link.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index f7a3fca97..d2d0d4c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ArduinoJson: change log HEAD ---- +* Add `JsonVariant::link()` (issue #1343) * Fix `9.22337e+18 is outside the range of representable values of type 'long'` v6.19.4 (2022-04-05) diff --git a/extras/tests/Cpp11/nullptr.cpp b/extras/tests/Cpp11/nullptr.cpp index 813b9cc2a..ae0eb634c 100644 --- a/extras/tests/Cpp11/nullptr.cpp +++ b/extras/tests/Cpp11/nullptr.cpp @@ -43,5 +43,13 @@ TEST_CASE("nullptr") { variant.clear(); REQUIRE(variant.is() == true); + + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + REQUIRE(variant.is() == false); + + doc2.clear(); + REQUIRE(variant.is() == true); } } diff --git a/extras/tests/JsonDocument/ElementProxy.cpp b/extras/tests/JsonDocument/ElementProxy.cpp index 0fe3c4713..e54560c17 100644 --- a/extras/tests/JsonDocument/ElementProxy.cpp +++ b/extras/tests/JsonDocument/ElementProxy.cpp @@ -245,3 +245,11 @@ TEST_CASE("ElementProxy cast to JsonVariant") { CHECK(doc.as() == "[\"toto\"]"); } + +TEST_CASE("ElementProxy::link()") { + StaticJsonDocument<1024> doc1, doc2; + doc1[0].link(doc2); + doc2["hello"] = "world"; + + CHECK(doc1.as() == "[{\"hello\":\"world\"}]"); +} diff --git a/extras/tests/JsonDocument/MemberProxy.cpp b/extras/tests/JsonDocument/MemberProxy.cpp index 087f53928..a4de1257f 100644 --- a/extras/tests/JsonDocument/MemberProxy.cpp +++ b/extras/tests/JsonDocument/MemberProxy.cpp @@ -318,3 +318,10 @@ TEST_CASE("MemberProxy::createNestedObject(key)") { CHECK(doc["status"]["weather"]["temp"] == 42); } +TEST_CASE("MemberProxy::link()") { + StaticJsonDocument<1024> doc1, doc2; + doc1["obj"].link(doc2); + doc2["hello"] = "world"; + + CHECK(doc1.as() == "{\"obj\":{\"hello\":\"world\"}}"); +} diff --git a/extras/tests/JsonDocument/size.cpp b/extras/tests/JsonDocument/size.cpp index 4fbb6e23c..683cc7334 100644 --- a/extras/tests/JsonDocument/size.cpp +++ b/extras/tests/JsonDocument/size.cpp @@ -25,4 +25,21 @@ TEST_CASE("JsonDocument::size()") { REQUIRE(doc.size() == 2); } + + SECTION("linked array") { + StaticJsonDocument<128> doc2; + doc2.add(1); + doc2.add(2); + doc.as().link(doc2); + + REQUIRE(doc.size() == 2); + } + + SECTION("linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + doc.as().link(doc2); + + REQUIRE(doc.size() == 1); + } } diff --git a/extras/tests/JsonVariant/CMakeLists.txt b/extras/tests/JsonVariant/CMakeLists.txt index 78f810e12..35cf3818f 100644 --- a/extras/tests/JsonVariant/CMakeLists.txt +++ b/extras/tests/JsonVariant/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(JsonVariantTests createNested.cpp is.cpp isnull.cpp + link.cpp memoryUsage.cpp misc.cpp nesting.cpp diff --git a/extras/tests/JsonVariant/add.cpp b/extras/tests/JsonVariant/add.cpp index 526e914d8..fec99c5a0 100644 --- a/extras/tests/JsonVariant/add.cpp +++ b/extras/tests/JsonVariant/add.cpp @@ -43,4 +43,14 @@ TEST_CASE("JsonVariant::add()") { REQUIRE(var.as() == "{\"val\":123}"); } + + SECTION("add to linked array") { + StaticJsonDocument<1024> doc2; + doc2.add(42); + var.link(doc2); + + var.add(666); // no-op + + CHECK(var.as() == "[42]"); + } } diff --git a/extras/tests/JsonVariant/as.cpp b/extras/tests/JsonVariant/as.cpp index c9956778b..43c301857 100644 --- a/extras/tests/JsonVariant/as.cpp +++ b/extras/tests/JsonVariant/as.cpp @@ -267,4 +267,97 @@ TEST_CASE("JsonVariant::as()") { REQUIRE(variant.as() == ONE); } + + SECTION("linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + + SECTION("as()") { + CHECK(variant.as() == "{\"hello\":\"world\"}"); + } + + SECTION("as()") { + JsonArray a = variant.as(); + CHECK(a.isNull() == true); + } + + SECTION("as()") { + JsonObject o = variant.as(); + CHECK(o.isNull() == true); + } + + SECTION("as()") { + JsonObjectConst o = variant.as(); + CHECK(o.isNull() == false); + CHECK(o.size() == 1); + CHECK(o["hello"] == "world"); + } + } + + SECTION("linked array") { + StaticJsonDocument<128> doc2; + doc2.add("hello"); + doc2.add("world"); + variant.link(doc2); + + SECTION("as()") { + CHECK(variant.as() == "[\"hello\",\"world\"]"); + } + + SECTION("as()") { + JsonArray a = variant.as(); + CHECK(a.isNull() == true); + } + + SECTION("as()") { + JsonArrayConst a = variant.as(); + CHECK(a.isNull() == false); + CHECK(a.size() == 2); + CHECK(a[0] == "hello"); + CHECK(a[1] == "world"); + } + + SECTION("as()") { + JsonObject o = variant.as(); + CHECK(o.isNull() == true); + } + } + + SECTION("linked int") { + StaticJsonDocument<128> doc2; + doc2.set(42); + variant.link(doc2); + + CHECK(variant.as() == 42); + CHECK(variant.as() == 42.0); + } + + SECTION("linked double") { + StaticJsonDocument<128> doc2; + doc2.set(42.0); + variant.link(doc2); + + CHECK(variant.as() == 42); + CHECK(variant.as() == 42.0); + } + + SECTION("linked string") { + StaticJsonDocument<128> doc2; + doc2.set("hello"); + variant.link(doc2); + + CHECK(variant.as() == "hello"); + } + + SECTION("linked bool") { + StaticJsonDocument<128> doc2; + variant.link(doc2); + + doc2.set(true); + CHECK(variant.as() == true); + + doc2.set(false); + CHECK(variant.as() == false); + } } diff --git a/extras/tests/JsonVariant/clear.cpp b/extras/tests/JsonVariant/clear.cpp index 2b40e324d..173a7dea3 100644 --- a/extras/tests/JsonVariant/clear.cpp +++ b/extras/tests/JsonVariant/clear.cpp @@ -23,4 +23,15 @@ TEST_CASE("JsonVariant::clear()") { REQUIRE(var.isNull() == true); } + + SECTION("doesn't alter linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + var.link(doc2); + + var.clear(); + + CHECK(var.isNull() == true); + CHECK(doc2.as() == "{\"hello\":\"world\"}"); + } } diff --git a/extras/tests/JsonVariant/compare.cpp b/extras/tests/JsonVariant/compare.cpp index 63adbc261..c6211a55f 100644 --- a/extras/tests/JsonVariant/compare.cpp +++ b/extras/tests/JsonVariant/compare.cpp @@ -34,6 +34,20 @@ TEST_CASE("Compare JsonVariant with value") { CHECK_FALSE(a < b); CHECK_FALSE(a > b); } + + SECTION("linked 42 vs 42") { + StaticJsonDocument<128> doc2; + doc2.set(42); + a.link(doc2); + int b = 42; + + CHECK(a == b); + CHECK(a <= b); + CHECK(a >= b); + CHECK_FALSE(a != b); + CHECK_FALSE(a < b); + CHECK_FALSE(a > b); + } } TEST_CASE("Compare JsonVariant with JsonVariant") { @@ -313,4 +327,19 @@ TEST_CASE("Compare JsonVariant with JsonVariant") { CHECK_FALSE(a > b); CHECK_FALSE(a >= b); } + + SECTION("linked 42 vs link 42") { + StaticJsonDocument<128> doc2, doc3; + doc2.set(42); + doc3.set(42); + a.link(doc2); + b.link(doc3); + + CHECK(a == b); + CHECK(a <= b); + CHECK(a >= b); + CHECK_FALSE(a != b); + CHECK_FALSE(a < b); + CHECK_FALSE(a > b); + } } diff --git a/extras/tests/JsonVariant/containsKey.cpp b/extras/tests/JsonVariant/containsKey.cpp index 51bf500f0..739e33ecf 100644 --- a/extras/tests/JsonVariant/containsKey.cpp +++ b/extras/tests/JsonVariant/containsKey.cpp @@ -12,19 +12,28 @@ TEST_CASE("JsonVariant::containsKey()") { DynamicJsonDocument doc(4096); JsonVariant var = doc.to(); - SECTION("containsKey(const char*) returns true") { + SECTION("containsKey(const char*)") { var["hello"] = "world"; REQUIRE(var.containsKey("hello") == true); REQUIRE(var.containsKey("world") == false); } - SECTION("containsKey(std::string) returns true") { + SECTION("containsKey(std::string)") { var["hello"] = "world"; REQUIRE(var.containsKey(std::string("hello")) == true); REQUIRE(var.containsKey(std::string("world")) == false); } + + SECTION("linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + var.link(doc2); + + CHECK(var.containsKey("hello") == true); + CHECK(var.containsKey("world") == false); + } } TEST_CASE("JsonVariantConst::containsKey()") { diff --git a/extras/tests/JsonVariant/copy.cpp b/extras/tests/JsonVariant/copy.cpp index 7784f0cad..b8f96297b 100644 --- a/extras/tests/JsonVariant/copy.cpp +++ b/extras/tests/JsonVariant/copy.cpp @@ -84,6 +84,16 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7)); } + SECTION("stores linked object by pointer") { + StaticJsonDocument<128> doc3; + doc3["hello"] = "world"; + var1.link(doc3); + var2.set(var1); + + REQUIRE(doc1.memoryUsage() == 0); + REQUIRE(doc2.memoryUsage() == 0); + } + SECTION("destination is unbound") { JsonVariant unboundVariant; diff --git a/extras/tests/JsonVariant/createNested.cpp b/extras/tests/JsonVariant/createNested.cpp index efd750158..0636762e9 100644 --- a/extras/tests/JsonVariant/createNested.cpp +++ b/extras/tests/JsonVariant/createNested.cpp @@ -18,6 +18,17 @@ TEST_CASE("JsonVariant::createNestedObject()") { REQUIRE(variant[0]["value"] == 42); REQUIRE(obj.isNull() == false); } + + SECTION("does nothing on linked array") { + StaticJsonDocument<128> doc2; + doc2[0] = 42; + variant.link(doc2); + + variant.createNestedObject(); + + CHECK(variant.size() == 1); + CHECK(variant[0] == 42); + } } TEST_CASE("JsonVariant::createNestedArray()") { @@ -30,6 +41,17 @@ TEST_CASE("JsonVariant::createNestedArray()") { REQUIRE(variant.is() == true); REQUIRE(arr.isNull() == false); } + + SECTION("does nothing on linked array") { + StaticJsonDocument<128> doc2; + doc2[0] = 42; + variant.link(doc2); + + variant.createNestedArray(); + + CHECK(variant.size() == 1); + CHECK(variant[0] == 42); + } } TEST_CASE("JsonVariant::createNestedObject(key)") { @@ -43,6 +65,17 @@ TEST_CASE("JsonVariant::createNestedObject(key)") { REQUIRE(variant.is() == true); REQUIRE(variant["weather"]["temp"] == 42); } + + SECTION("does nothing on linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + + variant.createNestedObject("weather"); + + CHECK(variant.size() == 1); + CHECK(variant["hello"] == "world"); + } } TEST_CASE("JsonVariant::createNestedArray(key)") { @@ -55,4 +88,15 @@ TEST_CASE("JsonVariant::createNestedArray(key)") { REQUIRE(variant.is() == true); REQUIRE(arr.isNull() == false); } + + SECTION("does nothing on linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + + variant.createNestedArray("items"); + + CHECK(variant.size() == 1); + CHECK(variant["hello"] == "world"); + } } diff --git a/extras/tests/JsonVariant/is.cpp b/extras/tests/JsonVariant/is.cpp index 6d04f0afe..0cc63e3de 100644 --- a/extras/tests/JsonVariant/is.cpp +++ b/extras/tests/JsonVariant/is.cpp @@ -144,6 +144,24 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == false); } + SECTION("linked array") { + StaticJsonDocument<1024> doc2; + doc2[0] = "world"; + variant.link(doc2); + + CHECK(variant.is() == false); + CHECK(variant.is() == true); + CHECK(variant.is() == true); + CHECK(variant.is() == true); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + } + SECTION("JsonObject") { variant.to(); @@ -161,6 +179,44 @@ TEST_CASE("JsonVariant::is()") { CHECK(variant.is() == true); CHECK(variant.is() == true); } + + SECTION("linked object") { + StaticJsonDocument<1024> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + + CHECK(variant.is() == false); + CHECK(variant.is() == true); + CHECK(variant.is() == true); + CHECK(variant.is() == true); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == true); + CHECK(variant.is() == true); + } + + SECTION("linked int") { + StaticJsonDocument<1024> doc2; + doc2.set(42); + variant.link(doc2); + + CHECK(variant.is() == false); + CHECK(variant.is() == true); + CHECK(variant.is() == false); + CHECK(variant.is() == true); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == true); + CHECK(variant.is() == true); + CHECK(variant.is() == false); + CHECK(variant.is() == false); + CHECK(variant.is() == true); + } } TEST_CASE("JsonVariantConst::is()") { @@ -316,4 +372,58 @@ TEST_CASE("JsonVariantConst::is()") { CHECK(cvariant.is() == false); CHECK(cvariant.is() == false); } + + SECTION("linked array") { + StaticJsonDocument<1024> doc2; + doc2[0] = "world"; + variant.link(doc2); + + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + } + + SECTION("linked object") { + StaticJsonDocument<1024> doc2; + doc2["hello"] = "world"; + variant.link(doc2); + + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + } + + SECTION("linked int") { + StaticJsonDocument<1024> doc2; + doc2.set(42); + variant.link(doc2); + + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == true); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == false); + CHECK(cvariant.is() == true); + } } diff --git a/extras/tests/JsonVariant/isnull.cpp b/extras/tests/JsonVariant/isnull.cpp index 7e29039f5..ec5e0a34d 100644 --- a/extras/tests/JsonVariant/isnull.cpp +++ b/extras/tests/JsonVariant/isnull.cpp @@ -9,17 +9,17 @@ TEST_CASE("JsonVariant::isNull()") { DynamicJsonDocument doc(4096); JsonVariant variant = doc.to(); - SECTION("return true when Undefined") { + SECTION("returns true when Undefined") { REQUIRE(variant.isNull() == true); } - SECTION("return false when Integer") { + SECTION("returns false when Integer") { variant.set(42); REQUIRE(variant.isNull() == false); } - SECTION("return false when EmptyArray") { + SECTION("returns false when EmptyArray") { DynamicJsonDocument doc2(4096); JsonArray array = doc2.to(); @@ -27,7 +27,7 @@ TEST_CASE("JsonVariant::isNull()") { REQUIRE(variant.isNull() == false); } - SECTION("return false when EmptyObject") { + SECTION("returns false when EmptyObject") { DynamicJsonDocument doc2(4096); JsonObject obj = doc2.to(); @@ -35,41 +35,54 @@ TEST_CASE("JsonVariant::isNull()") { REQUIRE(variant.isNull() == false); } - SECTION("return true after set(JsonArray())") { + SECTION("returns true after set(JsonArray())") { variant.set(JsonArray()); REQUIRE(variant.isNull() == true); } - SECTION("return true after set(JsonObject())") { + SECTION("returns true after set(JsonObject())") { variant.set(JsonObject()); REQUIRE(variant.isNull() == true); } - SECTION("return false after set('hello')") { + SECTION("returns false after set('hello')") { variant.set("hello"); REQUIRE(variant.isNull() == false); } - SECTION("return true after set((char*)0)") { + SECTION("returns true after set((char*)0)") { variant.set(static_cast(0)); REQUIRE(variant.isNull() == true); } - SECTION("return true after set((const char*)0)") { + SECTION("returns true after set((const char*)0)") { variant.set(static_cast(0)); REQUIRE(variant.isNull() == true); } - SECTION("return true after set(serialized((char*)0))") { + SECTION("returns true after set(serialized((char*)0))") { variant.set(serialized(static_cast(0))); REQUIRE(variant.isNull() == true); } - SECTION("return true after set(serialized((const char*)0))") { + SECTION("returns true after set(serialized((const char*)0))") { variant.set(serialized(static_cast(0))); REQUIRE(variant.isNull() == true); } + SECTION("returns true for a linked null") { + StaticJsonDocument<128> doc2; + variant.link(doc2); + CHECK(variant.isNull() == true); + } + + SECTION("returns false for a linked array") { + StaticJsonDocument<128> doc2; + doc2[0] = 42; + variant.link(doc2); + CHECK(variant.isNull() == false); + } + SECTION("works with JsonVariantConst") { variant.set(42); diff --git a/extras/tests/JsonVariant/link.cpp b/extras/tests/JsonVariant/link.cpp new file mode 100644 index 000000000..28ef2ecd0 --- /dev/null +++ b/extras/tests/JsonVariant/link.cpp @@ -0,0 +1,77 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2022, Benoit BLANCHON +// MIT License + +#include +#include + +TEST_CASE("JsonVariant::link()") { + StaticJsonDocument<1024> doc1, doc2; + JsonVariant variant = doc1.to(); + + SECTION("JsonVariant::link(JsonDocument&)") { + doc2["hello"] = "world"; + + variant.link(doc2); + + CHECK(variant.as() == "{\"hello\":\"world\"}"); + CHECK(variant.memoryUsage() == 0); + + // altering the linked document should change the result + doc2["hello"] = "WORLD!"; + + CHECK(variant.as() == "{\"hello\":\"WORLD!\"}"); + } + + SECTION("JsonVariant::link(MemberProxy)") { + doc2["obj"]["hello"] = "world"; + + variant.link(doc2["obj"]); + + CHECK(variant.as() == "{\"hello\":\"world\"}"); + CHECK(variant.memoryUsage() == 0); + + // altering the linked document should change the result + doc2["obj"]["hello"] = "WORLD!"; + + CHECK(variant.as() == "{\"hello\":\"WORLD!\"}"); + } + + SECTION("JsonVariant::link(ElementProxy)") { + doc2[0]["hello"] = "world"; + + variant.link(doc2[0]); + + CHECK(variant.as() == "{\"hello\":\"world\"}"); + CHECK(variant.memoryUsage() == 0); + + // altering the linked document should change the result + doc2[0]["hello"] = "WORLD!"; + + CHECK(variant.as() == "{\"hello\":\"WORLD!\"}"); + } + + SECTION("target is unbound") { + JsonVariant unbound; + variant["hello"] = "world"; + + variant.link(unbound); + + CHECK(variant.isUnbound() == false); + CHECK(variant.isNull() == true); + CHECK(variant.memoryUsage() == 0); + CHECK(variant.size() == 0); + } + + SECTION("variant is unbound") { + JsonVariant unbound; + doc2["hello"] = "world"; + + unbound.link(doc2); + + CHECK(unbound.isUnbound() == true); + CHECK(unbound.isNull() == true); + CHECK(unbound.memoryUsage() == 0); + CHECK(unbound.size() == 0); + } +} diff --git a/extras/tests/JsonVariant/memoryUsage.cpp b/extras/tests/JsonVariant/memoryUsage.cpp index 59587dbba..5c18b906b 100644 --- a/extras/tests/JsonVariant/memoryUsage.cpp +++ b/extras/tests/JsonVariant/memoryUsage.cpp @@ -38,4 +38,12 @@ TEST_CASE("JsonVariant::memoryUsage()") { REQUIRE(var.memoryUsage() == 6); REQUIRE(var.memoryUsage() == doc.memoryUsage()); } + + SECTION("ignore size of linked document") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + var.link(doc2); + CHECK(var.memoryUsage() == 0); + CHECK(var.memoryUsage() == doc.memoryUsage()); + } } diff --git a/extras/tests/JsonVariant/nesting.cpp b/extras/tests/JsonVariant/nesting.cpp index 3e936fbe4..e7602e510 100644 --- a/extras/tests/JsonVariant/nesting.cpp +++ b/extras/tests/JsonVariant/nesting.cpp @@ -28,4 +28,12 @@ TEST_CASE("JsonVariant::nesting()") { var.to(); REQUIRE(var.nesting() == 1); } + + SECTION("returns depth of linked array") { + StaticJsonDocument<128> doc2; + doc2[0][0] = 42; + var.link(doc2); + + CHECK(var.nesting() == 2); + } } diff --git a/extras/tests/JsonVariant/or.cpp b/extras/tests/JsonVariant/or.cpp index 7d1c190b8..41bd2f794 100644 --- a/extras/tests/JsonVariant/or.cpp +++ b/extras/tests/JsonVariant/or.cpp @@ -156,4 +156,12 @@ TEST_CASE("JsonVariant::operator|()") { int result = variant | 42; REQUIRE(result == 42); } + + SECTION("linked int | int") { + StaticJsonDocument<128> doc2; + doc2.set(42); + variant.link(doc2); + int result = variant | 666; + CHECK(result == 42); + } } diff --git a/extras/tests/JsonVariant/remove.cpp b/extras/tests/JsonVariant/remove.cpp index c744e19bb..adaa07615 100644 --- a/extras/tests/JsonVariant/remove.cpp +++ b/extras/tests/JsonVariant/remove.cpp @@ -39,4 +39,24 @@ TEST_CASE("JsonVariant::remove()") { REQUIRE(var.as() == "{\"a\":1}"); } + + SECTION("linked array") { + StaticJsonDocument<128> doc2; + doc2[0] = 42; + var.link(doc2); + + var.remove(0); + + CHECK(var.as() == "[42]"); + } + + SECTION("linked object") { + StaticJsonDocument<128> doc2; + doc2["hello"] = "world"; + var.link(doc2); + + var.remove("hello"); + + CHECK(var.as() == "{\"hello\":\"world\"}"); + } } diff --git a/extras/tests/JsonVariant/size.cpp b/extras/tests/JsonVariant/size.cpp index a08aaead0..9db3c538d 100644 --- a/extras/tests/JsonVariant/size.cpp +++ b/extras/tests/JsonVariant/size.cpp @@ -41,4 +41,13 @@ TEST_CASE("JsonVariant::size()") { CHECK(variant.size() == 1); } + + SECTION("linked array") { + StaticJsonDocument<1024> doc2; + doc2.add(1); + doc2.add(2); + variant.link(doc2); + + CHECK(variant.size() == 2); + } } diff --git a/extras/tests/JsonVariant/subscript.cpp b/extras/tests/JsonVariant/subscript.cpp index f466cd275..a1bba08c6 100644 --- a/extras/tests/JsonVariant/subscript.cpp +++ b/extras/tests/JsonVariant/subscript.cpp @@ -129,6 +129,44 @@ TEST_CASE("JsonVariant::operator[]") { REQUIRE(std::string("world") == variant[vla]); } #endif + + SECTION("get value from linked object") { + StaticJsonDocument<1024> doc2; + doc2["hello"] = "world"; + var.link(doc2); + + CHECK(var["hello"].as() == "world"); + } + + SECTION("set value to linked object") { + StaticJsonDocument<1024> doc2; + doc2["hello"] = "world"; + var.link(doc2); + + var["tutu"] = "toto"; // no-op + + CHECK(doc.as() == "{\"hello\":\"world\"}"); + CHECK(doc2.as() == "{\"hello\":\"world\"}"); + } + + SECTION("get value from linked array") { + StaticJsonDocument<1024> doc2; + doc2.add(42); + var.link(doc2); + + CHECK(var[0].as() == 42); + } + + SECTION("set value to linked array") { + StaticJsonDocument<1024> doc2; + doc2.add(42); + var.link(doc2); + + var[0] = 666; // no-op + + CHECK(doc.as() == "[42]"); + CHECK(doc2.as() == "[42]"); + } } TEST_CASE("JsonVariantConst::operator[]") { @@ -201,4 +239,20 @@ TEST_CASE("JsonVariantConst::operator[]") { REQUIRE(var.is() == false); REQUIRE(value == 0); } + + SECTION("get value from linked object") { + StaticJsonDocument<1024> doc2; + doc2["hello"] = "world"; + var.link(doc2); + + CHECK(cvar["hello"].as() == "world"); + } + + SECTION("get value from linked array") { + StaticJsonDocument<1024> doc2; + doc2.add(42); + var.link(doc2); + + CHECK(cvar[0].as() == 42); + } } diff --git a/src/ArduinoJson/Array/ArrayRef.hpp b/src/ArduinoJson/Array/ArrayRef.hpp index e890bc95d..e5962a498 100644 --- a/src/ArduinoJson/Array/ArrayRef.hpp +++ b/src/ArduinoJson/Array/ArrayRef.hpp @@ -211,7 +211,7 @@ struct Converter { static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isArray(); + return data && data->resolve()->isArray(); } }; diff --git a/src/ArduinoJson/Array/ElementProxy.hpp b/src/ArduinoJson/Array/ElementProxy.hpp index fe2d71cca..490d17eee 100644 --- a/src/ArduinoJson/Array/ElementProxy.hpp +++ b/src/ArduinoJson/Array/ElementProxy.hpp @@ -109,6 +109,10 @@ class ElementProxy : public VariantOperators >, return getOrAddUpstreamElement().template to(); } + FORCE_INLINE void link(VariantConstRef value) const { + getOrAddUpstreamElement().link(value); + } + // Replaces the value // // bool set(const TValue&) diff --git a/src/ArduinoJson/Document/JsonDocument.hpp b/src/ArduinoJson/Document/JsonDocument.hpp index d1d7cb63a..9f0892a3f 100644 --- a/src/ArduinoJson/Document/JsonDocument.hpp +++ b/src/ArduinoJson/Document/JsonDocument.hpp @@ -68,7 +68,7 @@ class JsonDocument : public Visitable, } size_t size() const { - return _data.size(); + return _data.resolve()->size(); } bool set(const JsonDocument& src) { diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index db5803c96..7c6f64243 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -25,7 +25,7 @@ class JsonSerializer : public Visitor { VariantSlot *slot = array.head(); while (slot != 0) { - slot->data()->accept(*this); + slot->data()->resolve()->accept(*this); slot = slot->next(); if (slot == 0) @@ -46,7 +46,7 @@ class JsonSerializer : public Visitor { while (slot != 0) { _formatter.writeString(slot->key()); write(':'); - slot->data()->accept(*this); + slot->data()->resolve()->accept(*this); slot = slot->next(); if (slot == 0) diff --git a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp index cbd66540b..3f7fd2629 100644 --- a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp +++ b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp @@ -25,7 +25,7 @@ class PrettyJsonSerializer : public JsonSerializer { _nesting++; while (slot != 0) { indent(); - slot->data()->accept(*this); + slot->data()->resolve()->accept(*this); slot = slot->next(); base::write(slot ? ",\r\n" : "\r\n"); @@ -48,7 +48,7 @@ class PrettyJsonSerializer : public JsonSerializer { indent(); base::visitString(slot->key()); base::write(": "); - slot->data()->accept(*this); + slot->data()->resolve()->accept(*this); slot = slot->next(); base::write(slot ? ",\r\n" : "\r\n"); diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index a1053f0a4..8cf99801a 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -56,7 +56,7 @@ class MsgPackSerializer : public Visitor { writeInteger(uint32_t(n)); } for (VariantSlot* slot = array.head(); slot; slot = slot->next()) { - slot->data()->accept(*this); + slot->data()->resolve()->accept(*this); } return bytesWritten(); } @@ -74,7 +74,7 @@ class MsgPackSerializer : public Visitor { } for (VariantSlot* slot = object.head(); slot; slot = slot->next()) { visitString(slot->key()); - slot->data()->accept(*this); + slot->data()->resolve()->accept(*this); } return bytesWritten(); } diff --git a/src/ArduinoJson/Object/MemberProxy.hpp b/src/ArduinoJson/Object/MemberProxy.hpp index b0cbd9d28..c7f93e943 100644 --- a/src/ArduinoJson/Object/MemberProxy.hpp +++ b/src/ArduinoJson/Object/MemberProxy.hpp @@ -135,6 +135,10 @@ class MemberProxy : public VariantOperators >, getUpstreamMember().remove(key); } + FORCE_INLINE void link(VariantConstRef value) { + getOrAddUpstreamMember().link(value); + } + template FORCE_INLINE typename VariantTo::type to() { return getOrAddUpstreamMember().template to(); diff --git a/src/ArduinoJson/Object/ObjectRef.hpp b/src/ArduinoJson/Object/ObjectRef.hpp index 934452c15..3ef65c87c 100644 --- a/src/ArduinoJson/Object/ObjectRef.hpp +++ b/src/ArduinoJson/Object/ObjectRef.hpp @@ -278,7 +278,7 @@ struct Converter { static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isObject(); + return data && data->resolve()->isObject(); } }; diff --git a/src/ArduinoJson/Variant/ConverterImpl.hpp b/src/ArduinoJson/Variant/ConverterImpl.hpp index e02225921..a510aa23a 100644 --- a/src/ArduinoJson/Variant/ConverterImpl.hpp +++ b/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -48,12 +48,12 @@ struct Converter< static T fromJson(VariantConstRef src) { ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); const VariantData* data = getData(src); - return data ? data->asIntegral() : T(); + return data ? data->resolve()->asIntegral() : T(); } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isInteger(); + return data && data->resolve()->isInteger(); } }; @@ -65,12 +65,12 @@ struct Converter::value>::type> { static T fromJson(VariantConstRef src) { const VariantData* data = getData(src); - return data ? static_cast(data->asIntegral()) : T(); + return data ? static_cast(data->resolve()->asIntegral()) : T(); } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isInteger(); + return data && data->resolve()->isInteger(); } }; @@ -84,12 +84,12 @@ struct Converter { static bool fromJson(VariantConstRef src) { const VariantData* data = getData(src); - return data ? data->asBoolean() : false; + return data ? data->resolve()->asBoolean() : false; } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isBoolean(); + return data && data->resolve()->isBoolean(); } }; @@ -103,12 +103,12 @@ struct Converter::value>::type> { static T fromJson(VariantConstRef src) { const VariantData* data = getData(src); - return data ? data->asFloat() : false; + return data ? data->resolve()->asFloat() : 0; } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isFloat(); + return data && data->resolve()->isFloat(); } }; @@ -121,12 +121,12 @@ struct Converter { static const char* fromJson(VariantConstRef src) { const VariantData* data = getData(src); - return data ? data->asString().c_str() : 0; + return data ? data->resolve()->asString().c_str() : 0; } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isString(); + return data && data->resolve()->isString(); } }; @@ -139,12 +139,12 @@ struct Converter { static String fromJson(VariantConstRef src) { const VariantData* data = getData(src); - return data ? data->asString() : 0; + return data ? data->resolve()->asString() : 0; } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data && data->isString(); + return data && data->resolve()->isString(); } }; @@ -192,7 +192,7 @@ struct Converter { } static bool checkJson(VariantConstRef src) { const VariantData* data = getData(src); - return data == 0 || data->isNull(); + return data == 0 || data->resolve()->isNull(); } }; diff --git a/src/ArduinoJson/Variant/VariantContent.hpp b/src/ArduinoJson/Variant/VariantContent.hpp index dbe833e5b..3ede76ef3 100644 --- a/src/ArduinoJson/Variant/VariantContent.hpp +++ b/src/ArduinoJson/Variant/VariantContent.hpp @@ -31,6 +31,8 @@ enum { VALUE_IS_SIGNED_INTEGER = 0x0A, VALUE_IS_FLOAT = 0x0C, + VALUE_IS_POINTER = 0x10, + COLLECTION_MASK = 0x60, VALUE_IS_OBJECT = 0x20, VALUE_IS_ARRAY = 0x40, @@ -49,6 +51,7 @@ union VariantContent { UInt asUnsignedInteger; Integer asSignedInteger; CollectionData asCollection; + const class VariantData *asPointer; struct { const char *data; size_t size; diff --git a/src/ArduinoJson/Variant/VariantData.hpp b/src/ArduinoJson/Variant/VariantData.hpp index 162eab0c1..e99400fdb 100644 --- a/src/ArduinoJson/Variant/VariantData.hpp +++ b/src/ArduinoJson/Variant/VariantData.hpp @@ -83,6 +83,12 @@ class VariantData { bool asBoolean() const; + const VariantData *resolve() const { + if (isPointer()) + return _content.asPointer->resolve(); + return this; + } + CollectionData *asArray() { return isArray() ? &_content.asCollection : 0; } @@ -117,6 +123,10 @@ class VariantData { return (_flags & COLLECTION_MASK) != 0; } + bool isPointer() const { + return type() == VALUE_IS_POINTER; + } + template bool isInteger() const { switch (type()) { @@ -212,6 +222,12 @@ class VariantData { setType(VALUE_IS_NULL); } + void setPointer(const VariantData *p) { + ARDUINOJSON_ASSERT(p); + setType(VALUE_IS_POINTER); + _content.asPointer = p; + } + void setString(String s) { ARDUINOJSON_ASSERT(s); if (s.isLinked()) @@ -262,7 +278,8 @@ class VariantData { } VariantData *getElement(size_t index) const { - return isArray() ? _content.asCollection.getElement(index) : 0; + const CollectionData *col = asArray(); + return col ? col->getElement(index) : 0; } VariantData *getOrAddElement(size_t index, MemoryPool *pool) { @@ -275,7 +292,8 @@ class VariantData { template VariantData *getMember(TAdaptedString key) const { - return isObject() ? _content.asCollection.getMember(key) : 0; + const CollectionData *col = asObject(); + return col ? col->getMember(key) : 0; } template diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index 51ffd64d8..c6b64f8a0 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/src/ArduinoJson/Variant/VariantFunctions.hpp @@ -15,17 +15,17 @@ template inline typename TVisitor::result_type variantAccept(const VariantData *var, TVisitor &visitor) { if (var != 0) - return var->accept(visitor); + return var->resolve()->accept(visitor); else return visitor.visitNull(); } inline const CollectionData *variantAsArray(const VariantData *var) { - return var != 0 ? var->asArray() : 0; + return var != 0 ? var->resolve()->asArray() : 0; } inline const CollectionData *variantAsObject(const VariantData *var) { - return var != 0 ? var->asObject() : 0; + return var != 0 ? var->resolve()->asObject() : 0; } inline CollectionData *variantAsObject(VariantData *var) { @@ -56,7 +56,7 @@ inline bool variantSetString(VariantData *var, TAdaptedString value, } inline size_t variantSize(const VariantData *var) { - return var != 0 ? var->size() : 0; + return var != 0 ? var->resolve()->size() : 0; } inline CollectionData *variantToArray(VariantData *var) { @@ -102,14 +102,14 @@ NO_INLINE VariantData *variantGetOrAddMember(VariantData *var, } inline bool variantIsNull(const VariantData *var) { - return var == 0 || var->isNull(); + return var == 0 || var->resolve()->isNull(); } inline size_t variantNesting(const VariantData *var) { if (!var) return 0; - const CollectionData *collection = var->asCollection(); + const CollectionData *collection = var->resolve()->asCollection(); if (!collection) return 0; diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index a26e62a30..7a6c45225 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -124,7 +124,8 @@ class VariantConstRef : public VariantRefBase, } FORCE_INLINE VariantConstRef getElementConst(size_t index) const { - return VariantConstRef(_data != 0 ? _data->getElement(index) : 0); + return VariantConstRef(_data != 0 ? _data->resolve()->getElement(index) + : 0); } FORCE_INLINE VariantConstRef operator[](size_t index) const { @@ -135,8 +136,8 @@ class VariantConstRef : public VariantRefBase, // getMemberConst(const String&) const template FORCE_INLINE VariantConstRef getMemberConst(const TString &key) const { - return VariantConstRef( - objectGetMember(variantAsObject(_data), adaptString(key))); + return VariantConstRef(_data ? _data->resolve()->getMember(adaptString(key)) + : 0); } // getMemberConst(char*) const @@ -144,8 +145,8 @@ class VariantConstRef : public VariantRefBase, // getMemberConst(const __FlashStringHelper*) const template FORCE_INLINE VariantConstRef getMemberConst(TChar *key) const { - const CollectionData *obj = variantAsObject(_data); - return VariantConstRef(obj ? obj->getMember(adaptString(key)) : 0); + return VariantConstRef(_data ? _data->resolve()->getMember(adaptString(key)) + : 0); } // operator[](const std::string&) const @@ -293,7 +294,8 @@ class VariantRef : public VariantRefBase, } FORCE_INLINE VariantConstRef getElementConst(size_t index) const { - return VariantConstRef(_data != 0 ? _data->getElement(index) : 0); + return VariantConstRef(_data != 0 ? _data->resolve()->getElement(index) + : 0); } FORCE_INLINE VariantRef getOrAddElement(size_t index) const { @@ -321,7 +323,8 @@ class VariantRef : public VariantRefBase, // getMemberConst(const __FlashStringHelper*) const template FORCE_INLINE VariantConstRef getMemberConst(TChar *key) const { - return VariantConstRef(_data ? _data->getMember(adaptString(key)) : 0); + return VariantConstRef(_data ? _data->resolve()->getMember(adaptString(key)) + : 0); } // getMemberConst(const std::string&) const @@ -330,7 +333,8 @@ class VariantRef : public VariantRefBase, FORCE_INLINE typename enable_if::value, VariantConstRef>::type getMemberConst(const TString &key) const { - return VariantConstRef(_data ? _data->getMember(adaptString(key)) : 0); + return VariantConstRef(_data ? _data->resolve()->getMember(adaptString(key)) + : 0); } // getOrAddMember(char*) const @@ -370,6 +374,16 @@ class VariantRef : public VariantRefBase, _data->remove(adaptString(key)); } + inline void link(VariantConstRef target) { + if (!_data) + return; + const VariantData *targetData = getData(target); + if (targetData) + _data->setPointer(targetData); + else + _data->setNull(); + } + private: MemoryPool *_pool; From 37faa7ce13390ef056e560338b37378ee70b0c88 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sun, 22 May 2022 17:03:09 +0200 Subject: [PATCH 030/202] Merge definitions of `VariantConstRef::accept()` and `VariantRef::accept()` --- src/ArduinoJson/Variant/VariantRef.hpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 7a6c45225..4ab82cd7e 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -28,6 +28,11 @@ class ObjectRef; template class VariantRefBase : public VariantTag { public: + template + typename TVisitor::result_type accept(TVisitor &visitor) const { + return variantAccept(_data, visitor); + } + FORCE_INLINE bool isNull() const { return variantIsNull(_data); } @@ -67,11 +72,6 @@ class VariantConstRef : public VariantRefBase, VariantConstRef() : base_type(0) {} explicit VariantConstRef(const VariantData *data) : base_type(data) {} - template - typename TVisitor::result_type accept(TVisitor &visitor) const { - return variantAccept(_data, visitor); - } - template FORCE_INLINE typename enable_if::value && !is_same::value, @@ -265,11 +265,6 @@ class VariantRef : public VariantRefBase, return VariantConstRef(_data); } - template - typename TVisitor::result_type accept(TVisitor &visitor) const { - return variantAccept(_data, visitor); - } - // Change the type of the variant // // ArrayRef to() From ee74c3bb1cb22124c0ca4a1b21f96fcf49132060 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sun, 22 May 2022 17:07:16 +0200 Subject: [PATCH 031/202] Inline `variantAccept()` --- src/ArduinoJson/Variant/VariantFunctions.hpp | 9 --------- src/ArduinoJson/Variant/VariantRef.hpp | 4 +++- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ArduinoJson/Variant/VariantFunctions.hpp b/src/ArduinoJson/Variant/VariantFunctions.hpp index c6b64f8a0..379ea9c0a 100644 --- a/src/ArduinoJson/Variant/VariantFunctions.hpp +++ b/src/ArduinoJson/Variant/VariantFunctions.hpp @@ -11,15 +11,6 @@ namespace ARDUINOJSON_NAMESPACE { -template -inline typename TVisitor::result_type variantAccept(const VariantData *var, - TVisitor &visitor) { - if (var != 0) - return var->resolve()->accept(visitor); - else - return visitor.visitNull(); -} - inline const CollectionData *variantAsArray(const VariantData *var) { return var != 0 ? var->resolve()->asArray() : 0; } diff --git a/src/ArduinoJson/Variant/VariantRef.hpp b/src/ArduinoJson/Variant/VariantRef.hpp index 4ab82cd7e..a31983f9c 100644 --- a/src/ArduinoJson/Variant/VariantRef.hpp +++ b/src/ArduinoJson/Variant/VariantRef.hpp @@ -30,7 +30,9 @@ class VariantRefBase : public VariantTag { public: template typename TVisitor::result_type accept(TVisitor &visitor) const { - return variantAccept(_data, visitor); + if (!_data) + return visitor.visitNull(); + return _data->resolve()->accept(visitor); } FORCE_INLINE bool isNull() const { From e6cd16aec410bee287187631974d18666f6ae966 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Mon, 23 May 2022 19:20:15 +0200 Subject: [PATCH 032/202] Add missing `const` specifiers in serializers --- src/ArduinoJson/Json/JsonSerializer.hpp | 4 ++-- src/ArduinoJson/Json/PrettyJsonSerializer.hpp | 4 ++-- src/ArduinoJson/MsgPack/MsgPackSerializer.hpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index 7c6f64243..200c7f3a2 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -22,7 +22,7 @@ class JsonSerializer : public Visitor { FORCE_INLINE size_t visitArray(const CollectionData &array) { write('['); - VariantSlot *slot = array.head(); + const VariantSlot *slot = array.head(); while (slot != 0) { slot->data()->resolve()->accept(*this); @@ -41,7 +41,7 @@ class JsonSerializer : public Visitor { size_t visitObject(const CollectionData &object) { write('{'); - VariantSlot *slot = object.head(); + const VariantSlot *slot = object.head(); while (slot != 0) { _formatter.writeString(slot->key()); diff --git a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp index 3f7fd2629..9485cd751 100644 --- a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp +++ b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp @@ -19,7 +19,7 @@ class PrettyJsonSerializer : public JsonSerializer { PrettyJsonSerializer(TWriter writer) : base(writer), _nesting(0) {} size_t visitArray(const CollectionData &array) { - VariantSlot *slot = array.head(); + const VariantSlot *slot = array.head(); if (slot) { base::write("[\r\n"); _nesting++; @@ -40,7 +40,7 @@ class PrettyJsonSerializer : public JsonSerializer { } size_t visitObject(const CollectionData &object) { - VariantSlot *slot = object.head(); + const VariantSlot *slot = object.head(); if (slot) { base::write("{\r\n"); _nesting++; diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index 8cf99801a..d70e9c98e 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -55,7 +55,7 @@ class MsgPackSerializer : public Visitor { writeByte(0xDD); writeInteger(uint32_t(n)); } - for (VariantSlot* slot = array.head(); slot; slot = slot->next()) { + for (const VariantSlot* slot = array.head(); slot; slot = slot->next()) { slot->data()->resolve()->accept(*this); } return bytesWritten(); @@ -72,7 +72,7 @@ class MsgPackSerializer : public Visitor { writeByte(0xDF); writeInteger(uint32_t(n)); } - for (VariantSlot* slot = object.head(); slot; slot = slot->next()) { + for (const VariantSlot* slot = object.head(); slot; slot = slot->next()) { visitString(slot->key()); slot->data()->resolve()->accept(*this); } From fb904033d35b34ffb54c027d3f35302dfadd4757 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 2 Jun 2022 20:36:32 +0200 Subject: [PATCH 033/202] Replace `serializeJson()`'s template parameter with `JsonVariantConst` --- src/ArduinoJson/Json/JsonSerializer.hpp | 11 ++++----- src/ArduinoJson/Json/PrettyJsonSerializer.hpp | 12 ++++------ src/ArduinoJson/MsgPack/MsgPackSerializer.hpp | 10 ++++---- src/ArduinoJson/Serialization/measure.hpp | 4 ++-- src/ArduinoJson/Serialization/serialize.hpp | 23 ++++++++----------- 5 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/ArduinoJson/Json/JsonSerializer.hpp b/src/ArduinoJson/Json/JsonSerializer.hpp index 200c7f3a2..f45ed3298 100644 --- a/src/ArduinoJson/Json/JsonSerializer.hpp +++ b/src/ArduinoJson/Json/JsonSerializer.hpp @@ -116,18 +116,17 @@ class JsonSerializer : public Visitor { TextFormatter _formatter; }; -template -size_t serializeJson(const TSource &source, TDestination &destination) { +template +size_t serializeJson(VariantConstRef source, TDestination &destination) { return serialize(source, destination); } -template -size_t serializeJson(const TSource &source, void *buffer, size_t bufferSize) { +inline size_t serializeJson(VariantConstRef source, void *buffer, + size_t bufferSize) { return serialize(source, buffer, bufferSize); } -template -size_t measureJson(const TSource &source) { +inline size_t measureJson(VariantConstRef source) { return measure(source); } diff --git a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp index 9485cd751..3f63b2c55 100644 --- a/src/ArduinoJson/Json/PrettyJsonSerializer.hpp +++ b/src/ArduinoJson/Json/PrettyJsonSerializer.hpp @@ -70,19 +70,17 @@ class PrettyJsonSerializer : public JsonSerializer { uint8_t _nesting; }; -template -size_t serializeJsonPretty(const TSource &source, TDestination &destination) { +template +size_t serializeJsonPretty(VariantConstRef source, TDestination &destination) { return serialize(source, destination); } -template -size_t serializeJsonPretty(const TSource &source, void *buffer, - size_t bufferSize) { +inline size_t serializeJsonPretty(VariantConstRef source, void *buffer, + size_t bufferSize) { return serialize(source, buffer, bufferSize); } -template -size_t measureJsonPretty(const TSource &source) { +inline size_t measureJsonPretty(VariantConstRef source) { return measure(source); } diff --git a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp index d70e9c98e..1abc7cc7e 100644 --- a/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp @@ -197,19 +197,17 @@ class MsgPackSerializer : public Visitor { CountingDecorator _writer; }; -template -inline size_t serializeMsgPack(const TSource& source, TDestination& output) { +template +inline size_t serializeMsgPack(VariantConstRef source, TDestination& output) { return serialize(source, output); } -template -inline size_t serializeMsgPack(const TSource& source, void* output, +inline size_t serializeMsgPack(VariantConstRef source, void* output, size_t size) { return serialize(source, output, size); } -template -inline size_t measureMsgPack(const TSource& source) { +inline size_t measureMsgPack(VariantConstRef source) { return measure(source); } diff --git a/src/ArduinoJson/Serialization/measure.hpp b/src/ArduinoJson/Serialization/measure.hpp index d1391277b..3de588d2e 100644 --- a/src/ArduinoJson/Serialization/measure.hpp +++ b/src/ArduinoJson/Serialization/measure.hpp @@ -8,8 +8,8 @@ namespace ARDUINOJSON_NAMESPACE { -template