Update ArduinoJSON

This commit is contained in:
iranl
2025-01-06 22:27:44 +01:00
parent 565a887c95
commit a379b30c11
82 changed files with 1098 additions and 1500 deletions

View File

@@ -1,6 +1,46 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
v7.3.0 (2024-12-29)
------
* Fix support for NUL characters in `deserializeJson()`
* Make `ElementProxy` and `MemberProxy` non-copyable
* Change string copy policy: only string literal are stored by pointer
* `JsonString` is now stored by copy, unless specified otherwise
* Replace undocumented `JsonString::Ownership` with `bool`
* Rename undocumented `JsonString::isLinked()` to `isStatic()`
* Move public facing SFINAEs to template declarations
> ### BREAKING CHANGES
>
> In previous versions, `MemberProxy` (the class returned by `operator[]`) could lead to dangling pointers when used with a temporary string.
> To prevent this issue, `MemberProxy` and `ElementProxy` are now non-copyable.
>
> Your code is likely to be affected if you use `auto` to store the result of `operator[]`. For example, the following line won't compile anymore:
>
> ```cpp
> auto value = doc["key"];
> ```
>
> To fix the issue, you must append either `.as<T>()` or `.to<T>()`, depending on the situation.
>
> For example, if you are extracting values from a JSON document, you should update like this:
>
> ```diff
> - auto config = doc["config"];
> + auto config = doc["config"].as<JsonObject>();
> const char* name = config["name"];
> ```
>
> However, if you are building a JSON document, you should update like this:
>
> ```diff
> - auto config = doc["config"];
> + auto config = doc["config"].to<JsonObject>();
> config["name"] = "ArduinoJson";
> ```
v7.2.1 (2024-11-15) v7.2.1 (2024-11-15)
------ ------

View File

@@ -10,7 +10,7 @@ if(ESP_PLATFORM)
return() return()
endif() endif()
project(ArduinoJson VERSION 7.2.1) project(ArduinoJson VERSION 7.3.0)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest) include(CTest)

View File

@@ -1,46 +0,0 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -1,4 +1,4 @@
version: 7.2.1.{build} version: 7.3.0.{build}
environment: environment:
matrix: matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022

View File

@@ -0,0 +1,29 @@
#!/usr/bin/awk -f
# Start echoing after the first list item
/\* / {
STARTED=1
EMPTY_LINE=0
}
# Remember if we have seen an empty line
/^[[:space:]]*$/ {
EMPTY_LINE=1
}
# Exit when seeing a new version number
/^v[[:digit:]]/ {
if (STARTED) exit
}
# Print if the line is not empty
# and restore the empty line we have skipped
!/^[[:space:]]*$/ {
if (STARTED) {
if (EMPTY_LINE) {
print ""
EMPTY_LINE=0
}
print
}
}

View File

@@ -1,14 +0,0 @@
#!/bin/bash
set -eu
TAG="$1"
CHANGELOG="$2"
cat << END
## Changes
$(awk '/\* /{ FOUND=1 } /^[[:space:]]*$/ { if(FOUND) exit } { if(FOUND) print }' "$CHANGELOG")
[View version history](https://github.com/bblanchon/ArduinoJson/blob/$TAG/CHANGELOG.md)
END

View File

@@ -14,5 +14,5 @@ date: '$(date +'%Y-%m-%d')'
$(extras/scripts/wandbox/publish.sh "$ARDUINOJSON_H") $(extras/scripts/wandbox/publish.sh "$ARDUINOJSON_H")
--- ---
$(awk '/\* /{ FOUND=1; print; next } { if (FOUND) exit}' "$CHANGELOG") $(extras/scripts/extract_changes.awk "$CHANGELOG")
END END

View File

@@ -15,7 +15,7 @@ compile() {
"code":$(read_string "$FILE_PATH"), "code":$(read_string "$FILE_PATH"),
"codes": [{"file":"ArduinoJson.h","code":$(read_string "$ARDUINOJSON_H")}], "codes": [{"file":"ArduinoJson.h","code":$(read_string "$ARDUINOJSON_H")}],
"options": "warning,c++11", "options": "warning,c++11",
"compiler": "gcc-5.5.0", "compiler": "gcc-head",
"save": true "save": true
} }
END END

View File

