mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-29 18:27:37 +02:00
Change string copy policy: only string literal are stored by pointer
This commit is contained in:
@ -6,6 +6,7 @@ HEAD
|
|||||||
|
|
||||||
* Fix support for NUL characters in `deserializeJson()`
|
* Fix support for NUL characters in `deserializeJson()`
|
||||||
* Make `ElementProxy` and `MemberProxy` non-copyable
|
* Make `ElementProxy` and `MemberProxy` non-copyable
|
||||||
|
* Change string copy policy: only string literal are stored by pointer
|
||||||
|
|
||||||
> ### BREAKING CHANGES
|
> ### BREAKING CHANGES
|
||||||
>
|
>
|
||||||
|
@ -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>()") {
|
||||||
|
@ -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;
|
||||||
|
@ -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>();
|
||||||
const 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
|
||||||
}
|
}
|
||||||
|
@ -15,23 +15,53 @@ using ArduinoJson::detail::sizeofArray;
|
|||||||
using ArduinoJson::detail::sizeofObject;
|
using ArduinoJson::detail::sizeofObject;
|
||||||
|
|
||||||
TEST_CASE("MemberProxy::add()") {
|
TEST_CASE("MemberProxy::add()") {
|
||||||
JsonDocument doc;
|
SpyingAllocator spy;
|
||||||
|
JsonDocument doc(&spy);
|
||||||
const auto& mp = doc["hello"];
|
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");
|
||||||
@ -39,6 +69,10 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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}");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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")),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -16,20 +16,29 @@ 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.isLinked() == 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.isLinked() == false);
|
||||||
|
CHECK(s.data() == p);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("null const char* + size") {
|
SECTION("null const char* + size") {
|
||||||
|
@ -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*)") {
|
||||||
|
@ -66,7 +66,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,
|
||||||
|
typename = detail::enable_if_t<!detail::is_const<T>::value>>
|
||||||
bool add(T* value) const {
|
bool add(T* value) const {
|
||||||
return detail::ArrayData::addValue(data_, value, resources_);
|
return detail::ArrayData::addValue(data_, value, resources_);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,8 @@ 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 TChar>
|
template <typename TChar,
|
||||||
|
typename = detail::enable_if_t<!detail::is_const<TChar>::value>>
|
||||||
bool set(TChar* src) {
|
bool set(TChar* src) {
|
||||||
return to<JsonVariant>().set(src);
|
return to<JsonVariant>().set(src);
|
||||||
}
|
}
|
||||||
@ -197,7 +198,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
// 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::enable_if_t<
|
||||||
detail::IsString<TChar*>::value,
|
detail::IsString<TChar*>::value && !detail::is_const<TChar>::value,
|
||||||
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>>>
|
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>>>
|
||||||
operator[](TChar* key) {
|
operator[](TChar* key) {
|
||||||
return {*this, detail::adaptString(key)};
|
return {*this, detail::adaptString(key)};
|
||||||
@ -215,7 +216,9 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
// 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 &&
|
||||||
|
!detail::is_const<TChar>::value,
|
||||||
|
JsonVariantConst>
|
||||||
operator[](TChar* key) const {
|
operator[](TChar* key) const {
|
||||||
return JsonVariantConst(
|
return JsonVariantConst(
|
||||||
data_.getMember(detail::adaptString(key), &resources_), &resources_);
|
data_.getMember(detail::adaptString(key), &resources_), &resources_);
|
||||||
@ -273,7 +276,8 @@ 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,
|
||||||
|
typename = detail::enable_if_t<!detail::is_const<TChar>::value>>
|
||||||
bool add(TChar* value) {
|
bool add(TChar* value) {
|
||||||
return data_.addValue(value, &resources_);
|
return data_.addValue(value, &resources_);
|
||||||
}
|
}
|
||||||
@ -289,7 +293,9 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
|
|||||||
// 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>
|
||||||
|
remove(TChar* key) {
|
||||||
detail::VariantData::removeMember(getData(), detail::adaptString(key),
|
detail::VariantData::removeMember(getData(), detail::adaptString(key),
|
||||||
getResourceManager());
|
getResourceManager());
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
// 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::enable_if_t<
|
||||||
detail::IsString<TChar*>::value,
|
detail::IsString<TChar*>::value && !detail::is_const<TChar>::value,
|
||||||
detail::MemberProxy<JsonObject, detail::AdaptedString<TChar*>>>
|
detail::MemberProxy<JsonObject, detail::AdaptedString<TChar*>>>
|
||||||
operator[](TChar* key) const {
|
operator[](TChar* key) const {
|
||||||
return {*this, detail::adaptString(key)};
|
return {*this, detail::adaptString(key)};
|
||||||
@ -175,8 +175,9 @@ class JsonObject : public detail::VariantOperators<JsonObject> {
|
|||||||
// https://arduinojson.org/v7/api/jsonobject/containskey/
|
// https://arduinojson.org/v7/api/jsonobject/containskey/
|
||||||
template <typename TChar>
|
template <typename TChar>
|
||||||
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(
|
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||||
TChar* key) const {
|
!detail::is_const<TChar>::value,
|
||||||
|
bool> containsKey(TChar* key) const {
|
||||||
return detail::ObjectData::getMember(data_, detail::adaptString(key),
|
return detail::ObjectData::getMember(data_, detail::adaptString(key),
|
||||||
resources_) != 0;
|
resources_) != 0;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,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 TChar>
|
template <typename TChar>
|
||||||
detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst>
|
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||||
|
!detail::is_const<TChar>::value,
|
||||||
|
JsonVariantConst>
|
||||||
operator[](TChar* key) const {
|
operator[](TChar* key) const {
|
||||||
return JsonVariantConst(detail::ObjectData::getMember(
|
return JsonVariantConst(detail::ObjectData::getMember(
|
||||||
data_, detail::adaptString(key), resources_),
|
data_, detail::adaptString(key), resources_),
|
||||||
|
@ -39,7 +39,7 @@ class MemberProxy
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename = enable_if_t<!is_const<T>::value>>
|
||||||
MemberProxy& operator=(T* src) {
|
MemberProxy& operator=(T* src) {
|
||||||
this->set(src);
|
this->set(src);
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -76,6 +76,15 @@ struct StringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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>
|
template <typename TChar, size_t N>
|
||||||
struct StringAdapter<TChar[N], enable_if_t<IsChar<TChar>::value>> {
|
struct StringAdapter<TChar[N], enable_if_t<IsChar<TChar>::value>> {
|
||||||
using AdaptedString = RamString;
|
using AdaptedString = RamString;
|
||||||
@ -86,15 +95,6 @@ struct StringAdapter<TChar[N], enable_if_t<IsChar<TChar>::value>> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
|
||||||
struct StringAdapter<const char*, void> {
|
|
||||||
using AdaptedString = RamString;
|
|
||||||
|
|
||||||
static AdaptedString adapt(const char* p) {
|
|
||||||
return AdaptedString(p, p ? ::strlen(p) : 0, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = RamString;
|
using AdaptedString = RamString;
|
||||||
|
@ -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
|
||||||
|
@ -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, typename = enable_if_t<!is_const<TChar>::value>>
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +70,4 @@ static void stringGetChars(TAdaptedString s, char* p, size_t n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using AdaptedString =
|
|
||||||
typename StringAdapter<remove_reference_t<T>>::AdaptedString;
|
|
||||||
|
|
||||||
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
ARDUINOJSON_END_PRIVATE_NAMESPACE
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -120,7 +120,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 TChar>
|
template <typename TChar>
|
||||||
detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst>
|
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||||
|
!detail::is_const<TChar>::value,
|
||||||
|
JsonVariantConst>
|
||||||
operator[](TChar* key) const {
|
operator[](TChar* key) const {
|
||||||
return JsonVariantConst(detail::VariantData::getMember(
|
return JsonVariantConst(detail::VariantData::getMember(
|
||||||
data_, detail::adaptString(key), resources_),
|
data_, detail::adaptString(key), resources_),
|
||||||
@ -153,8 +155,9 @@ class JsonVariantConst : public detail::VariantTag,
|
|||||||
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
|
// https://arduinojson.org/v7/api/jsonvariantconst/containskey/
|
||||||
template <typename TChar>
|
template <typename TChar>
|
||||||
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(
|
detail::enable_if_t<detail::IsString<TChar*>::value &&
|
||||||
TChar* key) const {
|
!detail::is_const<TChar>::value,
|
||||||
|
bool> containsKey(TChar* key) const {
|
||||||
return detail::VariantData::getMember(getData(), detail::adaptString(key),
|
return detail::VariantData::getMember(getData(), detail::adaptString(key),
|
||||||
resources_) != 0;
|
resources_) != 0;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
typename = detail::enable_if_t<!detail::is_const<T>::value>>
|
||||||
bool set(T* value) const {
|
bool set(T* value) const {
|
||||||
return doSet<Converter<T*>>(value);
|
return doSet<Converter<T*>>(value);
|
||||||
}
|
}
|
||||||
@ -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, typename = enable_if_t<!is_const<T>::value>>
|
||||||
bool add(T* value) const {
|
bool add(T* value) const {
|
||||||
return detail::VariantData::addValue(getOrCreateData(), value,
|
return detail::VariantData::addValue(getOrCreateData(), value,
|
||||||
getResourceManager());
|
getResourceManager());
|
||||||
@ -195,7 +198,7 @@ class VariantRefBase : public VariantTag {
|
|||||||
// 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 <typename TChar>
|
||||||
FORCE_INLINE enable_if_t<IsString<TChar*>::value,
|
FORCE_INLINE enable_if_t<IsString<TChar*>::value && !is_const<TChar>::value,
|
||||||
MemberProxy<TDerived, AdaptedString<TChar*>>>
|
MemberProxy<TDerived, AdaptedString<TChar*>>>
|
||||||
operator[](TChar* key) const;
|
operator[](TChar* key) const;
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ inline ElementProxy<TDerived> VariantRefBase<TDerived>::operator[](
|
|||||||
|
|
||||||
template <typename TDerived>
|
template <typename TDerived>
|
||||||
template <typename TChar>
|
template <typename TChar>
|
||||||
inline enable_if_t<IsString<TChar*>::value,
|
inline enable_if_t<IsString<TChar*>::value && !is_const<TChar>::value,
|
||||||
MemberProxy<TDerived, AdaptedString<TChar*>>>
|
MemberProxy<TDerived, AdaptedString<TChar*>>>
|
||||||
VariantRefBase<TDerived>::operator[](TChar* key) const {
|
VariantRefBase<TDerived>::operator[](TChar* key) const {
|
||||||
return {derived(), adaptString(key)};
|
return {derived(), adaptString(key)};
|
||||||
|
Reference in New Issue
Block a user