mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-12-23 07:02:34 +01:00
Compare commits
41 Commits
issues/219
...
7.6-with-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
589bbbfcca | ||
|
|
03da4aad99 | ||
|
|
60a767c706 | ||
|
|
274fe06b33 | ||
|
|
f680598a2f | ||
|
|
e86c6acf2e | ||
|
|
0021ec81d1 | ||
|
|
372f2f2767 | ||
|
|
fc8da90ba7 | ||
|
|
38f61d322f | ||
|
|
3793996d83 | ||
|
|
43548db37d | ||
|
|
59573ac1f9 | ||
|
|
14a48978d9 | ||
|
|
2fa8b9664c | ||
|
|
2e20ce0795 | ||
|
|
a5164f7fe3 | ||
|
|
a9996d2293 | ||
|
|
f9655a90ed | ||
|
|
25942cce2d | ||
|
|
c6fa8c1c1f | ||
|
|
5589633697 | ||
|
|
7b7ee6cdeb | ||
|
|
dca5ba481f | ||
|
|
ec53525452 | ||
|
|
0b9a8b6d2b | ||
|
|
f62d6f26e0 | ||
|
|
1f1227d595 | ||
|
|
6f28ec0384 | ||
|
|
6ba4507252 | ||
|
|
a265ef4927 | ||
|
|
8790802ce3 | ||
|
|
f1b06b588e | ||
|
|
84fcd90658 | ||
|
|
bd551fa4ce | ||
|
|
d9c6a70595 | ||
|
|
b44aa3a8b5 | ||
|
|
c2290a2386 | ||
|
|
4457cb7cc5 | ||
|
|
1e0bbd518c | ||
|
|
cc077c1b63 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -398,9 +398,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Particle CLI
|
||||
run: |
|
||||
bash <( curl -sL https://particle.io/install-cli )
|
||||
echo "$HOME/bin" >> $GITHUB_PATH
|
||||
run: sudo npm install -g particle-cli
|
||||
- name: Login to Particle
|
||||
run: particle login -t "${{ secrets.PARTICLE_TOKEN }}"
|
||||
- name: Compile
|
||||
|
||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -4,35 +4,38 @@ ArduinoJson: change log
|
||||
HEAD
|
||||
----
|
||||
|
||||
* Don't store string literals by pointer anymore (issue #2189)
|
||||
Version 7.3 introduced a new way to detect string literals, but it fails in some edge cases.
|
||||
I could not find a way to fix it, so I chose to remove the optimization rather than keep it broken.
|
||||
* Replace the "extension slots" mechanism with a memory pool dedicated to 8-byte values.
|
||||
* Optimize storage of static strings
|
||||
|
||||
> ### BREAKING CHANGES
|
||||
>
|
||||
> #### `JsonString` constructor's boolean parameter
|
||||
> Static string cannot contain NUL characters anymore (they could since 7.3.0).
|
||||
> This is an extremely rare case, so you probably won't be affected.
|
||||
>
|
||||
> Since version 7.3, you could pass a boolean to `JsonString`'s constructor to force the string to be stored by pointer.
|
||||
> This optimization has been removed, and you'll get a deprecation warning if you use it.
|
||||
> To fix the issue, you must remove the boolean argument from the constructor, or better yet, remove `JsonString` altogether.
|
||||
> For example, the following code produces different output in 7.3 and 7.4:
|
||||
>
|
||||
> ```diff
|
||||
> char name[] = "ArduinoJson";
|
||||
> - doc["name"] = JsonString(name, true);
|
||||
> + doc["name"] = name;
|
||||
> ```cpp
|
||||
> JsonDocument doc;
|
||||
> doc["a\0b"] = "c\0d";
|
||||
> serializeJson(doc, Serial);
|
||||
> // With Arduino 7.3 -> {"a\u0000b":"c\u0000d"}
|
||||
> // With Arduino 7.4 -> {"a":"c"}
|
||||
> ```
|
||||
>
|
||||
> #### NUL characters in string literals
|
||||
>
|
||||
> Since version 7.3, ArduinoJson has supported NUL characters (`\0`) in string literals.
|
||||
> This feature has been removed as part of the storage policy change.
|
||||
> `JsonString` contructor now only accepts two arguments, not three.
|
||||
> If your code uses `JsonString` to store a string as a pointer, you must remove the size argument.
|
||||
>
|
||||
> If you do need to include NULs in your string, you must use a `JsonString` instead:
|
||||
> For example, if you have something like this:
|
||||
>
|
||||
> ```diff
|
||||
> - doc["strings"] = "hello\0world"
|
||||
> + doc["strings"] = JsonString("hello\0world", 11)
|
||||
> ```cpp
|
||||
> doc["key"] = JsonString(str.c_str(), str.size(), true);
|
||||
> ```
|
||||
>
|
||||
> You must replace with either:
|
||||
>
|
||||
> ```cpp
|
||||
> doc["key"] = JsonString(str.c_str(), true); // store as pointer, cannot contain NUL characters
|
||||
> doc["key"] = JsonString(str.c_str(), str.size()); // store by copy, NUL characters allowed
|
||||
> doc["key"] = str; // same as previous line for supported string classes (`String`, `std::string`, etc.)
|
||||
> ```
|
||||
|
||||
v7.4.2 (2025-06-20)
|
||||
|
||||
@@ -54,7 +54,7 @@ TEST_CASE("BasicJsonDocument") {
|
||||
doc["hello"] = "world";
|
||||
auto copy = doc;
|
||||
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
REQUIRE(allocatorLog == "AAAAAA");
|
||||
REQUIRE(allocatorLog == "AAAA");
|
||||
}
|
||||
|
||||
SECTION("capacity") {
|
||||
|
||||
@@ -275,6 +275,12 @@ inline size_t sizeofPool(
|
||||
return ArduinoJson::detail::MemoryPool<T>::slotsToBytes(n);
|
||||
}
|
||||
|
||||
inline size_t sizeofStaticStringPool(
|
||||
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
|
||||
using namespace ArduinoJson::detail;
|
||||
return MemoryPool<const char*>::slotsToBytes(n);
|
||||
}
|
||||
|
||||
inline size_t sizeofStringBuffer(size_t iteration = 1) {
|
||||
// returns 31, 63, 127, 255, etc.
|
||||
auto capacity = ArduinoJson::detail::StringBuilder::initialCapacity;
|
||||
|
||||
@@ -57,7 +57,7 @@ TEST_CASE("JsonArray::add(T)") {
|
||||
REQUIRE(array[0].is<int>() == false);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ TEST_CASE("deserializeJson(MemberProxy)") {
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("value")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -825,7 +825,9 @@ TEST_CASE("shrink filter") {
|
||||
|
||||
deserializeJson(doc, "{}", DeserializationOption::Filter(filter));
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ TEST_CASE("ElementProxy::add()") {
|
||||
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ TEST_CASE("MemberProxy::add()") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ TEST_CASE("MemberProxy::add()") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -47,7 +46,7 @@ TEST_CASE("MemberProxy::add()") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -59,9 +58,8 @@ TEST_CASE("MemberProxy::add()") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -76,7 +74,7 @@ TEST_CASE("MemberProxy::add()") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
@@ -405,7 +403,7 @@ TEST_CASE("MemberProxy under memory constraints") {
|
||||
}
|
||||
|
||||
SECTION("value slot allocation fails") {
|
||||
timebomb.setCountdown(1);
|
||||
timebomb.setCountdown(2);
|
||||
|
||||
// fill the pool entirely, but leave one slot for the key
|
||||
doc["foo"][ARDUINOJSON_POOL_CAPACITY - 4] = 1;
|
||||
@@ -418,6 +416,7 @@ TEST_CASE("MemberProxy under memory constraints") {
|
||||
REQUIRE(doc.overflowed() == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
AllocateFail(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ TEST_CASE("JsonDocument::add(T)") {
|
||||
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::addPadding;
|
||||
|
||||
TEST_CASE("JsonDocument constructor") {
|
||||
SpyingAllocator spyingAllocator;
|
||||
|
||||
@@ -62,8 +64,7 @@ TEST_CASE("JsonDocument constructor") {
|
||||
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -87,7 +88,7 @@ TEST_CASE("JsonDocument constructor") {
|
||||
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ TEST_CASE("JsonDocument::remove()") {
|
||||
|
||||
SECTION("string literal") {
|
||||
doc["a"] = 1;
|
||||
doc["ab"_s] = 2;
|
||||
doc["x"] = 2;
|
||||
doc["b"] = 3;
|
||||
|
||||
doc.remove("ab");
|
||||
doc.remove("x");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"b\":3}");
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ TEST_CASE("JsonDocument::set()") {
|
||||
|
||||
REQUIRE(doc.as<const char*>() == "example"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("example")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,21 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{});
|
||||
}
|
||||
|
||||
SECTION("string") {
|
||||
doc.set("abcdefg");
|
||||
SECTION("linked string") {
|
||||
doc.set("hello");
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "hello");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("owned string") {
|
||||
doc.set("abcdefg"_s);
|
||||
REQUIRE(doc.as<std::string>() == "abcdefg");
|
||||
|
||||
doc.shrinkToFit();
|
||||
@@ -92,7 +105,22 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("object key") {
|
||||
SECTION("linked key") {
|
||||
doc["key"] = 42;
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("owned key") {
|
||||
doc["abcdefg"_s] = 42;
|
||||
|
||||
doc.shrinkToFit();
|
||||
@@ -106,7 +134,22 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("string in array") {
|
||||
SECTION("linked string in array") {
|
||||
doc.add("hello");
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Reallocate(sizeofPool(), sizeofArray(1)),
|
||||
Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("owned string in array") {
|
||||
doc.add("abcdefg"_s);
|
||||
|
||||
doc.shrinkToFit();
|
||||
@@ -120,16 +163,32 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("string in object") {
|
||||
doc["key"] = "abcdefg"_s;
|
||||
SECTION("linked string in object") {
|
||||
doc["key"] = "hello";
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("abcdefg")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(2)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("owned string in object") {
|
||||
doc["key1"_s] = "value"_s;
|
||||
|
||||
doc.shrinkToFit();
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"key1\":\"value\"}");
|
||||
REQUIRE(spyingAllocator.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("key1")),
|
||||
Allocate(sizeofString("value")),
|
||||
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ TEST_CASE("JsonDocument::operator[]") {
|
||||
|
||||
SECTION("object") {
|
||||
doc["abc"_s] = "ABC";
|
||||
doc["abcd"_s] = "ABCD";
|
||||
doc["abc\0d"_s] = "ABCD";
|
||||
|
||||
SECTION("const char*") {
|
||||
const char* key = "abc";
|
||||
@@ -25,20 +25,18 @@ TEST_CASE("JsonDocument::operator[]") {
|
||||
SECTION("string literal") {
|
||||
REQUIRE(doc["abc"] == "ABC");
|
||||
REQUIRE(cdoc["abc"] == "ABC");
|
||||
REQUIRE(doc["abcd"] == "ABCD");
|
||||
REQUIRE(cdoc["abcd"] == "ABCD");
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
REQUIRE(doc["abc"_s] == "ABC");
|
||||
REQUIRE(cdoc["abc"_s] == "ABC");
|
||||
REQUIRE(doc["abcd"_s] == "ABCD");
|
||||
REQUIRE(cdoc["abcd"_s] == "ABCD");
|
||||
REQUIRE(doc["abc\0d"_s] == "ABCD");
|
||||
REQUIRE(cdoc["abc\0d"_s] == "ABCD");
|
||||
}
|
||||
|
||||
SECTION("JsonVariant") {
|
||||
doc["key1"] = "abc";
|
||||
doc["key2"] = "abcd"_s;
|
||||
doc["key2"] = "abc\0d"_s;
|
||||
doc["key3"] = "foo";
|
||||
|
||||
CHECK(doc[doc["key1"]] == "ABC");
|
||||
@@ -114,7 +112,7 @@ TEST_CASE("JsonDocument::operator[] key storage") {
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,26 @@ TEST_CASE("JsonObject::set()") {
|
||||
JsonObject obj1 = doc1.to<JsonObject>();
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
SECTION("copy key and string value") {
|
||||
SECTION("doesn't copy static string in key or value") {
|
||||
obj1["hello"] = "world";
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("copy local string key and value") {
|
||||
obj1["hello"_s] = "world"_s;
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
|
||||
@@ -101,8 +101,34 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
obj[key] = 42;
|
||||
REQUIRE(42 == obj[key]);
|
||||
}
|
||||
SECTION("should duplicate key and value strings") {
|
||||
|
||||
SECTION("string literals") {
|
||||
obj["hello"] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* value") {
|
||||
obj["hello"] = const_cast<char*>("world");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key") {
|
||||
obj[const_cast<char*>("hello")] = 42;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key&value") {
|
||||
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
@@ -110,6 +136,48 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string value") {
|
||||
obj["hello"] = "world"_s;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key") {
|
||||
obj["hello"_s] = 42;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key&value") {
|
||||
obj["hello"_s] = "world"_s;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate a non-static JsonString key") {
|
||||
obj[JsonString("hello", false)] = 42;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should not duplicate a static JsonString key") {
|
||||
obj[JsonString("hello", true)] = 42;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should ignore null key") {
|
||||
// object must have a value to make a call to strcmp()
|
||||
obj["dummy"] = 42;
|
||||
|
||||
@@ -9,90 +9,124 @@
|
||||
#include "Literals.hpp"
|
||||
|
||||
template <typename T>
|
||||
std::string serialize(T value) {
|
||||
void check(T value, const std::string& expected) {
|
||||
JsonDocument doc;
|
||||
doc.to<JsonVariant>().set(value);
|
||||
std::string output;
|
||||
serializeJson(doc, output);
|
||||
return output;
|
||||
char buffer[256] = "";
|
||||
size_t returnValue = serializeJson(doc, buffer, sizeof(buffer));
|
||||
REQUIRE(expected == buffer);
|
||||
REQUIRE(expected.size() == returnValue);
|
||||
}
|
||||
|
||||
TEST_CASE("serializeJson(JsonVariant)") {
|
||||
SECTION("JsonVariant") {
|
||||
CHECK(serialize(JsonVariant()) == "null");
|
||||
SECTION("Undefined") {
|
||||
check(JsonVariant(), "null");
|
||||
}
|
||||
|
||||
SECTION("Null string") {
|
||||
check(static_cast<char*>(0), "null");
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
CHECK(serialize(static_cast<const char*>(0)) == "null");
|
||||
CHECK(serialize("hello") == "\"hello\"");
|
||||
check("hello", "\"hello\"");
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
CHECK(serialize("hello"_s) == "\"hello\"");
|
||||
CHECK(serialize("hello \"world\""_s) == "\"hello \\\"world\\\"\"");
|
||||
CHECK(serialize("hello\\world"_s) == "\"hello\\\\world\"");
|
||||
CHECK(serialize("fifty/fifty"_s) == "\"fifty/fifty\"");
|
||||
CHECK(serialize("hello'world"_s) == "\"hello'world\"");
|
||||
CHECK(serialize("hello\bworld"_s) == "\"hello\\bworld\"");
|
||||
CHECK(serialize("hello\fworld"_s) == "\"hello\\fworld\"");
|
||||
CHECK(serialize("hello\nworld"_s) == "\"hello\\nworld\"");
|
||||
CHECK(serialize("hello\rworld"_s) == "\"hello\\rworld\"");
|
||||
CHECK(serialize("hello\tworld"_s) == "\"hello\\tworld\"");
|
||||
CHECK(serialize("hello\0world"_s) == "\"hello\\u0000world\"");
|
||||
SECTION("string") {
|
||||
check("hello"_s, "\"hello\"");
|
||||
|
||||
SECTION("Escape quotation mark") {
|
||||
check("hello \"world\""_s, "\"hello \\\"world\\\"\"");
|
||||
}
|
||||
|
||||
SECTION("Escape reverse solidus") {
|
||||
check("hello\\world"_s, "\"hello\\\\world\"");
|
||||
}
|
||||
|
||||
SECTION("Don't escape solidus") {
|
||||
check("fifty/fifty"_s, "\"fifty/fifty\"");
|
||||
}
|
||||
|
||||
SECTION("Don't escape single quote") {
|
||||
check("hello'world"_s, "\"hello'world\"");
|
||||
}
|
||||
|
||||
SECTION("Escape backspace") {
|
||||
check("hello\bworld"_s, "\"hello\\bworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape formfeed") {
|
||||
check("hello\fworld"_s, "\"hello\\fworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape linefeed") {
|
||||
check("hello\nworld"_s, "\"hello\\nworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape carriage return") {
|
||||
check("hello\rworld"_s, "\"hello\\rworld\"");
|
||||
}
|
||||
|
||||
SECTION("Escape tab") {
|
||||
check("hello\tworld"_s, "\"hello\\tworld\"");
|
||||
}
|
||||
|
||||
SECTION("NUL char") {
|
||||
check("hello\0world"_s, "\"hello\\u0000world\"");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SerializedValue<const char*>") {
|
||||
CHECK(serialize(serialized("[1,2]")) == "[1,2]");
|
||||
check(serialized("[1,2]"), "[1,2]");
|
||||
}
|
||||
|
||||
SECTION("SerializedValue<std::string>") {
|
||||
CHECK(serialize(serialized("[1,2]"_s)) == "[1,2]");
|
||||
check(serialized("[1,2]"_s), "[1,2]");
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
CHECK(serialize(0.0) == "0");
|
||||
CHECK(serialize(-0.0) == "0");
|
||||
CHECK(serialize(10.0) == "10");
|
||||
CHECK(serialize(100.0) == "100");
|
||||
CHECK(serialize(0.1) == "0.1");
|
||||
CHECK(serialize(0.01) == "0.01");
|
||||
CHECK(serialize(3.1415927) == "3.1415927");
|
||||
CHECK(serialize(-3.1415927) == "-3.1415927");
|
||||
CHECK(serialize(1.7976931348623157E+308) == "1.79769313e308");
|
||||
CHECK(serialize(4.94065645841247e-324) == "4.94065646e-324");
|
||||
SECTION("Double") {
|
||||
check(3.1415927, "3.1415927");
|
||||
}
|
||||
|
||||
SECTION("float") {
|
||||
SECTION("Float") {
|
||||
REQUIRE(sizeof(float) == 4);
|
||||
CHECK(serialize(3.1415927f) == "3.141593");
|
||||
CHECK(serialize(-3.1415927f) == "-3.141593");
|
||||
CHECK(serialize(3.4E+38f) == "3.4e38");
|
||||
CHECK(serialize(1.17549435e-38f) == "1.175494e-38");
|
||||
check(3.1415927f, "3.141593");
|
||||
}
|
||||
|
||||
SECTION("int") {
|
||||
CHECK(serialize(0) == "0");
|
||||
CHECK(serialize(42) == "42");
|
||||
CHECK(serialize(-42) == "-42");
|
||||
SECTION("Zero") {
|
||||
check(0, "0");
|
||||
}
|
||||
|
||||
SECTION("unsigned long") {
|
||||
CHECK(serialize(4294967295UL) == "4294967295");
|
||||
SECTION("Integer") {
|
||||
check(42, "42");
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
CHECK(serialize(true) == "true");
|
||||
CHECK(serialize(false) == "false");
|
||||
SECTION("NegativeLong") {
|
||||
check(-42, "-42");
|
||||
}
|
||||
|
||||
SECTION("UnsignedLong") {
|
||||
check(4294967295UL, "4294967295");
|
||||
}
|
||||
|
||||
SECTION("True") {
|
||||
check(true, "true");
|
||||
}
|
||||
|
||||
SECTION("OneFalse") {
|
||||
check(false, "false");
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
SECTION("int64_t") {
|
||||
CHECK(serialize(-9223372036854775807 - 1) == "-9223372036854775808");
|
||||
CHECK(serialize(9223372036854775807) == "9223372036854775807");
|
||||
SECTION("NegativeInt64") {
|
||||
check(-9223372036854775807 - 1, "-9223372036854775808");
|
||||
}
|
||||
|
||||
SECTION("uint64_t") {
|
||||
CHECK(serialize(18446744073709551615U) == "18446744073709551615");
|
||||
SECTION("PositiveInt64") {
|
||||
check(9223372036854775807, "9223372036854775807");
|
||||
}
|
||||
|
||||
SECTION("UInt64") {
|
||||
check(18446744073709551615U, "18446744073709551615");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<long>() == 42L);
|
||||
REQUIRE(variant.as<double>() == 42);
|
||||
REQUIRE(variant.as<JsonString>() == "42");
|
||||
REQUIRE(variant.as<JsonString>().isStatic() == true);
|
||||
}
|
||||
|
||||
SECTION("set(\"hello\")") {
|
||||
@@ -207,6 +208,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<const char*>() == "4.2"_s);
|
||||
REQUIRE(variant.as<std::string>() == "4.2"_s);
|
||||
REQUIRE(variant.as<JsonString>() == "4.2");
|
||||
REQUIRE(variant.as<JsonString>().isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("set(std::string(\"123.45\"))") {
|
||||
@@ -218,6 +220,7 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(variant.as<const char*>() == "123.45"_s);
|
||||
REQUIRE(variant.as<std::string>() == "123.45"_s);
|
||||
REQUIRE(variant.as<JsonString>() == "123.45");
|
||||
REQUIRE(variant.as<JsonString>().isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("set(\"true\")") {
|
||||
|
||||
@@ -38,14 +38,14 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
REQUIRE(var1.as<std::string>() == "{\"value\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("stores string literals by copy") {
|
||||
SECTION("stores string literals by pointer") {
|
||||
var1.set("hello!!");
|
||||
spyingAllocator.clearLog();
|
||||
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(spyingAllocator.log() == AllocatorLog{
|
||||
Allocate(sizeofString("hello!!")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,14 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("string literal") {
|
||||
bool result = variant.set("hello world");
|
||||
bool result = variant.set("hello\0world");
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello world"_s); // stores by copy
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString(11)),
|
||||
});
|
||||
CHECK(variant ==
|
||||
"hello"_s); // linked string cannot contain '\0' at the moment
|
||||
CHECK(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
@@ -141,7 +142,21 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("JsonString") {
|
||||
SECTION("static JsonString") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
bool result = variant.set(JsonString(str, true));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "world"); // stores by pointer
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("non-static JsonString") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
@@ -254,6 +269,20 @@ TEST_CASE("JsonVariant::set() with not enough memory") {
|
||||
|
||||
JsonVariant v = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("string literal") {
|
||||
bool result = v.set("hello world");
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("static JsonString") {
|
||||
bool result = v.set(JsonString("hello world", true));
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
bool result = v.set("hello world!!"_s);
|
||||
|
||||
|
||||
@@ -52,12 +52,12 @@ TEST_CASE("JsonVariantConst::operator[]") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["ab"_s] = "AB";
|
||||
object["abc"_s] = "ABC";
|
||||
object["abcd"_s] = "ABCD";
|
||||
object["abc\0d"_s] = "ABCD";
|
||||
|
||||
SECTION("string literal") {
|
||||
REQUIRE(var["ab"] == "AB"_s);
|
||||
REQUIRE(var["abc"] == "ABC"_s);
|
||||
REQUIRE(var["abcd"] == "ABCD"_s);
|
||||
REQUIRE(var["abc\0d"] == "ABC"_s);
|
||||
REQUIRE(var["def"].isNull());
|
||||
REQUIRE(var[0].isNull());
|
||||
}
|
||||
@@ -73,7 +73,7 @@ TEST_CASE("JsonVariantConst::operator[]") {
|
||||
SECTION("supports std::string") {
|
||||
REQUIRE(var["ab"_s] == "AB"_s);
|
||||
REQUIRE(var["abc"_s] == "ABC"_s);
|
||||
REQUIRE(var["abcd"_s] == "ABCD"_s);
|
||||
REQUIRE(var["abc\0d"_s] == "ABCD"_s);
|
||||
REQUIRE(var["def"_s].isNull());
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ TEST_CASE("JsonVariantConst::operator[]") {
|
||||
SECTION("supports JsonVariant") {
|
||||
object["key1"] = "ab";
|
||||
object["key2"] = "abc";
|
||||
object["key3"] = "abcd"_s;
|
||||
object["key3"] = "abc\0d"_s;
|
||||
object["key4"] = "foo";
|
||||
|
||||
REQUIRE(var[var["key1"]] == "AB"_s);
|
||||
|
||||
@@ -13,6 +13,7 @@ TEST_CASE("JsonString") {
|
||||
|
||||
CHECK(s.isNull() == true);
|
||||
CHECK(s.c_str() == 0);
|
||||
CHECK(s.isStatic() == true);
|
||||
CHECK(s == JsonString());
|
||||
CHECK(s != "");
|
||||
}
|
||||
@@ -95,6 +96,7 @@ TEST_CASE("JsonString") {
|
||||
JsonString s("hello world", 5);
|
||||
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
CHECK(s == "hello");
|
||||
CHECK(s != "hello world");
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == true);
|
||||
}
|
||||
|
||||
SECTION("null const char*") {
|
||||
@@ -37,6 +38,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
CHECK(s.data() == p);
|
||||
}
|
||||
|
||||
@@ -44,6 +46,7 @@ TEST_CASE("adaptString()") {
|
||||
auto s = adaptString(static_cast<const char*>(0), 10);
|
||||
|
||||
CHECK(s.isNull() == true);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("non-null const char* + size") {
|
||||
@@ -51,6 +54,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("null Flash string") {
|
||||
@@ -58,6 +62,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == true);
|
||||
CHECK(s.size() == 0);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("non-null Flash string") {
|
||||
@@ -65,6 +70,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
@@ -73,6 +79,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
@@ -81,6 +88,7 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("custom_string") {
|
||||
@@ -89,14 +97,25 @@ TEST_CASE("adaptString()") {
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
|
||||
SECTION("JsonString") {
|
||||
JsonString orig("hello");
|
||||
SECTION("JsonString linked") {
|
||||
JsonString orig("hello", true);
|
||||
auto s = adaptString(orig);
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == true);
|
||||
}
|
||||
|
||||
SECTION("JsonString copied") {
|
||||
JsonString orig("hello", false);
|
||||
auto s = adaptString(orig);
|
||||
|
||||
CHECK(s.isNull() == false);
|
||||
CHECK(s.size() == 5);
|
||||
CHECK(s.isStatic() == false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#define ARDUINOJSON_ENABLE_ALIGNMENT 0
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("ARDUINOJSON_ENABLE_ALIGNMENT == 0") {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#define ARDUINOJSON_ENABLE_ALIGNMENT 1
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("ARDUINOJSON_ENABLE_ALIGNMENT == 1") {
|
||||
|
||||
@@ -89,71 +89,58 @@ TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 4") {
|
||||
|
||||
REQUIRE(err != DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("bin 32") {
|
||||
auto str = std::string(65536, '?');
|
||||
auto input = "\xc6\x00\x01\x00\x00"_s + str;
|
||||
|
||||
auto err = deserializeMsgPack(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<MsgPackBinary>());
|
||||
auto binary = doc.as<MsgPackBinary>();
|
||||
REQUIRE(binary.size() == 65536);
|
||||
REQUIRE(binary.data() != nullptr);
|
||||
REQUIRE(std::string(reinterpret_cast<const char*>(binary.data()),
|
||||
binary.size()) == str);
|
||||
}
|
||||
|
||||
SECTION("ext 32 deserialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
auto input = "\xc9\x00\x01\x00\x00\x2a"_s + str;
|
||||
|
||||
auto err = deserializeMsgPack(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<MsgPackExtension>());
|
||||
auto value = doc.as<MsgPackExtension>();
|
||||
REQUIRE(value.type() == 42);
|
||||
REQUIRE(value.size() == 65536);
|
||||
REQUIRE(value.data() != nullptr);
|
||||
REQUIRE(std::string(reinterpret_cast<const char*>(value.data()),
|
||||
value.size()) == str);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("serializeMsgPack()") {
|
||||
SECTION("bin 32 serialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
doc.set(MsgPackBinary(str.data(), str.size()));
|
||||
SECTION("bin 32 deserialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
auto input = "\xc6\x00\x01\x00\x00"_s + str;
|
||||
|
||||
std::string output;
|
||||
auto result = serializeMsgPack(doc, output);
|
||||
auto err = deserializeMsgPack(doc, input);
|
||||
|
||||
REQUIRE(result == 5 + str.size());
|
||||
REQUIRE(output == "\xc6\x00\x01\x00\x00"_s + str);
|
||||
}
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<MsgPackBinary>());
|
||||
auto binary = doc.as<MsgPackBinary>();
|
||||
REQUIRE(binary.size() == 65536);
|
||||
REQUIRE(binary.data() != nullptr);
|
||||
REQUIRE(std::string(reinterpret_cast<const char*>(binary.data()),
|
||||
binary.size()) == str);
|
||||
}
|
||||
|
||||
SECTION("ext 32 serialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
doc.set(MsgPackExtension(42, str.data(), str.size()));
|
||||
SECTION("bin 32 serialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
doc.set(MsgPackBinary(str.data(), str.size()));
|
||||
|
||||
std::string output;
|
||||
auto result = serializeMsgPack(doc, output);
|
||||
std::string output;
|
||||
auto result = serializeMsgPack(doc, output);
|
||||
|
||||
REQUIRE(result == 6 + str.size());
|
||||
REQUIRE(output == "\xc9\x00\x01\x00\x00\x2a"_s + str);
|
||||
}
|
||||
REQUIRE(result == 5 + str.size());
|
||||
REQUIRE(output == "\xc6\x00\x01\x00\x00"_s + str);
|
||||
}
|
||||
|
||||
SECTION("str 32 serialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
doc.set(str);
|
||||
SECTION("ext 32 deserialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
auto input = "\xc9\x00\x01\x00\x00\x2a"_s + str;
|
||||
|
||||
std::string output;
|
||||
auto result = serializeMsgPack(doc, output);
|
||||
auto err = deserializeMsgPack(doc, input);
|
||||
|
||||
REQUIRE(result == 5 + str.size());
|
||||
REQUIRE(output == "\xDB\x00\x01\x00\x00"_s + str);
|
||||
}
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<MsgPackExtension>());
|
||||
auto value = doc.as<MsgPackExtension>();
|
||||
REQUIRE(value.type() == 42);
|
||||
REQUIRE(value.size() == 65536);
|
||||
REQUIRE(value.data() != nullptr);
|
||||
REQUIRE(std::string(reinterpret_cast<const char*>(value.data()),
|
||||
value.size()) == str);
|
||||
}
|
||||
|
||||
SECTION("ext 32 serialization") {
|
||||
auto str = std::string(65536, '?');
|
||||
doc.set(MsgPackExtension(42, str.data(), str.size()));
|
||||
|
||||
std::string output;
|
||||
auto result = serializeMsgPack(doc, output);
|
||||
|
||||
REQUIRE(result == 6 + str.size());
|
||||
REQUIRE(output == "\xc9\x00\x01\x00\x00\x2a"_s + str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ TEST_CASE("deserializeMsgPack(MemberProxy)") {
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofString("value")),
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,12 +137,11 @@ TEST_CASE("serialize MsgPack value") {
|
||||
checkVariant(longest.c_str(), "\xDA\xFF\xFF"_s + longest);
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_STRING_LENGTH_SIZE > 2
|
||||
SECTION("str 32") {
|
||||
std::string shortest(65536, '?');
|
||||
checkVariant(shortest.c_str(), "\xDB\x00\x01\x00\x00"_s + shortest);
|
||||
checkVariant(JsonString(shortest.c_str(), true), // force store by pointer
|
||||
"\xDB\x00\x01\x00\x00"_s + shortest);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("serialized(const char*)") {
|
||||
checkVariant(serialized("\xDA\xFF\xFF"), "\xDA\xFF\xFF");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
add_executable(NumbersTests
|
||||
convertNumber.cpp
|
||||
decomposeFloat.cpp
|
||||
parseDouble.cpp
|
||||
parseFloat.cpp
|
||||
parseInteger.cpp
|
||||
|
||||
42
extras/tests/Numbers/decomposeFloat.cpp
Normal file
42
extras/tests/Numbers/decomposeFloat.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Numbers/FloatParts.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("decomposeFloat()") {
|
||||
SECTION("1.7976931348623157E+308") {
|
||||
auto parts = decomposeFloat(1.7976931348623157E+308, 9);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 797693135);
|
||||
REQUIRE(parts.decimalPlaces == 9);
|
||||
REQUIRE(parts.exponent == 308);
|
||||
}
|
||||
|
||||
SECTION("4.94065645841247e-324") {
|
||||
auto parts = decomposeFloat(4.94065645841247e-324, 9);
|
||||
REQUIRE(parts.integral == 4);
|
||||
REQUIRE(parts.decimal == 940656458);
|
||||
REQUIRE(parts.decimalPlaces == 9);
|
||||
REQUIRE(parts.exponent == -324);
|
||||
}
|
||||
|
||||
SECTION("3.4E+38") {
|
||||
auto parts = decomposeFloat(3.4E+38f, 6);
|
||||
REQUIRE(parts.integral == 3);
|
||||
REQUIRE(parts.decimal == 4);
|
||||
REQUIRE(parts.decimalPlaces == 1);
|
||||
REQUIRE(parts.exponent == 38);
|
||||
}
|
||||
|
||||
SECTION("1.17549435e−38") {
|
||||
auto parts = decomposeFloat(1.17549435e-38f, 6);
|
||||
REQUIRE(parts.integral == 1);
|
||||
REQUIRE(parts.decimal == 175494);
|
||||
REQUIRE(parts.decimalPlaces == 6);
|
||||
REQUIRE(parts.exponent == -38);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
add_executable(ResourceManagerTests
|
||||
allocVariant.cpp
|
||||
clear.cpp
|
||||
saveStaticString.cpp
|
||||
saveString.cpp
|
||||
shrinkToFit.cpp
|
||||
size.cpp
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/StringBuffer.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <ArduinoJson.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
@@ -23,7 +22,7 @@ TEST_CASE("StringBuffer") {
|
||||
sb.save(&variant);
|
||||
|
||||
REQUIRE(variant.type == VariantType::TinyString);
|
||||
REQUIRE(variant.asString() == "hi!");
|
||||
REQUIRE(VariantImpl(&variant, &resources).asString() == "hi!");
|
||||
}
|
||||
|
||||
SECTION("Tiny string can't contain NUL") {
|
||||
@@ -31,9 +30,9 @@ TEST_CASE("StringBuffer") {
|
||||
memcpy(ptr, "a\0b", 3);
|
||||
sb.save(&variant);
|
||||
|
||||
REQUIRE(variant.type == VariantType::LongString);
|
||||
REQUIRE(variant.type == VariantType::OwnedString);
|
||||
|
||||
auto str = variant.asString();
|
||||
auto str = VariantImpl(&variant, &resources).asString();
|
||||
REQUIRE(str.size() == 3);
|
||||
REQUIRE(str.c_str()[0] == 'a');
|
||||
REQUIRE(str.c_str()[1] == 0);
|
||||
@@ -45,7 +44,7 @@ TEST_CASE("StringBuffer") {
|
||||
strcpy(ptr, "alfa");
|
||||
sb.save(&variant);
|
||||
|
||||
REQUIRE(variant.type == VariantType::LongString);
|
||||
REQUIRE(variant.asString() == "alfa");
|
||||
REQUIRE(variant.type == VariantType::OwnedString);
|
||||
REQUIRE(VariantImpl(&variant, &resources).asString() == "alfa");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <ArduinoJson.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
@@ -47,7 +46,7 @@ TEST_CASE("StringBuilder") {
|
||||
|
||||
REQUIRE(resources.overflowed() == false);
|
||||
REQUIRE(data.type == VariantType::TinyString);
|
||||
REQUIRE(data.asString() == "url");
|
||||
REQUIRE(VariantImpl(&data, &resources).asString() == "url");
|
||||
}
|
||||
|
||||
SECTION("Short string fits in first allocation") {
|
||||
@@ -135,9 +134,10 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
|
||||
auto s2 = saveString(builder, "world");
|
||||
auto s3 = saveString(builder, "hello");
|
||||
|
||||
REQUIRE(s1.asString() == "hello");
|
||||
REQUIRE(s2.asString() == "world");
|
||||
REQUIRE(+s1.asString().c_str() == +s3.asString().c_str()); // same address
|
||||
REQUIRE(VariantImpl(&s1, &resources).asString() == "hello");
|
||||
REQUIRE(VariantImpl(&s2, &resources).asString() == "world");
|
||||
REQUIRE(+VariantImpl(&s1, &resources).asString().c_str() ==
|
||||
+VariantImpl(&s3, &resources).asString().c_str()); // same address
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
@@ -153,10 +153,11 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
|
||||
auto s1 = saveString(builder, "hello world");
|
||||
auto s2 = saveString(builder, "hello");
|
||||
|
||||
REQUIRE(s1.asString() == "hello world");
|
||||
REQUIRE(s2.asString() == "hello");
|
||||
REQUIRE(+s2.asString().c_str() !=
|
||||
+s1.asString().c_str()); // different address
|
||||
REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
|
||||
REQUIRE(VariantImpl(&s2, &resources).asString() == "hello");
|
||||
REQUIRE(
|
||||
+VariantImpl(&s1, &resources).asString().c_str() !=
|
||||
+VariantImpl(&s2, &resources).asString().c_str()); // different address
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
@@ -171,10 +172,11 @@ TEST_CASE("StringBuilder::save() deduplicates strings") {
|
||||
auto s1 = saveString(builder, "hello world");
|
||||
auto s2 = saveString(builder, "worl");
|
||||
|
||||
REQUIRE(s1.asString() == "hello world");
|
||||
REQUIRE(s2.asString() == "worl");
|
||||
REQUIRE(s2.asString().c_str() !=
|
||||
s1.asString().c_str()); // different address
|
||||
REQUIRE(VariantImpl(&s1, &resources).asString() == "hello world");
|
||||
REQUIRE(VariantImpl(&s2, &resources).asString() == "worl");
|
||||
REQUIRE(
|
||||
VariantImpl(&s1, &resources).asString().c_str() !=
|
||||
VariantImpl(&s2, &resources).asString().c_str()); // different address
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <ArduinoJson.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
47
extras/tests/ResourceManager/saveStaticString.cpp
Normal file
47
extras/tests/ResourceManager/saveStaticString.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
TEST_CASE("ResourceManager::saveStaticString() deduplicates strings") {
|
||||
SpyingAllocator spy;
|
||||
ResourceManager resources(&spy);
|
||||
|
||||
auto str1 = "hello";
|
||||
auto str2 = "world";
|
||||
|
||||
auto id1 = resources.saveStaticString(str1);
|
||||
auto id2 = resources.saveStaticString(str2);
|
||||
REQUIRE(id1 != id2);
|
||||
|
||||
auto id3 = resources.saveStaticString(str1);
|
||||
REQUIRE(id1 == id3);
|
||||
|
||||
resources.shrinkToFit();
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStaticStringPool()),
|
||||
Reallocate(sizeofStaticStringPool(), sizeofStaticStringPool(2)),
|
||||
});
|
||||
REQUIRE(resources.overflowed() == false);
|
||||
}
|
||||
|
||||
TEST_CASE("ResourceManager::saveStaticString() when allocation fails") {
|
||||
SpyingAllocator spy(FailingAllocator::instance());
|
||||
ResourceManager resources(&spy);
|
||||
|
||||
auto slotId = resources.saveStaticString("hello");
|
||||
|
||||
REQUIRE(slotId == NULL_SLOT);
|
||||
REQUIRE(resources.overflowed() == true);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStaticStringPool()),
|
||||
});
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
@@ -14,112 +14,106 @@
|
||||
using namespace ArduinoJson::detail;
|
||||
|
||||
template <typename TFloat>
|
||||
static std::string toString(TFloat input) {
|
||||
void check(TFloat input, const std::string& expected) {
|
||||
std::string output;
|
||||
Writer<std::string> sb(output);
|
||||
TextFormatter<Writer<std::string>> writer(sb);
|
||||
writer.writeFloat(input);
|
||||
return output;
|
||||
REQUIRE(writer.bytesWritten() == output.size());
|
||||
CHECK(expected == output);
|
||||
}
|
||||
|
||||
TEST_CASE("TextFormatter::writeFloat(double)") {
|
||||
SECTION("Pi") {
|
||||
REQUIRE(toString(3.14159265359) == "3.14159265");
|
||||
check<double>(3.14159265359, "3.141592654");
|
||||
}
|
||||
|
||||
SECTION("Signaling NaN") {
|
||||
double nan = std::numeric_limits<double>::signaling_NaN();
|
||||
REQUIRE(toString(nan) == "NaN");
|
||||
check<double>(nan, "NaN");
|
||||
}
|
||||
|
||||
SECTION("Quiet NaN") {
|
||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
REQUIRE(toString(nan) == "NaN");
|
||||
check<double>(nan, "NaN");
|
||||
}
|
||||
|
||||
SECTION("Infinity") {
|
||||
double inf = std::numeric_limits<double>::infinity();
|
||||
REQUIRE(toString(inf) == "Infinity");
|
||||
REQUIRE(toString(-inf) == "-Infinity");
|
||||
check<double>(inf, "Infinity");
|
||||
check<double>(-inf, "-Infinity");
|
||||
}
|
||||
|
||||
SECTION("Zero") {
|
||||
REQUIRE(toString(0.0) == "0");
|
||||
REQUIRE(toString(-0.0) == "0");
|
||||
check<double>(0.0, "0");
|
||||
check<double>(-0.0, "0");
|
||||
}
|
||||
|
||||
SECTION("Espilon") {
|
||||
REQUIRE(toString(2.2250738585072014E-308) == "2.22507386e-308");
|
||||
REQUIRE(toString(-2.2250738585072014E-308) == "-2.22507386e-308");
|
||||
check<double>(2.2250738585072014E-308, "2.225073859e-308");
|
||||
check<double>(-2.2250738585072014E-308, "-2.225073859e-308");
|
||||
}
|
||||
|
||||
SECTION("Max double") {
|
||||
REQUIRE(toString(1.7976931348623157E+308) == "1.79769313e308");
|
||||
REQUIRE(toString(-1.7976931348623157E+308) == "-1.79769313e308");
|
||||
check<double>(1.7976931348623157E+308, "1.797693135e308");
|
||||
check<double>(-1.7976931348623157E+308, "-1.797693135e308");
|
||||
}
|
||||
|
||||
SECTION("Big exponent") {
|
||||
REQUIRE(toString(1e255) == "1e255");
|
||||
REQUIRE(toString(1e-255) == "1e-255");
|
||||
// this test increases coverage of normalize()
|
||||
check<double>(1e255, "1e255");
|
||||
check<double>(1e-255, "1e-255");
|
||||
}
|
||||
|
||||
SECTION("Exponentation when <= 1e-5") {
|
||||
REQUIRE(toString(1e-4) == "0.0001");
|
||||
REQUIRE(toString(1e-5) == "1e-5");
|
||||
check<double>(1e-4, "0.0001");
|
||||
check<double>(1e-5, "1e-5");
|
||||
|
||||
REQUIRE(toString(-1e-4) == "-0.0001");
|
||||
REQUIRE(toString(-1e-5) == "-1e-5");
|
||||
check<double>(-1e-4, "-0.0001");
|
||||
check<double>(-1e-5, "-1e-5");
|
||||
}
|
||||
|
||||
SECTION("Exponentation when >= 1e7") {
|
||||
REQUIRE(toString(9999999.99) == "9999999.99");
|
||||
REQUIRE(toString(10000000.0) == "1e7");
|
||||
check<double>(9999999.999, "9999999.999");
|
||||
check<double>(10000000.0, "1e7");
|
||||
|
||||
REQUIRE(toString(-9999999.99) == "-9999999.99");
|
||||
REQUIRE(toString(-10000000.0) == "-1e7");
|
||||
check<double>(-9999999.999, "-9999999.999");
|
||||
check<double>(-10000000.0, "-1e7");
|
||||
}
|
||||
|
||||
SECTION("Rounding when too many decimals") {
|
||||
REQUIRE(toString(0.000099999999999) == "0.0001");
|
||||
REQUIRE(toString(0.0000099999999999) == "1e-5");
|
||||
REQUIRE(toString(0.9999999996) == "1");
|
||||
check<double>(0.000099999999999, "0.0001");
|
||||
check<double>(0.0000099999999999, "1e-5");
|
||||
check<double>(0.9999999996, "1");
|
||||
}
|
||||
|
||||
SECTION("9 decimal places") {
|
||||
REQUIRE(toString(0.10000001) == "0.10000001");
|
||||
REQUIRE(toString(0.99999999) == "0.99999999");
|
||||
check<double>(0.100000001, "0.100000001");
|
||||
check<double>(0.999999999, "0.999999999");
|
||||
|
||||
REQUIRE(toString(9.00000001) == "9.00000001");
|
||||
REQUIRE(toString(9.99999999) == "9.99999999");
|
||||
}
|
||||
|
||||
SECTION("9 decimal places") {
|
||||
REQUIRE(toString(0.100000001) == "0.100000001");
|
||||
REQUIRE(toString(0.999999999) == "0.999999999");
|
||||
|
||||
REQUIRE(toString(9.000000001) == "9");
|
||||
REQUIRE(toString(9.999999999) == "10");
|
||||
check<double>(9.000000001, "9.000000001");
|
||||
check<double>(9.999999999, "9.999999999");
|
||||
}
|
||||
|
||||
SECTION("10 decimal places") {
|
||||
REQUIRE(toString(0.1000000001) == "0.1");
|
||||
REQUIRE(toString(0.9999999999) == "1");
|
||||
check<double>(0.1000000001, "0.1");
|
||||
check<double>(0.9999999999, "1");
|
||||
|
||||
REQUIRE(toString(9.0000000001) == "9");
|
||||
REQUIRE(toString(9.9999999999) == "10");
|
||||
check<double>(9.0000000001, "9");
|
||||
check<double>(9.9999999999, "10");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("TextFormatter::writeFloat(float)") {
|
||||
SECTION("Pi") {
|
||||
REQUIRE(toString(3.14159265359f) == "3.141593");
|
||||
check<float>(3.14159265359f, "3.141593");
|
||||
}
|
||||
|
||||
SECTION("999.9") { // issue #543
|
||||
REQUIRE(toString(999.9f) == "999.9");
|
||||
check<float>(999.9f, "999.9");
|
||||
}
|
||||
|
||||
SECTION("24.3") { // # issue #588
|
||||
REQUIRE(toString(24.3f) == "24.3");
|
||||
check<float>(24.3f, "24.3");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include "ArduinoJson/Object/MemberProxy.hpp"
|
||||
#include "ArduinoJson/Object/ObjectImpl.hpp"
|
||||
#include "ArduinoJson/Variant/ConverterImpl.hpp"
|
||||
#include "ArduinoJson/Variant/JsonVariantCopier.hpp"
|
||||
#include "ArduinoJson/Variant/VariantCompare.hpp"
|
||||
#include "ArduinoJson/Variant/VariantRefBaseImpl.hpp"
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
#include <ArduinoJson/Collection/CollectionIterator.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
@@ -15,36 +15,46 @@ inline VariantImpl::iterator VariantImpl::at(size_t index) const {
|
||||
|
||||
auto it = createIterator();
|
||||
while (!it.done() && index) {
|
||||
it.move(resources_);
|
||||
it.move();
|
||||
--index;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::addNewElement(VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isArray());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto slot = resources->allocVariant();
|
||||
inline VariantData* VariantImpl::addElement() {
|
||||
if (!isArray())
|
||||
return nullptr;
|
||||
auto slot = allocVariant();
|
||||
if (!slot)
|
||||
return nullptr;
|
||||
addElement(slot, data, resources);
|
||||
addElement(slot);
|
||||
return slot.ptr();
|
||||
}
|
||||
|
||||
inline void VariantImpl::addElement(Slot<VariantData> slot) {
|
||||
auto coll = getCollectionData();
|
||||
|
||||
if (coll->tail != NULL_SLOT) {
|
||||
auto tail = getVariant(coll->tail);
|
||||
tail->next = slot.id();
|
||||
coll->tail = slot.id();
|
||||
} else {
|
||||
coll->head = slot.id();
|
||||
coll->tail = slot.id();
|
||||
}
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::getOrAddElement(size_t index) {
|
||||
auto it = createIterator();
|
||||
while (!it.done() && index > 0) {
|
||||
it.move(resources_);
|
||||
it.move();
|
||||
index--;
|
||||
}
|
||||
if (it.done())
|
||||
index++;
|
||||
VariantData* element = it.data();
|
||||
VariantData* element = it->data();
|
||||
while (index > 0) {
|
||||
element = addNewElement();
|
||||
element = addElement();
|
||||
if (!element)
|
||||
return nullptr;
|
||||
index--;
|
||||
@@ -53,13 +63,44 @@ inline VariantData* VariantImpl::getOrAddElement(size_t index) {
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::getElement(size_t index) const {
|
||||
return at(index).data();
|
||||
return at(index)->data();
|
||||
}
|
||||
|
||||
inline void VariantImpl::removeElement(iterator it) {
|
||||
if (!isArray())
|
||||
return;
|
||||
removeOne(it);
|
||||
}
|
||||
|
||||
inline void VariantImpl::removeElement(size_t index) {
|
||||
removeElement(at(index));
|
||||
}
|
||||
|
||||
inline bool VariantImpl::copyArray(const VariantImpl& src) {
|
||||
ARDUINOJSON_ASSERT(isNull());
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
data_->toArray();
|
||||
|
||||
for (auto it = src.createIterator(); !it.done(); it.move()) {
|
||||
auto slot = allocVariant();
|
||||
if (!slot)
|
||||
return false;
|
||||
|
||||
VariantImpl element(slot.ptr(), resources_);
|
||||
if (!element.copyVariant(*it)) {
|
||||
freeVariant(slot);
|
||||
return false;
|
||||
}
|
||||
|
||||
addElement(slot);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the size (in bytes) of an array with n elements.
|
||||
constexpr size_t sizeofArray(size_t n) {
|
||||
return n * sizeof(VariantData);
|
||||
|
||||
@@ -50,20 +50,15 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
|
||||
: upstream_(src.upstream_), index_(src.index_) {}
|
||||
// clang-format on
|
||||
|
||||
ResourceManager* getResourceManager() const {
|
||||
return VariantAttorney::getResourceManager(upstream_);
|
||||
VariantImpl getImpl() const {
|
||||
auto impl = VariantAttorney::getImpl(upstream_);
|
||||
return VariantImpl(impl.getElement(index_), impl.resources());
|
||||
}
|
||||
|
||||
FORCE_INLINE VariantData* getData() const {
|
||||
return VariantAttorney::getVariantImpl(upstream_).getElement(index_);
|
||||
}
|
||||
|
||||
VariantData* getOrCreateData() const {
|
||||
auto data = VariantAttorney::getOrCreateData(upstream_);
|
||||
auto resources = VariantAttorney::getResourceManager(upstream_);
|
||||
if (data && data->type == VariantType::Null)
|
||||
data->toArray();
|
||||
return VariantImpl(data, resources).getOrAddElement(index_);
|
||||
VariantImpl getOrCreateImpl() const {
|
||||
auto impl = VariantAttorney::getOrCreateImpl(upstream_);
|
||||
impl.toArrayIfNull();
|
||||
return VariantImpl(impl.getOrAddElement(index_), impl.resources());
|
||||
}
|
||||
|
||||
TUpstream upstream_;
|
||||
|
||||
@@ -23,22 +23,18 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
JsonArray() {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonArray(detail::VariantData* data, detail::ResourceManager* resources)
|
||||
: impl_(data, resources) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonArray(const detail::VariantImpl& impl) : impl_(impl) {}
|
||||
JsonArray(detail::VariantImpl impl) : impl_(impl) {}
|
||||
|
||||
// Returns a JsonVariant pointing to the array.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/
|
||||
operator JsonVariant() {
|
||||
return JsonVariant(getData(), getResourceManager());
|
||||
return JsonVariant(impl_);
|
||||
}
|
||||
|
||||
// Returns a read-only reference to the array.
|
||||
// https://arduinojson.org/v7/api/jsonarrayconst/
|
||||
operator JsonArrayConst() const {
|
||||
return JsonArrayConst(getData(), getResourceManager());
|
||||
return JsonArrayConst(impl_);
|
||||
}
|
||||
|
||||
// Appends a new (empty) element to the array.
|
||||
@@ -56,16 +52,14 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
template <typename T, detail::enable_if_t<
|
||||
detail::is_same<T, JsonVariant>::value, int> = 0>
|
||||
JsonVariant add() const {
|
||||
return JsonVariant(impl_.addNewElement(), impl_.resources());
|
||||
return JsonVariant(impl_.addElement(), impl_.resources());
|
||||
}
|
||||
|
||||
// Appends a value to the array.
|
||||
// https://arduinojson.org/v7/api/jsonarray/add/
|
||||
template <typename T>
|
||||
bool add(const T& value) const {
|
||||
if (!impl_.isArray())
|
||||
return false;
|
||||
return addValue(value, impl_.data(), impl_.resources());
|
||||
return doAdd(value);
|
||||
}
|
||||
|
||||
// Appends a value to the array.
|
||||
@@ -73,15 +67,13 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
template <typename T,
|
||||
detail::enable_if_t<!detail::is_const<T>::value, int> = 0>
|
||||
bool add(T* value) const {
|
||||
if (!impl_.isArray())
|
||||
return false;
|
||||
return addValue(value, impl_.data(), impl_.resources());
|
||||
return doAdd(value);
|
||||
}
|
||||
|
||||
// Returns an iterator to the first element of the array.
|
||||
// https://arduinojson.org/v7/api/jsonarray/begin/
|
||||
iterator begin() const {
|
||||
return iterator(impl_.createIterator(), impl_.resources());
|
||||
return iterator(impl_.createIterator());
|
||||
}
|
||||
|
||||
// Returns an iterator following the last element of the array.
|
||||
@@ -93,13 +85,8 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
// Copies an array.
|
||||
// https://arduinojson.org/v7/api/jsonarray/set/
|
||||
bool set(JsonArrayConst src) const {
|
||||
clear();
|
||||
for (auto element : src) {
|
||||
if (!add(element))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
impl_.clear();
|
||||
return impl_.copyArray(detail::VariantAttorney::getImpl(src));
|
||||
}
|
||||
|
||||
// Removes the element at the specified iterator.
|
||||
@@ -126,7 +113,8 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
// Removes all the elements of the array.
|
||||
// https://arduinojson.org/v7/api/jsonarray/clear/
|
||||
void clear() const {
|
||||
impl_.empty();
|
||||
if (impl_.isArray())
|
||||
impl_.empty();
|
||||
}
|
||||
|
||||
// Gets or sets the element at the specified index.
|
||||
@@ -149,7 +137,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(getData(), getResourceManager());
|
||||
return JsonVariantConst(impl_);
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
@@ -199,25 +187,20 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
}
|
||||
|
||||
private:
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return impl_.resources();
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
detail::VariantData* getData() const {
|
||||
return impl_.data();
|
||||
const detail::VariantImpl& getOrCreateImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
detail::VariantData* getOrCreateData() const {
|
||||
return impl_.data();
|
||||
}
|
||||
|
||||
// HACK: this function has been pulled out of VariantImpl to avoid the
|
||||
// circular dependency between VariantImpl and JsonVariant
|
||||
template <typename T>
|
||||
static bool addValue(const T& value, detail::VariantData* data,
|
||||
detail::ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isArray());
|
||||
bool doAdd(const T& value) const {
|
||||
if (!impl_.isArray())
|
||||
return false;
|
||||
|
||||
auto resources = impl_.resources();
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto slot = resources->allocVariant();
|
||||
@@ -225,11 +208,11 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
|
||||
return false;
|
||||
|
||||
if (!JsonVariant(slot.ptr(), resources).set(value)) {
|
||||
detail::VariantImpl::freeVariant(slot, resources);
|
||||
detail::VariantImpl(slot.ptr(), resources).clear();
|
||||
resources->freeVariant(slot);
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::VariantImpl::addElement(slot, data, resources);
|
||||
impl_.addElement(slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
// Returns an iterator to the first element of the array.
|
||||
// https://arduinojson.org/v7/api/jsonarrayconst/begin/
|
||||
iterator begin() const {
|
||||
return iterator(impl_.createIterator(), impl_.resources());
|
||||
return iterator(impl_.createIterator());
|
||||
}
|
||||
|
||||
// Returns an iterator to the element following the last element of the array.
|
||||
@@ -36,10 +36,6 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
// Creates an unbound reference.
|
||||
JsonArrayConst() {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonArrayConst(detail::VariantData* data, detail::ResourceManager* resources)
|
||||
: impl_(data, resources) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonArrayConst(const detail::VariantImpl& impl) : impl_(impl) {}
|
||||
|
||||
@@ -63,7 +59,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(impl_.data(), impl_.resources());
|
||||
return JsonVariantConst(impl_);
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
@@ -97,8 +93,8 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
|
||||
}
|
||||
|
||||
private:
|
||||
const detail::VariantData* getData() const {
|
||||
return impl_.data();
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
detail::VariantImpl impl_;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionIterator.hpp>
|
||||
#include <ArduinoJson/Variant/JsonVariant.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
@@ -30,12 +31,11 @@ class JsonArrayIterator {
|
||||
|
||||
public:
|
||||
JsonArrayIterator() {}
|
||||
explicit JsonArrayIterator(detail::CollectionIterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
explicit JsonArrayIterator(const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
|
||||
JsonVariant operator*() {
|
||||
return JsonVariant(iterator_.data(), resources_);
|
||||
return JsonVariant(*iterator_);
|
||||
}
|
||||
Ptr<JsonVariant> operator->() {
|
||||
return operator*();
|
||||
@@ -50,13 +50,12 @@ class JsonArrayIterator {
|
||||
}
|
||||
|
||||
JsonArrayIterator& operator++() {
|
||||
iterator_.move(resources_);
|
||||
iterator_.move();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::CollectionIterator iterator_;
|
||||
detail::ResourceManager* resources_;
|
||||
detail::VariantImpl::iterator iterator_;
|
||||
};
|
||||
|
||||
class JsonArrayConstIterator {
|
||||
@@ -64,12 +63,11 @@ class JsonArrayConstIterator {
|
||||
|
||||
public:
|
||||
JsonArrayConstIterator() {}
|
||||
explicit JsonArrayConstIterator(detail::CollectionIterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
explicit JsonArrayConstIterator(const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
|
||||
JsonVariantConst operator*() const {
|
||||
return JsonVariantConst(iterator_.data(), resources_);
|
||||
return JsonVariantConst(*iterator_);
|
||||
}
|
||||
Ptr<JsonVariantConst> operator->() {
|
||||
return operator*();
|
||||
@@ -84,13 +82,12 @@ class JsonArrayConstIterator {
|
||||
}
|
||||
|
||||
JsonArrayConstIterator& operator++() {
|
||||
iterator_.move(resources_);
|
||||
iterator_.move();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable detail::CollectionIterator iterator_;
|
||||
mutable detail::ResourceManager* resources_;
|
||||
mutable detail::VariantImpl::iterator iterator_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -4,56 +4,32 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <ArduinoJson/Variant/VariantCompare.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline void CollectionIterator::move(const ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(slot_);
|
||||
auto nextId = slot_->next;
|
||||
slot_ = resources->getVariant(nextId);
|
||||
currentId_ = nextId;
|
||||
inline VariantImpl::iterator VariantImpl::createIterator() const {
|
||||
if (!data_ || !data_->isCollection())
|
||||
return iterator();
|
||||
auto coll = getCollectionData();
|
||||
return iterator(coll->head, resources_);
|
||||
}
|
||||
|
||||
inline VariantImpl::iterator VariantImpl::createIterator(
|
||||
VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isCollection());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
auto head = data->content.asCollection.head;
|
||||
return iterator(resources->getVariant(head), head);
|
||||
}
|
||||
inline void VariantImpl::addMember(Slot<VariantData> key,
|
||||
Slot<VariantData> value) {
|
||||
ARDUINOJSON_ASSERT(isObject());
|
||||
ARDUINOJSON_ASSERT(key);
|
||||
ARDUINOJSON_ASSERT(value);
|
||||
|
||||
inline void VariantImpl::addElement(Slot<VariantData> slot, VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isCollection());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto coll = &data->content.asCollection;
|
||||
|
||||
if (coll->tail != NULL_SLOT) {
|
||||
auto tail = resources->getVariant(coll->tail);
|
||||
tail->next = slot.id();
|
||||
coll->tail = slot.id();
|
||||
} else {
|
||||
coll->head = slot.id();
|
||||
coll->tail = slot.id();
|
||||
}
|
||||
}
|
||||
|
||||
inline void VariantImpl::appendPair(Slot<VariantData> key,
|
||||
Slot<VariantData> value, VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
auto coll = getCollectionData();
|
||||
|
||||
key->next = value.id();
|
||||
|
||||
auto coll = &data->content.asCollection;
|
||||
|
||||
if (coll->tail != NULL_SLOT) {
|
||||
auto tail = resources->getVariant(coll->tail);
|
||||
auto tail = getVariant(coll->tail);
|
||||
tail->next = key.id();
|
||||
coll->tail = value.id();
|
||||
} else {
|
||||
@@ -61,36 +37,13 @@ inline void VariantImpl::appendPair(Slot<VariantData> key,
|
||||
coll->tail = value.id();
|
||||
}
|
||||
}
|
||||
|
||||
inline void VariantImpl::empty(VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isCollection());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto coll = &data->content.asCollection;
|
||||
|
||||
auto next = coll->head;
|
||||
while (next != NULL_SLOT) {
|
||||
auto currId = next;
|
||||
auto slot = resources->getVariant(next);
|
||||
next = slot->next;
|
||||
freeVariant({slot, currId}, resources);
|
||||
}
|
||||
|
||||
coll->head = NULL_SLOT;
|
||||
coll->tail = NULL_SLOT;
|
||||
}
|
||||
|
||||
inline Slot<VariantData> VariantImpl::getPreviousSlot(
|
||||
VariantData* target) const {
|
||||
ARDUINOJSON_ASSERT(data_ != nullptr);
|
||||
ARDUINOJSON_ASSERT(data_->isCollection());
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
|
||||
auto coll = getCollectionData();
|
||||
auto prev = Slot<VariantData>();
|
||||
auto currentId = data_->content.asCollection.head;
|
||||
auto currentId = coll->head;
|
||||
while (currentId != NULL_SLOT) {
|
||||
auto currentSlot = resources_->getVariant(currentId);
|
||||
auto currentSlot = getVariant(currentId);
|
||||
if (currentSlot == target)
|
||||
break;
|
||||
prev = Slot<VariantData>(currentSlot, currentId);
|
||||
@@ -102,31 +55,31 @@ inline Slot<VariantData> VariantImpl::getPreviousSlot(
|
||||
inline void VariantImpl::removeOne(iterator it) {
|
||||
if (it.done())
|
||||
return;
|
||||
auto curr = it.slot_;
|
||||
auto coll = getCollectionData();
|
||||
auto curr = it->data();
|
||||
auto prev = getPreviousSlot(curr);
|
||||
auto next = curr->next;
|
||||
auto coll = &data_->content.asCollection;
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
coll->head = next;
|
||||
if (next == NULL_SLOT)
|
||||
coll->tail = prev.id();
|
||||
freeVariant({it.slot_, it.currentId_}, resources_);
|
||||
freeVariant({it->data(), it.slotId()});
|
||||
}
|
||||
|
||||
inline void VariantImpl::removePair(iterator it) {
|
||||
inline void VariantImpl::removePair(VariantImpl::iterator it) {
|
||||
if (it.done())
|
||||
return;
|
||||
|
||||
auto keySlot = it.slot_;
|
||||
auto keySlot = it->data();
|
||||
|
||||
auto valueId = keySlot->next;
|
||||
auto valueSlot = resources_->getVariant(valueId);
|
||||
auto valueSlot = getVariant(valueId);
|
||||
|
||||
// remove value slot
|
||||
keySlot->next = valueSlot->next;
|
||||
freeVariant({valueSlot, valueId}, resources_);
|
||||
freeVariant({valueSlot, valueId});
|
||||
|
||||
// remove key slot
|
||||
removeOne(it);
|
||||
@@ -136,12 +89,27 @@ inline size_t VariantImpl::nesting() const {
|
||||
if (!data_ || !data_->isCollection())
|
||||
return 0;
|
||||
size_t maxChildNesting = 0;
|
||||
for (auto it = createIterator(); !it.done(); it.move(resources_)) {
|
||||
size_t childNesting = VariantImpl(it.data(), resources_).nesting();
|
||||
for (auto it = createIterator(); !it.done(); it.move()) {
|
||||
auto childNesting = it->nesting();
|
||||
if (childNesting > maxChildNesting)
|
||||
maxChildNesting = childNesting;
|
||||
}
|
||||
return maxChildNesting + 1;
|
||||
}
|
||||
|
||||
inline size_t VariantImpl::size() const {
|
||||
if (!data_)
|
||||
return 0;
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
for (auto it = createIterator(); !it.done(); it.move())
|
||||
count++;
|
||||
|
||||
if (data_->type == VariantType::Object)
|
||||
count /= 2; // TODO: do this in JsonObject?
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -6,63 +6,52 @@
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
struct VariantData;
|
||||
class ResourceManager;
|
||||
|
||||
class CollectionIterator {
|
||||
friend class VariantImpl;
|
||||
|
||||
public:
|
||||
CollectionIterator() : slot_(nullptr), currentId_(NULL_SLOT) {}
|
||||
CollectionIterator() {}
|
||||
|
||||
void move(const ResourceManager* resources);
|
||||
CollectionIterator(SlotId slotId, ResourceManager* resources)
|
||||
: value_(resources->getVariant(slotId), resources), slotId_(slotId) {}
|
||||
|
||||
void move() {
|
||||
ARDUINOJSON_ASSERT(!done());
|
||||
auto nextId = value_.data()->next;
|
||||
auto resources = value_.resources();
|
||||
value_ = VariantImpl(resources->getVariant(nextId), resources);
|
||||
slotId_ = nextId;
|
||||
}
|
||||
|
||||
const VariantImpl& operator*() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
const VariantImpl* operator->() const {
|
||||
return &value_;
|
||||
}
|
||||
|
||||
bool done() const {
|
||||
return slot_ == nullptr;
|
||||
return value_.isUnbound();
|
||||
}
|
||||
|
||||
bool operator==(const CollectionIterator& other) const {
|
||||
return slot_ == other.slot_;
|
||||
return value_.data() == other->data();
|
||||
}
|
||||
|
||||
bool operator!=(const CollectionIterator& other) const {
|
||||
return slot_ != other.slot_;
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
VariantData* operator->() {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return data();
|
||||
}
|
||||
|
||||
VariantData& operator*() {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return *data();
|
||||
}
|
||||
|
||||
const VariantData& operator*() const {
|
||||
ARDUINOJSON_ASSERT(slot_ != nullptr);
|
||||
return *data();
|
||||
}
|
||||
|
||||
VariantData* data() {
|
||||
return slot_;
|
||||
}
|
||||
|
||||
const VariantData* data() const {
|
||||
return slot_;
|
||||
SlotId slotId() const {
|
||||
return slotId_;
|
||||
}
|
||||
|
||||
private:
|
||||
CollectionIterator(VariantData* slot, SlotId slotId)
|
||||
: slot_(slot), currentId_(slotId) {}
|
||||
|
||||
VariantData* slot_;
|
||||
SlotId currentId_;
|
||||
VariantImpl value_;
|
||||
SlotId slotId_ = NULL_SLOT;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -44,13 +44,13 @@ template <template <typename> class TDeserializer, typename TDestination,
|
||||
typename TReader, typename TOptions>
|
||||
DeserializationError doDeserialize(TDestination&& dst, TReader reader,
|
||||
TOptions options) {
|
||||
auto data = VariantAttorney::getOrCreateData(dst);
|
||||
if (!data)
|
||||
auto impl = VariantAttorney::getOrCreateImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return DeserializationError::NoMemory;
|
||||
auto resources = VariantAttorney::getResourceManager(dst);
|
||||
auto resources = impl.resources();
|
||||
dst.clear();
|
||||
auto err = TDeserializer<TReader>(resources, reader)
|
||||
.parse(data, options.filter, options.nestingLimit);
|
||||
.parse(impl.data(), options.filter, options.nestingLimit);
|
||||
shrinkJsonDocument(dst);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <ArduinoJson/Object/MemberProxy.hpp>
|
||||
#include <ArduinoJson/Polyfills/utility.hpp>
|
||||
#include <ArduinoJson/Variant/JsonVariantConst.hpp>
|
||||
#include <ArduinoJson/Variant/VariantTo.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -119,13 +120,13 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
// Returns the depth (nesting level) of the array.
|
||||
// https://arduinojson.org/v7/api/jsondocument/nesting/
|
||||
size_t nesting() const {
|
||||
return getVariantImpl().nesting();
|
||||
return getImpl().nesting();
|
||||
}
|
||||
|
||||
// Returns the number of elements in the root array or object.
|
||||
// https://arduinojson.org/v7/api/jsondocument/size/
|
||||
size_t size() const {
|
||||
return getVariantImpl().size();
|
||||
return getImpl().size();
|
||||
}
|
||||
|
||||
// Copies the specified document.
|
||||
@@ -145,38 +146,18 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
|
||||
// Replaces the root with the specified value.
|
||||
// https://arduinojson.org/v7/api/jsondocument/set/
|
||||
template <typename TChar>
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
|
||||
bool set(TChar* src) {
|
||||
return to<JsonVariant>().set(src);
|
||||
}
|
||||
|
||||
// Sets the document to an empty array.
|
||||
// Clears the document and converts it to the specified type.
|
||||
// https://arduinojson.org/v7/api/jsondocument/to/
|
||||
template <typename T,
|
||||
detail::enable_if_t<detail::is_same<T, JsonArray>::value, int> = 0>
|
||||
JsonArray to() {
|
||||
template <typename T>
|
||||
typename detail::VariantTo<T>::type to() {
|
||||
clear();
|
||||
data_.toArray();
|
||||
return JsonArray(&data_, &resources_);
|
||||
}
|
||||
|
||||
// Sets the document to an empty object.
|
||||
// https://arduinojson.org/v7/api/jsondocument/to/
|
||||
template <typename T,
|
||||
detail::enable_if_t<detail::is_same<T, JsonObject>::value, int> = 0>
|
||||
JsonObject to() {
|
||||
clear();
|
||||
data_.toObject();
|
||||
return JsonObject(&data_, &resources_);
|
||||
}
|
||||
|
||||
// Sets the document to null.
|
||||
// https://arduinojson.org/v7/api/jsondocument/to/
|
||||
template <typename T, detail::enable_if_t<
|
||||
detail::is_same<T, JsonVariant>::value, int> = 0>
|
||||
JsonVariant to() {
|
||||
clear();
|
||||
return JsonVariant(&data_, &resources_);
|
||||
return getVariant().template to<T>();
|
||||
}
|
||||
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
@@ -184,7 +165,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
template <typename TChar>
|
||||
ARDUINOJSON_DEPRECATED("use doc[\"key\"].is<T>() instead")
|
||||
bool containsKey(TChar* key) const {
|
||||
return getVariantImpl().getMember(detail::adaptString(key)) != 0;
|
||||
return getImpl().getMember(detail::adaptString(key)) != 0;
|
||||
}
|
||||
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
@@ -193,7 +174,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
|
||||
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
|
||||
bool containsKey(const TString& key) const {
|
||||
return getVariantImpl().getMember(detail::adaptString(key)) != 0;
|
||||
return getImpl().getMember(detail::adaptString(key)) != 0;
|
||||
}
|
||||
|
||||
// DEPRECATED: use obj[key].is<T>() instead
|
||||
@@ -217,7 +198,9 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
// Gets or sets a root object's member.
|
||||
// https://arduinojson.org/v7/api/jsondocument/subscript/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>> operator[](
|
||||
TChar* key) {
|
||||
return {*this, detail::adaptString(key)};
|
||||
@@ -228,17 +211,19 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
template <typename TString,
|
||||
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
|
||||
JsonVariantConst operator[](const TString& key) const {
|
||||
return JsonVariantConst(
|
||||
getVariantImpl().getMember(detail::adaptString(key)), &resources_);
|
||||
return JsonVariantConst(getImpl().getMember(detail::adaptString(key)),
|
||||
&resources_);
|
||||
}
|
||||
|
||||
// Gets a root object's member.
|
||||
// https://arduinojson.org/v7/api/jsondocument/subscript/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
JsonVariantConst operator[](TChar* key) const {
|
||||
return JsonVariantConst(
|
||||
getVariantImpl().getMember(detail::adaptString(key)), &resources_);
|
||||
return JsonVariantConst(getImpl().getMember(detail::adaptString(key)),
|
||||
&resources_);
|
||||
}
|
||||
|
||||
// Gets or sets a root array's element.
|
||||
@@ -252,7 +237,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
// Gets a root array's member.
|
||||
// https://arduinojson.org/v7/api/jsondocument/subscript/
|
||||
JsonVariantConst operator[](size_t index) const {
|
||||
return JsonVariantConst(getVariantImpl().getElement(index), &resources_);
|
||||
return JsonVariantConst(getImpl().getElement(index), &resources_);
|
||||
}
|
||||
|
||||
// Gets or sets a root object's member.
|
||||
@@ -294,7 +279,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
|
||||
// Appends a value to the root array.
|
||||
// https://arduinojson.org/v7/api/jsondocument/add/
|
||||
template <typename TChar>
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
|
||||
bool add(TChar* value) {
|
||||
return getOrCreateArray().add(value);
|
||||
}
|
||||
@@ -304,15 +290,17 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
template <typename T,
|
||||
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
|
||||
void remove(T index) {
|
||||
getVariantImpl().removeElement(size_t(index));
|
||||
getImpl().removeElement(size_t(index));
|
||||
}
|
||||
|
||||
// Removes a member of the root object.
|
||||
// https://arduinojson.org/v7/api/jsondocument/remove/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
void remove(TChar* key) {
|
||||
getVariantImpl().removeMember(detail::adaptString(key));
|
||||
getImpl().removeMember(detail::adaptString(key));
|
||||
}
|
||||
|
||||
// Removes a member of the root object.
|
||||
@@ -320,7 +308,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
template <typename TString,
|
||||
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
|
||||
void remove(const TString& key) {
|
||||
getVariantImpl().removeMember(detail::adaptString(key));
|
||||
getImpl().removeMember(detail::adaptString(key));
|
||||
}
|
||||
|
||||
// Removes a member of the root object or an element of the root array.
|
||||
@@ -400,14 +388,18 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl getVariantImpl() const {
|
||||
return detail::VariantImpl(&data_, &resources_);
|
||||
detail::VariantImpl getImpl() const {
|
||||
return {&data_, &resources_};
|
||||
}
|
||||
|
||||
JsonArray getOrCreateArray() const {
|
||||
if (data_.type == detail::VariantType::Null)
|
||||
data_.toArray();
|
||||
return JsonArray(&data_, &resources_);
|
||||
detail::VariantImpl getOrCreateImpl() {
|
||||
return {&data_, &resources_};
|
||||
}
|
||||
|
||||
JsonArray getOrCreateArray() {
|
||||
auto impl = getImpl();
|
||||
impl.toArrayIfNull();
|
||||
return JsonArray(impl);
|
||||
}
|
||||
|
||||
JsonVariant getVariant() {
|
||||
@@ -418,22 +410,6 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
||||
return JsonVariantConst(&data_, &resources_);
|
||||
}
|
||||
|
||||
detail::ResourceManager* getResourceManager() {
|
||||
return &resources_;
|
||||
}
|
||||
|
||||
detail::VariantData* getData() {
|
||||
return &data_;
|
||||
}
|
||||
|
||||
const detail::VariantData* getData() const {
|
||||
return &data_;
|
||||
}
|
||||
|
||||
detail::VariantData* getOrCreateData() {
|
||||
return &data_;
|
||||
}
|
||||
|
||||
mutable detail::ResourceManager resources_;
|
||||
mutable detail::VariantData data_;
|
||||
};
|
||||
|
||||
@@ -64,6 +64,8 @@ class JsonDeserializer {
|
||||
DeserializationOption::NestingLimit nestingLimit) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
ARDUINOJSON_ASSERT(variant != nullptr);
|
||||
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
@@ -71,13 +73,13 @@ class JsonDeserializer {
|
||||
switch (current()) {
|
||||
case '[':
|
||||
if (filter.allowArray())
|
||||
return parseArray(variant, filter, nestingLimit);
|
||||
return parseArray(variant->toArray(), filter, nestingLimit);
|
||||
else
|
||||
return skipArray(nestingLimit);
|
||||
|
||||
case '{':
|
||||
if (filter.allowObject())
|
||||
return parseObject(variant, filter, nestingLimit);
|
||||
return parseObject(variant->toObject(), filter, nestingLimit);
|
||||
else
|
||||
return skipObject(nestingLimit);
|
||||
|
||||
@@ -146,12 +148,10 @@ class JsonDeserializer {
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError::Code parseArray(
|
||||
VariantData* array, TFilter filter,
|
||||
VariantData* arrayData, TFilter filter,
|
||||
DeserializationOption::NestingLimit nestingLimit) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
array->toArray();
|
||||
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
|
||||
@@ -173,8 +173,10 @@ class JsonDeserializer {
|
||||
// Read each value
|
||||
for (;;) {
|
||||
if (elementFilter.allow()) {
|
||||
VariantImpl array(arrayData, resources_);
|
||||
|
||||
// Allocate slot in array
|
||||
VariantData* value = VariantImpl::addNewElement(array, resources_);
|
||||
VariantData* value = array.addElement();
|
||||
if (!value)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
@@ -234,12 +236,10 @@ class JsonDeserializer {
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError::Code parseObject(
|
||||
VariantData* object, TFilter filter,
|
||||
VariantData* objectData, TFilter filter,
|
||||
DeserializationOption::NestingLimit nestingLimit) {
|
||||
DeserializationError::Code err;
|
||||
|
||||
object->toObject();
|
||||
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
|
||||
@@ -277,16 +277,23 @@ class JsonDeserializer {
|
||||
TFilter memberFilter = filter[key];
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
auto member =
|
||||
VariantImpl::getMember(adaptString(key), object, resources_);
|
||||
VariantImpl object(objectData, resources_);
|
||||
auto member = object.getMember(adaptString(key));
|
||||
if (!member) {
|
||||
auto keyVariant = VariantImpl::addPair(&member, object, resources_);
|
||||
if (!keyVariant)
|
||||
auto keySlot = resources_->allocVariant();
|
||||
if (!keySlot)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
stringBuilder_.save(keyVariant);
|
||||
auto valueSlot = resources_->allocVariant();
|
||||
if (!valueSlot)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
object.addMember(keySlot, valueSlot);
|
||||
|
||||
stringBuilder_.save(keySlot.ptr());
|
||||
member = valueSlot.ptr();
|
||||
} else {
|
||||
VariantImpl::clear(member, resources_);
|
||||
VariantImpl(member, resources_).clear();
|
||||
}
|
||||
|
||||
// Parse value
|
||||
@@ -523,28 +530,28 @@ class JsonDeserializer {
|
||||
auto number = parseNumber(buffer_);
|
||||
switch (number.type()) {
|
||||
case NumberType::UnsignedInteger:
|
||||
if (VariantImpl::setInteger(number.asUnsignedInteger(), result,
|
||||
resources_))
|
||||
if (VariantImpl(result, resources_)
|
||||
.setInteger(number.asUnsignedInteger()))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
case NumberType::SignedInteger:
|
||||
if (VariantImpl::setInteger(number.asSignedInteger(), result,
|
||||
resources_))
|
||||
if (VariantImpl(result, resources_)
|
||||
.setInteger(number.asSignedInteger()))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
case NumberType::Float:
|
||||
if (VariantImpl::setFloat(number.asFloat(), result, resources_))
|
||||
if (VariantImpl(result, resources_).setFloat(number.asFloat()))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case NumberType::Double:
|
||||
if (VariantImpl::setFloat(number.asDouble(), result, resources_))
|
||||
if (VariantImpl(result, resources_).setFloat(number.asDouble()))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
@@ -19,49 +19,39 @@ class JsonSerializer : public VariantDataVisitor<size_t> {
|
||||
JsonSerializer(TWriter writer, ResourceManager* resources)
|
||||
: formatter_(writer), resources_(resources) {}
|
||||
|
||||
size_t visitArray(VariantData* array) {
|
||||
ARDUINOJSON_ASSERT(array != nullptr);
|
||||
ARDUINOJSON_ASSERT(array->isArray());
|
||||
|
||||
size_t visitArray(const VariantImpl& array) {
|
||||
write('[');
|
||||
|
||||
auto slotId = array->content.asCollection.head;
|
||||
bool first = true;
|
||||
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
|
||||
VariantImpl::accept(*this, slot, resources_);
|
||||
|
||||
slotId = slot->next;
|
||||
|
||||
if (slotId != NULL_SLOT)
|
||||
for (auto it = array.createIterator(); !it.done(); it.move()) {
|
||||
if (!first)
|
||||
write(',');
|
||||
|
||||
it->accept(*this);
|
||||
first = false;
|
||||
}
|
||||
|
||||
write(']');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(VariantData* object) {
|
||||
ARDUINOJSON_ASSERT(object != nullptr);
|
||||
ARDUINOJSON_ASSERT(object->isObject());
|
||||
|
||||
size_t visitObject(const VariantImpl& object) {
|
||||
write('{');
|
||||
|
||||
auto slotId = object->content.asCollection.head;
|
||||
bool first = true;
|
||||
bool isValue = false;
|
||||
|
||||
bool isKey = true;
|
||||
for (auto it = object.createIterator(); !it.done(); it.move()) {
|
||||
if (isValue)
|
||||
write(':');
|
||||
else if (!first)
|
||||
write(',');
|
||||
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
VariantImpl::accept(*this, slot, resources_);
|
||||
it->accept(*this);
|
||||
|
||||
slotId = slot->next;
|
||||
|
||||
if (slotId != NULL_SLOT)
|
||||
write(isKey ? ':' : ',');
|
||||
|
||||
isKey = !isKey;
|
||||
first = false;
|
||||
isValue = !isValue;
|
||||
}
|
||||
|
||||
write('}');
|
||||
|
||||
@@ -19,21 +19,16 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
PrettyJsonSerializer(TWriter writer, ResourceManager* resources)
|
||||
: base(writer, resources), nesting_(0) {}
|
||||
|
||||
size_t visitArray(VariantData* array) {
|
||||
ARDUINOJSON_ASSERT(array != nullptr);
|
||||
ARDUINOJSON_ASSERT(array->isArray());
|
||||
|
||||
auto slotId = array->content.asCollection.head;
|
||||
if (slotId != NULL_SLOT) {
|
||||
size_t visitArray(const VariantImpl& array) {
|
||||
auto it = array.createIterator();
|
||||
if (!it.done()) {
|
||||
base::write("[\r\n");
|
||||
nesting_++;
|
||||
while (slotId != NULL_SLOT) {
|
||||
while (!it.done()) {
|
||||
indent();
|
||||
auto slot = base::resources_->getVariant(slotId);
|
||||
VariantImpl::accept(*this, slot, base::resources_);
|
||||
|
||||
slotId = slot->next;
|
||||
base::write(slotId == NULL_SLOT ? "\r\n" : ",\r\n");
|
||||
it->accept(*this);
|
||||
it.move();
|
||||
base::write(it.done() ? "\r\n" : ",\r\n");
|
||||
}
|
||||
nesting_--;
|
||||
indent();
|
||||
@@ -44,25 +39,21 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
return this->bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(VariantData* object) {
|
||||
ARDUINOJSON_ASSERT(object != nullptr);
|
||||
ARDUINOJSON_ASSERT(object->isObject());
|
||||
|
||||
auto slotId = object->content.asCollection.head;
|
||||
if (slotId != NULL_SLOT) {
|
||||
size_t visitObject(const VariantImpl& object) {
|
||||
auto it = object.createIterator();
|
||||
if (!it.done()) {
|
||||
base::write("{\r\n");
|
||||
nesting_++;
|
||||
bool isKey = true;
|
||||
while (slotId != NULL_SLOT) {
|
||||
while (!it.done()) {
|
||||
if (isKey)
|
||||
indent();
|
||||
auto slot = base::resources_->getVariant(slotId);
|
||||
VariantImpl::accept(*this, slot, base::resources_);
|
||||
slotId = slot->next;
|
||||
it->accept(*this);
|
||||
it.move();
|
||||
if (isKey)
|
||||
base::write(": ");
|
||||
else
|
||||
base::write(slotId == NULL_SLOT ? "\r\n" : ",\r\n");
|
||||
base::write(it.done() ? "\r\n" : ",\r\n");
|
||||
isKey = !isKey;
|
||||
}
|
||||
nesting_--;
|
||||
|
||||
@@ -66,16 +66,13 @@ class TextFormatter {
|
||||
|
||||
template <typename T>
|
||||
void writeFloat(T value) {
|
||||
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 7);
|
||||
writeFloat(JsonFloat(value), sizeof(T) >= 8 ? 9 : 6);
|
||||
}
|
||||
|
||||
void writeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||
if (isnan(value))
|
||||
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
|
||||
|
||||
if (!value)
|
||||
return writeRaw("0");
|
||||
|
||||
#if ARDUINOJSON_ENABLE_INFINITY
|
||||
if (value < 0.0) {
|
||||
writeRaw('-');
|
||||
@@ -96,28 +93,9 @@ class TextFormatter {
|
||||
|
||||
auto parts = decomposeFloat(value, decimalPlaces);
|
||||
|
||||
// buffer should be big enough for all digits and the dot
|
||||
char buffer[32];
|
||||
char* end = buffer + sizeof(buffer);
|
||||
char* begin = end;
|
||||
|
||||
// write the string in reverse order
|
||||
while (parts.mantissa != 0 || parts.pointIndex > 0) {
|
||||
*--begin = char(parts.mantissa % 10 + '0');
|
||||
parts.mantissa /= 10;
|
||||
if (parts.pointIndex == 1) {
|
||||
*--begin = '.';
|
||||
}
|
||||
parts.pointIndex--;
|
||||
}
|
||||
|
||||
// Avoid a leading dot
|
||||
if (parts.pointIndex == 0) {
|
||||
*--begin = '0';
|
||||
}
|
||||
|
||||
// and dump it in the right order
|
||||
writeRaw(begin, end);
|
||||
writeInteger(parts.integral);
|
||||
if (parts.decimalPlaces)
|
||||
writeDecimals(parts.decimal, parts.decimalPlaces);
|
||||
|
||||
if (parts.exponent) {
|
||||
writeRaw('e');
|
||||
@@ -154,6 +132,23 @@ class TextFormatter {
|
||||
writeRaw(begin, end);
|
||||
}
|
||||
|
||||
void writeDecimals(uint32_t value, int8_t width) {
|
||||
// buffer should be big enough for all digits and the dot
|
||||
char buffer[16];
|
||||
char* end = buffer + sizeof(buffer);
|
||||
char* begin = end;
|
||||
|
||||
// write the string in reverse order
|
||||
while (width--) {
|
||||
*--begin = char(value % 10 + '0');
|
||||
value /= 10;
|
||||
}
|
||||
*--begin = '.';
|
||||
|
||||
// and dump it in the right order
|
||||
writeRaw(begin, end);
|
||||
}
|
||||
|
||||
void writeRaw(const char* s) {
|
||||
writer_.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ class Slot {
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
T& operator*() const {
|
||||
ARDUINOJSON_ASSERT(ptr_ != nullptr);
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
T* operator->() const {
|
||||
ARDUINOJSON_ASSERT(ptr_ != nullptr);
|
||||
return ptr_;
|
||||
@@ -76,6 +81,14 @@ class MemoryPool {
|
||||
return slots_ + id;
|
||||
}
|
||||
|
||||
SlotId find(const T& value) const {
|
||||
for (SlotId i = 0; i < usage_; i++) {
|
||||
if (slots_[i] == value)
|
||||
return i;
|
||||
}
|
||||
return NULL_SLOT;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
usage_ = 0;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,15 @@ class MemoryPoolList {
|
||||
return pools_[poolIndex].getSlot(indexInPool);
|
||||
}
|
||||
|
||||
SlotId find(const T& value) const {
|
||||
for (PoolCount i = 0; i < count_; i++) {
|
||||
SlotId id = pools_[i].find(value);
|
||||
if (id != NULL_SLOT)
|
||||
return SlotId(i * ARDUINOJSON_POOL_CAPACITY + id);
|
||||
}
|
||||
return NULL_SLOT;
|
||||
}
|
||||
|
||||
void clear(Allocator* allocator) {
|
||||
for (PoolCount i = 0; i < count_; i++)
|
||||
pools_[i].destroy(allocator);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
struct VariantData;
|
||||
class VariantWithId;
|
||||
|
||||
class ResourceManager {
|
||||
@@ -24,6 +25,7 @@ class ResourceManager {
|
||||
~ResourceManager() {
|
||||
stringPool_.clear(allocator_);
|
||||
variantPools_.clear(allocator_);
|
||||
staticStringsPools_.clear(allocator_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
eightBytePools_.clear(allocator_);
|
||||
#endif
|
||||
@@ -35,6 +37,7 @@ class ResourceManager {
|
||||
friend void swap(ResourceManager& a, ResourceManager& b) {
|
||||
swap(a.stringPool_, b.stringPool_);
|
||||
swap(a.variantPools_, b.variantPools_);
|
||||
swap(a.staticStringsPools_, b.staticStringsPools_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
swap(a.eightBytePools_, b.eightBytePools_);
|
||||
#endif
|
||||
@@ -65,12 +68,11 @@ class ResourceManager {
|
||||
}
|
||||
|
||||
void freeVariant(Slot<VariantData> slot) {
|
||||
ARDUINOJSON_ASSERT(slot->type == VariantType::Null);
|
||||
variantPools_.freeSlot(slot);
|
||||
}
|
||||
|
||||
VariantData* getVariant(SlotId id) const {
|
||||
return reinterpret_cast<VariantData*>(variantPools_.getSlot(id));
|
||||
return variantPools_.getSlot(id);
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
@@ -132,14 +134,33 @@ class ResourceManager {
|
||||
StringNode::destroy(node, allocator_);
|
||||
}
|
||||
|
||||
void dereferenceString(const char* s) {
|
||||
stringPool_.dereference(s, allocator_);
|
||||
void dereferenceString(StringNode* node) {
|
||||
stringPool_.dereference(node, allocator_);
|
||||
}
|
||||
|
||||
SlotId saveStaticString(const char* s) {
|
||||
auto existingSlotId = staticStringsPools_.find(s);
|
||||
if (existingSlotId != NULL_SLOT)
|
||||
return existingSlotId;
|
||||
|
||||
auto slot = staticStringsPools_.allocSlot(allocator_);
|
||||
if (slot)
|
||||
*slot = s;
|
||||
else
|
||||
overflowed_ = true;
|
||||
|
||||
return slot.id();
|
||||
}
|
||||
|
||||
const char* getStaticString(SlotId id) const {
|
||||
return *staticStringsPools_.getSlot(id);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
variantPools_.clear(allocator_);
|
||||
overflowed_ = false;
|
||||
variantPools_.clear(allocator_);
|
||||
stringPool_.clear(allocator_);
|
||||
staticStringsPools_.clear(allocator_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
eightBytePools_.clear(allocator_);
|
||||
#endif
|
||||
@@ -147,6 +168,7 @@ class ResourceManager {
|
||||
|
||||
void shrinkToFit() {
|
||||
variantPools_.shrinkToFit(allocator_);
|
||||
staticStringsPools_.shrinkToFit(allocator_);
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
eightBytePools_.shrinkToFit(allocator_);
|
||||
#endif
|
||||
@@ -157,6 +179,7 @@ class ResourceManager {
|
||||
bool overflowed_;
|
||||
StringPool stringPool_;
|
||||
MemoryPoolList<VariantData> variantPools_;
|
||||
MemoryPoolList<const char*> staticStringsPools_;
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
MemoryPoolList<EightByteValue> eightBytePools_;
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Strings/JsonString.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -44,7 +43,7 @@ class StringBuffer {
|
||||
if (isTinyString(s, size_))
|
||||
data->setTinyString(adaptString(s, size_));
|
||||
else
|
||||
data->setLongString(commitStringNode());
|
||||
data->setOwnedString(commitStringNode());
|
||||
}
|
||||
|
||||
void saveRaw(VariantData* data) {
|
||||
|
||||
@@ -46,7 +46,7 @@ class StringBuilder {
|
||||
} else {
|
||||
node->references++;
|
||||
}
|
||||
variant->setLongString(node);
|
||||
variant->setOwnedString(node);
|
||||
}
|
||||
|
||||
void append(const char* s) {
|
||||
|
||||
@@ -78,20 +78,20 @@ class StringPool {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void dereference(const char* s, Allocator* allocator) {
|
||||
void dereference(StringNode* nodeToRemove, Allocator* allocator) {
|
||||
StringNode* prev = nullptr;
|
||||
for (auto node = strings_; node; node = node->next) {
|
||||
if (node->data == s) {
|
||||
if (--node->references == 0) {
|
||||
for (auto current = strings_; current; current = current->next) {
|
||||
if (current == nodeToRemove) {
|
||||
if (--current->references == 0) {
|
||||
if (prev)
|
||||
prev->next = node->next;
|
||||
prev->next = current->next;
|
||||
else
|
||||
strings_ = node->next;
|
||||
StringNode::destroy(node, allocator);
|
||||
strings_ = current->next;
|
||||
StringNode::destroy(current, allocator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
prev = node;
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,23 +25,21 @@ class MsgPackBinary {
|
||||
template <>
|
||||
struct Converter<MsgPackBinary> : private detail::VariantAttorney {
|
||||
static bool toJson(MsgPackBinary src, JsonVariant dst) {
|
||||
auto data = VariantAttorney::getData(dst);
|
||||
if (!data)
|
||||
auto impl = getImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return false;
|
||||
|
||||
auto resources = getResourceManager(dst);
|
||||
detail::VariantImpl::clear(data, resources);
|
||||
|
||||
if (!src.data())
|
||||
return true;
|
||||
|
||||
size_t headerSize = src.size() >= 0x10000 ? 5 : src.size() >= 0x100 ? 3 : 2;
|
||||
|
||||
auto resources = impl.resources();
|
||||
auto str = resources->createString(src.size() + headerSize);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
resources->saveString(str);
|
||||
|
||||
auto ptr = reinterpret_cast<uint8_t*>(str->data);
|
||||
switch (headerSize) {
|
||||
case 2:
|
||||
@@ -64,15 +62,13 @@ struct Converter<MsgPackBinary> : private detail::VariantAttorney {
|
||||
ARDUINOJSON_ASSERT(false);
|
||||
}
|
||||
memcpy(ptr + headerSize, src.data(), src.size());
|
||||
data->setRawString(str);
|
||||
impl.data()->setRawString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MsgPackBinary fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
if (!data)
|
||||
return {};
|
||||
auto rawstr = data->asRawString();
|
||||
auto variant = getImpl(src);
|
||||
auto rawstr = variant.asRawString();
|
||||
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
|
||||
auto n = rawstr.size();
|
||||
if (n >= 2 && p[0] == 0xc4) { // bin 8
|
||||
|
||||
@@ -91,7 +91,7 @@ class MsgPackDeserializer {
|
||||
|
||||
if (code <= 0x7f || code >= 0xe0) { // fixint
|
||||
if (allowValue)
|
||||
VariantImpl::setInteger(static_cast<int8_t>(code), variant, resources_);
|
||||
VariantImpl(variant, resources_).setInteger(static_cast<int8_t>(code));
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
@@ -231,14 +231,14 @@ class MsgPackDeserializer {
|
||||
if (isSigned) {
|
||||
auto truncatedValue = static_cast<JsonInteger>(signedValue);
|
||||
if (truncatedValue == signedValue) {
|
||||
if (!VariantImpl::setInteger(truncatedValue, variant, resources_))
|
||||
if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
|
||||
return DeserializationError::NoMemory;
|
||||
}
|
||||
// else set null on overflow
|
||||
} else {
|
||||
auto truncatedValue = static_cast<JsonUInt>(unsignedValue);
|
||||
if (truncatedValue == unsignedValue)
|
||||
if (!VariantImpl::setInteger(truncatedValue, variant, resources_))
|
||||
if (!VariantImpl(variant, resources_).setInteger(truncatedValue))
|
||||
return DeserializationError::NoMemory;
|
||||
// else set null on overflow
|
||||
}
|
||||
@@ -257,7 +257,7 @@ class MsgPackDeserializer {
|
||||
return err;
|
||||
|
||||
fixEndianness(value);
|
||||
VariantImpl::setFloat(value, variant, resources_);
|
||||
VariantImpl(variant, resources_).setFloat(value);
|
||||
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
@@ -273,7 +273,7 @@ class MsgPackDeserializer {
|
||||
return err;
|
||||
|
||||
fixEndianness(value);
|
||||
if (VariantImpl::setFloat(value, variant, resources_))
|
||||
if (VariantImpl(variant, resources_).setFloat(value))
|
||||
return DeserializationError::Ok;
|
||||
else
|
||||
return DeserializationError::NoMemory;
|
||||
@@ -293,7 +293,7 @@ class MsgPackDeserializer {
|
||||
|
||||
doubleToFloat(i, o);
|
||||
fixEndianness(value);
|
||||
VariantImpl::setFloat(value, variant, resources_);
|
||||
VariantImpl(variant, resources_).setFloat(value);
|
||||
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
@@ -349,9 +349,10 @@ class MsgPackDeserializer {
|
||||
|
||||
bool allowArray = filter.allowArray();
|
||||
|
||||
VariantImpl array;
|
||||
if (allowArray) {
|
||||
ARDUINOJSON_ASSERT(variant != 0);
|
||||
variant->toArray();
|
||||
array = VariantImpl(variant->toArray(), resources_);
|
||||
}
|
||||
|
||||
TFilter elementFilter = filter[0U];
|
||||
@@ -360,7 +361,7 @@ class MsgPackDeserializer {
|
||||
VariantData* value;
|
||||
|
||||
if (elementFilter.allow()) {
|
||||
value = VariantImpl::addNewElement(variant, resources_);
|
||||
value = array.addElement();
|
||||
if (!value)
|
||||
return DeserializationError::NoMemory;
|
||||
} else {
|
||||
@@ -384,9 +385,10 @@ class MsgPackDeserializer {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
|
||||
VariantImpl object;
|
||||
if (filter.allowObject()) {
|
||||
ARDUINOJSON_ASSERT(variant != 0);
|
||||
variant->toObject();
|
||||
object = VariantImpl(variant->toObject(), resources_);
|
||||
}
|
||||
|
||||
for (; n; --n) {
|
||||
@@ -399,11 +401,18 @@ class MsgPackDeserializer {
|
||||
VariantData* member = 0;
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
auto keyVariant = VariantImpl::addPair(&member, variant, resources_);
|
||||
if (!keyVariant)
|
||||
auto keySlot = resources_->allocVariant();
|
||||
if (!keySlot)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
stringBuffer_.save(keyVariant);
|
||||
auto valueSlot = resources_->allocVariant();
|
||||
if (!valueSlot)
|
||||
return DeserializationError::NoMemory;
|
||||
|
||||
object.addMember(keySlot, valueSlot);
|
||||
|
||||
member = valueSlot.ptr();
|
||||
stringBuffer_.save(keySlot.ptr());
|
||||
}
|
||||
|
||||
err = parseVariant(member, memberFilter, nestingLimit.decrement());
|
||||
|
||||
@@ -31,12 +31,11 @@ class MsgPackExtension {
|
||||
template <>
|
||||
struct Converter<MsgPackExtension> : private detail::VariantAttorney {
|
||||
static bool toJson(MsgPackExtension src, JsonVariant dst) {
|
||||
auto data = getData(dst);
|
||||
if (!data)
|
||||
auto impl = getImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return false;
|
||||
|
||||
auto resources = getResourceManager(dst);
|
||||
detail::VariantImpl::clear(data, resources);
|
||||
impl.clear();
|
||||
|
||||
if (!src.data())
|
||||
return true;
|
||||
@@ -68,26 +67,25 @@ struct Converter<MsgPackExtension> : private detail::VariantAttorney {
|
||||
sizeBytes = 1;
|
||||
}
|
||||
|
||||
auto resources = impl.resources();
|
||||
auto str = resources->createString(src.size() + 2 + sizeBytes);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
resources->saveString(str);
|
||||
|
||||
auto ptr = reinterpret_cast<uint8_t*>(str->data);
|
||||
*ptr++ = uint8_t(format);
|
||||
for (uint8_t i = 0; i < sizeBytes; i++)
|
||||
*ptr++ = uint8_t(src.size() >> (sizeBytes - i - 1) * 8 & 0xff);
|
||||
*ptr++ = uint8_t(src.type());
|
||||
memcpy(ptr, src.data(), src.size());
|
||||
data->setRawString(str);
|
||||
impl.data()->setRawString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MsgPackExtension fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
if (!data)
|
||||
return {};
|
||||
auto rawstr = data->asRawString();
|
||||
auto variant = detail::VariantAttorney::getImpl(src);
|
||||
auto rawstr = variant.asRawString();
|
||||
if (rawstr.size() == 0)
|
||||
return {};
|
||||
auto p = reinterpret_cast<const uint8_t*>(rawstr.c_str());
|
||||
|
||||
@@ -47,11 +47,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitArray(VariantData* array) {
|
||||
ARDUINOJSON_ASSERT(array != nullptr);
|
||||
ARDUINOJSON_ASSERT(array->isArray());
|
||||
|
||||
auto n = VariantImpl::size(array, resources_);
|
||||
size_t visitArray(const VariantImpl& array) {
|
||||
size_t n = array.size();
|
||||
if (n < 0x10) {
|
||||
writeByte(uint8_t(0x90 + n));
|
||||
} else if (n < 0x10000) {
|
||||
@@ -62,21 +59,14 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
writeInteger(uint32_t(n));
|
||||
}
|
||||
|
||||
auto slotId = array->content.asCollection.head;
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
VariantImpl::accept(*this, slot, resources_);
|
||||
slotId = slot->next;
|
||||
}
|
||||
for (auto it = array.createIterator(); !it.done(); it.move())
|
||||
it->accept(*this);
|
||||
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(VariantData* object) {
|
||||
ARDUINOJSON_ASSERT(object != nullptr);
|
||||
ARDUINOJSON_ASSERT(object->isObject());
|
||||
|
||||
auto n = VariantImpl::size(object, resources_);
|
||||
size_t visitObject(const VariantImpl& object) {
|
||||
size_t n = object.size();
|
||||
if (n < 0x10) {
|
||||
writeByte(uint8_t(0x80 + n));
|
||||
} else if (n < 0x10000) {
|
||||
@@ -87,12 +77,8 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
|
||||
writeInteger(uint32_t(n));
|
||||
}
|
||||
|
||||
auto slotId = object->content.asCollection.head;
|
||||
while (slotId != NULL_SLOT) {
|
||||
auto slot = resources_->getVariant(slotId);
|
||||
VariantImpl::accept(*this, slot, resources_);
|
||||
slotId = slot->next;
|
||||
}
|
||||
for (auto it = object.createIterator(); !it.done(); it.move())
|
||||
it->accept(*this);
|
||||
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
@@ -7,86 +7,89 @@
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Numbers/FloatTraits.hpp>
|
||||
#include <ArduinoJson/Numbers/JsonFloat.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
struct FloatParts {
|
||||
uint32_t mantissa;
|
||||
uint32_t integral;
|
||||
uint32_t decimal;
|
||||
int16_t exponent;
|
||||
int8_t pointIndex;
|
||||
int8_t decimalPlaces;
|
||||
};
|
||||
|
||||
template <typename TFloat>
|
||||
inline int16_t normalize(TFloat& value) {
|
||||
using traits = FloatTraits<TFloat>;
|
||||
int16_t powersOf10 = 0;
|
||||
|
||||
int8_t index = sizeof(TFloat) == 8 ? 8 : 5;
|
||||
int bit = 1 << index;
|
||||
|
||||
if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value >= traits::positiveBinaryPowersOfTen()[index]) {
|
||||
value *= traits::negativeBinaryPowersOfTen()[index];
|
||||
powersOf10 = int16_t(powersOf10 + bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value < traits::negativeBinaryPowersOfTen()[index] * 10) {
|
||||
value *= traits::positiveBinaryPowersOfTen()[index];
|
||||
powersOf10 = int16_t(powersOf10 - bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return powersOf10;
|
||||
}
|
||||
|
||||
constexpr uint32_t pow10(int exponent) {
|
||||
return (exponent == 0) ? 1 : 10 * pow10(exponent - 1);
|
||||
}
|
||||
|
||||
inline FloatParts decomposeFloat(JsonFloat value, int8_t significantDigits) {
|
||||
ARDUINOJSON_ASSERT(value > 0);
|
||||
ARDUINOJSON_ASSERT(significantDigits > 1);
|
||||
ARDUINOJSON_ASSERT(significantDigits <= 9); // to prevent uint32_t overflow
|
||||
inline FloatParts decomposeFloat(JsonFloat value, int8_t decimalPlaces) {
|
||||
uint32_t maxDecimalPart = pow10(decimalPlaces);
|
||||
|
||||
using traits = FloatTraits<JsonFloat>;
|
||||
int16_t exponent = normalize(value);
|
||||
|
||||
bool useScientificNotation =
|
||||
value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD ||
|
||||
value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD;
|
||||
|
||||
int16_t exponent = 0;
|
||||
int8_t index = traits::binaryPowersOfTenArraySize - 1;
|
||||
int bit = 1 << index;
|
||||
|
||||
// Normalize value to range [1..10) and compute exponent
|
||||
if (value > 1) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value >= traits::positiveBinaryPowersOfTen()[index]) {
|
||||
value *= traits::negativeBinaryPowersOfTen()[index];
|
||||
exponent = int16_t(exponent + bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
uint32_t integral = uint32_t(value);
|
||||
// reduce number of decimal places by the number of integral places
|
||||
for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) {
|
||||
maxDecimalPart /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
ARDUINOJSON_ASSERT(value < 10);
|
||||
if (value < 1) {
|
||||
for (; index >= 0; index--) {
|
||||
if (value < traits::negativeBinaryPowersOfTen()[index] * 10) {
|
||||
value *= traits::positiveBinaryPowersOfTen()[index];
|
||||
exponent = int16_t(exponent - bit);
|
||||
}
|
||||
bit >>= 1;
|
||||
|
||||
JsonFloat remainder =
|
||||
(value - JsonFloat(integral)) * JsonFloat(maxDecimalPart);
|
||||
|
||||
uint32_t decimal = uint32_t(remainder);
|
||||
remainder = remainder - JsonFloat(decimal);
|
||||
|
||||
// rounding:
|
||||
// increment by 1 if remainder >= 0.5
|
||||
decimal += uint32_t(remainder * 2);
|
||||
if (decimal >= maxDecimalPart) {
|
||||
decimal = 0;
|
||||
integral++;
|
||||
if (exponent && integral >= 10) {
|
||||
exponent++;
|
||||
integral = 1;
|
||||
}
|
||||
}
|
||||
ARDUINOJSON_ASSERT(value >= 1);
|
||||
// ARDUINOJSON_ASSERT(value < 10);
|
||||
|
||||
value *= JsonFloat(pow10(significantDigits - 1));
|
||||
|
||||
auto mantissa = uint32_t(value);
|
||||
ARDUINOJSON_ASSERT(mantissa > 0);
|
||||
|
||||
// rounding
|
||||
auto remainder = value - JsonFloat(mantissa);
|
||||
if (remainder >= 0.5)
|
||||
mantissa++;
|
||||
|
||||
auto pointIndex = int8_t(significantDigits - 1);
|
||||
|
||||
if (!useScientificNotation) {
|
||||
pointIndex = int8_t(pointIndex - int8_t(exponent));
|
||||
exponent = 0;
|
||||
}
|
||||
|
||||
// remove trailing zeros
|
||||
while (mantissa % 10 == 0 && (useScientificNotation || pointIndex > 0)) {
|
||||
mantissa /= 10;
|
||||
if (pointIndex > 0)
|
||||
pointIndex--;
|
||||
else
|
||||
exponent++;
|
||||
while (decimal % 10 == 0 && decimalPlaces > 0) {
|
||||
decimal /= 10;
|
||||
decimalPlaces--;
|
||||
}
|
||||
|
||||
return {mantissa, exponent, pointIndex};
|
||||
return {integral, decimal, exponent, decimalPlaces};
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -29,8 +29,6 @@ struct FloatTraits<T, 8 /*64bits*/> {
|
||||
using exponent_type = int16_t;
|
||||
static const exponent_type exponent_max = 308;
|
||||
|
||||
static const int8_t binaryPowersOfTenArraySize = 9;
|
||||
|
||||
static pgm_ptr<T> positiveBinaryPowersOfTen() {
|
||||
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
|
||||
uint64_t, factors,
|
||||
@@ -115,8 +113,6 @@ struct FloatTraits<T, 4 /*32bits*/> {
|
||||
using exponent_type = int8_t;
|
||||
static const exponent_type exponent_max = 38;
|
||||
|
||||
static const int8_t binaryPowersOfTenArraySize = 6;
|
||||
|
||||
static pgm_ptr<T> positiveBinaryPowersOfTen() {
|
||||
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
|
||||
{
|
||||
|
||||
@@ -23,28 +23,24 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
JsonObject() {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonObject(const detail::VariantImpl& impl) : impl_(impl) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonObject(detail::VariantData* data, detail::ResourceManager* resource)
|
||||
: impl_(data, resource) {}
|
||||
JsonObject(detail::VariantImpl impl) : impl_(impl) {}
|
||||
|
||||
operator JsonVariant() const {
|
||||
return JsonVariant(getData(), getResourceManager());
|
||||
return JsonVariant(impl_);
|
||||
}
|
||||
|
||||
operator JsonObjectConst() const {
|
||||
return JsonObjectConst(getData(), getResourceManager());
|
||||
return JsonObjectConst(impl_);
|
||||
}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(getData(), getResourceManager());
|
||||
return JsonVariantConst(impl_);
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
// https://arduinojson.org/v7/api/jsonobject/isnull/
|
||||
bool isNull() const {
|
||||
return !impl_.isObject();
|
||||
return !operator bool();
|
||||
}
|
||||
|
||||
// Returns true if the reference is bound.
|
||||
@@ -68,7 +64,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
// Returns an iterator to the first key-value pair of the object.
|
||||
// https://arduinojson.org/v7/api/jsonobject/begin/
|
||||
iterator begin() const {
|
||||
return iterator(impl_.createIterator(), impl_.resources());
|
||||
return iterator(impl_.createIterator());
|
||||
}
|
||||
|
||||
// Returns an iterator following the last key-value pair of the object.
|
||||
@@ -80,22 +76,18 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
// Removes all the members of the object.
|
||||
// https://arduinojson.org/v7/api/jsonobject/clear/
|
||||
void clear() const {
|
||||
impl_.empty();
|
||||
if (impl_.isObject())
|
||||
impl_.empty();
|
||||
}
|
||||
|
||||
// Copies an object.
|
||||
// https://arduinojson.org/v7/api/jsonobject/set/
|
||||
bool set(JsonObjectConst src) {
|
||||
if (isNull() || src.isNull())
|
||||
if (isNull() ||
|
||||
src.isNull()) // TODO: this check is not consistent with JsonArray
|
||||
return false;
|
||||
|
||||
clear();
|
||||
for (auto kvp : src) {
|
||||
if (!operator[](kvp.key()).set(kvp.value()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
impl_.clear();
|
||||
return impl_.copyObject(detail::VariantAttorney::getImpl(src));
|
||||
}
|
||||
|
||||
// Gets or sets the member with specified key.
|
||||
@@ -110,7 +102,9 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
// Gets or sets the member with specified key.
|
||||
// https://arduinojson.org/v7/api/jsonobject/subscript/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
detail::MemberProxy<JsonObject, detail::AdaptedString<TChar*>> operator[](
|
||||
TChar* key) const {
|
||||
return {*this, detail::adaptString(key)};
|
||||
@@ -167,7 +161,9 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonobject/containskey/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
|
||||
bool containsKey(TChar* key) const {
|
||||
return impl_.getMember(detail::adaptString(key)) != 0;
|
||||
@@ -217,16 +213,12 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
||||
}
|
||||
|
||||
private:
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return impl_.resources();
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
detail::VariantData* getData() const {
|
||||
return impl_.data();
|
||||
}
|
||||
|
||||
detail::VariantData* getOrCreateData() const {
|
||||
return impl_.data();
|
||||
const detail::VariantImpl& getOrCreateImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
mutable detail::VariantImpl impl_;
|
||||
|
||||
@@ -21,15 +21,11 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
|
||||
// Creates an unbound reference.
|
||||
JsonObjectConst() {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonObjectConst(detail::VariantData* data, detail::ResourceManager* resources)
|
||||
: impl_(data, resources) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
JsonObjectConst(const detail::VariantImpl& impl) : impl_(impl) {}
|
||||
|
||||
operator JsonVariantConst() const {
|
||||
return JsonVariantConst(impl_.data(), impl_.resources());
|
||||
return JsonVariantConst(impl_);
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
@@ -59,7 +55,7 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
|
||||
// Returns an iterator to the first key-value pair of the object.
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/begin/
|
||||
iterator begin() const {
|
||||
return iterator(impl_.createIterator(), impl_.resources());
|
||||
return iterator(impl_.createIterator());
|
||||
}
|
||||
|
||||
// Returns an iterator following the last key-value pair of the object.
|
||||
@@ -106,7 +102,9 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
|
||||
// Gets the member with specified key.
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/subscript/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
JsonVariantConst operator[](TChar* key) const {
|
||||
return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
|
||||
impl_.resources());
|
||||
@@ -130,8 +128,8 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
|
||||
}
|
||||
|
||||
private:
|
||||
const detail::VariantData* getData() const {
|
||||
return impl_.data();
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
detail::VariantImpl impl_;
|
||||
|
||||
@@ -14,12 +14,11 @@ class JsonObjectIterator {
|
||||
public:
|
||||
JsonObjectIterator() {}
|
||||
|
||||
explicit JsonObjectIterator(detail::VariantImpl::iterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
explicit JsonObjectIterator(const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
|
||||
JsonPair operator*() const {
|
||||
return JsonPair(iterator_, resources_);
|
||||
return JsonPair(iterator_);
|
||||
}
|
||||
Ptr<JsonPair> operator->() {
|
||||
return operator*();
|
||||
@@ -34,14 +33,13 @@ class JsonObjectIterator {
|
||||
}
|
||||
|
||||
JsonObjectIterator& operator++() {
|
||||
iterator_.move(resources_); // key
|
||||
iterator_.move(resources_); // value
|
||||
iterator_.move(); // key
|
||||
iterator_.move(); // value
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl::iterator iterator_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
class JsonObjectConstIterator {
|
||||
@@ -50,12 +48,12 @@ class JsonObjectConstIterator {
|
||||
public:
|
||||
JsonObjectConstIterator() {}
|
||||
|
||||
explicit JsonObjectConstIterator(detail::VariantImpl::iterator iterator,
|
||||
detail::ResourceManager* resources)
|
||||
: iterator_(iterator), resources_(resources) {}
|
||||
explicit JsonObjectConstIterator(
|
||||
const detail::VariantImpl::iterator& iterator)
|
||||
: iterator_(iterator) {}
|
||||
|
||||
JsonPairConst operator*() const {
|
||||
return JsonPairConst(iterator_, resources_);
|
||||
return JsonPairConst(iterator_);
|
||||
}
|
||||
Ptr<JsonPairConst> operator->() {
|
||||
return operator*();
|
||||
@@ -70,14 +68,13 @@ class JsonObjectConstIterator {
|
||||
}
|
||||
|
||||
JsonObjectConstIterator& operator++() {
|
||||
iterator_.move(resources_); // key
|
||||
iterator_.move(resources_); // value
|
||||
iterator_.move(); // key
|
||||
iterator_.move(); // value
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
detail::VariantImpl::iterator iterator_;
|
||||
detail::ResourceManager* resources_;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
@@ -15,12 +15,11 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
class JsonPair {
|
||||
public:
|
||||
// INTERNAL USE ONLY
|
||||
JsonPair(detail::VariantImpl::iterator iterator,
|
||||
detail::ResourceManager* resources) {
|
||||
JsonPair(detail::VariantImpl::iterator iterator) {
|
||||
if (!iterator.done()) {
|
||||
key_ = iterator->asString();
|
||||
iterator.move(resources);
|
||||
value_ = JsonVariant(iterator.data(), resources);
|
||||
iterator.move();
|
||||
value_ = JsonVariant(*iterator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +42,11 @@ class JsonPair {
|
||||
// https://arduinojson.org/v7/api/jsonobjectconst/begin_end/
|
||||
class JsonPairConst {
|
||||
public:
|
||||
JsonPairConst(detail::VariantImpl::iterator iterator,
|
||||
detail::ResourceManager* resources) {
|
||||
JsonPairConst(detail::VariantImpl::iterator iterator) {
|
||||
if (!iterator.done()) {
|
||||
key_ = iterator->asString();
|
||||
iterator.move(resources);
|
||||
value_ = JsonVariantConst(iterator.data(), resources);
|
||||
iterator.move();
|
||||
value_ = JsonVariantConst(*iterator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class MemberProxy
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
|
||||
MemberProxy& operator=(T* src) {
|
||||
this->set(src);
|
||||
return *this;
|
||||
@@ -51,20 +51,15 @@ class MemberProxy
|
||||
: upstream_(src.upstream_), key_(src.key_) {}
|
||||
// clang-format on
|
||||
|
||||
ResourceManager* getResourceManager() const {
|
||||
return VariantAttorney::getResourceManager(upstream_);
|
||||
VariantImpl getImpl() const {
|
||||
auto impl = VariantAttorney::getImpl(upstream_);
|
||||
return VariantImpl(impl.getMember(key_), impl.resources());
|
||||
}
|
||||
|
||||
VariantData* getData() const {
|
||||
return VariantAttorney::getVariantImpl(upstream_).getMember(key_);
|
||||
}
|
||||
|
||||
VariantData* getOrCreateData() const {
|
||||
auto data = VariantAttorney::getOrCreateData(upstream_);
|
||||
auto resources = VariantAttorney::getResourceManager(upstream_);
|
||||
if (data && data->type == VariantType::Null)
|
||||
data->toObject();
|
||||
return VariantImpl(data, resources).getOrAddMember(key_);
|
||||
VariantImpl getOrCreateImpl() const {
|
||||
auto impl = VariantAttorney::getOrCreateImpl(upstream_);
|
||||
impl.toObjectIfNull();
|
||||
return VariantImpl(impl.getOrAddMember(key_), impl.resources());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -9,43 +9,71 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
inline bool VariantImpl::copyObject(const VariantImpl& src) {
|
||||
ARDUINOJSON_ASSERT(isNull());
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
data_->toObject();
|
||||
|
||||
for (auto it = src.createIterator(); !it.done(); it.move()) {
|
||||
auto keySlot = allocVariant();
|
||||
if (!keySlot)
|
||||
return false;
|
||||
|
||||
auto key = VariantImpl(keySlot.ptr(), resources_);
|
||||
if (!key.copyVariant(*it)) {
|
||||
freeVariant(keySlot);
|
||||
return false;
|
||||
}
|
||||
|
||||
it.move(); // move to value
|
||||
ARDUINOJSON_ASSERT(!it.done());
|
||||
|
||||
auto valueSlot = allocVariant();
|
||||
if (!valueSlot) {
|
||||
freeVariant(keySlot);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we add the pair before copying the value to be keep the old
|
||||
// behavior but this is not consistent with issue #2081
|
||||
addMember(keySlot, valueSlot);
|
||||
|
||||
auto value = VariantImpl(valueSlot.ptr(), resources_);
|
||||
if (!value.copyVariant(*it))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* VariantImpl::getMember(TAdaptedString key,
|
||||
VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
auto it = findKey(key, data, resources);
|
||||
inline VariantData* VariantImpl::getMember(TAdaptedString key) const {
|
||||
auto it = findKey(key);
|
||||
if (it.done())
|
||||
return nullptr;
|
||||
it.move(resources);
|
||||
return it.data();
|
||||
it.move();
|
||||
return it->data();
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* VariantImpl::getOrAddMember(TAdaptedString key, VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isObject());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto member = getMember(key, data, resources);
|
||||
if (member)
|
||||
return member;
|
||||
return addMember(key, data, resources);
|
||||
VariantData* VariantImpl::getOrAddMember(TAdaptedString key) {
|
||||
auto data = getMember(key);
|
||||
if (data)
|
||||
return data;
|
||||
return addMember(key);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantImpl::iterator VariantImpl::findKey(TAdaptedString key,
|
||||
VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isObject());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
inline VariantImpl::iterator VariantImpl::findKey(TAdaptedString key) const {
|
||||
if (!isObject())
|
||||
return iterator();
|
||||
if (key.isNull())
|
||||
return iterator();
|
||||
bool isKey = true;
|
||||
for (auto it = createIterator(data, resources); !it.done();
|
||||
it.move(resources)) {
|
||||
for (auto it = createIterator(); !it.done(); it.move()) {
|
||||
if (isKey && stringEquals(key, adaptString(it->asString())))
|
||||
return it;
|
||||
isKey = !isKey;
|
||||
@@ -54,53 +82,39 @@ inline VariantImpl::iterator VariantImpl::findKey(TAdaptedString key,
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* VariantImpl::addMember(TAdaptedString key,
|
||||
VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isObject());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
inline void VariantImpl::removeMember(TAdaptedString key) {
|
||||
removeMember(findKey(key));
|
||||
}
|
||||
|
||||
inline void VariantImpl::removeMember(iterator it) {
|
||||
removePair(it);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
inline VariantData* VariantImpl::addMember(TAdaptedString key) {
|
||||
if (!isObject())
|
||||
return nullptr;
|
||||
|
||||
if (key.isNull())
|
||||
return nullptr; // Ignore null key
|
||||
|
||||
auto keySlot = resources->allocVariant();
|
||||
auto keySlot = allocVariant();
|
||||
if (!keySlot)
|
||||
return nullptr;
|
||||
|
||||
auto valueSlot = resources->allocVariant();
|
||||
auto valueSlot = allocVariant();
|
||||
if (!valueSlot)
|
||||
return nullptr;
|
||||
|
||||
if (!VariantImpl::setString(key, keySlot.ptr(), resources))
|
||||
VariantImpl keyImpl(keySlot.ptr(), resources_);
|
||||
if (!keyImpl.setString(key))
|
||||
return nullptr;
|
||||
|
||||
appendPair(keySlot, valueSlot, data, resources);
|
||||
addMember(keySlot, valueSlot);
|
||||
|
||||
return valueSlot.ptr();
|
||||
}
|
||||
|
||||
inline VariantData* VariantImpl::addPair(VariantData** value, VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(value != nullptr);
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isObject());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
auto keySlot = resources->allocVariant();
|
||||
if (!keySlot)
|
||||
return nullptr;
|
||||
|
||||
auto valueSlot = resources->allocVariant();
|
||||
if (!valueSlot)
|
||||
return nullptr;
|
||||
*value = valueSlot.ptr();
|
||||
|
||||
appendPair(keySlot, valueSlot, data, resources);
|
||||
|
||||
return keySlot.ptr();
|
||||
}
|
||||
|
||||
// Returns the size (in bytes) of an object with n members.
|
||||
constexpr size_t sizeofObject(size_t n) {
|
||||
return 2 * n * sizeof(VariantData);
|
||||
|
||||
@@ -11,10 +11,9 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
template <template <typename> class TSerializer>
|
||||
size_t measure(ArduinoJson::JsonVariantConst source) {
|
||||
DummyWriter dp;
|
||||
auto data = VariantAttorney::getData(source);
|
||||
auto resources = VariantAttorney::getResourceManager(source);
|
||||
TSerializer<DummyWriter> serializer(dp, resources);
|
||||
return VariantImpl::accept(serializer, data, resources);
|
||||
auto impl = VariantAttorney::getImpl(source);
|
||||
TSerializer<DummyWriter> serializer(dp, impl.resources());
|
||||
return impl.accept(serializer);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -10,10 +10,9 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
template <template <typename> class TSerializer, typename TWriter>
|
||||
size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
|
||||
auto data = VariantAttorney::getData(source);
|
||||
auto resources = VariantAttorney::getResourceManager(source);
|
||||
TSerializer<TWriter> serializer(writer, resources);
|
||||
return VariantImpl::accept(serializer, data, resources);
|
||||
auto impl = VariantAttorney::getImpl(source);
|
||||
TSerializer<TWriter> serializer(writer, impl.resources());
|
||||
return impl.accept(serializer);
|
||||
}
|
||||
|
||||
template <template <typename> class TSerializer, typename TDestination>
|
||||
|
||||
@@ -63,6 +63,10 @@ class FlashString {
|
||||
::memcpy_P(p, s.str_, n);
|
||||
}
|
||||
|
||||
bool isStatic() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* str_;
|
||||
size_t size_;
|
||||
|
||||
@@ -20,8 +20,14 @@ struct IsChar
|
||||
class RamString {
|
||||
public:
|
||||
static const size_t typeSortKey = 2;
|
||||
#if ARDUINOJSON_SIZEOF_POINTER <= 2
|
||||
static constexpr size_t sizeMask = size_t(-1) >> 1;
|
||||
#else
|
||||
static constexpr size_t sizeMask = size_t(-1);
|
||||
#endif
|
||||
|
||||
RamString(const char* str, size_t sz) : str_(str), size_(sz) {
|
||||
RamString(const char* str, size_t sz, bool isStatic = false)
|
||||
: str_(str), size_(sz & sizeMask), static_(isStatic) {
|
||||
ARDUINOJSON_ASSERT(size_ == sz);
|
||||
}
|
||||
|
||||
@@ -43,9 +49,21 @@ class RamString {
|
||||
return str_;
|
||||
}
|
||||
|
||||
bool isStatic() const {
|
||||
return static_;
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* str_;
|
||||
|
||||
#if ARDUINOJSON_SIZEOF_POINTER <= 2
|
||||
// Use a bitfield only on 8-bit microcontrollers
|
||||
size_t size_ : sizeof(size_t) * 8 - 1;
|
||||
bool static_ : 1;
|
||||
#else
|
||||
size_t size_;
|
||||
bool static_;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
@@ -58,6 +76,37 @@ struct StringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
struct StringAdapter<TChar[], enable_if_t<IsChar<TChar>::value>> {
|
||||
using AdaptedString = RamString;
|
||||
|
||||
static AdaptedString adapt(const TChar* p) {
|
||||
auto str = reinterpret_cast<const char*>(p);
|
||||
return AdaptedString(str, str ? ::strlen(str) : 0);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct StringAdapter<const char (&)[N]> {
|
||||
using AdaptedString = RamString;
|
||||
|
||||
static AdaptedString adapt(const char* p) {
|
||||
ARDUINOJSON_ASSERT(p);
|
||||
return RamString(p, ::strlen(p), true);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TChar, size_t N>
|
||||
struct StringAdapter<TChar[N], enable_if_t<IsChar<TChar>::value>> {
|
||||
using AdaptedString = RamString;
|
||||
|
||||
static AdaptedString adapt(const TChar* p) {
|
||||
ARDUINOJSON_ASSERT(p);
|
||||
auto str = reinterpret_cast<const char*>(p);
|
||||
return AdaptedString(str, ::strlen(str));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
struct SizedStringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
|
||||
using AdaptedString = RamString;
|
||||
|
||||
@@ -19,25 +19,16 @@ class JsonString {
|
||||
friend struct detail::StringAdapter<JsonString>;
|
||||
|
||||
public:
|
||||
JsonString() : str_(nullptr, 0) {}
|
||||
JsonString() : str_(nullptr, 0, true) {}
|
||||
|
||||
JsonString(const char* data) : str_(data, data ? ::strlen(data) : 0) {}
|
||||
|
||||
ARDUINOJSON_DEPRECATED(
|
||||
"ArduinoJson doesn't differentiate between static and dynamic strings "
|
||||
"anymore. Remove the second argument to fix this warning.")
|
||||
JsonString(const char* data, bool) : JsonString(data) {}
|
||||
JsonString(const char* data, bool isStatic = false)
|
||||
: str_(data, data ? ::strlen(data) : 0, isStatic) {}
|
||||
|
||||
template <typename TSize,
|
||||
detail::enable_if_t<detail::is_integral<TSize>::value &&
|
||||
!detail::is_same<TSize, bool>::value,
|
||||
int> = 0>
|
||||
JsonString(const char* data, TSize sz) : str_(data, size_t(sz)) {}
|
||||
|
||||
ARDUINOJSON_DEPRECATED(
|
||||
"ArduinoJson doesn't differentiate between static and dynamic strings "
|
||||
"anymore. Remove the third argument to fix this warning.")
|
||||
JsonString(const char* data, size_t sz, bool) : JsonString(data, sz) {}
|
||||
JsonString(const char* data, TSize sz) : str_(data, size_t(sz), false) {}
|
||||
|
||||
// Returns a pointer to the characters.
|
||||
const char* c_str() const {
|
||||
@@ -49,10 +40,10 @@ class JsonString {
|
||||
return str_.isNull();
|
||||
}
|
||||
|
||||
// Deprecated: always returns false.
|
||||
ARDUINOJSON_DEPRECATED("The isStatic() was removed in v7.5")
|
||||
// Returns true if the string is stored by address.
|
||||
// Returns false if the string is stored by copy.
|
||||
bool isStatic() const {
|
||||
return false;
|
||||
return str_.isStatic();
|
||||
}
|
||||
|
||||
// Returns length of the string.
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
// a meta function that tells if the type is a string literal (const char[N])
|
||||
template <typename T>
|
||||
struct IsStringLiteral : false_type {};
|
||||
|
||||
template <size_t N>
|
||||
struct IsStringLiteral<const char (&)[N]> : true_type {};
|
||||
|
||||
template <typename TString, typename Enable = void>
|
||||
struct StringAdapter;
|
||||
|
||||
@@ -15,7 +22,9 @@ template <typename TString, typename Enable = void>
|
||||
struct SizedStringAdapter;
|
||||
|
||||
template <typename TString>
|
||||
using StringAdapterFor = StringAdapter<decay_t<TString>>;
|
||||
using StringAdapterFor =
|
||||
StringAdapter<conditional_t<IsStringLiteral<TString>::value, TString,
|
||||
remove_cv_t<remove_reference_t<TString>>>>;
|
||||
|
||||
template <typename T>
|
||||
using AdaptedString = typename StringAdapterFor<T>::AdaptedString;
|
||||
@@ -25,7 +34,7 @@ AdaptedString<TString> adaptString(TString&& s) {
|
||||
return StringAdapterFor<TString>::adapt(detail::forward<TString>(s));
|
||||
}
|
||||
|
||||
template <typename TChar>
|
||||
template <typename TChar, enable_if_t<!is_const<TChar>::value, int> = 0>
|
||||
AdaptedString<TChar*> adaptString(TChar* p) {
|
||||
return StringAdapter<TChar*>::adapt(p);
|
||||
}
|
||||
|
||||
@@ -61,16 +61,18 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
|
||||
: private detail::VariantAttorney {
|
||||
static bool toJson(T src, JsonVariant dst) {
|
||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
|
||||
return getVariantImpl(dst).setInteger(src);
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setInteger(src);
|
||||
}
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
|
||||
return getVariantImpl(src).template asIntegral<T>();
|
||||
return getImpl(src).template asIntegral<T>();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getVariantImpl(src).template isInteger<T>();
|
||||
return getImpl(src).template isInteger<T>();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,27 +84,28 @@ struct Converter<T, detail::enable_if_t<detail::is_enum<T>::value>>
|
||||
}
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
return static_cast<T>(getVariantImpl(src).template asIntegral<int>());
|
||||
return static_cast<T>(getImpl(src).template asIntegral<int>());
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return getVariantImpl(src).template isInteger<int>();
|
||||
return getImpl(src).template isInteger<int>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<bool> : private detail::VariantAttorney {
|
||||
static bool toJson(bool src, JsonVariant dst) {
|
||||
return getVariantImpl(dst).setBoolean(src);
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setBoolean(src);
|
||||
}
|
||||
|
||||
static bool fromJson(JsonVariantConst src) {
|
||||
return getVariantImpl(src).asBoolean();
|
||||
return getImpl(src).asBoolean();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isBoolean();
|
||||
return getImpl(src).type() == detail::VariantType::Boolean;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -110,58 +113,60 @@ template <typename T>
|
||||
struct Converter<T, detail::enable_if_t<detail::is_floating_point<T>::value>>
|
||||
: private detail::VariantAttorney {
|
||||
static bool toJson(T src, JsonVariant dst) {
|
||||
return getVariantImpl(dst).setFloat(src);
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setFloat(src);
|
||||
}
|
||||
|
||||
static T fromJson(JsonVariantConst src) {
|
||||
return getVariantImpl(src).template asFloat<T>();
|
||||
return getImpl(src).template asFloat<T>();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isFloat();
|
||||
return getImpl(src).isFloat();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<const char*> : private detail::VariantAttorney {
|
||||
static bool toJson(const char* src, JsonVariant dst) {
|
||||
return getVariantImpl(dst).setString(detail::adaptString(src));
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setString(detail::adaptString(src));
|
||||
}
|
||||
|
||||
static const char* fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data ? data->asString().c_str() : 0;
|
||||
return getImpl(src).asString().c_str();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isString();
|
||||
return getImpl(src).isString();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonString> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonString src, JsonVariant dst) {
|
||||
return getVariantImpl(dst).setString(detail::adaptString(src));
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setString(detail::adaptString(src));
|
||||
}
|
||||
|
||||
static JsonString fromJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data ? data->asString() : JsonString();
|
||||
return getImpl(src).asString();
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isString();
|
||||
return getImpl(src).isString();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline detail::enable_if_t<detail::IsString<T>::value, bool> convertToJson(
|
||||
const T& src, JsonVariant dst) {
|
||||
return detail::VariantAttorney::getVariantImpl(dst).setString(
|
||||
detail::adaptString(src));
|
||||
auto impl = detail::VariantAttorney::getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setString(detail::adaptString(src));
|
||||
}
|
||||
|
||||
// SerializedValue<std::string>
|
||||
@@ -170,22 +175,25 @@ inline detail::enable_if_t<detail::IsString<T>::value, bool> convertToJson(
|
||||
template <typename T>
|
||||
struct Converter<SerializedValue<T>> : private detail::VariantAttorney {
|
||||
static bool toJson(SerializedValue<T> src, JsonVariant dst) {
|
||||
return getVariantImpl(dst).setRawString(
|
||||
detail::adaptString(src.data(), src.size()));
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.setRawString(detail::adaptString(src.data(), src.size()));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<detail::nullptr_t> : private detail::VariantAttorney {
|
||||
static bool toJson(detail::nullptr_t, JsonVariant dst) {
|
||||
return getVariantImpl(dst).clear();
|
||||
if (dst.isUnbound())
|
||||
return false;
|
||||
getImpl(dst).clear();
|
||||
return true;
|
||||
}
|
||||
static detail::nullptr_t fromJson(JsonVariantConst) {
|
||||
return nullptr;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data == 0 || data->isNull();
|
||||
return getImpl(src).isNull();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -227,16 +235,15 @@ class StringBuilderPrint : public Print {
|
||||
} // namespace detail
|
||||
|
||||
inline bool convertToJson(const ::Printable& src, JsonVariant dst) {
|
||||
auto resources = detail::VariantAttorney::getResourceManager(dst);
|
||||
auto data = detail::VariantAttorney::getData(dst);
|
||||
if (!resources || !data)
|
||||
auto impl = detail::VariantAttorney::getImpl(dst);
|
||||
if (impl.isUnbound())
|
||||
return false;
|
||||
detail::VariantImpl::clear(data, resources);
|
||||
detail::StringBuilderPrint print(resources);
|
||||
impl.clear();
|
||||
detail::StringBuilderPrint print(impl.resources());
|
||||
src.printTo(print);
|
||||
if (print.overflowed())
|
||||
return false;
|
||||
print.save(data);
|
||||
print.save(impl.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -298,12 +305,11 @@ struct Converter<JsonArrayConst> : private detail::VariantAttorney {
|
||||
}
|
||||
|
||||
static JsonArrayConst fromJson(JsonVariantConst src) {
|
||||
return JsonArrayConst(getData(src), getResourceManager(src));
|
||||
return JsonArrayConst(getImpl(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isArray();
|
||||
return getImpl(src).type() == detail::VariantType::Array;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -317,12 +323,11 @@ struct Converter<JsonArray> : private detail::VariantAttorney {
|
||||
}
|
||||
|
||||
static JsonArray fromJson(JsonVariant src) {
|
||||
return JsonArray(getData(src), getResourceManager(src));
|
||||
return JsonArray(getImpl(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariant src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isArray();
|
||||
return getImpl(src).type() == detail::VariantType::Array;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -336,12 +341,11 @@ struct Converter<JsonObjectConst> : private detail::VariantAttorney {
|
||||
}
|
||||
|
||||
static JsonObjectConst fromJson(JsonVariantConst src) {
|
||||
return JsonObjectConst(getData(src), getResourceManager(src));
|
||||
return JsonObjectConst(getImpl(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isObject();
|
||||
return getImpl(src).type() == detail::VariantType::Object;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -355,12 +359,11 @@ struct Converter<JsonObject> : private detail::VariantAttorney {
|
||||
}
|
||||
|
||||
static JsonObject fromJson(JsonVariant src) {
|
||||
return JsonObject(getData(src), getResourceManager(src));
|
||||
return JsonObject(getImpl(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariant src) {
|
||||
auto data = getData(src);
|
||||
return data && data->isObject();
|
||||
return getImpl(src).type() == detail::VariantType::Object;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,29 +26,23 @@ class JsonVariant : public detail::VariantRefBase<JsonVariant>,
|
||||
JsonVariant(detail::VariantImpl impl) : impl_(impl) {}
|
||||
|
||||
private:
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return impl_.resources();
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
detail::VariantData* getData() const {
|
||||
return impl_.data();
|
||||
}
|
||||
|
||||
detail::VariantData* getOrCreateData() const {
|
||||
return impl_.data();
|
||||
const detail::VariantImpl& getOrCreateImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
mutable detail::VariantImpl impl_;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
bool copyVariant(JsonVariant dst, JsonVariantConst src);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct Converter<JsonVariant> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
return copyVariant(dst, src);
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.copyVariant(getImpl(src));
|
||||
}
|
||||
|
||||
static JsonVariant fromJson(JsonVariant src) {
|
||||
@@ -56,24 +50,24 @@ struct Converter<JsonVariant> : private detail::VariantAttorney {
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariant src) {
|
||||
auto data = getData(src);
|
||||
return !!data;
|
||||
return !getImpl(src).isUnbound();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<JsonVariantConst> : private detail::VariantAttorney {
|
||||
static bool toJson(JsonVariantConst src, JsonVariant dst) {
|
||||
return copyVariant(dst, src);
|
||||
auto impl = getImpl(dst);
|
||||
impl.clear();
|
||||
return impl.copyVariant(getImpl(src));
|
||||
}
|
||||
|
||||
static JsonVariantConst fromJson(JsonVariantConst src) {
|
||||
return JsonVariantConst(getData(src), getResourceManager(src));
|
||||
return JsonVariantConst(getImpl(src));
|
||||
}
|
||||
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
auto data = getData(src);
|
||||
return !!data;
|
||||
return !getImpl(src).isUnbound();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
detail::ResourceManager* resources)
|
||||
: impl_(data, resources) {}
|
||||
|
||||
// INTERNAL USE ONLY
|
||||
explicit JsonVariantConst(detail::VariantImpl impl) : impl_(impl) {}
|
||||
|
||||
// Returns true if the value is null or the reference is unbound.
|
||||
// https://arduinojson.org/v7/api/jsonvariantconst/isnull/
|
||||
bool isNull() const {
|
||||
@@ -50,7 +53,7 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
bool isUnbound() const {
|
||||
return impl_.data() == nullptr;
|
||||
return impl_.isUnbound();
|
||||
}
|
||||
|
||||
// Returns the depth (nesting level) of the value.
|
||||
@@ -119,7 +122,9 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
// Gets object's member with specified key.
|
||||
// https://arduinojson.org/v7/api/jsonvariantconst/subscript/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
JsonVariantConst operator[](TChar* key) const {
|
||||
return JsonVariantConst(impl_.getMember(detail::adaptString(key)),
|
||||
impl_.resources());
|
||||
@@ -149,7 +154,9 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
// DEPRECATED: use obj["key"].is<T>() instead
|
||||
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
|
||||
template <typename TChar,
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value, int> = 0>
|
||||
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||
!detail::is_const<TChar>::value,
|
||||
int> = 0>
|
||||
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
|
||||
bool containsKey(TChar* key) const {
|
||||
return impl_.getMember(detail::adaptString(key)) != 0;
|
||||
@@ -171,12 +178,8 @@ class JsonVariantConst : public detail::VariantTag,
|
||||
}
|
||||
|
||||
protected:
|
||||
detail::VariantData* getData() const {
|
||||
return impl_.data();
|
||||
}
|
||||
|
||||
detail::ResourceManager* getResourceManager() const {
|
||||
return impl_.resources();
|
||||
const detail::VariantImpl& getImpl() const {
|
||||
return impl_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Variant/JsonVariant.hpp>
|
||||
#include <ArduinoJson/Variant/JsonVariantVisitor.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
class JsonVariantCopier {
|
||||
public:
|
||||
using result_type = bool;
|
||||
|
||||
JsonVariantCopier(JsonVariant dst) : dst_(dst) {}
|
||||
|
||||
template <typename T>
|
||||
bool visit(T src) {
|
||||
return dst_.set(src);
|
||||
}
|
||||
|
||||
private:
|
||||
JsonVariant dst_;
|
||||
};
|
||||
|
||||
inline bool copyVariant(JsonVariant dst, JsonVariantConst src) {
|
||||
if (dst.isUnbound())
|
||||
return false;
|
||||
JsonVariantCopier copier(dst);
|
||||
return accept(src, copier);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
@@ -26,15 +26,14 @@ class VisitorAdapter {
|
||||
public:
|
||||
using result_type = typename TVisitor::result_type;
|
||||
|
||||
VisitorAdapter(TVisitor& visitor, ResourceManager* resources)
|
||||
: visitor_(&visitor), resources_(resources) {}
|
||||
VisitorAdapter(TVisitor& visitor) : visitor_(&visitor) {}
|
||||
|
||||
result_type visitArray(VariantData* data) {
|
||||
return visitor_->visit(JsonArrayConst(data, resources_));
|
||||
result_type visitArray(const VariantImpl& array) {
|
||||
return visitor_->visit(JsonArrayConst(array));
|
||||
}
|
||||
|
||||
result_type visitObject(VariantData* data) {
|
||||
return visitor_->visit(JsonObjectConst(data, resources_));
|
||||
result_type visitObject(const VariantImpl& object) {
|
||||
return visitor_->visit(JsonObjectConst(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -44,15 +43,14 @@ class VisitorAdapter {
|
||||
|
||||
private:
|
||||
TVisitor* visitor_;
|
||||
ResourceManager* resources_;
|
||||
};
|
||||
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(JsonVariantConst variant,
|
||||
TVisitor& visit) {
|
||||
VisitorAdapter<TVisitor> adapter(
|
||||
visit, VariantAttorney::getResourceManager(variant));
|
||||
return VariantAttorney::getVariantImpl(variant).accept(adapter);
|
||||
auto impl = VariantAttorney::getImpl(variant);
|
||||
VisitorAdapter<TVisitor> adapter(visit);
|
||||
return impl.accept(adapter);
|
||||
}
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <ArduinoJson/Polyfills/attributes.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <ArduinoJson/Variant/VariantTo.hpp>
|
||||
#include "JsonVariantConst.hpp"
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
@@ -15,29 +16,13 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
class VariantAttorney {
|
||||
public:
|
||||
template <typename TClient>
|
||||
static auto getResourceManager(TClient& client)
|
||||
-> decltype(client.getResourceManager()) {
|
||||
return client.getResourceManager();
|
||||
static VariantImpl getImpl(TClient& client) {
|
||||
return client.getImpl();
|
||||
}
|
||||
|
||||
template <typename TClient>
|
||||
static auto getData(TClient& client) -> decltype(client.getData()) {
|
||||
return client.getData();
|
||||
}
|
||||
|
||||
template <typename TClient>
|
||||
static VariantImpl getVariantImpl(TClient& client) {
|
||||
return VariantImpl(client.getData(), client.getResourceManager());
|
||||
}
|
||||
|
||||
template <typename TClient>
|
||||
static VariantImpl getOrCreateVariantImpl(TClient& client) {
|
||||
return VariantImpl(client.getOrCreateData(), client.getResourceManager());
|
||||
}
|
||||
|
||||
template <typename TClient>
|
||||
static VariantData* getOrCreateData(TClient& client) {
|
||||
return client.getOrCreateData();
|
||||
static VariantImpl getOrCreateImpl(TClient& client) {
|
||||
return client.getOrCreateImpl();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ struct Comparer<
|
||||
T, enable_if_t<is_convertible<T, ArduinoJson::JsonVariantConst>::value>>
|
||||
: VariantComparer {
|
||||
explicit Comparer(const T& value)
|
||||
: VariantComparer(static_cast<JsonVariantConst>(value)) {}
|
||||
: VariantComparer(JsonVariantConst(VariantAttorney::getImpl(value))) {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -21,14 +21,15 @@ enum class VariantTypeBits : uint8_t {
|
||||
};
|
||||
|
||||
enum class VariantType : uint8_t {
|
||||
Null = 0, // 0000 0000
|
||||
TinyString = 0x02, // 0000 0010
|
||||
RawString = 0x03, // 0000 0011
|
||||
LongString = 0x05, // 0000 0101
|
||||
Boolean = 0x06, // 0000 0110
|
||||
Uint32 = 0x0A, // 0000 1010
|
||||
Int32 = 0x0C, // 0000 1100
|
||||
Float = 0x0E, // 0000 1110
|
||||
Null = 0, // 0000 0000
|
||||
TinyString = 0x02, // 0000 0010
|
||||
RawString = 0x03, // 0000 0011
|
||||
LinkedString = 0x04, // 0000 0100
|
||||
OwnedString = 0x05, // 0000 0101
|
||||
Boolean = 0x06, // 0000 0110
|
||||
Uint32 = 0x0A, // 0000 1010
|
||||
Int32 = 0x0C, // 0000 1100
|
||||
Float = 0x0E, // 0000 1110
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
Uint64 = 0x1A, // 0001 1010
|
||||
Int64 = 0x1C, // 0001 1100
|
||||
@@ -65,11 +66,9 @@ union VariantContent {
|
||||
bool asBoolean;
|
||||
uint32_t asUint32;
|
||||
int32_t asInt32;
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
SlotId asSlotId;
|
||||
#endif
|
||||
CollectionData asCollection;
|
||||
struct StringNode* asStringNode;
|
||||
struct StringNode* asOwnedString;
|
||||
char asTinyString[tinyStringMaxLength + 1];
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/StringNode.hpp>
|
||||
#include <ArduinoJson/Strings/JsonString.hpp>
|
||||
#include <ArduinoJson/Variant/VariantContent.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
@@ -35,69 +34,12 @@ struct VariantData {
|
||||
|
||||
static void operator delete(void*, void*) noexcept {}
|
||||
|
||||
JsonString asRawString() const {
|
||||
switch (type) {
|
||||
case VariantType::RawString:
|
||||
return JsonString(content.asStringNode->data,
|
||||
content.asStringNode->length);
|
||||
default:
|
||||
return JsonString();
|
||||
}
|
||||
}
|
||||
|
||||
JsonString asString() const {
|
||||
switch (type) {
|
||||
case VariantType::TinyString:
|
||||
return JsonString(content.asTinyString);
|
||||
case VariantType::LongString:
|
||||
return JsonString(content.asStringNode->data,
|
||||
content.asStringNode->length);
|
||||
default:
|
||||
return JsonString();
|
||||
}
|
||||
}
|
||||
|
||||
bool isArray() const {
|
||||
return type == VariantType::Array;
|
||||
}
|
||||
|
||||
bool isBoolean() const {
|
||||
return type == VariantType::Boolean;
|
||||
}
|
||||
|
||||
bool isCollection() const {
|
||||
return type & VariantTypeBits::CollectionMask;
|
||||
}
|
||||
|
||||
bool isFloat() const {
|
||||
return type & VariantTypeBits::NumberBit;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return type == VariantType::Null;
|
||||
}
|
||||
|
||||
bool isObject() const {
|
||||
return type == VariantType::Object;
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return type == VariantType::LongString || type == VariantType::TinyString;
|
||||
}
|
||||
|
||||
void setBoolean(bool value) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
type = VariantType::Boolean;
|
||||
content.asBoolean = value;
|
||||
}
|
||||
|
||||
void setRawString(StringNode* s) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
type = VariantType::RawString;
|
||||
content.asStringNode = s;
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
void setTinyString(const TAdaptedString& s) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
@@ -115,23 +57,47 @@ struct VariantData {
|
||||
content.asTinyString[n] = 0;
|
||||
}
|
||||
|
||||
void setLongString(StringNode* s) {
|
||||
void setOwnedString(StringNode* s) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
type = VariantType::LongString;
|
||||
content.asStringNode = s;
|
||||
type = VariantType::OwnedString;
|
||||
content.asOwnedString = s;
|
||||
}
|
||||
|
||||
CollectionData* toArray() {
|
||||
void setRawString(StringNode* s) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
type = VariantType::Array;
|
||||
return new (&content.asCollection) CollectionData();
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
type = VariantType::RawString;
|
||||
content.asOwnedString = s;
|
||||
}
|
||||
|
||||
CollectionData* toObject() {
|
||||
bool isCollection() const {
|
||||
return type & VariantTypeBits::CollectionMask;
|
||||
}
|
||||
|
||||
bool isFloat() const {
|
||||
return type & VariantTypeBits::NumberBit;
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return type == VariantType::LinkedString ||
|
||||
type == VariantType::OwnedString || type == VariantType::TinyString;
|
||||
}
|
||||
|
||||
VariantData* toArray() {
|
||||
return toCollection(VariantType::Array);
|
||||
}
|
||||
|
||||
VariantData* toCollection(VariantType collectionType) {
|
||||
ARDUINOJSON_ASSERT(type == VariantType::Null);
|
||||
type = VariantType::Object;
|
||||
return new (&content.asCollection) CollectionData();
|
||||
ARDUINOJSON_ASSERT(collectionType & VariantTypeBits::CollectionMask);
|
||||
type = collectionType;
|
||||
new (&content.asCollection) CollectionData();
|
||||
return this;
|
||||
}
|
||||
|
||||
VariantData* toObject() {
|
||||
return toCollection(VariantType::Object);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,18 +13,18 @@ template <typename TResult>
|
||||
struct VariantDataVisitor {
|
||||
using result_type = TResult;
|
||||
|
||||
TResult visitArray(const VariantImpl&) {
|
||||
return TResult();
|
||||
}
|
||||
|
||||
TResult visitObject(const VariantImpl&) {
|
||||
return TResult();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TResult visit(const T&) {
|
||||
return TResult();
|
||||
}
|
||||
|
||||
TResult visitArray(VariantData*) {
|
||||
return TResult();
|
||||
}
|
||||
|
||||
TResult visitObject(VariantData*) {
|
||||
return TResult();
|
||||
}
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Collection/CollectionIterator.hpp>
|
||||
#include <ArduinoJson/Memory/ResourceManager.hpp>
|
||||
#include <ArduinoJson/Misc/SerializedValue.hpp>
|
||||
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
||||
@@ -14,9 +13,12 @@
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
|
||||
// HACK: large functions are implemented in static function to give opportunity
|
||||
// to the compiler to optimize the `this` pointer away.
|
||||
class CollectionIterator;
|
||||
|
||||
class VariantImpl {
|
||||
VariantData* data_;
|
||||
ResourceManager* resources_;
|
||||
|
||||
public:
|
||||
using iterator = CollectionIterator;
|
||||
|
||||
@@ -34,23 +36,16 @@ class VariantImpl {
|
||||
}
|
||||
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(TVisitor& visit) {
|
||||
return accept(visit, data_, resources_);
|
||||
}
|
||||
|
||||
template <typename TVisitor>
|
||||
static typename TVisitor::result_type accept(TVisitor& visit,
|
||||
VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
if (!data)
|
||||
typename TVisitor::result_type accept(TVisitor& visit) const {
|
||||
if (!data_)
|
||||
return visit.visit(nullptr);
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
auto eightByteValue = getEightByte(data, resources);
|
||||
auto eightByteValue = getEightByte();
|
||||
#endif
|
||||
switch (data->type) {
|
||||
switch (data_->type) {
|
||||
case VariantType::Float:
|
||||
return visit.visit(data->content.asFloat);
|
||||
return visit.visit(data_->content.asFloat);
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
@@ -58,27 +53,32 @@ class VariantImpl {
|
||||
#endif
|
||||
|
||||
case VariantType::Array:
|
||||
return visit.visitArray(data);
|
||||
return visit.visitArray(VariantImpl(data_, resources_));
|
||||
|
||||
case VariantType::Object:
|
||||
return visit.visitObject(data);
|
||||
return visit.visitObject(VariantImpl(data_, resources_));
|
||||
|
||||
case VariantType::TinyString:
|
||||
return visit.visit(JsonString(data->content.asTinyString));
|
||||
return visit.visit(JsonString(data_->content.asTinyString));
|
||||
|
||||
case VariantType::LongString:
|
||||
return visit.visit(JsonString(data->content.asStringNode->data,
|
||||
data->content.asStringNode->length));
|
||||
case VariantType::LinkedString:
|
||||
return visit.visit(JsonString(asLinkedString(), true));
|
||||
|
||||
case VariantType::RawString:
|
||||
return visit.visit(RawString(data->content.asStringNode->data,
|
||||
data->content.asStringNode->length));
|
||||
case VariantType::OwnedString: {
|
||||
auto s = asOwnedString();
|
||||
return visit.visit(JsonString(s->data, s->length));
|
||||
}
|
||||
|
||||
case VariantType::RawString: {
|
||||
auto s = asOwnedString();
|
||||
return visit.visit(RawString(s->data, s->length));
|
||||
}
|
||||
|
||||
case VariantType::Int32:
|
||||
return visit.visit(static_cast<JsonInteger>(data->content.asInt32));
|
||||
return visit.visit(static_cast<JsonInteger>(data_->content.asInt32));
|
||||
|
||||
case VariantType::Uint32:
|
||||
return visit.visit(static_cast<JsonUInt>(data->content.asUint32));
|
||||
return visit.visit(static_cast<JsonUInt>(data_->content.asUint32));
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case VariantType::Int64:
|
||||
@@ -89,63 +89,31 @@ class VariantImpl {
|
||||
#endif
|
||||
|
||||
case VariantType::Boolean:
|
||||
return visit.visit(data->content.asBoolean != 0);
|
||||
return visit.visit(data_->content.asBoolean != 0);
|
||||
|
||||
default:
|
||||
return visit.visit(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VariantData* addNewElement() {
|
||||
if (!isArray())
|
||||
return nullptr;
|
||||
return addNewElement(data_, resources_);
|
||||
}
|
||||
|
||||
static VariantData* addNewElement(VariantData*, ResourceManager*);
|
||||
|
||||
static void addElement(Slot<VariantData> slot, VariantData*,
|
||||
ResourceManager*);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* addMember(TAdaptedString key) {
|
||||
if (!isObject())
|
||||
return nullptr;
|
||||
return addMember(key, data_, resources_);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
static VariantData* addMember(TAdaptedString key, VariantData*,
|
||||
ResourceManager*);
|
||||
|
||||
VariantData* addPair(VariantData** value) {
|
||||
if (isNull())
|
||||
return nullptr;
|
||||
return addPair(value, data_, resources_);
|
||||
}
|
||||
|
||||
static VariantData* addPair(VariantData** value, VariantData*,
|
||||
ResourceManager*);
|
||||
VariantData* addElement();
|
||||
void addElement(Slot<VariantData> slot);
|
||||
|
||||
bool asBoolean() const {
|
||||
return asBoolean(data_, resources_);
|
||||
}
|
||||
|
||||
static bool asBoolean(VariantData* data, ResourceManager* resources) {
|
||||
if (!data)
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
auto eightByteValue = getEightByte(data, resources);
|
||||
auto eightByteValue = getEightByte();
|
||||
#endif
|
||||
switch (data->type) {
|
||||
switch (data_->type) {
|
||||
case VariantType::Boolean:
|
||||
return data->content.asBoolean;
|
||||
return data_->content.asBoolean;
|
||||
case VariantType::Uint32:
|
||||
case VariantType::Int32:
|
||||
return data->content.asUint32 != 0;
|
||||
return data_->content.asUint32 != 0;
|
||||
case VariantType::Float:
|
||||
return data->content.asFloat != 0;
|
||||
return data_->content.asFloat != 0;
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
return eightByteValue->asDouble != 0;
|
||||
@@ -164,26 +132,21 @@ class VariantImpl {
|
||||
|
||||
template <typename T>
|
||||
T asFloat() const {
|
||||
return asFloat<T>(data_, resources_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T asFloat(VariantData* data, ResourceManager* resources) {
|
||||
if (!data)
|
||||
if (!data_)
|
||||
return 0.0;
|
||||
|
||||
static_assert(is_floating_point<T>::value, "T must be a floating point");
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
auto eightByteValue = getEightByte(data, resources);
|
||||
auto eightByteValue = getEightByte();
|
||||
#endif
|
||||
const char* str = nullptr;
|
||||
switch (data->type) {
|
||||
switch (data_->type) {
|
||||
case VariantType::Boolean:
|
||||
return static_cast<T>(data->content.asBoolean);
|
||||
return static_cast<T>(data_->content.asBoolean);
|
||||
case VariantType::Uint32:
|
||||
return static_cast<T>(data->content.asUint32);
|
||||
return static_cast<T>(data_->content.asUint32);
|
||||
case VariantType::Int32:
|
||||
return static_cast<T>(data->content.asInt32);
|
||||
return static_cast<T>(data_->content.asInt32);
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case VariantType::Uint64:
|
||||
return static_cast<T>(eightByteValue->asUint64);
|
||||
@@ -191,13 +154,16 @@ class VariantImpl {
|
||||
return static_cast<T>(eightByteValue->asInt64);
|
||||
#endif
|
||||
case VariantType::TinyString:
|
||||
str = data->content.asTinyString;
|
||||
str = data_->content.asTinyString;
|
||||
break;
|
||||
case VariantType::LongString:
|
||||
str = data->content.asStringNode->data;
|
||||
case VariantType::LinkedString:
|
||||
str = asLinkedString();
|
||||
break;
|
||||
case VariantType::OwnedString:
|
||||
str = asOwnedString()->data;
|
||||
break;
|
||||
case VariantType::Float:
|
||||
return static_cast<T>(data->content.asFloat);
|
||||
return static_cast<T>(data_->content.asFloat);
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
return static_cast<T>(eightByteValue->asDouble);
|
||||
@@ -212,26 +178,21 @@ class VariantImpl {
|
||||
|
||||
template <typename T>
|
||||
T asIntegral() const {
|
||||
return asIntegral<T>(data_, resources_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T asIntegral(VariantData* data, ResourceManager* resources) {
|
||||
if (!data)
|
||||
if (!data_)
|
||||
return 0;
|
||||
|
||||
static_assert(is_integral<T>::value, "T must be an integral type");
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
auto eightByteValue = getEightByte(data, resources);
|
||||
auto eightByteValue = getEightByte();
|
||||
#endif
|
||||
const char* str = nullptr;
|
||||
switch (data->type) {
|
||||
switch (data_->type) {
|
||||
case VariantType::Boolean:
|
||||
return data->content.asBoolean;
|
||||
return data_->content.asBoolean;
|
||||
case VariantType::Uint32:
|
||||
return convertNumber<T>(data->content.asUint32);
|
||||
return convertNumber<T>(data_->content.asUint32);
|
||||
case VariantType::Int32:
|
||||
return convertNumber<T>(data->content.asInt32);
|
||||
return convertNumber<T>(data_->content.asInt32);
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case VariantType::Uint64:
|
||||
return convertNumber<T>(eightByteValue->asUint64);
|
||||
@@ -239,13 +200,16 @@ class VariantImpl {
|
||||
return convertNumber<T>(eightByteValue->asInt64);
|
||||
#endif
|
||||
case VariantType::TinyString:
|
||||
str = data->content.asTinyString;
|
||||
str = data_->content.asTinyString;
|
||||
break;
|
||||
case VariantType::LongString:
|
||||
str = data->content.asStringNode->data;
|
||||
case VariantType::LinkedString:
|
||||
str = asLinkedString();
|
||||
break;
|
||||
case VariantType::OwnedString:
|
||||
str = asOwnedString()->data;
|
||||
break;
|
||||
case VariantType::Float:
|
||||
return convertNumber<T>(data->content.asFloat);
|
||||
return convertNumber<T>(data_->content.asFloat);
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
case VariantType::Double:
|
||||
return convertNumber<T>(eightByteValue->asDouble);
|
||||
@@ -258,82 +222,93 @@ class VariantImpl {
|
||||
return parseNumber<T>(str);
|
||||
}
|
||||
|
||||
iterator at(size_t index) const;
|
||||
|
||||
iterator createIterator() const {
|
||||
if (!isCollection())
|
||||
return iterator();
|
||||
return createIterator(data_, resources_);
|
||||
JsonString asRawString() const {
|
||||
switch (type()) {
|
||||
case VariantType::RawString: {
|
||||
auto s = asOwnedString();
|
||||
return JsonString(s->data, s->length);
|
||||
}
|
||||
default:
|
||||
return JsonString();
|
||||
}
|
||||
}
|
||||
|
||||
static iterator createIterator(VariantData*, ResourceManager*);
|
||||
const char* asLinkedString() const {
|
||||
ARDUINOJSON_ASSERT(type() == VariantType::LinkedString);
|
||||
return resources_->getStaticString(data_->content.asSlotId);
|
||||
}
|
||||
|
||||
JsonString asString() const {
|
||||
switch (type()) {
|
||||
case VariantType::TinyString:
|
||||
return JsonString(data_->content.asTinyString);
|
||||
case VariantType::LinkedString:
|
||||
return JsonString(asLinkedString(), true);
|
||||
case VariantType::OwnedString: {
|
||||
auto s = asOwnedString();
|
||||
return JsonString(s->data, s->length);
|
||||
}
|
||||
default:
|
||||
return JsonString();
|
||||
}
|
||||
}
|
||||
|
||||
StringNode* asOwnedString() const {
|
||||
ARDUINOJSON_ASSERT(type() & VariantTypeBits::OwnedStringBit);
|
||||
return data_->content.asOwnedString;
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
static const EightByteValue* getEightByte(VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
return data->type & VariantTypeBits::EightByteBit
|
||||
? resources->getEightByte(data->content.asSlotId)
|
||||
const EightByteValue* getEightByte() const {
|
||||
return type() & VariantTypeBits::EightByteBit
|
||||
? resources_->getEightByte(data_->content.asSlotId)
|
||||
: 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
VariantData* getOrAddElement(size_t index);
|
||||
iterator createIterator() const;
|
||||
|
||||
VariantData* getElement(size_t index) const;
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getMember(TAdaptedString key) const {
|
||||
if (!isObject())
|
||||
return nullptr;
|
||||
return getMember(key, data_, resources_);
|
||||
}
|
||||
VariantData* getOrAddElement(size_t index);
|
||||
|
||||
void addMember(Slot<VariantData> key, Slot<VariantData> value);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
static VariantData* getMember(TAdaptedString key, VariantData*,
|
||||
ResourceManager*);
|
||||
VariantData* addMember(TAdaptedString key);
|
||||
|
||||
template <typename TAdaptedString>
|
||||
VariantData* getOrAddMember(TAdaptedString key) {
|
||||
if (!isObject())
|
||||
return nullptr;
|
||||
return getOrAddMember(key, data_, resources_);
|
||||
}
|
||||
VariantData* getMember(TAdaptedString key) const;
|
||||
|
||||
template <typename TAdaptedString>
|
||||
static VariantData* getOrAddMember(TAdaptedString key, VariantData*,
|
||||
ResourceManager*);
|
||||
VariantData* getOrAddMember(TAdaptedString key);
|
||||
|
||||
bool isArray() const {
|
||||
return type() == VariantType::Array;
|
||||
}
|
||||
|
||||
bool isCollection() const {
|
||||
return type() & VariantTypeBits::CollectionMask;
|
||||
bool isBoolean() const {
|
||||
return type() == VariantType::Boolean;
|
||||
}
|
||||
|
||||
bool isFloat() const {
|
||||
return data_ && data_->isFloat();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isInteger() const {
|
||||
return isInteger<T>(data_, resources_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool isInteger(VariantData* data, ResourceManager* resources) {
|
||||
if (!data)
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
auto eightByteValue = getEightByte(data, resources);
|
||||
#else
|
||||
(void)resources;
|
||||
auto eightByteValue = getEightByte();
|
||||
#endif
|
||||
switch (data->type) {
|
||||
switch (data_->type) {
|
||||
case VariantType::Uint32:
|
||||
return canConvertNumber<T>(data->content.asUint32);
|
||||
return canConvertNumber<T>(data_->content.asUint32);
|
||||
|
||||
case VariantType::Int32:
|
||||
return canConvertNumber<T>(data->content.asInt32);
|
||||
return canConvertNumber<T>(data_->content.asInt32);
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case VariantType::Uint64:
|
||||
@@ -348,6 +323,10 @@ class VariantImpl {
|
||||
}
|
||||
}
|
||||
|
||||
bool isUnbound() const {
|
||||
return !data_;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return type() == VariantType::Null;
|
||||
}
|
||||
@@ -356,133 +335,140 @@ class VariantImpl {
|
||||
return type() == VariantType::Object;
|
||||
}
|
||||
|
||||
bool isString() const {
|
||||
return data_ && data_->isString();
|
||||
}
|
||||
|
||||
size_t nesting() const;
|
||||
|
||||
void removeElement(iterator it);
|
||||
|
||||
void removeElement(size_t index);
|
||||
|
||||
void removeElement(CollectionIterator it) {
|
||||
removeOne(it);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
void removeMember(TAdaptedString key) {
|
||||
removePair(findKey(key));
|
||||
void removeMember(TAdaptedString key);
|
||||
|
||||
void removeMember(iterator it);
|
||||
|
||||
bool copyVariant(const VariantImpl& src) {
|
||||
switch (src.type()) {
|
||||
case VariantType::Null:
|
||||
return true;
|
||||
|
||||
case VariantType::Array:
|
||||
return copyArray(src);
|
||||
|
||||
case VariantType::Object:
|
||||
return copyObject(src);
|
||||
|
||||
case VariantType::RawString:
|
||||
return setRawString(adaptString(src.asRawString()));
|
||||
|
||||
case VariantType::LinkedString:
|
||||
return setLinkedString(src.asLinkedString());
|
||||
|
||||
case VariantType::OwnedString:
|
||||
return setOwnedString(adaptString(src.asString()));
|
||||
|
||||
default:
|
||||
data_->content = src.data_->content;
|
||||
data_->type = src.data_->type;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void removeMember(CollectionIterator it) {
|
||||
removePair(it);
|
||||
}
|
||||
bool copyArray(const VariantImpl& src);
|
||||
bool copyObject(const VariantImpl& src);
|
||||
|
||||
bool setBoolean(bool value) {
|
||||
if (!data_)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
data_->setBoolean(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool setFloat(T value) {
|
||||
enable_if_t<sizeof(T) == 4, bool> setFloat(T value) {
|
||||
ARDUINOJSON_ASSERT(type() == VariantType::Null); // must call clear() first
|
||||
if (!data_)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
return setFloat(value, data_, resources_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static enable_if_t<sizeof(T) == 4, bool> setFloat(T value, VariantData* data,
|
||||
ResourceManager*) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->type == VariantType::Null);
|
||||
data->type = VariantType::Float;
|
||||
data->content.asFloat = value;
|
||||
data_->type = VariantType::Float;
|
||||
data_->content.asFloat = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static enable_if_t<sizeof(T) == 8, bool> setFloat(
|
||||
T value, VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
enable_if_t<sizeof(T) == 8, bool> setFloat(T value) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
float valueAsFloat = static_cast<float>(value);
|
||||
|
||||
#if ARDUINOJSON_USE_DOUBLE
|
||||
if (value == valueAsFloat) {
|
||||
data->type = VariantType::Float;
|
||||
data->content.asFloat = valueAsFloat;
|
||||
data_->type = VariantType::Float;
|
||||
data_->content.asFloat = valueAsFloat;
|
||||
} else {
|
||||
auto slot = resources->allocEightByte();
|
||||
auto slot = resources_->allocEightByte();
|
||||
if (!slot)
|
||||
return false;
|
||||
data->type = VariantType::Double;
|
||||
data->content.asSlotId = slot.id();
|
||||
data_->type = VariantType::Double;
|
||||
data_->content.asSlotId = slot.id();
|
||||
slot->asDouble = value;
|
||||
}
|
||||
#else
|
||||
data->type = VariantType::Float;
|
||||
data->content.asFloat = valueAsFloat;
|
||||
data_->type = VariantType::Float;
|
||||
data_->content.asFloat = valueAsFloat;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool setInteger(T value) {
|
||||
enable_if_t<is_signed<T>::value, bool> setInteger(T value) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
return setInteger(value, data_, resources_);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static enable_if_t<is_signed<T>::value, bool> setInteger(
|
||||
T value, VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
if (canConvertNumber<int32_t>(value)) {
|
||||
data->type = VariantType::Int32;
|
||||
data->content.asInt32 = static_cast<int32_t>(value);
|
||||
data_->type = VariantType::Int32;
|
||||
data_->content.asInt32 = static_cast<int32_t>(value);
|
||||
}
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
else {
|
||||
auto slot = resources->allocEightByte();
|
||||
auto slot = resources_->allocEightByte();
|
||||
if (!slot)
|
||||
return false;
|
||||
data->type = VariantType::Int64;
|
||||
data->content.asSlotId = slot.id();
|
||||
data_->type = VariantType::Int64;
|
||||
data_->content.asSlotId = slot.id();
|
||||
slot->asInt64 = value;
|
||||
}
|
||||
#else
|
||||
(void)resources;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static enable_if_t<is_unsigned<T>::value, bool> setInteger(
|
||||
T value, VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
enable_if_t<is_unsigned<T>::value, bool> setInteger(T value) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
|
||||
if (canConvertNumber<uint32_t>(value)) {
|
||||
data->type = VariantType::Uint32;
|
||||
data->content.asUint32 = static_cast<uint32_t>(value);
|
||||
data_->type = VariantType::Uint32;
|
||||
data_->content.asUint32 = static_cast<uint32_t>(value);
|
||||
}
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
else {
|
||||
auto slot = resources->allocEightByte();
|
||||
auto slot = resources_->allocEightByte();
|
||||
if (!slot)
|
||||
return false;
|
||||
data->type = VariantType::Uint64;
|
||||
data->content.asSlotId = slot.id();
|
||||
data_->type = VariantType::Uint64;
|
||||
data_->content.asSlotId = slot.id();
|
||||
slot->asUint64 = value;
|
||||
}
|
||||
#else
|
||||
(void)resources;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -491,8 +477,7 @@ class VariantImpl {
|
||||
bool setRawString(TAdaptedString value) {
|
||||
if (!data_)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
auto dup = resources_->saveString(adaptString(value.data(), value.size()));
|
||||
auto dup = resources_->saveString(value);
|
||||
if (!dup)
|
||||
return false;
|
||||
data_->setRawString(dup);
|
||||
@@ -501,141 +486,130 @@ class VariantImpl {
|
||||
|
||||
template <typename TAdaptedString>
|
||||
bool setString(TAdaptedString value) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
|
||||
if (!data_)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
return setString(value, data_, resources_);
|
||||
|
||||
if (value.isNull())
|
||||
return true;
|
||||
|
||||
if (value.isStatic())
|
||||
return setLinkedString(value.data());
|
||||
|
||||
if (isTinyString(value, value.size())) {
|
||||
data_->setTinyString(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return setOwnedString(value);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
static bool setString(TAdaptedString value, VariantData* data,
|
||||
ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->type == VariantType::Null);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
if (value.isNull())
|
||||
return true; // TODO: should this be moved up to the member function?
|
||||
|
||||
if (isTinyString(value, value.size())) {
|
||||
data->setTinyString(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto dup = resources->saveString(value);
|
||||
if (dup) {
|
||||
data->setLongString(dup);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
if (!isCollection())
|
||||
return 0;
|
||||
|
||||
return size(data_, resources_);
|
||||
}
|
||||
|
||||
static size_t size(VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(data->isCollection());
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
size_t n = 0;
|
||||
for (auto it = createIterator(data, resources); !it.done();
|
||||
it.move(resources))
|
||||
n++;
|
||||
|
||||
if (data->type == VariantType::Object) {
|
||||
ARDUINOJSON_ASSERT((n % 2) == 0);
|
||||
n /= 2;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
bool toArray() {
|
||||
if (!data_)
|
||||
bool setOwnedString(TAdaptedString value) {
|
||||
auto dup = resources_->saveString(value);
|
||||
if (!dup)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
data_->toArray();
|
||||
|
||||
data_->setOwnedString(dup);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool toObject() {
|
||||
if (!data_)
|
||||
bool setLinkedString(const char* s) {
|
||||
ARDUINOJSON_ASSERT(isNull()); // must call clear() first
|
||||
ARDUINOJSON_ASSERT(s);
|
||||
|
||||
auto slotId = resources_->saveStaticString(s);
|
||||
if (slotId == NULL_SLOT)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
data_->toObject();
|
||||
|
||||
data_->type = VariantType::LinkedString;
|
||||
data_->content.asSlotId = slotId;
|
||||
return true;
|
||||
}
|
||||
|
||||
void toArrayIfNull() {
|
||||
if (data_ && data_->type == VariantType::Null)
|
||||
data_->toArray();
|
||||
}
|
||||
|
||||
void toObjectIfNull() {
|
||||
if (data_ && data_->type == VariantType::Null)
|
||||
data_->toObject();
|
||||
}
|
||||
|
||||
void empty() {
|
||||
auto coll = getCollectionData();
|
||||
|
||||
auto next = coll->head;
|
||||
while (next != NULL_SLOT) {
|
||||
auto currId = next;
|
||||
auto slot = getVariant(next);
|
||||
next = slot->next;
|
||||
freeVariant({slot, currId});
|
||||
}
|
||||
|
||||
coll->head = NULL_SLOT;
|
||||
coll->tail = NULL_SLOT;
|
||||
}
|
||||
|
||||
size_t size() const;
|
||||
|
||||
VariantType type() const {
|
||||
return data_ ? data_->type : VariantType::Null;
|
||||
}
|
||||
|
||||
// Release the resources used by this variant and set it to null.
|
||||
bool clear() {
|
||||
void clear() {
|
||||
if (!data_)
|
||||
return false;
|
||||
clear(data_, resources_);
|
||||
return true;
|
||||
}
|
||||
return;
|
||||
|
||||
static void clear(VariantData* data, ResourceManager* resources) {
|
||||
ARDUINOJSON_ASSERT(data != nullptr);
|
||||
ARDUINOJSON_ASSERT(resources != nullptr);
|
||||
|
||||
if (data->type & VariantTypeBits::OwnedStringBit)
|
||||
resources->dereferenceString(data->content.asStringNode->data);
|
||||
if (data_->type & VariantTypeBits::OwnedStringBit)
|
||||
resources_->dereferenceString(asOwnedString());
|
||||
|
||||
#if ARDUINOJSON_USE_8_BYTE_POOL
|
||||
if (data->type & VariantTypeBits::EightByteBit)
|
||||
resources->freeEightByte(data->content.asSlotId);
|
||||
if (data_->type & VariantTypeBits::EightByteBit)
|
||||
resources_->freeEightByte(data_->content.asSlotId);
|
||||
#endif
|
||||
|
||||
if (data->type & VariantTypeBits::CollectionMask)
|
||||
empty(data, resources);
|
||||
if (data_->type & VariantTypeBits::CollectionMask)
|
||||
empty();
|
||||
|
||||
data->type = VariantType::Null;
|
||||
}
|
||||
|
||||
void empty() {
|
||||
if (!isCollection())
|
||||
return;
|
||||
empty(data_, resources_);
|
||||
}
|
||||
|
||||
static void empty(VariantData*, ResourceManager*);
|
||||
|
||||
static void freeVariant(Slot<VariantData> slot, ResourceManager* resources) {
|
||||
clear(slot.ptr(), resources);
|
||||
resources->freeVariant(slot);
|
||||
data_->type = VariantType::Null;
|
||||
}
|
||||
|
||||
private:
|
||||
VariantData* data_;
|
||||
ResourceManager* resources_;
|
||||
|
||||
template <typename TAdaptedString>
|
||||
iterator findKey(TAdaptedString key) const {
|
||||
if (!isObject())
|
||||
return iterator();
|
||||
return findKey(key, data_, resources_);
|
||||
}
|
||||
iterator findKey(TAdaptedString key) const;
|
||||
|
||||
template <typename TAdaptedString>
|
||||
static iterator findKey(TAdaptedString key, VariantData*, ResourceManager*);
|
||||
|
||||
static void appendPair(Slot<VariantData> key, Slot<VariantData> value,
|
||||
VariantData*, ResourceManager*);
|
||||
iterator at(size_t index) const;
|
||||
|
||||
void removeOne(iterator it);
|
||||
void removePair(iterator it);
|
||||
|
||||
VariantData* getVariant(SlotId id) const {
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
return resources_->getVariant(id);
|
||||
}
|
||||
|
||||
void freeVariant(Slot<VariantData> slot) {
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
VariantImpl(slot.ptr(), resources_).clear();
|
||||
resources_->freeVariant(slot);
|
||||
}
|
||||
|
||||
Slot<VariantData> allocVariant() {
|
||||
ARDUINOJSON_ASSERT(resources_ != nullptr);
|
||||
return resources_->allocVariant();
|
||||
}
|
||||
|
||||
Slot<VariantData> getPreviousSlot(VariantData*) const;
|
||||
|
||||
CollectionData* getCollectionData() const {
|
||||
ARDUINOJSON_ASSERT(data_ != nullptr);
|
||||
ARDUINOJSON_ASSERT(data_->isCollection());
|
||||
return &data_->content.asCollection;
|
||||
}
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <ArduinoJson/Variant/Converter.hpp>
|
||||
#include <ArduinoJson/Variant/JsonVariantConst.hpp>
|
||||
#include <ArduinoJson/Variant/VariantOperators.hpp>
|
||||
#include <ArduinoJson/Variant/VariantTo.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
class JsonVariant;
|
||||
@@ -28,18 +29,18 @@ class VariantRefBase : public VariantTag {
|
||||
// Sets the value to null.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/clear/
|
||||
void clear() const {
|
||||
getOrCreateVariantImpl().clear();
|
||||
getOrCreateImpl().clear();
|
||||
}
|
||||
|
||||
// Returns true if the value is null or the reference is unbound.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/isnull/
|
||||
bool isNull() const {
|
||||
return getVariantImpl().isNull();
|
||||
return getImpl().isNull();
|
||||
}
|
||||
|
||||
// Returns true if the reference is unbound.
|
||||
bool isUnbound() const {
|
||||
return !getData();
|
||||
return getImpl().isUnbound();
|
||||
}
|
||||
|
||||
// Casts the value to the specified type.
|
||||
@@ -76,13 +77,15 @@ class VariantRefBase : public VariantTag {
|
||||
// https://arduinojson.org/v7/api/jsonvariant/set/
|
||||
template <typename T>
|
||||
bool set(const T& value) const {
|
||||
using TypeForConverter = remove_cv_t<remove_reference_t<T>>;
|
||||
using TypeForConverter = conditional_t<IsStringLiteral<T>::value, T,
|
||||
remove_cv_t<remove_reference_t<T>>>;
|
||||
return doSet<Converter<TypeForConverter>>(value);
|
||||
}
|
||||
|
||||
// Copies the specified value.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/set/
|
||||
template <typename T>
|
||||
template <typename T,
|
||||
detail::enable_if_t<!detail::is_const<T>::value, int> = 0>
|
||||
bool set(T* value) const {
|
||||
return doSet<Converter<T*>>(value);
|
||||
}
|
||||
@@ -90,13 +93,13 @@ class VariantRefBase : public VariantTag {
|
||||
// Returns the size of the array or object.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/size/
|
||||
size_t size() const {
|
||||
return getVariantImpl().size();
|
||||
return getImpl().size();
|
||||
}
|
||||
|
||||
// Returns the depth (nesting level) of the value.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/nesting/
|
||||
size_t nesting() const {
|
||||
return getVariantImpl().nesting();
|
||||
return getImpl().nesting();
|
||||
}
|
||||
|
||||
// Appends a new (empty) element to the array.
|
||||
@@ -122,7 +125,7 @@ class VariantRefBase : public VariantTag {
|
||||
|
||||
// Appends a value to the array.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/add/
|
||||
template <typename T>
|
||||
template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
|
||||
bool add(T* value) const {
|
||||
return getOrCreateArray().add(value);
|
||||
}
|
||||
@@ -130,21 +133,21 @@ class VariantRefBase : public VariantTag {
|
||||
// Removes an element of the array.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/remove/
|
||||
void remove(size_t index) const {
|
||||
getVariantImpl().removeElement(index);
|
||||
getImpl().removeElement(index);
|
||||
}
|
||||
|
||||
// Removes a member of the object.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/remove/
|
||||
template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
|
||||
void remove(TChar* key) const {
|
||||
getVariantImpl().removeMember(adaptString(key));
|
||||
getImpl().removeMember(adaptString(key));
|
||||
}
|
||||
|
||||
// Removes a member of the object.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/remove/
|
||||
template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
|
||||
void remove(const TString& key) const {
|
||||
getVariantImpl().removeMember(adaptString(key));
|
||||
getImpl().removeMember(adaptString(key));
|
||||
}
|
||||
|
||||
// Removes a member of the object or an element of the array.
|
||||
@@ -187,7 +190,9 @@ class VariantRefBase : public VariantTag {
|
||||
|
||||
// Gets or sets an object member.
|
||||
// https://arduinojson.org/v7/api/jsonvariant/subscript/
|
||||
template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
|
||||
template <
|
||||
typename TChar,
|
||||
enable_if_t<IsString<TChar*>::value && !is_const<TChar>::value, int> = 0>
|
||||
FORCE_INLINE MemberProxy<TDerived, AdaptedString<TChar*>> operator[](
|
||||
TChar* key) const;
|
||||
|
||||
@@ -254,24 +259,12 @@ class VariantRefBase : public VariantTag {
|
||||
return static_cast<const TDerived&>(*this);
|
||||
}
|
||||
|
||||
ResourceManager* getResourceManager() const {
|
||||
return VariantAttorney::getResourceManager(derived());
|
||||
VariantImpl getImpl() const {
|
||||
return VariantAttorney::getImpl(derived());
|
||||
}
|
||||
|
||||
VariantData* getData() const {
|
||||
return VariantAttorney::getData(derived());
|
||||
}
|
||||
|
||||
VariantData* getOrCreateData() const {
|
||||
return VariantAttorney::getOrCreateData(derived());
|
||||
}
|
||||
|
||||
VariantImpl getVariantImpl() const {
|
||||
return VariantImpl(getData(), getResourceManager());
|
||||
}
|
||||
|
||||
VariantImpl getOrCreateVariantImpl() const {
|
||||
return VariantImpl(getOrCreateData(), getResourceManager());
|
||||
VariantImpl getOrCreateImpl() const {
|
||||
return VariantAttorney::getOrCreateImpl(derived());
|
||||
}
|
||||
|
||||
JsonArray getOrCreateArray() const;
|
||||
@@ -279,7 +272,7 @@ class VariantRefBase : public VariantTag {
|
||||
FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
|
||||
|
||||
FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {
|
||||
return ArduinoJson::JsonVariantConst(getData(), getResourceManager());
|
||||
return ArduinoJson::JsonVariantConst(getImpl());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -307,8 +300,6 @@ class VariantRefBase : public VariantTag {
|
||||
|
||||
template <typename TConverter, typename T>
|
||||
bool doSet(const T& value, true_type) const;
|
||||
|
||||
ArduinoJson::JsonVariant getOrCreateVariant() const;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
|
||||
@@ -75,13 +75,13 @@ inline T VariantRefBase<TDerived>::add() const {
|
||||
template <typename TDerived>
|
||||
template <typename TString, enable_if_t<IsString<TString>::value, int>>
|
||||
inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const {
|
||||
return getVariantImpl().getMember(adaptString(key)) != 0;
|
||||
return getImpl().getMember(adaptString(key)) != 0;
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
|
||||
inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const {
|
||||
return getVariantImpl().getMember(adaptString(key)) != 0;
|
||||
return getImpl().getMember(adaptString(key)) != 0;
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
@@ -92,21 +92,7 @@ inline bool VariantRefBase<TDerived>::containsKey(const TVariant& key) const {
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonVariant VariantRefBase<TDerived>::getVariant() const {
|
||||
return JsonVariant(getData(), getResourceManager());
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonArray VariantRefBase<TDerived>::getOrCreateArray() const {
|
||||
auto data = getOrCreateData();
|
||||
auto resources = getResourceManager();
|
||||
if (data && data->type == VariantType::Null)
|
||||
data->toArray();
|
||||
return JsonArray(data, resources);
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonVariant VariantRefBase<TDerived>::getOrCreateVariant() const {
|
||||
return JsonVariant(getOrCreateData(), getResourceManager());
|
||||
return JsonVariant(getImpl());
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
@@ -124,7 +110,8 @@ inline ElementProxy<TDerived> VariantRefBase<TDerived>::operator[](
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
|
||||
template <typename TChar,
|
||||
enable_if_t<IsString<TChar*>::value && !is_const<TChar>::value, int>>
|
||||
inline MemberProxy<TDerived, AdaptedString<TChar*>>
|
||||
VariantRefBase<TDerived>::operator[](TChar* key) const {
|
||||
return {derived(), adaptString(key)};
|
||||
@@ -140,37 +127,52 @@ VariantRefBase<TDerived>::operator[](const TString& key) const {
|
||||
template <typename TDerived>
|
||||
template <typename TConverter, typename T>
|
||||
inline bool VariantRefBase<TDerived>::doSet(const T& value, false_type) const {
|
||||
TConverter::toJson(value, getOrCreateVariant());
|
||||
auto resources = getResourceManager();
|
||||
auto impl = getOrCreateImpl();
|
||||
TConverter::toJson(value, JsonVariant(impl));
|
||||
auto resources = impl.resources();
|
||||
return resources && !resources->overflowed();
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename TConverter, typename T>
|
||||
inline bool VariantRefBase<TDerived>::doSet(const T& value, true_type) const {
|
||||
return TConverter::toJson(value, getOrCreateVariant());
|
||||
auto impl = getOrCreateImpl();
|
||||
return TConverter::toJson(value, JsonVariant(impl));
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
inline JsonArray VariantRefBase<TDerived>::getOrCreateArray() const {
|
||||
auto impl = getOrCreateImpl();
|
||||
impl.toArrayIfNull();
|
||||
return JsonArray(impl);
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename T, enable_if_t<is_same<T, JsonArray>::value, int>>
|
||||
inline JsonArray VariantRefBase<TDerived>::to() const {
|
||||
auto impl = getOrCreateVariantImpl();
|
||||
impl.toArray();
|
||||
auto impl = getOrCreateImpl();
|
||||
if (impl.isUnbound())
|
||||
return JsonArray();
|
||||
impl.clear();
|
||||
impl.data()->toArray();
|
||||
return JsonArray(impl);
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename T, enable_if_t<is_same<T, JsonObject>::value, int>>
|
||||
JsonObject VariantRefBase<TDerived>::to() const {
|
||||
auto impl = getOrCreateVariantImpl();
|
||||
impl.toObject();
|
||||
auto impl = getOrCreateImpl();
|
||||
if (impl.isUnbound())
|
||||
return JsonObject();
|
||||
impl.clear();
|
||||
impl.data()->toObject();
|
||||
return JsonObject(impl);
|
||||
}
|
||||
|
||||
template <typename TDerived>
|
||||
template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
|
||||
JsonVariant VariantRefBase<TDerived>::to() const {
|
||||
auto impl = getOrCreateVariantImpl();
|
||||
auto impl = getOrCreateImpl();
|
||||
impl.clear();
|
||||
return JsonVariant(impl);
|
||||
}
|
||||
|
||||
34
src/ArduinoJson/Variant/VariantTo.hpp
Normal file
34
src/ArduinoJson/Variant/VariantTo.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
|
||||
class JsonArray;
|
||||
class JsonObject;
|
||||
class JsonVariant;
|
||||
ARDUINOJSON_END_PUBLIC_NAMESPACE
|
||||
|
||||
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
|
||||
// A metafunction that returns the type of the value returned by
|
||||
// JsonVariant::to<T>()
|
||||
template <typename T>
|
||||
struct VariantTo {};
|
||||
|
||||
template <>
|
||||
struct VariantTo<JsonArray> {
|
||||
using type = JsonArray;
|
||||
};
|
||||
template <>
|
||||
struct VariantTo<JsonObject> {
|
||||
using type = JsonObject;
|
||||
};
|
||||
template <>
|
||||
struct VariantTo<JsonVariant> {
|
||||
using type = JsonVariant;
|
||||
};
|
||||
|
||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||
Reference in New Issue
Block a user