@@ -67,7 +67,7 @@ TEST_CASE("JsonDocument::containsKey()") {
TEST_CASE("MemberProxy::containsKey()") { TEST_CASE("MemberProxy::containsKey()") {
JsonDocument doc; JsonDocument doc;
auto mp = doc["hello"]; const auto& mp = doc["hello"];
SECTION("containsKey(const char*)") { SECTION("containsKey(const char*)") {
mp["key"] = "value"; mp["key"] = "value";

View File

@@ -13,7 +13,6 @@ add_executable(JsonArrayTests
nesting.cpp nesting.cpp
remove.cpp remove.cpp
size.cpp size.cpp
std_string.cpp
subscript.cpp subscript.cpp
unbound.cpp unbound.cpp
) )

View File

@@ -17,31 +17,112 @@ TEST_CASE("JsonArray::add(T)") {
SECTION("int") { SECTION("int") {
array.add(123); array.add(123);
REQUIRE(123 == array[0].as<int>()); REQUIRE(123 == array[0].as<int>());
REQUIRE(array[0].is<int>()); REQUIRE(array[0].is<int>());
REQUIRE(array[0].is<double>()); REQUIRE(array[0].is<double>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
} }
SECTION("double") { SECTION("double") {
array.add(123.45); array.add(123.45);
REQUIRE(123.45 == array[0].as<double>()); REQUIRE(123.45 == array[0].as<double>());
REQUIRE(array[0].is<double>()); REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<bool>()); REQUIRE_FALSE(array[0].is<bool>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
} }
SECTION("bool") { SECTION("bool") {
array.add(true); array.add(true);
REQUIRE(true == array[0].as<bool>());
REQUIRE(array[0].as<bool>() == true);
REQUIRE(array[0].is<bool>()); REQUIRE(array[0].is<bool>());
REQUIRE_FALSE(array[0].is<int>()); REQUIRE_FALSE(array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("string literal") {
array.add("hello");
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].is<const char*>());
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("std::string") {
array.add("hello"_s);
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].is<const char*>() == true);
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
} }
SECTION("const char*") { SECTION("const char*") {
const char* str = "hello"; const char* str = "hello";
array.add(str); array.add(str);
REQUIRE(str == array[0].as<std::string>());
REQUIRE(array[0].is<const char*>()); REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE_FALSE(array[0].is<int>()); REQUIRE(array[0].as<const char*>() != str);
REQUIRE(array[0].is<const char*>() == true);
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("serialized(const char*)") {
array.add(serialized("{}"));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(std::string)") {
array.add(serialized("{}"_s));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(std::string)") {
array.add(serialized("\0XX"_s));
REQUIRE(doc.as<std::string>() == "[\0XX]"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString(" XX")),
});
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
@@ -52,7 +133,12 @@ TEST_CASE("JsonArray::add(T)") {
array.add(vla); array.add(vla);
REQUIRE("world"_s == array[0]); strcpy(vla, "hello");
REQUIRE(array[0] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
} }
#endif #endif
@@ -99,61 +185,6 @@ TEST_CASE("JsonArray::add(T)") {
REQUIRE(str == array[0]); REQUIRE(str == array[0]);
} }
SECTION("should not duplicate const char*") {
array.add("world");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("should duplicate char*") {
array.add(const_cast<char*>("world"));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate std::string") {
array.add("world"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate serialized(const char*)") {
array.add(serialized("{}"));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized("{}"_s));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized("\0XX"_s));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString(" XX")),
});
}
} }
TEST_CASE("JsonArray::add<T>()") { TEST_CASE("JsonArray::add<T>()") {

View File

@@ -1,34 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
static void eraseString(std::string& str) {
char* p = const_cast<char*>(str.c_str());
while (*p)
*p++ = '*';
}
TEST_CASE("std::string") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("add()") {
std::string value("hello");
array.add(value);
eraseString(value);
REQUIRE("hello"_s == array[0]);
}
SECTION("operator[]") {
std::string value("world");
array.add("hello");
array[0] = value;
eraseString(value);
REQUIRE("world"_s == array[0]);
}
}

View File

@@ -59,15 +59,56 @@ TEST_CASE("JsonArray::operator[]") {
REQUIRE(false == array[0].is<int>()); REQUIRE(false == array[0].is<int>());
} }
SECTION("const char*") { SECTION("string literal") {
const char* str = "hello"; array[0] = "hello";
array[0] = str; REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(str == array[0].as<const char*>());
REQUIRE(true == array[0].is<const char*>()); REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>()); REQUIRE(false == array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
} }
SECTION("const char*") {
const char* str = "hello";
array[0] = str;
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("std::string") {
array[0] = "hello"_s;
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0] = vla;
REQUIRE(array[0] == "world"_s);
}
#endif
SECTION("nested array") { SECTION("nested array") {
JsonDocument doc2; JsonDocument doc2;
JsonArray arr2 = doc2.to<JsonArray>(); JsonArray arr2 = doc2.to<JsonArray>();
@@ -114,58 +155,11 @@ TEST_CASE("JsonArray::operator[]") {
REQUIRE(str == array[0]); REQUIRE(str == array[0]);
} }
SECTION("should not duplicate const char*") {
array[0] = "world";
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("should duplicate char*") {
array[0] = const_cast<char*>("world");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate std::string") {
array[0] = "world"_s;
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("array[0].to<JsonObject>()") { SECTION("array[0].to<JsonObject>()") {
JsonObject obj = array[0].to<JsonObject>(); JsonObject obj = array[0].to<JsonObject>();
REQUIRE(obj.isNull() == false); REQUIRE(obj.isNull() == false);
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0].set(vla);
REQUIRE("world"_s == array[0]);
}
SECTION("operator=(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0] = vla;
REQUIRE("world"_s == array[0]);
}
#endif
SECTION("Use a JsonVariant as index") { SECTION("Use a JsonVariant as index") {
array[0] = 1; array[0] = 1;
array[1] = 2; array[1] = 2;

View File

@@ -693,6 +693,15 @@ TEST_CASE("Filtering") {
"null", "null",
0, 0,
}, },
{
"NUL character in key",
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}",
"{\"x\\u0000a\":true}",
10,
DeserializationError::Ok,
"{\"x\\u0000a\":1}",
sizeofObject(1) + sizeofString("x?a"),
},
}; };
for (auto& tc : testCases) { for (auto& tc : testCases) {

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -322,10 +323,11 @@ TEST_CASE("deserialize JSON object") {
SECTION("NUL in keys") { SECTION("NUL in keys") {
DeserializationError err = DeserializationError err =
deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}"); deserializeJson(doc, "{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"x\\u0000a\":1,\"x\\u0000b\":2}"); REQUIRE(doc.as<std::string>() ==
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
} }
} }

View File

@@ -5,37 +5,60 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp" #include "Literals.hpp"
using ElementProxy = ArduinoJson::detail::ElementProxy<JsonDocument&>; using ElementProxy = ArduinoJson::detail::ElementProxy<JsonDocument&>;
TEST_CASE("ElementProxy::add()") { TEST_CASE("ElementProxy::add()") {
JsonDocument doc; SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add<JsonVariant>(); doc.add<JsonVariant>();
ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
SECTION("add(int)") { SECTION("integer") {
ep.add(42); ep.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]"); REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
} }
SECTION("add(const char*)") { SECTION("string literal") {
ep.add("world"); ep.add("world");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]"); REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
} }
SECTION("add(char[])") { SECTION("const char*") {
const char* s = "world";
ep.add(s);
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("char[]") {
char s[] = "world"; char s[] = "world";
ep.add(s); ep.add(s);
strcpy(s, "!!!!!"); strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]"); REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(vla)") { SECTION("VLA") {
size_t i = 8; size_t i = 8;
char vla[i]; char vla[i];
strcpy(vla, "world"); strcpy(vla, "world");
@@ -43,6 +66,10 @@ TEST_CASE("ElementProxy::add()") {
ep.add(vla); ep.add(vla);
REQUIRE(doc.as<std::string>() == "[[\"world\"]]"); REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
} }
#endif #endif
} }
@@ -50,7 +77,7 @@ TEST_CASE("ElementProxy::add()") {
TEST_CASE("ElementProxy::clear()") { TEST_CASE("ElementProxy::clear()") {
JsonDocument doc; JsonDocument doc;
doc.add<JsonVariant>(); doc.add<JsonVariant>();
ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
SECTION("size goes back to zero") { SECTION("size goes back to zero") {
ep.add(42); ep.add(42);
@@ -110,7 +137,7 @@ TEST_CASE("ElementProxy::operator==()") {
TEST_CASE("ElementProxy::remove()") { TEST_CASE("ElementProxy::remove()") {
JsonDocument doc; JsonDocument doc;
doc.add<JsonVariant>(); doc.add<JsonVariant>();
ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
SECTION("remove(int)") { SECTION("remove(int)") {
ep.add(1); ep.add(1);
@@ -157,7 +184,7 @@ TEST_CASE("ElementProxy::remove()") {
TEST_CASE("ElementProxy::set()") { TEST_CASE("ElementProxy::set()") {
JsonDocument doc; JsonDocument doc;
ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
SECTION("set(int)") { SECTION("set(int)") {
ep.set(42); ep.set(42);
@@ -195,7 +222,7 @@ TEST_CASE("ElementProxy::set()") {
TEST_CASE("ElementProxy::size()") { TEST_CASE("ElementProxy::size()") {
JsonDocument doc; JsonDocument doc;
doc.add<JsonVariant>(); doc.add<JsonVariant>();
ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
SECTION("returns 0") { SECTION("returns 0") {
REQUIRE(ep.size() == 0); REQUIRE(ep.size() == 0);
@@ -216,7 +243,7 @@ TEST_CASE("ElementProxy::size()") {
TEST_CASE("ElementProxy::operator[]") { TEST_CASE("ElementProxy::operator[]") {
JsonDocument doc; JsonDocument doc;
ElementProxy ep = doc[1]; const ElementProxy& ep = doc[1];
SECTION("set member") { SECTION("set member") {
ep["world"] = 42; ep["world"] = 42;
@@ -247,7 +274,7 @@ TEST_CASE("ElementProxy cast to JsonVariantConst") {
JsonDocument doc; JsonDocument doc;
doc[0] = "world"; doc[0] = "world";
const ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
JsonVariantConst var = ep; JsonVariantConst var = ep;
@@ -258,7 +285,7 @@ TEST_CASE("ElementProxy cast to JsonVariant") {
JsonDocument doc; JsonDocument doc;
doc[0] = "world"; doc[0] = "world";
ElementProxy ep = doc[0]; const ElementProxy& ep = doc[0];
JsonVariant var = ep; JsonVariant var = ep;

View File

@@ -14,27 +14,54 @@
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
using MemberProxy =
ArduinoJson::detail::MemberProxy<JsonDocument&, const char*>;
TEST_CASE("MemberProxy::add()") { TEST_CASE("MemberProxy::add()") {
JsonDocument doc; SpyingAllocator spy;
MemberProxy mp = doc["hello"]; JsonDocument doc(&spy);
const auto& mp = doc["hello"];
SECTION("add(int)") { SECTION("integer") {
mp.add(42); mp.add(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}"); REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
} }
SECTION("add(const char*)") { SECTION("string literal") {
mp.add("world"); mp.add("world");
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}"); REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("const char*") {
const char* temp = "world";
mp.add(temp);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("char[]") {
char temp[] = "world";
mp.add(temp);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("add(vla)") { SECTION("VLA") {
size_t i = 16; size_t i = 16;
char vla[i]; char vla[i];
strcpy(vla, "world"); strcpy(vla, "world");
@@ -42,13 +69,17 @@ TEST_CASE("MemberProxy::add()") {
mp.add(vla); mp.add(vla);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}"); REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
} }
#endif #endif
} }
TEST_CASE("MemberProxy::clear()") { TEST_CASE("MemberProxy::clear()") {
JsonDocument doc; JsonDocument doc;
MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
SECTION("size goes back to zero") { SECTION("size goes back to zero") {
mp.add(42); mp.add(42);
@@ -139,7 +170,7 @@ TEST_CASE("MemberProxy::operator|()") {
TEST_CASE("MemberProxy::remove()") { TEST_CASE("MemberProxy::remove()") {
JsonDocument doc; JsonDocument doc;
MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
SECTION("remove(int)") { SECTION("remove(int)") {
mp.add(1); mp.add(1);
@@ -186,7 +217,7 @@ TEST_CASE("MemberProxy::remove()") {
TEST_CASE("MemberProxy::set()") { TEST_CASE("MemberProxy::set()") {
JsonDocument doc; JsonDocument doc;
MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
SECTION("set(int)") { SECTION("set(int)") {
mp.set(42); mp.set(42);
@@ -223,7 +254,7 @@ TEST_CASE("MemberProxy::set()") {
TEST_CASE("MemberProxy::size()") { TEST_CASE("MemberProxy::size()") {
JsonDocument doc; JsonDocument doc;
MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
SECTION("returns 0") { SECTION("returns 0") {
REQUIRE(mp.size() == 0); REQUIRE(mp.size() == 0);
@@ -246,7 +277,7 @@ TEST_CASE("MemberProxy::size()") {
TEST_CASE("MemberProxy::operator[]") { TEST_CASE("MemberProxy::operator[]") {
JsonDocument doc; JsonDocument doc;
MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
SECTION("set member") { SECTION("set member") {
mp["world"] = 42; mp["world"] = 42;
@@ -265,7 +296,7 @@ TEST_CASE("MemberProxy cast to JsonVariantConst") {
JsonDocument doc; JsonDocument doc;
doc["hello"] = "world"; doc["hello"] = "world";
const MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
JsonVariantConst var = mp; JsonVariantConst var = mp;
@@ -276,7 +307,7 @@ TEST_CASE("MemberProxy cast to JsonVariant") {
JsonDocument doc; JsonDocument doc;
doc["hello"] = "world"; doc["hello"] = "world";
MemberProxy mp = doc["hello"]; const auto& mp = doc["hello"];
JsonVariant var = mp; JsonVariant var = mp;

View File

@@ -26,7 +26,7 @@ TEST_CASE("JsonDocument::add(T)") {
}); });
} }
SECTION("const char*") { SECTION("string literal") {
doc.add("hello"); doc.add("hello");
REQUIRE(doc.as<std::string>() == "[\"hello\"]"); REQUIRE(doc.as<std::string>() == "[\"hello\"]");
@@ -35,6 +35,17 @@ TEST_CASE("JsonDocument::add(T)") {
}); });
} }
SECTION("const char*") {
const char* value = "hello";
doc.add(value);
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("std::string") { SECTION("std::string") {
doc.add("example"_s); doc.add("example"_s);
doc.add("example"_s); doc.add("example"_s);

View File

@@ -1,54 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::containsKey()") {
JsonDocument doc;
SECTION("returns true on object") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when value is null") {
doc["hello"] = static_cast<const char*>(0);
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when key is a std::string") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello"_s) == true);
}
SECTION("returns false on object") {
doc["world"] = "hello";
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on array") {
doc.add("hello");
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on null") {
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("support JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
}

View File

@@ -12,49 +12,41 @@ TEST_CASE("Issue #1120") {
SECTION("MemberProxy<std::string>::isNull()") { SECTION("MemberProxy<std::string>::isNull()") {
SECTION("returns false") { SECTION("returns false") {
auto value = doc["contents"_s]; CHECK(doc["contents"_s].isNull() == false);
CHECK(value.isNull() == false);
} }
SECTION("returns true") { SECTION("returns true") {
auto value = doc["zontents"_s]; CHECK(doc["zontents"_s].isNull() == true);
CHECK(value.isNull() == true);
} }
} }
SECTION("ElementProxy<MemberProxy<const char*> >::isNull()") { SECTION("ElementProxy<MemberProxy<const char*> >::isNull()") {
SECTION("returns false") { // Issue #1120 SECTION("returns false") { // Issue #1120
auto value = doc["contents"][1]; CHECK(doc["contents"][1].isNull() == false);
CHECK(value.isNull() == false);
} }
SECTION("returns true") { SECTION("returns true") {
auto value = doc["contents"][2]; CHECK(doc["contents"][2].isNull() == true);
CHECK(value.isNull() == true);
} }
} }
SECTION("MemberProxy<ElementProxy<MemberProxy>, const char*>::isNull()") { SECTION("MemberProxy<ElementProxy<MemberProxy>, const char*>::isNull()") {
SECTION("returns false") { SECTION("returns false") {
auto value = doc["contents"][1]["module"]; CHECK(doc["contents"][1]["module"].isNull() == false);
CHECK(value.isNull() == false);
} }
SECTION("returns true") { SECTION("returns true") {
auto value = doc["contents"][1]["zodule"]; CHECK(doc["contents"][1]["zodule"].isNull() == true);
CHECK(value.isNull() == true);
} }
} }
SECTION("MemberProxy<ElementProxy<MemberProxy>, std::string>::isNull()") { SECTION("MemberProxy<ElementProxy<MemberProxy>, std::string>::isNull()") {
SECTION("returns false") { SECTION("returns false") {
auto value = doc["contents"][1]["module"_s]; CHECK(doc["contents"][1]["module"_s].isNull() == false);
CHECK(value.isNull() == false);
} }
SECTION("returns true") { SECTION("returns true") {
auto value = doc["contents"][1]["zodule"_s]; CHECK(doc["contents"][1]["zodule"_s].isNull() == true);
CHECK(value.isNull() == true);
} }
} }
} }

View File

@@ -20,11 +20,21 @@ TEST_CASE("JsonDocument::remove()") {
REQUIRE(doc.as<std::string>() == "[1,3]"); REQUIRE(doc.as<std::string>() == "[1,3]");
} }
SECTION("string literal") {
doc["a"] = 1;
doc["a\0b"_s] = 2;
doc["b"] = 3;
doc.remove("a\0b");
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"b\":3}");
}
SECTION("remove(const char *)") { SECTION("remove(const char *)") {
doc["a"] = 1; doc["a"] = 1;
doc["b"] = 2; doc["b"] = 2;
doc.remove("a"); doc.remove(static_cast<const char*>("a"));
REQUIRE(doc.as<std::string>() == "{\"b\":2}"); REQUIRE(doc.as<std::string>() == "{\"b\":2}");
} }

View File

@@ -11,6 +11,21 @@ TEST_CASE("JsonDocument::set()") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
SECTION("nullptr") {
doc.set(nullptr);
REQUIRE(doc.isNull());
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("integer&") {
int toto = 42;
doc.set(toto);
REQUIRE(doc.as<std::string>() == "42");
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("integer") { SECTION("integer") {
doc.set(42); doc.set(42);
@@ -18,13 +33,23 @@ TEST_CASE("JsonDocument::set()") {
REQUIRE(spy.log() == AllocatorLog{}); REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("const char*") { SECTION("string literal") {
doc.set("example"); doc.set("example");
REQUIRE(doc.as<const char*>() == "example"_s); REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{}); REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("const char*") {
const char* value = "example";
doc.set(value);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("std::string") { SECTION("std::string") {
doc.set("example"_s); doc.set("example"_s);

View File

@@ -5,6 +5,7 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp" #include "Literals.hpp"
TEST_CASE("JsonDocument::operator[]") { TEST_CASE("JsonDocument::operator[]") {
@@ -16,8 +17,16 @@ TEST_CASE("JsonDocument::operator[]") {
doc["abc\0d"_s] = "ABCD"; doc["abc\0d"_s] = "ABCD";
SECTION("const char*") { SECTION("const char*") {
const char* key = "abc";
REQUIRE(doc[key] == "ABC");
REQUIRE(cdoc[key] == "ABC");
}
SECTION("string literal") {
REQUIRE(doc["abc"] == "ABC"); REQUIRE(doc["abc"] == "ABC");
REQUIRE(cdoc["abc"] == "ABC"); REQUIRE(cdoc["abc"] == "ABC");
REQUIRE(doc["abc\0d"] == "ABCD");
REQUIRE(cdoc["abc\0d"] == "ABCD");
} }
SECTION("std::string") { SECTION("std::string") {
@@ -94,3 +103,65 @@ TEST_CASE("JsonDocument automatically promotes to array") {
REQUIRE(doc.as<std::string>() == "[null,null,2]"); REQUIRE(doc.as<std::string>() == "[null,null,2]");
} }
TEST_CASE("JsonDocument::operator[] key storage") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("string literal") {
doc["hello"] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("const char*") {
const char* key = "hello";
doc[key] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("char[]") {
char key[] = "hello";
doc[key] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("std::string") {
doc["hello"_s] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
doc[vla] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
#endif
}

View File

@@ -1,39 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonObject::containsKey()") {
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = 42;
SECTION("returns true only if key is present") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("returns false after remove()") {
obj.remove("hello");
REQUIRE(false == obj.containsKey("hello"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("key is a JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}

View File

@@ -158,7 +158,7 @@ TEST_CASE("JsonObject::operator[]") {
} }
SECTION("should duplicate a non-static JsonString key") { SECTION("should duplicate a non-static JsonString key") {
obj[JsonString("hello", JsonString::Copied)] = "world"; obj[JsonString("hello", false)] = "world";
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString("hello")), Allocate(sizeofString("hello")),
@@ -166,7 +166,7 @@ TEST_CASE("JsonObject::operator[]") {
} }
SECTION("should not duplicate a static JsonString key") { SECTION("should not duplicate a static JsonString key") {
obj[JsonString("hello", JsonString::Linked)] = "world"; obj[JsonString("hello", true)] = "world";
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
}); });

View File

@@ -1,40 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonObjectConst::containsKey()") {
JsonDocument doc;
doc["hello"] = 42;
auto obj = doc.as<JsonObjectConst>();
SECTION("supports const char*") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("supports std::string") {
REQUIRE(false == obj.containsKey("world"_s));
REQUIRE(true == obj.containsKey("hello"_s));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}

View File

@@ -184,7 +184,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long>() == 42L); REQUIRE(variant.as<long>() == 42L);
REQUIRE(variant.as<JsonString>() == "42"); REQUIRE(variant.as<JsonString>() == "42");
REQUIRE(variant.as<JsonString>().isLinked() == true); REQUIRE(variant.as<JsonString>().isStatic() == true);
} }
SECTION("set(\"hello\")") { SECTION("set(\"hello\")") {
@@ -207,7 +207,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<const char*>() == "4.2"_s); REQUIRE(variant.as<const char*>() == "4.2"_s);
REQUIRE(variant.as<std::string>() == "4.2"_s); REQUIRE(variant.as<std::string>() == "4.2"_s);
REQUIRE(variant.as<JsonString>() == "4.2"); REQUIRE(variant.as<JsonString>() == "4.2");
REQUIRE(variant.as<JsonString>().isLinked() == false); REQUIRE(variant.as<JsonString>().isStatic() == false);
} }
SECTION("set(\"true\")") { SECTION("set(\"true\")") {

View File

@@ -1,36 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariant::containsKey()") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("containsKey(const char*)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("containsKey(std::string)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
SECTION("containsKey(JsonVariant)") {
var["hello"] = "world";
var["key"] = "hello";
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
}
}

View File

@@ -17,6 +17,15 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
JsonDocument doc(&spy); JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>(); JsonVariant variant = doc.to<JsonVariant>();
SECTION("string literal") {
bool result = variant.set("hello\0world");
REQUIRE(result == true);
CHECK(variant ==
"hello"_s); // linked string cannot contain '\0' at the moment
CHECK(spy.log() == AllocatorLog{});
}
SECTION("const char*") { SECTION("const char*") {
char str[16]; char str[16];
@@ -25,8 +34,10 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
strcpy(str, "world"); strcpy(str, "world");
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{}); REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
} }
SECTION("(const char*)0") { SECTION("(const char*)0") {
@@ -34,6 +45,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant.isNull()); REQUIRE(variant.isNull());
REQUIRE(variant.as<const char*>() == nullptr);
REQUIRE(spy.log() == AllocatorLog{}); REQUIRE(spy.log() == AllocatorLog{});
} }
@@ -105,16 +117,14 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
#endif #endif
SECTION("std::string") { SECTION("std::string") {
std::string str; std::string str = "hello\0world"_s;
str = "hello";
bool result = variant.set(str); bool result = variant.set(str);
str.replace(0, 5, "world"); str.replace(0, 5, "world");
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy REQUIRE(variant == "hello\0world"_s); // stores by copy
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")), Allocate(sizeofString("hello?world")),
}); });
} }
@@ -122,7 +132,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
char str[16]; char str[16];
strcpy(str, "hello"); strcpy(str, "hello");
bool result = variant.set(JsonString(str, JsonString::Linked)); bool result = variant.set(JsonString(str, true));
strcpy(str, "world"); strcpy(str, "world");
REQUIRE(result == true); REQUIRE(result == true);
@@ -134,7 +144,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
char str[16]; char str[16];
strcpy(str, "hello"); strcpy(str, "hello");
bool result = variant.set(JsonString(str, JsonString::Copied)); bool result = variant.set(JsonString(str));
strcpy(str, "world"); strcpy(str, "world");
REQUIRE(result == true); REQUIRE(result == true);

View File

@@ -7,14 +7,8 @@
#include <catch.hpp> #include <catch.hpp>
#include <limits> #include <limits>
template <typename T> #include "Allocators.hpp"
void checkValue(T expected) { #include "Literals.hpp"
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(expected);
REQUIRE(expected == variant.as<T>());
}
template <typename T> template <typename T>
void checkReference(T& expected) { void checkReference(T& expected) {
@@ -39,27 +33,29 @@ void checkNumericType() {
} }
TEST_CASE("JsonVariant set()/get()") { TEST_CASE("JsonVariant set()/get()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
SECTION("SizeOfJsonInteger") { SECTION("SizeOfJsonInteger") {
REQUIRE(8 == sizeof(JsonInteger)); REQUIRE(8 == sizeof(JsonInteger));
} }
#endif #endif
SECTION("Null") { // /!\ Most test were moved to `JsonVariant/set.cpp`
checkValue<const char*>(NULL); // TODO: move the remaining tests too
}
SECTION("const char*") {
checkValue<const char*>("hello");
}
SECTION("std::string") {
checkValue<std::string>("hello");
}
SECTION("False") { SECTION("False") {
checkValue<bool>(false); variant.set(false);
REQUIRE(variant.as<bool>() == false);
REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("True") { SECTION("True") {
checkValue<bool>(true); variant.set(true);
REQUIRE(variant.as<bool>() == true);
REQUIRE(spy.log() == AllocatorLog{});
} }
SECTION("Double") { SECTION("Double") {
@@ -129,10 +125,12 @@ TEST_CASE("JsonVariant set()/get()") {
#endif #endif
SECTION("CanStoreObject") { SECTION("CanStoreObject") {
JsonDocument doc; JsonDocument doc2;
JsonObject object = doc.to<JsonObject>(); JsonObject object = doc2.to<JsonObject>();
checkValue<JsonObject>(object); variant.set(object);
REQUIRE(variant.is<JsonObject>());
REQUIRE(variant.as<JsonObject>() == object);
} }
} }

View File

@@ -1,41 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariantConst::containsKey()") {
JsonDocument doc;
doc["hello"] = "world";
JsonVariantConst var = doc.as<JsonVariant>();
SECTION("support const char*") {
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("support std::string") {
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == var.containsKey(vla));
}
#endif
SECTION("support JsonVariant") {
doc["key"] = "hello";
REQUIRE(var.containsKey(var["key"]) == true);
REQUIRE(var.containsKey(var["foo"]) == false);
}
}

View File

@@ -54,13 +54,22 @@ TEST_CASE("JsonVariantConst::operator[]") {
object["abc"_s] = "ABC"; object["abc"_s] = "ABC";
object["abc\0d"_s] = "ABCD"; object["abc\0d"_s] = "ABCD";
SECTION("supports const char*") { SECTION("string literal") {
REQUIRE(var["ab"] == "AB"_s); REQUIRE(var["ab"] == "AB"_s);
REQUIRE(var["abc"] == "ABC"_s); REQUIRE(var["abc"] == "ABC"_s);
REQUIRE(var["abc\0d"] == "ABCD"_s);
REQUIRE(var["def"].isNull()); REQUIRE(var["def"].isNull());
REQUIRE(var[0].isNull()); REQUIRE(var[0].isNull());
} }
SECTION("const char*") {
REQUIRE(var[static_cast<const char*>("ab")] == "AB"_s);
REQUIRE(var[static_cast<const char*>("abc")] == "ABC"_s);
REQUIRE(var[static_cast<const char*>("abc\0d")] == "ABC"_s);
REQUIRE(var[static_cast<const char*>("def")].isNull());
REQUIRE(var[static_cast<const char*>(0)].isNull());
}
SECTION("supports std::string") { SECTION("supports std::string") {
REQUIRE(var["ab"_s] == "AB"_s); REQUIRE(var["ab"_s] == "AB"_s);
REQUIRE(var["abc"_s] == "ABC"_s); REQUIRE(var["abc"_s] == "ABC"_s);

View File

@@ -1,44 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Numbers/FloatParts.hpp>
#include <catch.hpp>
using namespace ArduinoJson::detail;
TEST_CASE("FloatParts<double>") {
SECTION("1.7976931348623157E+308") {
FloatParts<double> parts(1.7976931348623157E+308);
REQUIRE(parts.integral == 1);
REQUIRE(parts.decimal == 797693135);
REQUIRE(parts.decimalPlaces == 9);
REQUIRE(parts.exponent == 308);
}
SECTION("4.94065645841247e-324") {
FloatParts<double> parts(4.94065645841247e-324);
REQUIRE(parts.integral == 4);
REQUIRE(parts.decimal == 940656458);
REQUIRE(parts.decimalPlaces == 9);
REQUIRE(parts.exponent == -324);
}
}
TEST_CASE("FloatParts<float>") {
SECTION("3.4E+38") {
FloatParts<float> parts(3.4E+38f);
REQUIRE(parts.integral == 3);
REQUIRE(parts.decimal == 4);
REQUIRE(parts.decimalPlaces == 1);
REQUIRE(parts.exponent == 38);
}
SECTION("1.17549435e38") {
FloatParts<float> parts(1.17549435e-38f);
REQUIRE(parts.integral == 1);
REQUIRE(parts.decimal == 175494);
REQUIRE(parts.decimalPlaces == 6);
REQUIRE(parts.exponent == -38);
}
}

View File

@@ -13,7 +13,7 @@ TEST_CASE("JsonString") {
CHECK(s.isNull() == true); CHECK(s.isNull() == true);
CHECK(s.c_str() == 0); CHECK(s.c_str() == 0);
CHECK(s.isLinked() == true); CHECK(s.isStatic() == true);
CHECK(s == JsonString()); CHECK(s == JsonString());
CHECK(s != ""); CHECK(s != "");
} }
@@ -96,7 +96,7 @@ TEST_CASE("JsonString") {
JsonString s("hello world", 5); JsonString s("hello world", 5);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == true); CHECK(s.isStatic() == false);
CHECK(s == "hello"); CHECK(s == "hello");
CHECK(s != "hello world"); CHECK(s != "hello world");
} }

View File

@@ -5,6 +5,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson/Strings/IsString.hpp> #include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp> #include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp> #include <catch.hpp>
@@ -16,27 +17,36 @@ using ArduinoJson::JsonString;
using namespace ArduinoJson::detail; using namespace ArduinoJson::detail;
TEST_CASE("adaptString()") { TEST_CASE("adaptString()") {
SECTION("string literal") {
auto s = adaptString("bravo\0alpha");
CHECK(s.isNull() == false);
CHECK(s.size() == 11);
CHECK(s.isStatic() == true);
}
SECTION("null const char*") { SECTION("null const char*") {
auto s = adaptString(static_cast<const char*>(0)); auto s = adaptString(static_cast<const char*>(0));
CHECK(s.isNull() == true); CHECK(s.isNull() == true);
CHECK(s.size() == 0); CHECK(s.size() == 0);
CHECK(s.isLinked() == true);
} }
SECTION("non-null const char*") { SECTION("non-null const char*") {
auto s = adaptString("bravo"); const char* p = "bravo";
auto s = adaptString(p);
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == true); CHECK(s.isStatic() == false);
CHECK(s.data() == p);
} }
SECTION("null const char* + size") { SECTION("null const char* + size") {
auto s = adaptString(static_cast<const char*>(0), 10); auto s = adaptString(static_cast<const char*>(0), 10);
CHECK(s.isNull() == true); CHECK(s.isNull() == true);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("non-null const char* + size") { SECTION("non-null const char* + size") {
@@ -44,7 +54,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("null Flash string") { SECTION("null Flash string") {
@@ -52,7 +62,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == true); CHECK(s.isNull() == true);
CHECK(s.size() == 0); CHECK(s.size() == 0);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("non-null Flash string") { SECTION("non-null Flash string") {
@@ -60,7 +70,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("std::string") { SECTION("std::string") {
@@ -69,7 +79,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("Arduino String") { SECTION("Arduino String") {
@@ -78,7 +88,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("custom_string") { SECTION("custom_string") {
@@ -87,25 +97,25 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
SECTION("JsonString linked") { SECTION("JsonString linked") {
JsonString orig("hello", JsonString::Ownership::Linked); JsonString orig("hello", true);
auto s = adaptString(orig); auto s = adaptString(orig);
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == true); CHECK(s.isStatic() == true);
} }
SECTION("JsonString copied") { SECTION("JsonString copied") {
JsonString orig("hello", JsonString::Ownership::Copied); JsonString orig("hello", false);
auto s = adaptString(orig); auto s = adaptString(orig);
CHECK(s.isNull() == false); CHECK(s.isNull() == false);
CHECK(s.size() == 5); CHECK(s.size() == 5);
CHECK(s.isLinked() == false); CHECK(s.isStatic() == false);
} }
} }

View File

@@ -211,6 +211,23 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_enum<bool>::value == false); CHECK(is_enum<bool>::value == false);
CHECK(is_enum<double>::value == false); CHECK(is_enum<double>::value == false);
} }
SECTION("remove_cv") {
CHECK(is_same<remove_cv_t<const int>, int>::value);
CHECK(is_same<remove_cv_t<volatile int>, int>::value);
CHECK(is_same<remove_cv_t<const volatile int>, int>::value);
CHECK(is_same<remove_cv_t<int>, int>::value);
CHECK(is_same<remove_cv_t<decltype("toto")>, decltype("toto")>::value);
}
SECTION("decay") {
CHECK(is_same<decay_t<int>, int>::value);
CHECK(is_same<decay_t<int&>, int>::value);
CHECK(is_same<decay_t<int&&>, int>::value);
CHECK(is_same<decay_t<int[]>, int*>::value);
CHECK(is_same<decay_t<int[10]>, int*>::value);
CHECK(is_same<decay_t<decltype("toto")>, const char*>::value);
}
} }
TEST_CASE("is_std_string") { TEST_CASE("is_std_string") {

View File

@@ -139,7 +139,8 @@ TEST_CASE("serialize MsgPack value") {
SECTION("str 32") { SECTION("str 32") {
std::string shortest(65536, '?'); 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);
} }
SECTION("serialized(const char*)") { SECTION("serialized(const char*)") {

View File

@@ -1,7 +1,7 @@
version: "7.2.1" version: "7.3.0"
description: >- description: >-
A simple and efficient JSON library for embedded C++. A simple and efficient JSON library for embedded C++.
★ 6739 stars on GitHub! ★ 6785 stars on GitHub!
Supports serialization, deserialization, MessagePack, streams, filtering, and more. Supports serialization, deserialization, MessagePack, streams, filtering, and more.
Fully tested and documented. Fully tested and documented.
url: https://arduinojson.org/ url: https://arduinojson.org/

View File

@@ -1,13 +1,13 @@
{ {
"name": "ArduinoJson", "name": "ArduinoJson",
"keywords": "json, rest, http, web", "keywords": "json, rest, http, web",
"description": "A simple and efficient JSON library for embedded C++. ⭐ 6739 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.", "description": "A simple and efficient JSON library for embedded C++. ⭐ 6785 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
"homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json", "homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git" "url": "https://github.com/bblanchon/ArduinoJson.git"
}, },
"version": "7.2.1", "version": "7.3.0",
"authors": { "authors": {
"name": "Benoit Blanchon", "name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr" "url": "https://blog.benoitblanchon.fr"

View File

@@ -1,9 +1,9 @@
name=ArduinoJson name=ArduinoJson
version=7.2.1 version=7.3.0
author=Benoit Blanchon <blog.benoitblanchon.fr> author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr> maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++. sentence=A simple and efficient JSON library for embedded C++.
paragraph=⭐ 6739 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented. paragraph=⭐ 6785 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.
category=Data Processing category=Data Processing
url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties
architectures=* architectures=*

View File

@@ -19,10 +19,10 @@ class ArrayData : public CollectionData {
} }
template <typename T> template <typename T>
bool addValue(T&& value, ResourceManager* resources); bool addValue(const T& value, ResourceManager* resources);
template <typename T> template <typename T>
static bool addValue(ArrayData* array, T&& value, static bool addValue(ArrayData* array, const T& value,
ResourceManager* resources) { ResourceManager* resources) {
if (!array) if (!array)
return false; return false;

View File

@@ -57,13 +57,13 @@ inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
} }
template <typename T> template <typename T>
inline bool ArrayData::addValue(T&& value, ResourceManager* resources) { inline bool ArrayData::addValue(const T& value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(resources != nullptr); ARDUINOJSON_ASSERT(resources != nullptr);
auto slot = resources->allocVariant(); auto slot = resources->allocVariant();
if (!slot) if (!slot)
return false; return false;
JsonVariant variant(slot.ptr(), resources); JsonVariant variant(slot.ptr(), resources);
if (!variant.set(detail::forward<T>(value))) { if (!variant.set(value)) {
resources->freeVariant(slot); resources->freeVariant(slot);
return false; return false;
} }

View File

@@ -15,13 +15,18 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
public VariantOperators<ElementProxy<TUpstream>> { public VariantOperators<ElementProxy<TUpstream>> {
friend class VariantAttorney; friend class VariantAttorney;
friend class VariantRefBase<ElementProxy<TUpstream>>;
template <typename, typename>
friend class MemberProxy;
template <typename>
friend class ElementProxy;
public: public:
ElementProxy(TUpstream upstream, size_t index) ElementProxy(TUpstream upstream, size_t index)
: upstream_(upstream), index_(index) {} : upstream_(upstream), index_(index) {}
ElementProxy(const ElementProxy& src)
: upstream_(src.upstream_), index_(src.index_) {}
ElementProxy& operator=(const ElementProxy& src) { ElementProxy& operator=(const ElementProxy& src) {
this->set(src); this->set(src);
return *this; return *this;
@@ -40,6 +45,11 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
} }
private: private:
// clang-format off
ElementProxy(const ElementProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/
: upstream_(src.upstream_), index_(src.index_) {}
// clang-format on
ResourceManager* getResourceManager() const { ResourceManager* getResourceManager() const {
return VariantAttorney::getResourceManager(upstream_); return VariantAttorney::getResourceManager(upstream_);
} }

View File

@@ -43,16 +43,18 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Appends a new (empty) element to the array. // Appends a new (empty) element to the array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T, detail::enable_if_t<
detail::enable_if_t<!detail::is_same<T, JsonVariant>::value, T> add() const { !detail::is_same<T, JsonVariant>::value, int> = 0>
T add() const {
return add<JsonVariant>().to<T>(); return add<JsonVariant>().to<T>();
} }
// Appends a new (null) element to the array. // Appends a new (null) element to the array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T, detail::enable_if_t<
detail::enable_if_t<detail::is_same<T, JsonVariant>::value, T> add() const { detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() const {
return JsonVariant(detail::ArrayData::addElement(data_, resources_), return JsonVariant(detail::ArrayData::addElement(data_, resources_),
resources_); resources_);
} }
@@ -66,7 +68,8 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Appends a value to the array. // Appends a value to the array.
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T,
detail::enable_if_t<!detail::is_const<T>::value, int> = 0>
bool add(T* value) const { bool add(T* value) const {
return detail::ArrayData::addValue(data_, value, resources_); return detail::ArrayData::addValue(data_, value, resources_);
} }
@@ -114,9 +117,9 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Removes the element at the specified index. // Removes the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/remove/ // https://arduinojson.org/v7/api/jsonarray/remove/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value> remove( detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
TVariant variant) const { void remove(const TVariant& variant) const {
if (variant.template is<size_t>()) if (variant.template is<size_t>())
remove(variant.template as<size_t>()); remove(variant.template as<size_t>());
} }
@@ -129,21 +132,19 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Gets or sets the element at the specified index. // Gets or sets the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/subscript/ // https://arduinojson.org/v7/api/jsonarray/subscript/
template <typename T> template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
detail::ElementProxy<JsonArray>> detail::ElementProxy<JsonArray> operator[](T index) const {
operator[](T index) const {
return {*this, size_t(index)}; return {*this, size_t(index)};
} }
// Gets or sets the element at the specified index. // Gets or sets the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/subscript/ // https://arduinojson.org/v7/api/jsonarray/subscript/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
detail::ElementProxy<JsonArray>> detail::ElementProxy<JsonArray> operator[](const TVariant& variant) const {
operator[](const TVariant& variant) const {
if (variant.template is<size_t>()) if (variant.template is<size_t>())
return operator[](variant.template as<size_t>()); return {*this, variant.template as<size_t>()};
else else
return {*this, size_t(-1)}; return {*this, size_t(-1)};
} }

View File

@@ -45,9 +45,9 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// Returns the element at the specified index. // Returns the element at the specified index.
// https://arduinojson.org/v7/api/jsonarrayconst/subscript/ // https://arduinojson.org/v7/api/jsonarrayconst/subscript/
template <typename T> template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, JsonVariantConst> detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
operator[](T index) const { JsonVariantConst operator[](T index) const {
return JsonVariantConst( return JsonVariantConst(
detail::ArrayData::getElement(data_, size_t(index), resources_), detail::ArrayData::getElement(data_, size_t(index), resources_),
resources_); resources_);
@@ -55,9 +55,9 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// Returns the element at the specified index. // Returns the element at the specified index.
// https://arduinojson.org/v7/api/jsonarrayconst/subscript/ // https://arduinojson.org/v7/api/jsonarrayconst/subscript/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst> detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
operator[](const TVariant& variant) const { JsonVariantConst operator[](const TVariant& variant) const {
if (variant.template is<size_t>()) if (variant.template is<size_t>())
return operator[](variant.template as<size_t>()); return operator[](variant.template as<size_t>());
else else

View File

@@ -11,27 +11,26 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Copies a value to a JsonVariant. // Copies a value to a JsonVariant.
// This is a degenerated form of copyArray() to stop the recursion. // This is a degenerated form of copyArray() to stop the recursion.
template <typename T> template <typename T, detail::enable_if_t<!detail::is_array<T>::value, int> = 0>
inline detail::enable_if_t<!detail::is_array<T>::value, bool> copyArray( inline bool copyArray(const T& src, JsonVariant dst) {
const T& src, JsonVariant dst) {
return dst.set(src); return dst.set(src);
} }
// Copies values from an array to a JsonArray or a JsonVariant. // Copies values from an array to a JsonArray or a JsonVariant.
// https://arduinojson.org/v7/api/misc/copyarray/ // https://arduinojson.org/v7/api/misc/copyarray/
template <typename T, size_t N, typename TDestination> template <typename T, size_t N, typename TDestination,
inline detail::enable_if_t< detail::enable_if_t<
!detail::is_base_of<JsonDocument, TDestination>::value, bool> !detail::is_base_of<JsonDocument, TDestination>::value, int> = 0>
copyArray(T (&src)[N], const TDestination& dst) { inline bool copyArray(T (&src)[N], const TDestination& dst) {
return copyArray(src, N, dst); return copyArray(src, N, dst);
} }
// Copies values from an array to a JsonArray or a JsonVariant. // Copies values from an array to a JsonArray or a JsonVariant.
// https://arduinojson.org/v7/api/misc/copyarray/ // https://arduinojson.org/v7/api/misc/copyarray/
template <typename T, typename TDestination> template <typename T, typename TDestination,
inline detail::enable_if_t< detail::enable_if_t<
!detail::is_base_of<JsonDocument, TDestination>::value, bool> !detail::is_base_of<JsonDocument, TDestination>::value, int> = 0>
copyArray(const T* src, size_t len, const TDestination& dst) { inline bool copyArray(const T* src, size_t len, const TDestination& dst) {
bool ok = true; bool ok = true;
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
ok &= copyArray(src[i], dst.template add<JsonVariant>()); ok &= copyArray(src[i], dst.template add<JsonVariant>());
@@ -62,9 +61,8 @@ inline bool copyArray(const T* src, size_t len, JsonDocument& dst) {
// Copies a value from a JsonVariant. // Copies a value from a JsonVariant.
// This is a degenerated form of copyArray() to stop the recursion. // This is a degenerated form of copyArray() to stop the recursion.
template <typename T> template <typename T, detail::enable_if_t<!detail::is_array<T>::value, int> = 0>
inline detail::enable_if_t<!detail::is_array<T>::value, size_t> copyArray( inline size_t copyArray(JsonVariantConst src, T& dst) {
JsonVariantConst src, T& dst) {
dst = src.as<T>(); dst = src.as<T>();
return 1; return 1;
} }
@@ -102,11 +100,12 @@ inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) {
// Copies values from a JsonDocument to an array. // Copies values from a JsonDocument to an array.
// https://arduinojson.org/v7/api/misc/copyarray/ // https://arduinojson.org/v7/api/misc/copyarray/
template <typename TSource, typename T> template <
inline detail::enable_if_t<detail::is_array<T>::value && typename TSource, typename T,
detail::enable_if_t<detail::is_array<T>::value &&
detail::is_base_of<JsonDocument, TSource>::value, detail::is_base_of<JsonDocument, TSource>::value,
size_t> int> = 0>
copyArray(const TSource& src, T& dst) { inline size_t copyArray(const TSource& src, T& dst) {
return copyArray(src.template as<JsonArrayConst>(), dst); return copyArray(src.template as<JsonArrayConst>(), dst);
} }

View File

@@ -11,7 +11,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// The default reader is a simple wrapper for Readers that are not copiable // The default reader is a simple wrapper for Readers that are not copyable
template <typename TSource, typename Enable = void> template <typename TSource, typename Enable = void>
struct Reader { struct Reader {
public: public:

View File

@@ -55,10 +55,11 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader,
return err; return err;
} }
template <template <typename> class TDeserializer, typename TDestination, template <
template <typename> class TDeserializer, typename TDestination,
typename TStream, typename... Args, typename TStream, typename... Args,
typename = enable_if_t< // issue #1897 enable_if_t< // issue #1897
!is_integral<typename first_or_void<Args...>::type>::value>> !is_integral<typename first_or_void<Args...>::type>::value, int> = 0>
DeserializationError deserialize(TDestination&& dst, TStream&& input, DeserializationError deserialize(TDestination&& dst, TStream&& input,
Args... args) { Args... args) {
return doDeserialize<TDeserializer>( return doDeserialize<TDeserializer>(
@@ -68,7 +69,7 @@ DeserializationError deserialize(TDestination&& dst, TStream&& input,
template <template <typename> class TDeserializer, typename TDestination, template <template <typename> class TDeserializer, typename TDestination,
typename TChar, typename Size, typename... Args, typename TChar, typename Size, typename... Args,
typename = enable_if_t<is_integral<Size>::value>> enable_if_t<is_integral<Size>::value, int> = 0>
DeserializationError deserialize(TDestination&& dst, TChar* input, DeserializationError deserialize(TDestination&& dst, TChar* input,
Size inputSize, Args... args) { Size inputSize, Args... args) {
return doDeserialize<TDeserializer>(dst, makeReader(input, size_t(inputSize)), return doDeserialize<TDeserializer>(dst, makeReader(input, size_t(inputSize)),

View File

@@ -36,14 +36,15 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
} }
// Construct from variant, array, or object // Construct from variant, array, or object
template <typename T> template <typename T,
JsonDocument(
const T& src, Allocator* alloc = detail::DefaultAllocator::instance(),
detail::enable_if_t<detail::IsVariant<T>::value || detail::enable_if_t<detail::IsVariant<T>::value ||
detail::is_same<T, JsonArray>::value || detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value || detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value || detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value>* = 0) detail::is_same<T, JsonObjectConst>::value,
int> = 0>
JsonDocument(const T& src,
Allocator* alloc = detail::DefaultAllocator::instance())
: JsonDocument(alloc) { : JsonDocument(alloc) {
set(src); set(src);
} }
@@ -136,15 +137,17 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Replaces the root with the specified value. // Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/ // https://arduinojson.org/v7/api/jsondocument/set/
template <typename T> template <
detail::enable_if_t<!detail::is_base_of<JsonDocument, T>::value, bool> set( typename T,
const T& src) { detail::enable_if_t<!detail::is_base_of<JsonDocument, T>::value, int> = 0>
bool set(const T& src) {
return to<JsonVariant>().set(src); return to<JsonVariant>().set(src);
} }
// Replaces the root with the specified value. // Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/ // 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) { bool set(TChar* src) {
return to<JsonVariant>().set(src); return to<JsonVariant>().set(src);
} }
@@ -167,64 +170,67 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/ // https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey( bool containsKey(const TString& key) const {
const TString& key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0; return data_.getMember(detail::adaptString(key), &resources_) != 0;
} }
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsondocument/containskey/ // https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use doc[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey( bool containsKey(const TVariant& key) const {
const TVariant& key) const {
return containsKey(key.template as<const char*>()); return containsKey(key.template as<const char*>());
} }
// Gets or sets a root object's member. // Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
detail::MemberProxy<JsonDocument&, TString>> detail::MemberProxy<JsonDocument&, detail::AdaptedString<TString>> operator[](
operator[](const TString& key) { const TString& key) {
return {*this, key}; return {*this, detail::adaptString(key)};
} }
// Gets or sets a root object's member. // Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value, detail::enable_if_t<detail::IsString<TChar*>::value &&
detail::MemberProxy<JsonDocument&, TChar*>> !detail::is_const<TChar>::value,
operator[](TChar* key) { int> = 0>
return {*this, key}; detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>> operator[](
TChar* key) {
return {*this, detail::adaptString(key)};
} }
// Gets a root object's member. // Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, JsonVariantConst> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
operator[](const TString& key) const { JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst( return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_); data_.getMember(detail::adaptString(key), &resources_), &resources_);
} }
// Gets a root object's member. // Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst> detail::enable_if_t<detail::IsString<TChar*>::value &&
operator[](TChar* key) const { !detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst( return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_); data_.getMember(detail::adaptString(key), &resources_), &resources_);
} }
// Gets or sets a root array's element. // Gets or sets a root array's element.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename T> template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
detail::ElementProxy<JsonDocument&>> detail::ElementProxy<JsonDocument&> operator[](T index) {
operator[](T index) {
return {*this, size_t(index)}; return {*this, size_t(index)};
} }
@@ -236,9 +242,9 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets or sets a root object's member. // Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst> detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
operator[](const TVariant& key) const { JsonVariantConst operator[](const TVariant& key) const {
if (key.template is<JsonString>()) if (key.template is<JsonString>())
return operator[](key.template as<JsonString>()); return operator[](key.template as<JsonString>());
if (key.template is<size_t>()) if (key.template is<size_t>())
@@ -249,16 +255,18 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Appends a new (empty) element to the root array. // Appends a new (empty) element to the root array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename T> template <typename T, detail::enable_if_t<
detail::enable_if_t<!detail::is_same<T, JsonVariant>::value, T> add() { !detail::is_same<T, JsonVariant>::value, int> = 0>
T add() {
return add<JsonVariant>().to<T>(); return add<JsonVariant>().to<T>();
} }
// Appends a new (null) element to the root array. // Appends a new (null) element to the root array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename T> template <typename T, detail::enable_if_t<
detail::enable_if_t<detail::is_same<T, JsonVariant>::value, T> add() { detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() {
return JsonVariant(data_.addElement(&resources_), &resources_); return JsonVariant(data_.addElement(&resources_), &resources_);
} }
@@ -271,41 +279,46 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Appends a value to the root array. // Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/ // 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) { bool add(TChar* value) {
return data_.addValue(value, &resources_); return data_.addValue(value, &resources_);
} }
// Removes an element of the root array. // Removes an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
template <typename T> template <typename T,
detail::enable_if_t<detail::is_integral<T>::value> remove(T index) { detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
void remove(T index) {
detail::VariantData::removeElement(getData(), size_t(index), detail::VariantData::removeElement(getData(), size_t(index),
getResourceManager()); getResourceManager());
} }
// Removes a member of the root object. // Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value> remove(TChar* key) { detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
void remove(TChar* key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key), detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager()); getResourceManager());
} }
// Removes a member of the root object. // Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value> remove( detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
const TString& key) { void remove(const TString& key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key), detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager()); getResourceManager());
} }
// Removes a member of the root object or an element of the root array. // Removes a member of the root object or an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value> remove( detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
const TVariant& key) { void remove(const TVariant& key) {
if (key.template is<const char*>()) if (key.template is<const char*>())
remove(key.template as<const char*>()); remove(key.template as<const char*>());
if (key.template is<size_t>()) if (key.template is<size_t>())

View File

@@ -270,10 +270,10 @@ class JsonDeserializer {
JsonString key = stringBuilder_.str(); JsonString key = stringBuilder_.str();
TFilter memberFilter = filter[key.c_str()]; TFilter memberFilter = filter[key];
if (memberFilter.allow()) { if (memberFilter.allow()) {
auto member = object.getMember(adaptString(key.c_str()), resources_); auto member = object.getMember(adaptString(key), resources_);
if (!member) { if (!member) {
// Save key in memory pool. // Save key in memory pool.
auto savedKey = stringBuilder_.save(); auto savedKey = stringBuilder_.save();
@@ -697,10 +697,11 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a JSON input, filters, and puts the result in a JsonDocument. // Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/json/deserializejson/ // https://arduinojson.org/v7/api/json/deserializejson/
template <typename TDestination, typename... Args> template <typename TDestination, typename... Args,
detail::enable_if_t<detail::is_deserialize_destination<TDestination>::value, detail::enable_if_t<
DeserializationError> detail::is_deserialize_destination<TDestination>::value, int> = 0>
deserializeJson(TDestination&& dst, Args&&... args) { inline DeserializationError deserializeJson(TDestination&& dst,
Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst), return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...); detail::forward<Args>(args)...);
@@ -708,10 +709,11 @@ deserializeJson(TDestination&& dst, Args&&... args) {
// Parses a JSON input, filters, and puts the result in a JsonDocument. // Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/json/deserializejson/ // https://arduinojson.org/v7/api/json/deserializejson/
template <typename TDestination, typename TChar, typename... Args> template <typename TDestination, typename TChar, typename... Args,
detail::enable_if_t<detail::is_deserialize_destination<TDestination>::value, detail::enable_if_t<
DeserializationError> detail::is_deserialize_destination<TDestination>::value, int> = 0>
deserializeJson(TDestination&& dst, TChar* input, Args&&... args) { inline DeserializationError deserializeJson(TDestination&& dst, TChar* input,
Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst), return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
input, detail::forward<Args>(args)...); input, detail::forward<Args>(args)...);

View File

@@ -129,9 +129,10 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Produces a minified JSON document. // Produces a minified JSON document.
// https://arduinojson.org/v7/api/json/serializejson/ // https://arduinojson.org/v7/api/json/serializejson/
template <typename TDestination> template <
detail::enable_if_t<!detail::is_pointer<TDestination>::value, size_t> typename TDestination,
serializeJson(JsonVariantConst source, TDestination& destination) { detail::enable_if_t<!detail::is_pointer<TDestination>::value, int> = 0>
size_t serializeJson(JsonVariantConst source, TDestination& destination) {
using namespace detail; using namespace detail;
return serialize<JsonSerializer>(source, destination); return serialize<JsonSerializer>(source, destination);
} }
@@ -152,10 +153,10 @@ inline size_t measureJson(JsonVariantConst source) {
} }
#if ARDUINOJSON_ENABLE_STD_STREAM #if ARDUINOJSON_ENABLE_STD_STREAM
template <typename T> template <typename T,
inline detail::enable_if_t<detail::is_convertible<T, JsonVariantConst>::value, detail::enable_if_t<
std::ostream&> detail::is_convertible<T, JsonVariantConst>::value, int> = 0>
operator<<(std::ostream& os, const T& source) { inline std::ostream& operator<<(std::ostream& os, const T& source) {
serializeJson(source, os); serializeJson(source, os);
return os; return os;
} }

View File

@@ -83,9 +83,11 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Produces JsonDocument to create a prettified JSON document. // Produces JsonDocument to create a prettified JSON document.
// https://arduinojson.org/v7/api/json/serializejsonpretty/ // https://arduinojson.org/v7/api/json/serializejsonpretty/
template <typename TDestination> template <
detail::enable_if_t<!detail::is_pointer<TDestination>::value, size_t> typename TDestination,
serializeJsonPretty(JsonVariantConst source, TDestination& destination) { detail::enable_if_t<!detail::is_pointer<TDestination>::value, int> = 0>
inline size_t serializeJsonPretty(JsonVariantConst source,
TDestination& destination) {
using namespace ArduinoJson::detail; using namespace ArduinoJson::detail;
return serialize<PrettyJsonSerializer>(source, destination); return serialize<PrettyJsonSerializer>(source, destination);
} }

View File

@@ -55,7 +55,7 @@ class StringBuffer {
JsonString str() const { JsonString str() const {
ARDUINOJSON_ASSERT(node_ != nullptr); ARDUINOJSON_ASSERT(node_ != nullptr);
return JsonString(node_->data, node_->length, JsonString::Copied); return JsonString(node_->data, node_->length);
} }
private: private:

View File

@@ -68,7 +68,7 @@ class StringBuilder {
JsonString str() const { JsonString str() const {
ARDUINOJSON_ASSERT(node_ != nullptr); ARDUINOJSON_ASSERT(node_ != nullptr);
node_->data[size_] = 0; node_->data[size_] = 0;
return JsonString(node_->data, size_, JsonString::Copied); return JsonString(node_->data, size_);
} }
private: private:

View File

@@ -1,63 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/integer.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class VariantSlot;
using SlotId = uint_t<ARDUINOJSON_SLOT_ID_SIZE * 8>;
using SlotCount = SlotId;
const SlotId NULL_SLOT = SlotId(-1);
class SlotWithId {
public:
SlotWithId() : slot_(nullptr), id_(NULL_SLOT) {}
SlotWithId(VariantSlot* slot, SlotId id) : slot_(slot), id_(id) {
ARDUINOJSON_ASSERT((slot == nullptr) == (id == NULL_SLOT));
}
SlotId id() const {
return id_;
}
operator VariantSlot*() {
return slot_;
}
VariantSlot* operator->() {
ARDUINOJSON_ASSERT(slot_ != nullptr);
return slot_;
}
private:
VariantSlot* slot_;
SlotId id_;
};
class VariantPool {
public:
void create(SlotCount cap, Allocator* allocator);
void destroy(Allocator* allocator);
SlotWithId allocSlot();
VariantSlot* getSlot(SlotId id) const;
void clear();
void shrinkToFit(Allocator*);
SlotCount usage() const;
static SlotCount bytesToSlots(size_t);
static size_t slotsToBytes(SlotCount);
private:
SlotCount capacity_;
SlotCount usage_;
VariantSlot* slots_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -1,81 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/VariantPool.hpp>
#include <ArduinoJson/Variant/VariantSlot.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
inline void VariantPool::create(SlotCount cap, Allocator* allocator) {
ARDUINOJSON_ASSERT(cap > 0);
slots_ =
reinterpret_cast<VariantSlot*>(allocator->allocate(slotsToBytes(cap)));
capacity_ = slots_ ? cap : 0;
usage_ = 0;
}
inline void VariantPool::destroy(Allocator* allocator) {
if (slots_)
allocator->deallocate(slots_);
slots_ = nullptr;
capacity_ = 0;
usage_ = 0;
}
inline void VariantPool::shrinkToFit(Allocator* allocator) {
auto newSlots = reinterpret_cast<VariantSlot*>(
allocator->reallocate(slots_, slotsToBytes(usage_)));
if (newSlots) {
slots_ = newSlots;
capacity_ = usage_;
}
}
inline SlotWithId VariantPool::allocSlot() {
if (!slots_)
return {};
if (usage_ >= capacity_)
return {};
auto index = usage_++;
auto slot = &slots_[index];
return {new (slot) VariantSlot, SlotId(index)};
}
inline VariantSlot* VariantPool::getSlot(SlotId id) const {
ARDUINOJSON_ASSERT(id < usage_);
return &slots_[id];
}
inline SlotCount VariantPool::usage() const {
return usage_;
}
inline void VariantPool::clear() {
usage_ = 0;
}
inline SlotCount VariantPool::bytesToSlots(size_t n) {
return static_cast<SlotCount>(n / sizeof(VariantSlot));
}
inline size_t VariantPool::slotsToBytes(SlotCount n) {
return n * sizeof(VariantSlot);
}
inline SlotWithId VariantPoolList::allocFromFreeList() {
ARDUINOJSON_ASSERT(freeList_ != NULL_SLOT);
auto id = freeList_;
auto slot = getSlot(freeList_);
freeList_ = slot->next();
return {new (slot) VariantSlot, id};
}
inline void VariantPoolList::freeSlot(SlotWithId slot) {
slot->setNext(freeList_);
freeList_ = slot.id();
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -1,189 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/VariantPool.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
using PoolCount = SlotId;
class VariantPoolList {
public:
VariantPoolList() = default;
~VariantPoolList() {
ARDUINOJSON_ASSERT(count_ == 0);
}
friend void swap(VariantPoolList& a, VariantPoolList& b) {
bool aUsedPreallocated = a.pools_ == a.preallocatedPools_;
bool bUsedPreallocated = b.pools_ == b.preallocatedPools_;
// Who is using preallocated pools?
if (aUsedPreallocated && bUsedPreallocated) {
// both of us => swap preallocated pools
for (PoolCount i = 0; i < ARDUINOJSON_INITIAL_POOL_COUNT; i++)
swap_(a.preallocatedPools_[i], b.preallocatedPools_[i]);
} else if (bUsedPreallocated) {
// only b => copy b's preallocated pools and give him a's pointer
for (PoolCount i = 0; i < b.count_; i++)
a.preallocatedPools_[i] = b.preallocatedPools_[i];
b.pools_ = a.pools_;
a.pools_ = a.preallocatedPools_;
} else if (aUsedPreallocated) {
// only a => copy a's preallocated pools and give him b's pointer
for (PoolCount i = 0; i < a.count_; i++)
b.preallocatedPools_[i] = a.preallocatedPools_[i];
a.pools_ = b.pools_;
b.pools_ = b.preallocatedPools_;
} else {
// neither => swap pointers
swap_(a.pools_, b.pools_);
}
swap_(a.count_, b.count_);
swap_(a.capacity_, b.capacity_);
swap_(a.freeList_, b.freeList_);
}
VariantPoolList& operator=(VariantPoolList&& src) {
ARDUINOJSON_ASSERT(count_ == 0);
if (src.pools_ == src.preallocatedPools_) {
memcpy(preallocatedPools_, src.preallocatedPools_,
sizeof(preallocatedPools_));
pools_ = preallocatedPools_;
} else {
pools_ = src.pools_;
src.pools_ = nullptr;
}
count_ = src.count_;
capacity_ = src.capacity_;
src.count_ = 0;
src.capacity_ = 0;
return *this;
}
SlotWithId allocSlot(Allocator* allocator) {
// try to allocate from free list
if (freeList_ != NULL_SLOT) {
return allocFromFreeList();
}
// try to allocate from last pool (other pools are full)
if (count_) {
auto slot = allocFromLastPool();
if (slot)
return slot;
}
// create a new pool and try again
auto pool = addPool(allocator);
if (!pool)
return {};
return allocFromLastPool();
}
void freeSlot(SlotWithId slot);
VariantSlot* getSlot(SlotId id) const {
if (id == NULL_SLOT)
return nullptr;
auto poolIndex = SlotId(id / ARDUINOJSON_POOL_CAPACITY);
auto indexInPool = SlotId(id % ARDUINOJSON_POOL_CAPACITY);
ARDUINOJSON_ASSERT(poolIndex < count_);
return pools_[poolIndex].getSlot(indexInPool);
}
void clear(Allocator* allocator) {
for (PoolCount i = 0; i < count_; i++)
pools_[i].destroy(allocator);
count_ = 0;
freeList_ = NULL_SLOT;
if (pools_ != preallocatedPools_) {
allocator->deallocate(pools_);
pools_ = preallocatedPools_;
capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
}
}
SlotCount usage() const {
SlotCount total = 0;
for (PoolCount i = 0; i < count_; i++)
total = SlotCount(total + pools_[i].usage());
return total;
}
void shrinkToFit(Allocator* allocator) {
if (count_ > 0)
pools_[count_ - 1].shrinkToFit(allocator);
if (pools_ != preallocatedPools_ && count_ != capacity_) {
pools_ = static_cast<VariantPool*>(
allocator->reallocate(pools_, count_ * sizeof(VariantPool)));
ARDUINOJSON_ASSERT(pools_ != nullptr); // realloc to smaller can't fail
capacity_ = count_;
}
}
private:
SlotWithId allocFromFreeList();
SlotWithId allocFromLastPool() {
ARDUINOJSON_ASSERT(count_ > 0);
auto poolIndex = SlotId(count_ - 1);
auto slot = pools_[poolIndex].allocSlot();
if (!slot)
return {};
return {slot, SlotId(poolIndex * ARDUINOJSON_POOL_CAPACITY + slot.id())};
}
VariantPool* addPool(Allocator* allocator) {
if (count_ == capacity_ && !increaseCapacity(allocator))
return nullptr;
auto pool = &pools_[count_++];
SlotCount poolCapacity = ARDUINOJSON_POOL_CAPACITY;
if (count_ == maxPools) // last pool is smaller because of NULL_SLOT
poolCapacity--;
pool->create(poolCapacity, allocator);
return pool;
}
bool increaseCapacity(Allocator* allocator) {
if (capacity_ == maxPools)
return false;
void* newPools;
auto newCapacity = PoolCount(capacity_ * 2);
if (pools_ == preallocatedPools_) {
newPools = allocator->allocate(newCapacity * sizeof(VariantPool));
if (!newPools)
return false;
memcpy(newPools, preallocatedPools_, sizeof(preallocatedPools_));
} else {
newPools =
allocator->reallocate(pools_, newCapacity * sizeof(VariantPool));
if (!newPools)
return false;
}
pools_ = static_cast<VariantPool*>(newPools);
capacity_ = newCapacity;
return true;
}
VariantPool preallocatedPools_[ARDUINOJSON_INITIAL_POOL_COUNT];
VariantPool* pools_ = preallocatedPools_;
PoolCount count_ = 0;
PoolCount capacity_ = ARDUINOJSON_INITIAL_POOL_COUNT;
SlotId freeList_ = NULL_SLOT;
public:
static const PoolCount maxPools =
PoolCount(NULL_SLOT / ARDUINOJSON_POOL_CAPACITY + 1);
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -464,10 +464,11 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a MessagePack input and puts the result in a JsonDocument. // Parses a MessagePack input and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/msgpack/deserializemsgpack/ // https://arduinojson.org/v7/api/msgpack/deserializemsgpack/
template <typename TDestination, typename... Args> template <typename TDestination, typename... Args,
detail::enable_if_t<detail::is_deserialize_destination<TDestination>::value, detail::enable_if_t<
DeserializationError> detail::is_deserialize_destination<TDestination>::value, int> = 0>
deserializeMsgPack(TDestination&& dst, Args&&... args) { inline DeserializationError deserializeMsgPack(TDestination&& dst,
Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst), return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst),
detail::forward<Args>(args)...); detail::forward<Args>(args)...);
@@ -475,10 +476,11 @@ deserializeMsgPack(TDestination&& dst, Args&&... args) {
// Parses a MessagePack input and puts the result in a JsonDocument. // Parses a MessagePack input and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/msgpack/deserializemsgpack/ // https://arduinojson.org/v7/api/msgpack/deserializemsgpack/
template <typename TDestination, typename TChar, typename... Args> template <typename TDestination, typename TChar, typename... Args,
detail::enable_if_t<detail::is_deserialize_destination<TDestination>::value, detail::enable_if_t<
DeserializationError> detail::is_deserialize_destination<TDestination>::value, int> = 0>
deserializeMsgPack(TDestination&& dst, TChar* input, Args&&... args) { inline DeserializationError deserializeMsgPack(TDestination&& dst, TChar* input,
Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst), return deserialize<MsgPackDeserializer>(detail::forward<TDestination>(dst),
input, input,

View File

@@ -96,7 +96,7 @@ class MsgPackSerializer : public VariantDataVisitor<size_t> {
} }
size_t visit(JsonString value) { size_t visit(JsonString value) {
ARDUINOJSON_ASSERT(value != NULL); ARDUINOJSON_ASSERT(!value.isNull());
auto n = value.size(); auto n = value.size();
@@ -218,9 +218,10 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Produces a MessagePack document. // Produces a MessagePack document.
// https://arduinojson.org/v7/api/msgpack/serializemsgpack/ // https://arduinojson.org/v7/api/msgpack/serializemsgpack/
template <typename TDestination> template <
detail::enable_if_t<!detail::is_pointer<TDestination>::value, size_t> typename TDestination,
serializeMsgPack(JsonVariantConst source, TDestination& output) { detail::enable_if_t<!detail::is_pointer<TDestination>::value, int> = 0>
inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) {
using namespace ArduinoJson::detail; using namespace ArduinoJson::detail;
return serialize<MsgPackSerializer>(source, output); return serialize<MsgPackSerializer>(source, output);
} }

View File

@@ -101,32 +101,31 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Gets or sets the member with specified key. // Gets or sets the member with specified key.
// https://arduinojson.org/v7/api/jsonobject/subscript/ // https://arduinojson.org/v7/api/jsonobject/subscript/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
detail::MemberProxy<JsonObject, TString>> detail::MemberProxy<JsonObject, detail::AdaptedString<TString>> operator[](
operator[](const TString& key) const { const TString& key) const {
return {*this, key}; return {*this, detail::adaptString(key)};
} }
// Gets or sets the member with specified key. // Gets or sets the member with specified key.
// https://arduinojson.org/v7/api/jsonobject/subscript/ // https://arduinojson.org/v7/api/jsonobject/subscript/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value, detail::enable_if_t<detail::IsString<TChar*>::value &&
detail::MemberProxy<JsonObject, TChar*>> !detail::is_const<TChar>::value,
operator[](TChar* key) const { int> = 0>
return {*this, key}; detail::MemberProxy<JsonObject, detail::AdaptedString<TChar*>> operator[](
TChar* key) const {
return {*this, detail::adaptString(key)};
} }
// Gets or sets the member with specified key. // Gets or sets the member with specified key.
// https://arduinojson.org/v7/api/jsonobject/subscript/ // https://arduinojson.org/v7/api/jsonobject/subscript/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
detail::MemberProxy<JsonObject, JsonString>> detail::MemberProxy<JsonObject, detail::AdaptedString<JsonString>> operator[](
operator[](const TVariant& key) const { const TVariant& key) const {
if (key.template is<JsonString>()) return {*this, detail::adaptString(key.template as<JsonString>())};
return {*this, key.template as<JsonString>()};
else
return {*this, nullptr};
} }
// Removes the member at the specified iterator. // Removes the member at the specified iterator.
@@ -137,18 +136,18 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// Removes the member with the specified key. // Removes the member with the specified key.
// https://arduinojson.org/v7/api/jsonobject/remove/ // https://arduinojson.org/v7/api/jsonobject/remove/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value> remove( detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
const TString& key) const { void remove(const TString& key) const {
detail::ObjectData::removeMember(data_, detail::adaptString(key), detail::ObjectData::removeMember(data_, detail::adaptString(key),
resources_); resources_);
} }
// Removes the member with the specified key. // Removes the member with the specified key.
// https://arduinojson.org/v7/api/jsonobject/remove/ // https://arduinojson.org/v7/api/jsonobject/remove/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value> remove( detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
const TVariant& key) const { void remove(const TVariant& key) const {
if (key.template is<const char*>()) if (key.template is<const char*>())
remove(key.template as<const char*>()); remove(key.template as<const char*>());
} }
@@ -163,30 +162,32 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobject/containskey/ // https://arduinojson.org/v7/api/jsonobject/containskey/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey( bool containsKey(const TString& key) const {
const TString& key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key), return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0; resources_) != 0;
} }
// DEPRECATED: use obj["key"].is<T>() instead // DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonobject/containskey/ // https://arduinojson.org/v7/api/jsonobject/containskey/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
detail::enable_if_t<detail::IsString<TChar*>::value, bool> containsKey( bool containsKey(TChar* key) const {
TChar* key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key), return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0; resources_) != 0;
} }
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobject/containskey/ // https://arduinojson.org/v7/api/jsonobject/containskey/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey( bool containsKey(const TVariant& key) const {
const TVariant& key) const {
return containsKey(key.template as<const char*>()); return containsKey(key.template as<const char*>());
} }

View File

@@ -70,10 +70,10 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/ // https://arduinojson.org/v7/api/jsonobjectconst/containskey/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey( bool containsKey(const TString& key) const {
const TString& key) const {
return detail::ObjectData::getMember(data_, detail::adaptString(key), return detail::ObjectData::getMember(data_, detail::adaptString(key),
resources_) != 0; resources_) != 0;
} }
@@ -89,18 +89,18 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonobjectconst/containskey/ // https://arduinojson.org/v7/api/jsonobjectconst/containskey/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey( bool containsKey(const TVariant& key) const {
const TVariant& key) const {
return containsKey(key.template as<const char*>()); return containsKey(key.template as<const char*>());
} }
// Gets the member with specified key. // Gets the member with specified key.
// https://arduinojson.org/v7/api/jsonobjectconst/subscript/ // https://arduinojson.org/v7/api/jsonobjectconst/subscript/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, JsonVariantConst> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
operator[](const TString& key) const { JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(detail::ObjectData::getMember( return JsonVariantConst(detail::ObjectData::getMember(
data_, detail::adaptString(key), resources_), data_, detail::adaptString(key), resources_),
resources_); resources_);
@@ -108,9 +108,11 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// Gets the member with specified key. // Gets the member with specified key.
// https://arduinojson.org/v7/api/jsonobjectconst/subscript/ // https://arduinojson.org/v7/api/jsonobjectconst/subscript/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst> detail::enable_if_t<detail::IsString<TChar*>::value &&
operator[](TChar* key) const { !detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(detail::ObjectData::getMember( return JsonVariantConst(detail::ObjectData::getMember(
data_, detail::adaptString(key), resources_), data_, detail::adaptString(key), resources_),
resources_); resources_);
@@ -118,9 +120,9 @@ class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
// Gets the member with specified key. // Gets the member with specified key.
// https://arduinojson.org/v7/api/jsonobjectconst/subscript/ // https://arduinojson.org/v7/api/jsonobjectconst/subscript/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst> detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
operator[](const TVariant& key) const { JsonVariantConst operator[](const TVariant& key) const {
if (key.template is<JsonString>()) if (key.template is<JsonString>())
return operator[](key.template as<JsonString>()); return operator[](key.template as<JsonString>());
else else

View File

@@ -10,18 +10,23 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// A proxy class to get or set a member of an object. // A proxy class to get or set a member of an object.
// https://arduinojson.org/v7/api/jsonobject/subscript/ // https://arduinojson.org/v7/api/jsonobject/subscript/
template <typename TUpstream, typename TStringRef> template <typename TUpstream, typename AdaptedString>
class MemberProxy class MemberProxy
: public VariantRefBase<MemberProxy<TUpstream, TStringRef>>, : public VariantRefBase<MemberProxy<TUpstream, AdaptedString>>,
public VariantOperators<MemberProxy<TUpstream, TStringRef>> { public VariantOperators<MemberProxy<TUpstream, AdaptedString>> {
friend class VariantAttorney; friend class VariantAttorney;
public: friend class VariantRefBase<MemberProxy<TUpstream, AdaptedString>>;
MemberProxy(TUpstream upstream, TStringRef key)
: upstream_(upstream), key_(key) {}
MemberProxy(const MemberProxy& src) template <typename, typename>
: upstream_(src.upstream_), key_(src.key_) {} friend class MemberProxy;
template <typename>
friend class ElementProxy;
public:
MemberProxy(TUpstream upstream, AdaptedString key)
: upstream_(upstream), key_(key) {}
MemberProxy& operator=(const MemberProxy& src) { MemberProxy& operator=(const MemberProxy& src) {
this->set(src); this->set(src);
@@ -34,20 +39,25 @@ class MemberProxy
return *this; return *this;
} }
template <typename T> template <typename T, enable_if_t<!is_const<T>::value, int> = 0>
MemberProxy& operator=(T* src) { MemberProxy& operator=(T* src) {
this->set(src); this->set(src);
return *this; return *this;
} }
private: private:
// clang-format off
MemberProxy(const MemberProxy& src) // Error here? See https://arduinojson.org/v7/proxy-non-copyable/
: upstream_(src.upstream_), key_(src.key_) {}
// clang-format on
ResourceManager* getResourceManager() const { ResourceManager* getResourceManager() const {
return VariantAttorney::getResourceManager(upstream_); return VariantAttorney::getResourceManager(upstream_);
} }
VariantData* getData() const { VariantData* getData() const {
return VariantData::getMember( return VariantData::getMember(
VariantAttorney::getData(upstream_), adaptString(key_), VariantAttorney::getData(upstream_), key_,
VariantAttorney::getResourceManager(upstream_)); VariantAttorney::getResourceManager(upstream_));
} }
@@ -55,13 +65,13 @@ class MemberProxy
auto data = VariantAttorney::getOrCreateData(upstream_); auto data = VariantAttorney::getOrCreateData(upstream_);
if (!data) if (!data)
return nullptr; return nullptr;
return data->getOrAddMember(adaptString(key_), return data->getOrAddMember(key_,
VariantAttorney::getResourceManager(upstream_)); VariantAttorney::getResourceManager(upstream_));
} }
private: private:
TUpstream upstream_; TUpstream upstream_;
TStringRef key_; AdaptedString key_;
}; };
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -5,6 +5,7 @@
#pragma once #pragma once
#include "type_traits/conditional.hpp" #include "type_traits/conditional.hpp"
#include "type_traits/decay.hpp"
#include "type_traits/enable_if.hpp" #include "type_traits/enable_if.hpp"
#include "type_traits/function_traits.hpp" #include "type_traits/function_traits.hpp"
#include "type_traits/integral_constant.hpp" #include "type_traits/integral_constant.hpp"

View File

@@ -0,0 +1,33 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <stddef.h> // size_t
#include <ArduinoJson/Namespace.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename T>
struct decay {
using type = T;
};
template <typename T>
struct decay<T&> : decay<T> {};
template <typename T>
struct decay<T&&> : decay<T> {};
template <typename T>
struct decay<T[]> : decay<T*> {};
template <typename T, size_t N>
struct decay<T[N]> : decay<T*> {};
template <typename T>
using decay_t = typename decay<T>::type;
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -8,7 +8,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// The default writer is a simple wrapper for Writers that are not copiable // The default writer is a simple wrapper for Writers that are not copyable
template <typename TDestination, typename Enable = void> template <typename TDestination, typename Enable = void>
class Writer { class Writer {
public: public:

View File

@@ -34,7 +34,7 @@ class FlashString {
return size_; return size_;
} }
friend bool stringEquals(FlashString a, SizedRamString b) { friend bool stringEquals(FlashString a, RamString b) {
ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey);
ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!a.isNull());
ARDUINOJSON_ASSERT(!b.isNull()); ARDUINOJSON_ASSERT(!b.isNull());
@@ -43,7 +43,7 @@ class FlashString {
return ::memcmp_P(b.data(), a.str_, a.size_) == 0; return ::memcmp_P(b.data(), a.str_, a.size_) == 0;
} }
friend int stringCompare(FlashString a, SizedRamString b) { friend int stringCompare(FlashString a, RamString b) {
ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey); ARDUINOJSON_ASSERT(a.typeSortKey < b.typeSortKey);
ARDUINOJSON_ASSERT(!a.isNull()); ARDUINOJSON_ASSERT(!a.isNull());
ARDUINOJSON_ASSERT(!b.isNull()); ARDUINOJSON_ASSERT(!b.isNull());
@@ -63,7 +63,7 @@ class FlashString {
::memcpy_P(p, s.str_, n); ::memcpy_P(p, s.str_, n);
} }
bool isLinked() const { bool isStatic() const {
return false; return false;
} }

View File

@@ -1,35 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Strings/Adapters/RamString.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapter.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class JsonStringAdapter : public SizedRamString {
public:
JsonStringAdapter(const JsonString& s)
: SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {}
bool isLinked() const {
return linked_;
}
private:
bool linked_;
};
template <>
struct StringAdapter<JsonString> {
using AdaptedString = JsonStringAdapter;
static AdaptedString adapt(const JsonString& s) {
return AdaptedString(s);
}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -17,79 +17,19 @@ template <typename T>
struct IsChar struct IsChar
: integral_constant<bool, is_integral<T>::value && sizeof(T) == 1> {}; : integral_constant<bool, is_integral<T>::value && sizeof(T) == 1> {};
class ZeroTerminatedRamString { class RamString {
public:
static const size_t typeSortKey = 3;
ZeroTerminatedRamString(const char* str) : str_(str) {}
bool isNull() const {
return !str_;
}
FORCE_INLINE size_t size() const {
return str_ ? ::strlen(str_) : 0;
}
char operator[](size_t i) const {
ARDUINOJSON_ASSERT(str_ != 0);
ARDUINOJSON_ASSERT(i <= size());
return str_[i];
}
const char* data() const {
return str_;
}
bool isLinked() const {
return false;
}
protected:
const char* str_;
};
template <typename TChar>
struct StringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
using AdaptedString = ZeroTerminatedRamString;
static AdaptedString adapt(const TChar* p) {
return AdaptedString(reinterpret_cast<const char*>(p));
}
};
template <typename TChar, size_t N>
struct StringAdapter<TChar[N], enable_if_t<IsChar<TChar>::value>> {
using AdaptedString = ZeroTerminatedRamString;
static AdaptedString adapt(const TChar* p) {
return AdaptedString(reinterpret_cast<const char*>(p));
}
};
class StaticStringAdapter : public ZeroTerminatedRamString {
public:
StaticStringAdapter(const char* str) : ZeroTerminatedRamString(str) {}
bool isLinked() const {
return true;
}
};
template <>
struct StringAdapter<const char*, void> {
using AdaptedString = StaticStringAdapter;
static AdaptedString adapt(const char* p) {
return AdaptedString(p);
}
};
class SizedRamString {
public: public:
static const size_t typeSortKey = 2; 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
SizedRamString(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);
}
bool isNull() const { bool isNull() const {
return !str_; return !str_;
@@ -109,18 +49,55 @@ class SizedRamString {
return str_; return str_;
} }
bool isLinked() const { bool isStatic() const {
return false; return static_;
} }
protected: protected:
const char* str_; 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_; size_t size_;
bool static_;
#endif
};
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)[N]) {
return RamString(p, N - 1, 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) {
auto str = reinterpret_cast<const char*>(p);
return AdaptedString(str, str ? ::strlen(str) : 0);
}
}; };
template <typename TChar> template <typename TChar>
struct SizedStringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> { struct SizedStringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
using AdaptedString = SizedRamString; using AdaptedString = RamString;
static AdaptedString adapt(const TChar* p, size_t n) { static AdaptedString adapt(const TChar* p, size_t n) {
return AdaptedString(reinterpret_cast<const char*>(p), n); return AdaptedString(reinterpret_cast<const char*>(p), n);

View File

@@ -15,7 +15,7 @@ struct StringAdapter<
T, T,
enable_if_t<(string_traits<T>::has_cstr || string_traits<T>::has_data) && enable_if_t<(string_traits<T>::has_cstr || string_traits<T>::has_data) &&
(string_traits<T>::has_length || string_traits<T>::has_size)>> { (string_traits<T>::has_length || string_traits<T>::has_size)>> {
using AdaptedString = SizedRamString; using AdaptedString = RamString;
static AdaptedString adapt(const T& s) { static AdaptedString adapt(const T& s) {
return AdaptedString(get_data(s), get_size(s)); return AdaptedString(get_data(s), get_size(s));

View File

@@ -13,7 +13,7 @@ template <typename T, typename Enable = void>
struct IsString : false_type {}; struct IsString : false_type {};
template <typename T> template <typename T>
struct IsString<T, void_t<typename StringAdapter<T>::AdaptedString>> struct IsString<T, void_t<typename StringAdapterFor<T>::AdaptedString>>
: true_type {}; : true_type {};
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -4,6 +4,9 @@
#pragma once #pragma once
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/Adapters/RamString.hpp>
#if ARDUINOJSON_ENABLE_STD_STREAM #if ARDUINOJSON_ENABLE_STD_STREAM
# include <ostream> # include <ostream>
#endif #endif
@@ -13,54 +16,58 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// A string. // A string.
// https://arduinojson.org/v7/api/jsonstring/ // https://arduinojson.org/v7/api/jsonstring/
class JsonString { class JsonString {
friend struct detail::StringAdapter<JsonString>;
public: public:
enum Ownership { Copied, Linked }; JsonString() : str_(nullptr, 0, true) {}
JsonString() : data_(0), size_(0), ownership_(Linked) {} JsonString(const char* data, bool isStatic = false)
: str_(data, data ? ::strlen(data) : 0, isStatic) {}
JsonString(const char* data, Ownership ownership = Linked) template <typename TSize,
: data_(data), size_(data ? ::strlen(data) : 0), ownership_(ownership) {} detail::enable_if_t<detail::is_integral<TSize>::value &&
!detail::is_same<TSize, bool>::value,
JsonString(const char* data, size_t sz, Ownership ownership = Linked) int> = 0>
: data_(data), size_(sz), ownership_(ownership) {} JsonString(const char* data, TSize sz, bool isStatic = false)
: str_(data, size_t(sz), isStatic) {}
// Returns a pointer to the characters. // Returns a pointer to the characters.
const char* c_str() const { const char* c_str() const {
return data_; return str_.data();
} }
// Returns true if the string is null. // Returns true if the string is null.
bool isNull() const { bool isNull() const {
return !data_; return str_.isNull();
} }
// Returns true if the string is stored by address. // Returns true if the string is stored by address.
// Returns false if the string is stored by copy. // Returns false if the string is stored by copy.
bool isLinked() const { bool isStatic() const {
return ownership_ == Linked; return str_.isStatic();
} }
// Returns length of the string. // Returns length of the string.
size_t size() const { size_t size() const {
return size_; return str_.size();
} }
// Returns true if the string is non-null // Returns true if the string is non-null
explicit operator bool() const { explicit operator bool() const {
return data_ != 0; return str_.data() != 0;
} }
// Returns true if strings are equal. // Returns true if strings are equal.
friend bool operator==(JsonString lhs, JsonString rhs) { friend bool operator==(JsonString lhs, JsonString rhs) {
if (lhs.size_ != rhs.size_) if (lhs.size() != rhs.size())
return false; return false;
if (lhs.data_ == rhs.data_) if (lhs.c_str() == rhs.c_str())
return true; return true;
if (!lhs.data_) if (!lhs.c_str())
return false; return false;
if (!rhs.data_) if (!rhs.c_str())
return false; return false;
return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0; return memcmp(lhs.c_str(), rhs.c_str(), lhs.size()) == 0;
} }
// Returns true if strings differs. // Returns true if strings differs.
@@ -76,9 +83,18 @@ class JsonString {
#endif #endif
private: private:
const char* data_; detail::RamString str_;
size_t size_;
Ownership ownership_;
}; };
namespace detail {
template <>
struct StringAdapter<JsonString> {
using AdaptedString = RamString;
static const AdaptedString& adapt(const JsonString& s) {
return s.str_;
}
};
} // namespace detail
ARDUINOJSON_END_PUBLIC_NAMESPACE ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@@ -4,8 +4,17 @@
#pragma once #pragma once
#include <ArduinoJson/Polyfills/utility.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE 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> template <typename TString, typename Enable = void>
struct StringAdapter; struct StringAdapter;
@@ -13,18 +22,25 @@ template <typename TString, typename Enable = void>
struct SizedStringAdapter; struct SizedStringAdapter;
template <typename TString> template <typename TString>
typename StringAdapter<TString>::AdaptedString adaptString(const TString& s) { using StringAdapterFor =
return StringAdapter<TString>::adapt(s); StringAdapter<conditional_t<IsStringLiteral<TString>::value, TString,
remove_cv_t<remove_reference_t<TString>>>>;
template <typename T>
using AdaptedString = typename StringAdapterFor<T>::AdaptedString;
template <typename TString>
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>
typename StringAdapter<TChar*>::AdaptedString adaptString(TChar* p) { AdaptedString<TChar*> adaptString(TChar* p) {
return StringAdapter<TChar*>::adapt(p); return StringAdapter<TChar*>::adapt(p);
} }
template <typename TChar> template <typename TChar>
typename SizedStringAdapter<TChar*>::AdaptedString adaptString(TChar* p, AdaptedString<TChar*> adaptString(TChar* p, size_t n) {
size_t n) {
return SizedStringAdapter<TChar*>::adapt(p, n); return SizedStringAdapter<TChar*>::adapt(p, n);
} }

View File

@@ -5,7 +5,6 @@
#pragma once #pragma once
#include <ArduinoJson/Polyfills/type_traits.hpp> #include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/Adapters/JsonString.hpp>
#include <ArduinoJson/Strings/Adapters/RamString.hpp> #include <ArduinoJson/Strings/Adapters/RamString.hpp>
#include <ArduinoJson/Strings/Adapters/StringObject.hpp> #include <ArduinoJson/Strings/Adapters/StringObject.hpp>

View File

@@ -31,7 +31,7 @@ struct Converter {
// clang-format on // clang-format on
} }
static T fromJson(JsonVariantConst src) { static detail::decay_t<T> fromJson(JsonVariantConst src) {
static_assert(!detail::is_same<T, char*>::value, static_assert(!detail::is_same<T, char*>::value,
"type 'char*' is not supported, use 'const char*' instead"); "type 'char*' is not supported, use 'const char*' instead");
@@ -178,7 +178,7 @@ struct Converter<JsonString> : private detail::VariantAttorney {
static JsonString fromJson(JsonVariantConst src) { static JsonString fromJson(JsonVariantConst src) {
auto data = getData(src); auto data = getData(src);
return data ? data->asString() : 0; return data ? data->asString() : JsonString();
} }
static bool checkJson(JsonVariantConst src) { static bool checkJson(JsonVariantConst src) {

View File

@@ -68,27 +68,29 @@ class JsonVariantConst : public detail::VariantTag,
// Casts the value to the specified type. // Casts the value to the specified type.
// https://arduinojson.org/v7/api/jsonvariantconst/as/ // https://arduinojson.org/v7/api/jsonvariantconst/as/
template <typename T, template <typename T,
detail::enable_if_t<ConversionSupported<T>::value, bool> = true> detail::enable_if_t<ConversionSupported<T>::value, int> = 0>
T as() const { T as() const {
return Converter<T>::fromJson(*this); return Converter<T>::fromJson(*this);
} }
// Invalid conversion. Will not compile. // Invalid conversion. Will not compile.
template <typename T, template <typename T,
detail::enable_if_t<!ConversionSupported<T>::value, bool> = true> detail::enable_if_t<!ConversionSupported<T>::value, int> = 0>
detail::InvalidConversion<JsonVariantConst, T> as() const; detail::InvalidConversion<JsonVariantConst, T> as() const;
// Returns true if the value is of the specified type. // Returns true if the value is of the specified type.
// https://arduinojson.org/v7/api/jsonvariantconst/is/ // https://arduinojson.org/v7/api/jsonvariantconst/is/
template <typename T> template <typename T,
detail::enable_if_t<ConversionSupported<T>::value, bool> is() const { detail::enable_if_t<ConversionSupported<T>::value, int> = 0>
bool is() const {
return Converter<T>::checkJson(*this); return Converter<T>::checkJson(*this);
} }
// Always returns false for the unsupported types. // Always returns false for the unsupported types.
// https://arduinojson.org/v7/api/jsonvariantconst/is/ // https://arduinojson.org/v7/api/jsonvariantconst/is/
template <typename T> template <typename T,
detail::enable_if_t<!ConversionSupported<T>::value, bool> is() const { detail::enable_if_t<!ConversionSupported<T>::value, int> = 0>
bool is() const {
return false; return false;
} }
@@ -99,9 +101,9 @@ class JsonVariantConst : public detail::VariantTag,
// Gets array's element at specified index. // Gets array's element at specified index.
// https://arduinojson.org/v7/api/jsonvariantconst/subscript/ // https://arduinojson.org/v7/api/jsonvariantconst/subscript/
template <typename T> template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, JsonVariantConst> detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
operator[](T index) const { JsonVariantConst operator[](T index) const {
return JsonVariantConst( return JsonVariantConst(
detail::VariantData::getElement(data_, size_t(index), resources_), detail::VariantData::getElement(data_, size_t(index), resources_),
resources_); resources_);
@@ -109,9 +111,9 @@ class JsonVariantConst : public detail::VariantTag,
// Gets object's member with specified key. // Gets object's member with specified key.
// https://arduinojson.org/v7/api/jsonvariantconst/subscript/ // https://arduinojson.org/v7/api/jsonvariantconst/subscript/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, JsonVariantConst> detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
operator[](const TString& key) const { JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(detail::VariantData::getMember( return JsonVariantConst(detail::VariantData::getMember(
data_, detail::adaptString(key), resources_), data_, detail::adaptString(key), resources_),
resources_); resources_);
@@ -119,9 +121,11 @@ class JsonVariantConst : public detail::VariantTag,
// Gets object's member with specified key. // Gets object's member with specified key.
// https://arduinojson.org/v7/api/jsonvariantconst/subscript/ // https://arduinojson.org/v7/api/jsonvariantconst/subscript/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst> detail::enable_if_t<detail::IsString<TChar*>::value &&
operator[](TChar* key) const { !detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(detail::VariantData::getMember( return JsonVariantConst(detail::VariantData::getMember(
data_, detail::adaptString(key), resources_), data_, detail::adaptString(key), resources_),
resources_); resources_);
@@ -130,9 +134,9 @@ class JsonVariantConst : public detail::VariantTag,
// Gets object's member with specified key or the array's element at the // Gets object's member with specified key or the array's element at the
// specified index. // specified index.
// https://arduinojson.org/v7/api/jsonvariantconst/subscript/ // https://arduinojson.org/v7/api/jsonvariantconst/subscript/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst> detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
operator[](const TVariant& key) const { JsonVariantConst operator[](const TVariant& key) const {
if (key.template is<size_t>()) if (key.template is<size_t>())
return operator[](key.template as<size_t>()); return operator[](key.template as<size_t>());
else else
@@ -141,30 +145,32 @@ class JsonVariantConst : public detail::VariantTag,
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/ // https://arduinojson.org/v7/api/jsonvariantconst/containskey/
template <typename TString> template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey( bool containsKey(const TString& key) const {
const TString& key) const {
return detail::VariantData::getMember(getData(), detail::adaptString(key), return detail::VariantData::getMember(getData(), detail::adaptString(key),
resources_) != 0; resources_) != 0;
} }
// DEPRECATED: use obj["key"].is<T>() instead // DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/ // https://arduinojson.org/v7/api/jsonvariantconst/containskey/
template <typename TChar> template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
detail::enable_if_t<detail::IsString<TChar*>::value, bool> containsKey( bool containsKey(TChar* key) const {
TChar* key) const {
return detail::VariantData::getMember(getData(), detail::adaptString(key), return detail::VariantData::getMember(getData(), detail::adaptString(key),
resources_) != 0; resources_) != 0;
} }
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/ // https://arduinojson.org/v7/api/jsonvariantconst/containskey/
template <typename TVariant> template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use var[key].is<T>() instead")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey( bool containsKey(const TVariant& key) const {
const TVariant& key) const {
return containsKey(key.template as<const char*>()); return containsKey(key.template as<const char*>());
} }

View File

@@ -64,12 +64,11 @@ class VariantData {
return visit.visit(content_.asObject); return visit.visit(content_.asObject);
case VariantType::LinkedString: case VariantType::LinkedString:
return visit.visit(JsonString(content_.asLinkedString)); return visit.visit(JsonString(content_.asLinkedString, true));
case VariantType::OwnedString: case VariantType::OwnedString:
return visit.visit(JsonString(content_.asOwnedString->data, return visit.visit(JsonString(content_.asOwnedString->data,
content_.asOwnedString->length, content_.asOwnedString->length));
JsonString::Copied));
case VariantType::RawString: case VariantType::RawString:
return visit.visit(RawString(content_.asOwnedString->data, return visit.visit(RawString(content_.asOwnedString->data,
@@ -119,14 +118,13 @@ class VariantData {
} }
template <typename T> template <typename T>
bool addValue(T&& value, ResourceManager* resources) { bool addValue(const T& value, ResourceManager* resources) {
auto array = isNull() ? &toArray() : asArray(); auto array = isNull() ? &toArray() : asArray();
return detail::ArrayData::addValue(array, detail::forward<T>(value), return detail::ArrayData::addValue(array, value, resources);
resources);
} }
template <typename T> template <typename T>
static bool addValue(VariantData* var, T&& value, static bool addValue(VariantData* var, const T& value,
ResourceManager* resources) { ResourceManager* resources) {
if (!var) if (!var)
return false; return false;
@@ -262,7 +260,7 @@ class VariantData {
switch (type_) { switch (type_) {
case VariantType::RawString: case VariantType::RawString:
return JsonString(content_.asOwnedString->data, return JsonString(content_.asOwnedString->data,
content_.asOwnedString->length, JsonString::Copied); content_.asOwnedString->length);
default: default:
return JsonString(); return JsonString();
} }
@@ -271,10 +269,10 @@ class VariantData {
JsonString asString() const { JsonString asString() const {
switch (type_) { switch (type_) {
case VariantType::LinkedString: case VariantType::LinkedString:
return JsonString(content_.asLinkedString, JsonString::Linked); return JsonString(content_.asLinkedString, true);
case VariantType::OwnedString: case VariantType::OwnedString:
return JsonString(content_.asOwnedString->data, return JsonString(content_.asOwnedString->data,
content_.asOwnedString->length, JsonString::Copied); content_.asOwnedString->length);
default: default:
return JsonString(); return JsonString();
} }

View File

@@ -26,7 +26,7 @@ inline bool VariantData::setString(TAdaptedString value,
if (value.isNull()) if (value.isNull())
return false; return false;
if (value.isLinked()) { if (value.isStatic()) {
setLinkedString(value.data()); setLinkedString(value.data());
return true; return true;
} }

View File

@@ -30,9 +30,9 @@ struct VariantOperators : VariantOperatorTag {
// int operator|(JsonVariant, int) // int operator|(JsonVariant, int)
// float operator|(JsonVariant, float) // float operator|(JsonVariant, float)
// bool operator|(JsonVariant, bool) // bool operator|(JsonVariant, bool)
template <typename T> template <typename T,
friend enable_if_t<!IsVariant<T>::value && !is_array<T>::value, T> operator|( enable_if_t<!IsVariant<T>::value && !is_array<T>::value, int> = 0>
const TVariant& variant, const T& defaultValue) { friend T operator|(const TVariant& variant, const T& defaultValue) {
if (variant.template is<T>()) if (variant.template is<T>())
return variant.template as<T>(); return variant.template as<T>();
else else
@@ -51,7 +51,7 @@ struct VariantOperators : VariantOperatorTag {
// JsonVariant operator|(JsonVariant, JsonVariant) // JsonVariant operator|(JsonVariant, JsonVariant)
template <typename T> template <typename T>
friend enable_if_t<IsVariant<T>::value, JsonVariantConst> operator|( friend enable_if_t<IsVariant<T>::value, JsonVariantConst> operator|(
const TVariant& variant, T defaultValue) { const TVariant& variant, const T& defaultValue) {
if (variant) if (variant)
return variant; return variant;
else else
@@ -60,127 +60,127 @@ struct VariantOperators : VariantOperatorTag {
// value == TVariant // value == TVariant
template <typename T> template <typename T>
friend bool operator==(T* lhs, TVariant rhs) { friend bool operator==(T* lhs, const TVariant& rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; return compare(rhs, lhs) == COMPARE_RESULT_EQUAL;
} }
template <typename T> template <typename T>
friend bool operator==(const T& lhs, TVariant rhs) { friend bool operator==(const T& lhs, const TVariant& rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_EQUAL; return compare(rhs, lhs) == COMPARE_RESULT_EQUAL;
} }
// TVariant == value // TVariant == value
template <typename T> template <typename T>
friend bool operator==(TVariant lhs, T* rhs) { friend bool operator==(const TVariant& lhs, T* rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; return compare(lhs, rhs) == COMPARE_RESULT_EQUAL;
} }
template <typename T> template <typename T,
friend enable_if_t<!is_base_of<VariantOperatorTag, T>::value, bool> enable_if_t<!is_base_of<VariantOperatorTag, T>::value, int> = 0>
operator==(TVariant lhs, const T& rhs) { friend bool operator==(const TVariant& lhs, const T& rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_EQUAL; return compare(lhs, rhs) == COMPARE_RESULT_EQUAL;
} }
// value != TVariant // value != TVariant
template <typename T> template <typename T>
friend bool operator!=(T* lhs, TVariant rhs) { friend bool operator!=(T* lhs, const TVariant& rhs) {
return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; return compare(rhs, lhs) != COMPARE_RESULT_EQUAL;
} }
template <typename T> template <typename T>
friend bool operator!=(const T& lhs, TVariant rhs) { friend bool operator!=(const T& lhs, const TVariant& rhs) {
return compare(rhs, lhs) != COMPARE_RESULT_EQUAL; return compare(rhs, lhs) != COMPARE_RESULT_EQUAL;
} }
// TVariant != value // TVariant != value
template <typename T> template <typename T>
friend bool operator!=(TVariant lhs, T* rhs) { friend bool operator!=(const TVariant& lhs, T* rhs) {
return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; return compare(lhs, rhs) != COMPARE_RESULT_EQUAL;
} }
template <typename T> template <typename T,
friend enable_if_t<!is_base_of<VariantOperatorTag, T>::value, bool> enable_if_t<!is_base_of<VariantOperatorTag, T>::value, int> = 0>
operator!=(TVariant lhs, const T& rhs) { friend bool operator!=(TVariant lhs, const T& rhs) {
return compare(lhs, rhs) != COMPARE_RESULT_EQUAL; return compare(lhs, rhs) != COMPARE_RESULT_EQUAL;
} }
// value < TVariant // value < TVariant
template <typename T> template <typename T>
friend bool operator<(T* lhs, TVariant rhs) { friend bool operator<(T* lhs, const TVariant& rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_GREATER; return compare(rhs, lhs) == COMPARE_RESULT_GREATER;
} }
template <typename T> template <typename T>
friend bool operator<(const T& lhs, TVariant rhs) { friend bool operator<(const T& lhs, const TVariant& rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_GREATER; return compare(rhs, lhs) == COMPARE_RESULT_GREATER;
} }
// TVariant < value // TVariant < value
template <typename T> template <typename T>
friend bool operator<(TVariant lhs, T* rhs) { friend bool operator<(const TVariant& lhs, T* rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_LESS; return compare(lhs, rhs) == COMPARE_RESULT_LESS;
} }
template <typename T> template <typename T,
friend enable_if_t<!is_base_of<VariantOperatorTag, T>::value, bool> operator<( enable_if_t<!is_base_of<VariantOperatorTag, T>::value, int> = 0>
TVariant lhs, const T& rhs) { friend bool operator<(TVariant lhs, const T& rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_LESS; return compare(lhs, rhs) == COMPARE_RESULT_LESS;
} }
// value <= TVariant // value <= TVariant
template <typename T> template <typename T>
friend bool operator<=(T* lhs, TVariant rhs) { friend bool operator<=(T* lhs, const TVariant& rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
} }
template <typename T> template <typename T>
friend bool operator<=(const T& lhs, TVariant rhs) { friend bool operator<=(const T& lhs, const TVariant& rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
} }
// TVariant <= value // TVariant <= value
template <typename T> template <typename T>
friend bool operator<=(TVariant lhs, T* rhs) { friend bool operator<=(const TVariant& lhs, T* rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
} }
template <typename T> template <typename T,
friend enable_if_t<!is_base_of<VariantOperatorTag, T>::value, bool> enable_if_t<!is_base_of<VariantOperatorTag, T>::value, int> = 0>
operator<=(TVariant lhs, const T& rhs) { friend bool operator<=(TVariant lhs, const T& rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
} }
// value > TVariant // value > TVariant
template <typename T> template <typename T>
friend bool operator>(T* lhs, TVariant rhs) { friend bool operator>(T* lhs, const TVariant& rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_LESS; return compare(rhs, lhs) == COMPARE_RESULT_LESS;
} }
template <typename T> template <typename T>
friend bool operator>(const T& lhs, TVariant rhs) { friend bool operator>(const T& lhs, const TVariant& rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_LESS; return compare(rhs, lhs) == COMPARE_RESULT_LESS;
} }
// TVariant > value // TVariant > value
template <typename T> template <typename T>
friend bool operator>(TVariant lhs, T* rhs) { friend bool operator>(const TVariant& lhs, T* rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_GREATER; return compare(lhs, rhs) == COMPARE_RESULT_GREATER;
} }
template <typename T> template <typename T,
friend enable_if_t<!is_base_of<VariantOperatorTag, T>::value, bool> operator>( enable_if_t<!is_base_of<VariantOperatorTag, T>::value, int> = 0>
TVariant lhs, const T& rhs) { friend bool operator>(TVariant lhs, const T& rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_GREATER; return compare(lhs, rhs) == COMPARE_RESULT_GREATER;
} }
// value >= TVariant // value >= TVariant
template <typename T> template <typename T>
friend bool operator>=(T* lhs, TVariant rhs) { friend bool operator>=(T* lhs, const TVariant& rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
} }
template <typename T> template <typename T>
friend bool operator>=(const T& lhs, TVariant rhs) { friend bool operator>=(const T& lhs, const TVariant& rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0; return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
} }
// TVariant >= value // TVariant >= value
template <typename T> template <typename T>
friend bool operator>=(TVariant lhs, T* rhs) { friend bool operator>=(const TVariant& lhs, T* rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
} }
template <typename T> template <typename T,
friend enable_if_t<!is_base_of<VariantOperatorTag, T>::value, bool> enable_if_t<!is_base_of<VariantOperatorTag, T>::value, int> = 0>
operator>=(TVariant lhs, const T& rhs) { friend bool operator>=(const TVariant& lhs, const T& rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0; return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
} }
}; };

View File

@@ -48,25 +48,25 @@ class VariantRefBase : public VariantTag {
template <typename T> template <typename T>
T as() const; T as() const;
template <typename T, typename = enable_if_t<!is_same<T, TDerived>::value>> template <typename T, enable_if_t<!is_same<T, TDerived>::value, int> = 0>
operator T() const { operator T() const {
return as<T>(); return as<T>();
} }
// Sets the value to an empty array. // Sets the value to an empty array.
// https://arduinojson.org/v7/api/jsonvariant/to/ // https://arduinojson.org/v7/api/jsonvariant/to/
template <typename T> template <typename T, enable_if_t<is_same<T, JsonArray>::value, int> = 0>
enable_if_t<is_same<T, JsonArray>::value, JsonArray> to() const; JsonArray to() const;
// Sets the value to an empty object. // Sets the value to an empty object.
// https://arduinojson.org/v7/api/jsonvariant/to/ // https://arduinojson.org/v7/api/jsonvariant/to/
template <typename T> template <typename T, enable_if_t<is_same<T, JsonObject>::value, int> = 0>
enable_if_t<is_same<T, JsonObject>::value, JsonObject> to() const; JsonObject to() const;
// Sets the value to null. // Sets the value to null.
// https://arduinojson.org/v7/api/jsonvariant/to/ // https://arduinojson.org/v7/api/jsonvariant/to/
template <typename T> template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int> = 0>
enable_if_t<is_same<T, JsonVariant>::value, JsonVariant> to() const; JsonVariant to() const;
// Returns true if the value is of the specified type. // Returns true if the value is of the specified type.
// https://arduinojson.org/v7/api/jsonvariant/is/ // https://arduinojson.org/v7/api/jsonvariant/is/
@@ -77,12 +77,15 @@ class VariantRefBase : public VariantTag {
// https://arduinojson.org/v7/api/jsonvariant/set/ // https://arduinojson.org/v7/api/jsonvariant/set/
template <typename T> template <typename T>
bool set(const T& value) const { bool set(const T& value) const {
return doSet<Converter<remove_cv_t<T>>>(value); using TypeForConverter = conditional_t<IsStringLiteral<T>::value, T,
remove_cv_t<remove_reference_t<T>>>;
return doSet<Converter<TypeForConverter>>(value);
} }
// Copies the specified value. // Copies the specified value.
// https://arduinojson.org/v7/api/jsonvariant/set/ // 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 { bool set(T* value) const {
return doSet<Converter<T*>>(value); return doSet<Converter<T*>>(value);
} }
@@ -102,16 +105,16 @@ class VariantRefBase : public VariantTag {
// Appends a new (empty) element to the array. // Appends a new (empty) element to the array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonvariant/add/ // https://arduinojson.org/v7/api/jsonvariant/add/
template <typename T> template <typename T, enable_if_t<!is_same<T, JsonVariant>::value, int> = 0>
enable_if_t<!is_same<T, JsonVariant>::value, T> add() const { T add() const {
return add<JsonVariant>().template to<T>(); return add<JsonVariant>().template to<T>();
} }
// Appends a new (null) element to the array. // Appends a new (null) element to the array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonvariant/add/ // https://arduinojson.org/v7/api/jsonvariant/add/
template <typename T> template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int> = 0>
enable_if_t<is_same<T, JsonVariant>::value, T> add() const; T add() const;
// Appends a value to the array. // Appends a value to the array.
// https://arduinojson.org/v7/api/jsonvariant/add/ // https://arduinojson.org/v7/api/jsonvariant/add/
@@ -123,7 +126,7 @@ class VariantRefBase : public VariantTag {
// Appends a value to the array. // Appends a value to the array.
// https://arduinojson.org/v7/api/jsonvariant/add/ // 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 { bool add(T* value) const {
return detail::VariantData::addValue(getOrCreateData(), value, return detail::VariantData::addValue(getOrCreateData(), value,
getResourceManager()); getResourceManager());
@@ -137,24 +140,24 @@ class VariantRefBase : public VariantTag {
// Removes a member of the object. // Removes a member of the object.
// https://arduinojson.org/v7/api/jsonvariant/remove/ // https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TChar> template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
enable_if_t<IsString<TChar*>::value> remove(TChar* key) const { void remove(TChar* key) const {
VariantData::removeMember(getData(), adaptString(key), VariantData::removeMember(getData(), adaptString(key),
getResourceManager()); getResourceManager());
} }
// Removes a member of the object. // Removes a member of the object.
// https://arduinojson.org/v7/api/jsonvariant/remove/ // https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TString> template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
enable_if_t<IsString<TString>::value> remove(const TString& key) const { void remove(const TString& key) const {
VariantData::removeMember(getData(), adaptString(key), VariantData::removeMember(getData(), adaptString(key),
getResourceManager()); getResourceManager());
} }
// Removes a member of the object or an element of the array. // Removes a member of the object or an element of the array.
// https://arduinojson.org/v7/api/jsonvariant/remove/ // https://arduinojson.org/v7/api/jsonvariant/remove/
template <typename TVariant> template <typename TVariant, enable_if_t<IsVariant<TVariant>::value, int> = 0>
enable_if_t<IsVariant<TVariant>::value> remove(const TVariant& key) const { void remove(const TVariant& key) const {
if (key.template is<size_t>()) if (key.template is<size_t>())
remove(key.template as<size_t>()); remove(key.template as<size_t>());
else else
@@ -167,43 +170,40 @@ class VariantRefBase : public VariantTag {
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariant/containskey/ // https://arduinojson.org/v7/api/jsonvariant/containskey/
template <typename TString> template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
enable_if_t<IsString<TString>::value, bool> containsKey( bool containsKey(const TString& key) const;
const TString& key) const;
// DEPRECATED: use obj["key"].is<T>() instead // DEPRECATED: use obj["key"].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariant/containskey/ // https://arduinojson.org/v7/api/jsonvariant/containskey/
template <typename TChar> template <typename TChar, enable_if_t<IsString<TChar*>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[\"key\"].is<T>() instead")
enable_if_t<IsString<TChar*>::value, bool> containsKey(TChar* key) const; bool containsKey(TChar* key) const;
// DEPRECATED: use obj[key].is<T>() instead // DEPRECATED: use obj[key].is<T>() instead
// https://arduinojson.org/v7/api/jsonvariant/containskey/ // https://arduinojson.org/v7/api/jsonvariant/containskey/
template <typename TVariant> template <typename TVariant, enable_if_t<IsVariant<TVariant>::value, int> = 0>
ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead") ARDUINOJSON_DEPRECATED("use obj[key].is<T>() instead")
enable_if_t<IsVariant<TVariant>::value, bool> containsKey( bool containsKey(const TVariant& key) const;
const TVariant& key) const;
// Gets or sets an object member. // Gets or sets an object member.
// https://arduinojson.org/v7/api/jsonvariant/subscript/ // https://arduinojson.org/v7/api/jsonvariant/subscript/
template <typename TString> template <typename TString, enable_if_t<IsString<TString>::value, int> = 0>
FORCE_INLINE FORCE_INLINE MemberProxy<TDerived, AdaptedString<TString>> operator[](
enable_if_t<IsString<TString>::value, MemberProxy<TDerived, TString>> const TString& key) const;
operator[](const TString& key) const;
// Gets or sets an object member. // Gets or sets an object member.
// https://arduinojson.org/v7/api/jsonvariant/subscript/ // https://arduinojson.org/v7/api/jsonvariant/subscript/
template <typename TChar> template <
FORCE_INLINE typename TChar,
enable_if_t<IsString<TChar*>::value, MemberProxy<TDerived, TChar*>> enable_if_t<IsString<TChar*>::value && !is_const<TChar>::value, int> = 0>
operator[](TChar* key) const; FORCE_INLINE MemberProxy<TDerived, AdaptedString<TChar*>> operator[](
TChar* key) const;
// Gets an object member or an array element. // Gets an object member or an array element.
// https://arduinojson.org/v7/api/jsonvariant/subscript/ // https://arduinojson.org/v7/api/jsonvariant/subscript/
template <typename TVariant> template <typename TVariant, enable_if_t<IsVariant<TVariant>::value, int> = 0>
enable_if_t<IsVariant<TVariant>::value, JsonVariantConst> operator[]( JsonVariantConst operator[](const TVariant& key) const {
const TVariant& key) const {
if (key.template is<size_t>()) if (key.template is<size_t>())
return operator[](key.template as<size_t>()); return operator[](key.template as<size_t>());
else else
@@ -294,19 +294,18 @@ class VariantRefBase : public VariantTag {
} }
template <typename TConverter, typename T> template <typename TConverter, typename T>
bool doSet(T&& value) const { bool doSet(const T& value) const {
return doSet<TConverter>( return doSet<TConverter>(
detail::forward<T>(value), value, is_same<typename function_traits<
is_same<typename function_traits<
decltype(&TConverter::toJson)>::return_type, decltype(&TConverter::toJson)>::return_type,
bool>{}); bool>{});
} }
template <typename TConverter, typename T> template <typename TConverter, typename T>
bool doSet(T&& value, false_type) const; bool doSet(const T& value, false_type) const;
template <typename TConverter, typename T> template <typename TConverter, typename T>
bool doSet(T&& value, true_type) const; bool doSet(const T& value, true_type) const;
ArduinoJson::JsonVariant getOrCreateVariant() const; ArduinoJson::JsonVariant getOrCreateVariant() const;
}; };

View File

@@ -67,34 +67,30 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
} }
template <typename TDerived> template <typename TDerived>
template <typename T> template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
inline enable_if_t<is_same<T, JsonVariant>::value, T> inline T VariantRefBase<TDerived>::add() const {
VariantRefBase<TDerived>::add() const {
return JsonVariant( return JsonVariant(
detail::VariantData::addElement(getOrCreateData(), getResourceManager()), detail::VariantData::addElement(getOrCreateData(), getResourceManager()),
getResourceManager()); getResourceManager());
} }
template <typename TDerived> template <typename TDerived>
template <typename TString> template <typename TString, enable_if_t<IsString<TString>::value, int>>
inline enable_if_t<IsString<TString>::value, bool> inline bool VariantRefBase<TDerived>::containsKey(const TString& key) const {
VariantRefBase<TDerived>::containsKey(const TString& key) const {
return VariantData::getMember(getData(), adaptString(key), return VariantData::getMember(getData(), adaptString(key),
getResourceManager()) != 0; getResourceManager()) != 0;
} }
template <typename TDerived> template <typename TDerived>
template <typename TChar> template <typename TChar, enable_if_t<IsString<TChar*>::value, int>>
inline enable_if_t<IsString<TChar*>::value, bool> inline bool VariantRefBase<TDerived>::containsKey(TChar* key) const {
VariantRefBase<TDerived>::containsKey(TChar* key) const {
return VariantData::getMember(getData(), adaptString(key), return VariantData::getMember(getData(), adaptString(key),
getResourceManager()) != 0; getResourceManager()) != 0;
} }
template <typename TDerived> template <typename TDerived>
template <typename TVariant> template <typename TVariant, enable_if_t<IsVariant<TVariant>::value, int>>
inline enable_if_t<IsVariant<TVariant>::value, bool> inline bool VariantRefBase<TDerived>::containsKey(const TVariant& key) const {
VariantRefBase<TDerived>::containsKey(const TVariant& key) const {
return containsKey(key.template as<const char*>()); return containsKey(key.template as<const char*>());
} }
@@ -119,26 +115,27 @@ inline bool VariantRefBase<TDerived>::is() const {
template <typename TDerived> template <typename TDerived>
inline ElementProxy<TDerived> VariantRefBase<TDerived>::operator[]( inline ElementProxy<TDerived> VariantRefBase<TDerived>::operator[](
size_t index) const { size_t index) const {
return ElementProxy<TDerived>(derived(), index); return {derived(), index};
} }
template <typename TDerived> template <typename TDerived>
template <typename TString> template <typename TChar,
inline enable_if_t<IsString<TString*>::value, MemberProxy<TDerived, TString*>> enable_if_t<IsString<TChar*>::value && !is_const<TChar>::value, int>>
VariantRefBase<TDerived>::operator[](TString* key) const { inline MemberProxy<TDerived, AdaptedString<TChar*>>
return MemberProxy<TDerived, TString*>(derived(), key); VariantRefBase<TDerived>::operator[](TChar* key) const {
return {derived(), adaptString(key)};
} }
template <typename TDerived> template <typename TDerived>
template <typename TString> template <typename TString, enable_if_t<IsString<TString>::value, int>>
inline enable_if_t<IsString<TString>::value, MemberProxy<TDerived, TString>> inline MemberProxy<TDerived, AdaptedString<TString>>
VariantRefBase<TDerived>::operator[](const TString& key) const { VariantRefBase<TDerived>::operator[](const TString& key) const {
return MemberProxy<TDerived, TString>(derived(), key); return {derived(), adaptString(key)};
} }
template <typename TDerived> template <typename TDerived>
template <typename TConverter, typename T> template <typename TConverter, typename T>
inline bool VariantRefBase<TDerived>::doSet(T&& value, false_type) const { inline bool VariantRefBase<TDerived>::doSet(const T& value, false_type) const {
TConverter::toJson(value, getOrCreateVariant()); TConverter::toJson(value, getOrCreateVariant());
auto resources = getResourceManager(); auto resources = getResourceManager();
return resources && !resources->overflowed(); return resources && !resources->overflowed();
@@ -146,32 +143,29 @@ inline bool VariantRefBase<TDerived>::doSet(T&& value, false_type) const {
template <typename TDerived> template <typename TDerived>
template <typename TConverter, typename T> template <typename TConverter, typename T>
inline bool VariantRefBase<TDerived>::doSet(T&& value, true_type) const { inline bool VariantRefBase<TDerived>::doSet(const T& value, true_type) const {
return TConverter::toJson(value, getOrCreateVariant()); return TConverter::toJson(value, getOrCreateVariant());
} }
template <typename TDerived> template <typename TDerived>
template <typename T> template <typename T, enable_if_t<is_same<T, JsonArray>::value, int>>
inline enable_if_t<is_same<T, JsonArray>::value, JsonArray> inline JsonArray VariantRefBase<TDerived>::to() const {
VariantRefBase<TDerived>::to() const {
return JsonArray( return JsonArray(
VariantData::toArray(getOrCreateData(), getResourceManager()), VariantData::toArray(getOrCreateData(), getResourceManager()),
getResourceManager()); getResourceManager());
} }
template <typename TDerived> template <typename TDerived>
template <typename T> template <typename T, enable_if_t<is_same<T, JsonObject>::value, int>>
enable_if_t<is_same<T, JsonObject>::value, JsonObject> JsonObject VariantRefBase<TDerived>::to() const {
VariantRefBase<TDerived>::to() const {
return JsonObject( return JsonObject(
VariantData::toObject(getOrCreateData(), getResourceManager()), VariantData::toObject(getOrCreateData(), getResourceManager()),
getResourceManager()); getResourceManager());
} }
template <typename TDerived> template <typename TDerived>
template <typename T> template <typename T, enable_if_t<is_same<T, JsonVariant>::value, int>>
enable_if_t<is_same<T, JsonVariant>::value, JsonVariant> JsonVariant VariantRefBase<TDerived>::to() const {
VariantRefBase<TDerived>::to() const {
auto data = getOrCreateData(); auto data = getOrCreateData();
auto resources = getResourceManager(); auto resources = getResourceManager();
detail::VariantData::clear(data, resources); detail::VariantData::clear(data, resources);

View File

@@ -1,86 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Polyfills/limits.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
struct StringNode;
class VariantSlot {
// CAUTION: same layout as VariantData
// we cannot use composition because it adds padding
// (+20% on ESP8266 for example)
VariantContent content_;
uint8_t flags_;
SlotId next_;
const char* key_;
public:
// Placement new
static void* operator new(size_t, void* p) noexcept {
return p;
}
static void operator delete(void*, void*) noexcept {}
VariantSlot() : flags_(0), next_(NULL_SLOT), key_(0) {}
VariantData* data() {
return reinterpret_cast<VariantData*>(&content_);
}
const VariantData* data() const {
return reinterpret_cast<const VariantData*>(&content_);
}
SlotId next() const {
return next_;
}
void setNext(SlotId slot) {
next_ = slot;
}
void setKey(const char* k) {
ARDUINOJSON_ASSERT(k);
flags_ &= VALUE_MASK;
key_ = k;
}
void setKey(StringNode* k) {
ARDUINOJSON_ASSERT(k);
flags_ |= OWNED_KEY_BIT;
key_ = k->data;
}
const char* key() const {
return key_;
}
bool ownsKey() const {
return (flags_ & OWNED_KEY_BIT) != 0;
}
};
inline VariantData* slotData(VariantSlot* slot) {
return reinterpret_cast<VariantData*>(slot);
}
// Returns the size (in bytes) of an array with n elements.
constexpr size_t sizeofArray(size_t n) {
return n * sizeof(VariantSlot);
}
// Returns the size (in bytes) of an object with n members.
constexpr size_t sizeofObject(size_t n) {
return n * sizeof(VariantSlot);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -4,8 +4,8 @@
#pragma once #pragma once
#define ARDUINOJSON_VERSION "7.2.1" #define ARDUINOJSON_VERSION "7.3.0"
#define ARDUINOJSON_VERSION_MAJOR 7 #define ARDUINOJSON_VERSION_MAJOR 7
#define ARDUINOJSON_VERSION_MINOR 2 #define ARDUINOJSON_VERSION_MINOR 3
#define ARDUINOJSON_VERSION_REVISION 1 #define ARDUINOJSON_VERSION_REVISION 0
#define ARDUINOJSON_VERSION_MACRO V721 #define ARDUINOJSON_VERSION_MACRO V730