Compare commits

..

33 Commits

Author SHA1 Message Date
8f7e793f37 Set version to 7.3.0 2024-12-29 17:15:57 +01:00
254fa5712a Update the release scripts to include the breaking changes section 2024-12-29 16:38:41 +01:00
de05814294 Move public facing SFINAEs to template declarations 2024-12-23 17:44:52 +01:00
e33e78d202 Rename undocumented JsonString::isLinked() to isStatic() 2024-11-26 14:32:50 +01:00
ed5f890d28 Replace JsonString::Ownership with bool 2024-11-26 14:32:36 +01:00
8931651317 JsonString: change default ownership to Copied 2024-11-26 10:10:52 +01:00
c078957282 Remove unnecessary universal references 2024-11-25 12:25:59 +01:00
cf084ae6b4 JsonString: move adapter class in the same file 2024-11-25 12:25:59 +01:00
f02fcc96a2 JsonArray: remove redundant tests 2024-11-25 12:25:59 +01:00
594dc707cb Change string copy policy: only string literal are stored by pointer 2024-11-25 12:25:59 +01:00
5f8e3c0f0f Polyfills: test remove_cv 2024-11-25 12:25:59 +01:00
afc0a29c2c Polyfills: add decay 2024-11-25 12:25:59 +01:00
a256ec7fff RamString: use a bitfield to reduce size 2024-11-25 12:25:59 +01:00
019e8326b7 Implement JsonString from RamString 2024-11-25 11:13:39 +01:00
bee1095042 Merge all RAM string adapters 2024-11-25 10:46:35 +01:00
de59dce527 Fix typo in comments 2024-11-25 10:46:34 +01:00
57a9c50b38 Make ElementProxy and MemberProxy non-copyable 2024-11-25 10:46:21 +01:00
5e7653b36a Store adapted string in MemberProxy 2024-11-17 15:46:46 +01:00
8110058729 Fix support for NUL characters in deserializeJson() 2024-11-17 15:40:21 +01:00
7946ebe1a3 Wandbox: compile with gcc-head 2024-11-15 09:13:00 +01:00
f9fe8557f1 Set version to 7.2.1 2024-11-15 09:05:29 +01:00
e007d71b4f Fix operator[](variant) ignoring NUL characters 2024-11-14 14:56:47 +01:00
67a512a923 Clean up tests of adaptString() 2024-11-14 14:22:51 +01:00
9cf4f3871d Remove unused overload of stringCompare() and stringEquals() 2024-11-14 14:17:13 +01:00
31253dbe13 Add more tests with VLAs 2024-11-08 09:37:49 +01:00
1110d62128 Fix VLA support in JsonDocument::set() 2024-11-08 09:37:23 +01:00
c6c0649d70 Replace typedef with using 2024-10-23 15:54:08 +02:00
61ec2c4f95 Reduce boilerplate for failing build tests 2024-10-23 15:47:59 +02:00
0dd6231b3f Forbid deserializeJson(JsonArray|JsonObject, ...)
Closes #2135
2024-10-23 15:41:34 +02:00
20219d74f0 Tests: don't link FailingBuilds with catch 2024-10-23 15:04:52 +02:00
64cbaa6ff7 Remove the workaround for particle-iot/particle-cli#716
This reverts commit 1404b1ef70.
2024-09-28 09:58:48 +02:00
2512993617 Replace problematic symbol in idf_component.yml
Fixes #2131
2024-09-26 09:18:23 +02:00
48ee4a178b Use a const reference in is_convertible() 2024-09-21 09:22:12 +02:00
104 changed files with 1623 additions and 929 deletions

View File

@ -403,7 +403,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install Particle CLI
run: sudo npm install -g particle-cli particle-usb
run: sudo npm install -g particle-cli
- name: Login to Particle
run: particle login -t "${{ secrets.PARTICLE_TOKEN }}"
- name: Compile

View File

@ -21,7 +21,13 @@ jobs:
id: body
run: |
FILENAME=RELEASE.md
extras/scripts/get-release-body.sh ${{ steps.init.outputs.tag }} CHANGELOG.md | tee $FILENAME
tee $FILENAME <<END
## Changes
$(extras/scripts/extract_changes.awk CHANGELOG.md)
[View version history](https://github.com/bblanchon/ArduinoJson/blob/${{ steps.init.outputs.tag }}/CHANGELOG.md)
END
echo "filename=$FILENAME" >> $GITHUB_OUTPUT
- name: Amalgamate ArduinoJson.h
id: amalgamate_h
@ -61,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install
run: npm install -g particle-cli particle-usb
run: npm install -g particle-cli
- name: Checkout
uses: actions/checkout@v4
- name: Login

View File

@ -1,6 +1,53 @@
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)
------
* Forbid `deserializeJson(JsonArray|JsonObject, ...)` (issue #2135)
* Fix VLA support in `JsonDocument::set()`
* Fix `operator[](variant)` ignoring NUL characters
v7.2.0 (2024-09-18)
------

View File

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

View File

@ -1,4 +1,4 @@
version: 7.2.0.{build}
version: 7.3.0.{build}
environment:
matrix:
- 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")
---
$(awk '/\* /{ FOUND=1; print; next } { if (FOUND) exit}' "$CHANGELOG")
$(extras/scripts/extract_changes.awk "$CHANGELOG")
END

View File

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

View File

@ -5,15 +5,18 @@
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(catch)
link_libraries(ArduinoJson)
link_libraries(ArduinoJson catch)
# Failing builds should only link with ArduinoJson, not catch
add_subdirectory(FailingBuilds)
add_subdirectory(catch)
link_libraries(catch)
include_directories(Helpers)
add_subdirectory(Cpp17)
add_subdirectory(Cpp20)
add_subdirectory(Deprecated)
add_subdirectory(FailingBuilds)
add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray)
add_subdirectory(JsonArrayConst)

View File

@ -44,18 +44,30 @@ TEST_CASE("JsonDocument::containsKey()") {
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("support JsonVariant") {
SECTION("supports JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
doc["hello"] = "world";
REQUIRE(doc.containsKey(vla) == true);
}
#endif
}
TEST_CASE("MemberProxy::containsKey()") {
JsonDocument doc;
auto mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("containsKey(const char*)") {
mp["key"] = "value";
@ -70,6 +82,18 @@ TEST_CASE("MemberProxy::containsKey()") {
REQUIRE(mp.containsKey("key"_s) == true);
REQUIRE(mp.containsKey("key"_s) == true);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
mp["hello"] = "world";
REQUIRE(mp.containsKey(vla) == true);
}
#endif
}
TEST_CASE("JsonObject::containsKey()") {
@ -175,6 +199,18 @@ TEST_CASE("JsonVariant::containsKey()") {
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
var["hello"] = "world";
REQUIRE(var.containsKey(vla) == true);
}
#endif
}
TEST_CASE("JsonVariantConst::containsKey()") {

View File

@ -2,7 +2,11 @@
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
macro(build_should_fail target)
macro(add_failing_build source_file)
get_filename_component(target ${source_file} NAME_WE)
add_executable(${target} ${source_file})
set_target_properties(${target}
PROPERTIES
EXCLUDE_FROM_ALL TRUE
@ -16,21 +20,13 @@ macro(build_should_fail target)
set_tests_properties(${target}
PROPERTIES
WILL_FAIL TRUE
LABELS "WillFail;Catch"
LABELS "WillFail"
)
endmacro()
add_executable(Issue978 Issue978.cpp)
build_should_fail(Issue978)
add_executable(read_long_long read_long_long.cpp)
build_should_fail(read_long_long)
add_executable(write_long_long write_long_long.cpp)
build_should_fail(write_long_long)
add_executable(variant_as_char variant_as_char.cpp)
build_should_fail(variant_as_char)
add_executable(assign_char assign_char.cpp)
build_should_fail(assign_char)
add_failing_build(Issue978.cpp)
add_failing_build(read_long_long.cpp)
add_failing_build(write_long_long.cpp)
add_failing_build(variant_as_char.cpp)
add_failing_build(assign_char.cpp)
add_failing_build(deserialize_object.cpp)

View File

@ -0,0 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
// See issue #2135
int main() {
JsonObject obj;
deserializeJson(obj, "");
}

View File

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

View File

@ -17,31 +17,112 @@ TEST_CASE("JsonArray::add(T)") {
SECTION("int") {
array.add(123);
REQUIRE(123 == array[0].as<int>());
REQUIRE(array[0].is<int>());
REQUIRE(array[0].is<double>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("double") {
array.add(123.45);
REQUIRE(123.45 == array[0].as<double>());
REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<bool>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("bool") {
array.add(true);
REQUIRE(true == array[0].as<bool>());
REQUIRE(array[0].as<bool>() == true);
REQUIRE(array[0].is<bool>());
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*") {
const char* str = "hello";
array.add(str);
REQUIRE(str == array[0].as<std::string>());
REQUIRE(array[0].is<const char*>());
REQUIRE_FALSE(array[0].is<int>());
REQUIRE(array[0].as<std::string>() == "hello");
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
@ -52,7 +133,12 @@ TEST_CASE("JsonArray::add(T)") {
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
@ -99,61 +185,6 @@ TEST_CASE("JsonArray::add(T)") {
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>()") {

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>());
}
SECTION("const char*") {
const char* str = "hello";
SECTION("string literal") {
array[0] = "hello";
array[0] = str;
REQUIRE(str == array[0].as<const char*>());
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("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") {
JsonDocument doc2;
JsonArray arr2 = doc2.to<JsonArray>();
@ -114,58 +155,11 @@ TEST_CASE("JsonArray::operator[]") {
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>()") {
JsonObject obj = array[0].to<JsonObject>();
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") {
array[0] = 1;
array[1] = 2;

View File

@ -693,6 +693,15 @@ TEST_CASE("Filtering") {
"null",
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) {

View File

@ -6,6 +6,7 @@
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject;
@ -322,10 +323,11 @@ TEST_CASE("deserialize JSON object") {
SECTION("NUL in keys") {
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(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

@ -16,6 +16,7 @@ add_executable(JsonDocumentTests
nesting.cpp
overflowed.cpp
remove.cpp
set.cpp
shrinkToFit.cpp
size.cpp
subscript.cpp

View File

@ -5,40 +5,79 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
typedef ArduinoJson::detail::ElementProxy<JsonDocument&> ElementProxy;
using ElementProxy = ArduinoJson::detail::ElementProxy<JsonDocument&>;
TEST_CASE("ElementProxy::add()") {
JsonDocument doc;
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("add(int)") {
SECTION("integer") {
ep.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("add(const char*)") {
SECTION("string literal") {
ep.add("world");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("set(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";
ep.add(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
ep.add(vla);
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#endif
}
TEST_CASE("ElementProxy::clear()") {
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("size goes back to zero") {
ep.add(42);
@ -98,7 +137,7 @@ TEST_CASE("ElementProxy::operator==()") {
TEST_CASE("ElementProxy::remove()") {
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("remove(int)") {
ep.add(1);
@ -145,7 +184,7 @@ TEST_CASE("ElementProxy::remove()") {
TEST_CASE("ElementProxy::set()") {
JsonDocument doc;
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("set(int)") {
ep.set(42);
@ -166,12 +205,24 @@ TEST_CASE("ElementProxy::set()") {
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(VLA)") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
ep.set(vla);
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
#endif
}
TEST_CASE("ElementProxy::size()") {
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("returns 0") {
REQUIRE(ep.size() == 0);
@ -192,7 +243,7 @@ TEST_CASE("ElementProxy::size()") {
TEST_CASE("ElementProxy::operator[]") {
JsonDocument doc;
ElementProxy ep = doc[1];
const ElementProxy& ep = doc[1];
SECTION("set member") {
ep["world"] = 42;
@ -205,13 +256,25 @@ TEST_CASE("ElementProxy::operator[]") {
REQUIRE(doc.as<std::string>() == "[null,[null,null,42]]");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set VLA") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
ep[0] = vla;
REQUIRE(doc.as<std::string>() == "[null,[\"world\"]]");
}
#endif
}
TEST_CASE("ElementProxy cast to JsonVariantConst") {
JsonDocument doc;
doc[0] = "world";
const ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
JsonVariantConst var = ep;
@ -222,7 +285,7 @@ TEST_CASE("ElementProxy cast to JsonVariant") {
JsonDocument doc;
doc[0] = "world";
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
JsonVariant var = ep;

View File

@ -14,29 +14,72 @@
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
typedef ArduinoJson::detail::MemberProxy<JsonDocument&, const char*>
MemberProxy;
TEST_CASE("MemberProxy::add()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
SpyingAllocator spy;
JsonDocument doc(&spy);
const auto& mp = doc["hello"];
SECTION("add(int)") {
SECTION("integer") {
mp.add(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("add(const char*)") {
SECTION("string literal") {
mp.add("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
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
mp.add(vla);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#endif
}
TEST_CASE("MemberProxy::clear()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("size goes back to zero") {
mp.add(42);
@ -127,7 +170,7 @@ TEST_CASE("MemberProxy::operator|()") {
TEST_CASE("MemberProxy::remove()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("remove(int)") {
mp.add(1);
@ -174,7 +217,7 @@ TEST_CASE("MemberProxy::remove()") {
TEST_CASE("MemberProxy::set()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("set(int)") {
mp.set(42);
@ -195,11 +238,23 @@ TEST_CASE("MemberProxy::set()") {
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(vla)") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
mp.set(vla);
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
#endif
}
TEST_CASE("MemberProxy::size()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("returns 0") {
REQUIRE(mp.size() == 0);
@ -222,7 +277,7 @@ TEST_CASE("MemberProxy::size()") {
TEST_CASE("MemberProxy::operator[]") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("set member") {
mp["world"] = 42;
@ -241,7 +296,7 @@ TEST_CASE("MemberProxy cast to JsonVariantConst") {
JsonDocument doc;
doc["hello"] = "world";
const MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
JsonVariantConst var = mp;
@ -252,7 +307,7 @@ TEST_CASE("MemberProxy cast to JsonVariant") {
JsonDocument doc;
doc["hello"] = "world";
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
JsonVariant var = mp;

View File

@ -26,7 +26,7 @@ TEST_CASE("JsonDocument::add(T)") {
});
}
SECTION("const char*") {
SECTION("string literal") {
doc.add("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") {
doc.add("example"_s);
doc.add("example"_s);
@ -79,6 +90,24 @@ TEST_CASE("JsonDocument::add(T)") {
Allocate(sizeofString("example")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "example");
doc.add(vla);
doc.add(vla);
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE("example"_s == doc[0]);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
#endif
}
TEST_CASE("JsonDocument::add<T>()") {

View File

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

View File

@ -20,11 +20,21 @@ TEST_CASE("JsonDocument::remove()") {
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 *)") {
doc["a"] = 1;
doc["b"] = 2;
doc.remove("a");
doc.remove(static_cast<const char*>("a"));
REQUIRE(doc.as<std::string>() == "{\"b\":2}");
}

View File

@ -0,0 +1,104 @@
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::set()") {
SpyingAllocator 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") {
doc.set(42);
REQUIRE(doc.as<std::string>() == "42");
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("string literal") {
doc.set("example");
REQUIRE(doc.as<const char*>() == "example"_s);
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") {
doc.set("example"_s);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("char*") {
char value[] = "example";
doc.set(value);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("Arduino String") {
doc.set(String("example"));
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("Flash string") {
doc.set(F("example"));
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "example");
doc.set(vla);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
#endif
}

View File

@ -5,6 +5,7 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::operator[]") {
@ -12,28 +13,63 @@ TEST_CASE("JsonDocument::operator[]") {
const JsonDocument& cdoc = doc;
SECTION("object") {
deserializeJson(doc, "{\"hello\":\"world\"}");
doc["abc"_s] = "ABC";
doc["abc\0d"_s] = "ABCD";
SECTION("const char*") {
REQUIRE(doc["hello"] == "world");
REQUIRE(cdoc["hello"] == "world");
const char* key = "abc";
REQUIRE(doc[key] == "ABC");
REQUIRE(cdoc[key] == "ABC");
}
SECTION("string literal") {
REQUIRE(doc["abc"] == "ABC");
REQUIRE(cdoc["abc"] == "ABC");
REQUIRE(doc["abc\0d"] == "ABCD");
REQUIRE(cdoc["abc\0d"] == "ABCD");
}
SECTION("std::string") {
REQUIRE(doc["hello"_s] == "world");
REQUIRE(cdoc["hello"_s] == "world");
REQUIRE(doc["abc"_s] == "ABC");
REQUIRE(cdoc["abc"_s] == "ABC");
REQUIRE(doc["abc\0d"_s] == "ABCD");
REQUIRE(cdoc["abc\0d"_s] == "ABCD");
}
SECTION("JsonVariant") {
doc["key"] = "hello";
REQUIRE(doc[doc["key"]] == "world");
REQUIRE(cdoc[cdoc["key"]] == "world");
doc["key1"] = "abc";
doc["key2"] = "abc\0d"_s;
doc["key3"] = "foo";
CHECK(doc[doc["key1"]] == "ABC");
CHECK(doc[doc["key2"]] == "ABCD");
CHECK(doc[doc["key3"]] == nullptr);
CHECK(doc[doc["key4"]] == nullptr);
CHECK(cdoc[cdoc["key1"]] == "ABC");
CHECK(cdoc[cdoc["key2"]] == "ABCD");
CHECK(cdoc[cdoc["key3"]] == nullptr);
CHECK(cdoc[cdoc["key4"]] == nullptr);
}
SECTION("supports operator|") {
REQUIRE((doc["hello"] | "nope") == "world"_s);
REQUIRE((doc["world"] | "nope") == "nope"_s);
REQUIRE((doc["abc"] | "nope") == "ABC"_s);
REQUIRE((doc["def"] | "nope") == "nope"_s);
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
doc[vla] = "world";
REQUIRE(doc[vla] == "world");
REQUIRE(cdoc[vla] == "world");
}
#endif
}
SECTION("array") {
@ -67,3 +103,65 @@ TEST_CASE("JsonDocument automatically promotes to array") {
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

@ -158,7 +158,7 @@ TEST_CASE("JsonObject::operator[]") {
}
SECTION("should duplicate a non-static JsonString key") {
obj[JsonString("hello", JsonString::Copied)] = "world";
obj[JsonString("hello", false)] = "world";
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
@ -166,7 +166,7 @@ TEST_CASE("JsonObject::operator[]") {
}
SECTION("should not duplicate a static JsonString key") {
obj[JsonString("hello", JsonString::Linked)] = "world";
obj[JsonString("hello", true)] = "world";
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
@ -253,9 +253,15 @@ TEST_CASE("JsonObject::operator[]") {
SECTION("JsonVariant") {
obj["hello"] = "world";
doc["key"] = "hello";
obj["a\0b"_s] = "ABC";
REQUIRE(obj[obj["key"]] == "world");
REQUIRE(obj[obj["foo"]] == nullptr);
doc["key1"] = "hello";
doc["key2"] = "a\0b"_s;
doc["key3"] = "foo";
REQUIRE(obj[obj["key1"]] == "world");
REQUIRE(obj[obj["key2"]] == "ABC");
REQUIRE(obj[obj["key3"]] == nullptr);
REQUIRE(obj[obj["key4"]] == nullptr);
}
}

View File

@ -11,6 +11,7 @@
TEST_CASE("JsonObjectConst::operator[]") {
JsonDocument doc;
doc["hello"] = "world";
doc["a\0b"_s] = "ABC";
JsonObjectConst obj = doc.as<JsonObjectConst>();
SECTION("supports const char*") {
@ -19,6 +20,7 @@ TEST_CASE("JsonObjectConst::operator[]") {
SECTION("supports std::string") {
REQUIRE(obj["hello"_s] == "world"); // issue #2019
REQUIRE(obj["a\0b"_s] == "ABC");
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@ -28,13 +30,17 @@ TEST_CASE("JsonObjectConst::operator[]") {
char vla[i];
strcpy(vla, "hello");
REQUIRE("world"_s == obj[vla]);
REQUIRE(obj[vla] == "world"_s);
}
#endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(obj[obj["key"]] == "world");
REQUIRE(obj[obj["foo"]] == nullptr);
doc["key1"] = "hello";
doc["key2"] = "a\0b"_s;
doc["key3"] = "foo";
REQUIRE(obj[obj["key1"]] == "world");
REQUIRE(obj[obj["key2"]] == "ABC");
REQUIRE(obj[obj["key3"]] == nullptr);
REQUIRE(obj[obj["key4"]] == nullptr);
}
}

View File

@ -46,6 +46,18 @@ TEST_CASE("JsonVariant::add(T)") {
REQUIRE(var.as<std::string>() == "{\"val\":123}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
var.add(vla);
REQUIRE(var.as<std::string>() == "[\"hello\"]");
}
#endif
}
TEST_CASE("JsonVariant::add<T>()") {

View File

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

View File

@ -83,6 +83,23 @@ TEST_CASE("JsonVariant::remove(std::string)") {
REQUIRE(var.as<std::string>() == "{\"a\":1}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
TEST_CASE("JsonVariant::remove(VLA)") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var["a"] = 1;
var["b"] = 2;
size_t i = 16;
char vla[i];
strcpy(vla, "b");
var.remove("b"_s);
REQUIRE(var.as<std::string>() == "{\"a\":1}");
}
#endif
TEST_CASE("JsonVariant::remove(JsonVariant) from object") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();

View File

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

View File

@ -99,7 +99,7 @@ TEST_CASE("vector<int>") {
}
TEST_CASE("array<int, 2>") {
typedef std::array<int, 2> array_type;
using array_type = std::array<int, 2>;
SECTION("toJson") {
array_type v;

View File

@ -116,12 +116,19 @@ TEST_CASE("JsonVariant::operator[]") {
}
SECTION("use JsonVariant as key") {
object["a"] = "a";
object["b"] = "b";
object["c"] = "b";
object["a"] = "A";
object["ab"] = "AB";
object["ab\0c"_s] = "ABC";
object["key1"] = "a";
object["key2"] = "ab";
object["key3"] = "ab\0c"_s;
object["key4"] = "foo";
REQUIRE(var[var["c"]] == "b");
REQUIRE(var[var["d"]].isNull());
REQUIRE(var[var["key1"]] == "A");
REQUIRE(var[var["key2"]] == "AB");
REQUIRE(var[var["key3"]] == "ABC");
REQUIRE(var[var["key4"]].isNull());
REQUIRE(var[var["key5"]].isNull());
}
}

View File

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

View File

@ -50,20 +50,31 @@ TEST_CASE("JsonVariantConst::operator[]") {
SECTION("object") {
JsonObject object = doc.to<JsonObject>();
object["a"] = "A";
object["b"] = "B";
object["ab"_s] = "AB";
object["abc"_s] = "ABC";
object["abc\0d"_s] = "ABCD";
SECTION("supports const char*") {
REQUIRE("A"_s == var["a"]);
REQUIRE("B"_s == var["b"]);
REQUIRE(var["c"].isNull());
SECTION("string literal") {
REQUIRE(var["ab"] == "AB"_s);
REQUIRE(var["abc"] == "ABC"_s);
REQUIRE(var["abc\0d"] == "ABCD"_s);
REQUIRE(var["def"].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") {
REQUIRE("A"_s == var["a"_s]);
REQUIRE("B"_s == var["b"_s]);
REQUIRE(var["c"_s].isNull());
REQUIRE(var["ab"_s] == "AB"_s);
REQUIRE(var["abc"_s] == "ABC"_s);
REQUIRE(var["abc\0d"_s] == "ABCD"_s);
REQUIRE(var["def"_s].isNull());
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@ -71,16 +82,23 @@ TEST_CASE("JsonVariantConst::operator[]") {
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "a");
strcpy(vla, "abc");
REQUIRE("A"_s == var[vla]);
REQUIRE(var[vla] == "ABC"_s);
}
#endif
SECTION("supports JsonVariant") {
object["c"] = "b";
REQUIRE(var[var["c"]] == "B");
REQUIRE(var[var["d"]].isNull());
object["key1"] = "ab";
object["key2"] = "abc";
object["key3"] = "abc\0d"_s;
object["key4"] = "foo";
REQUIRE(var[var["key1"]] == "AB"_s);
REQUIRE(var[var["key2"]] == "ABC"_s);
REQUIRE(var[var["key3"]] == "ABCD"_s);
REQUIRE(var[var["key4"]].isNull());
REQUIRE(var[var["key5"]].isNull());
}
}
}

View File

@ -6,6 +6,7 @@ add_executable(MiscTests
arithmeticCompare.cpp
conflicts.cpp
issue1967.cpp
issue2129.cpp
JsonString.cpp
NoArduinoHeader.cpp
printable.cpp

View File

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

View File

@ -5,6 +5,7 @@
#include <Arduino.h>
#include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
@ -12,77 +13,110 @@
#include "custom_string.hpp"
#include "weird_strcmp.hpp"
using ArduinoJson::JsonString;
using namespace ArduinoJson::detail;
TEST_CASE("ZeroTerminatedRamString") {
SECTION("null") {
ZeroTerminatedRamString s = adaptString(static_cast<const char*>(0));
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*") {
auto s = adaptString(static_cast<const char*>(0));
CHECK(s.isNull() == true);
CHECK(s.size() == 0);
}
SECTION("non-null") {
ZeroTerminatedRamString s = adaptString("bravo");
SECTION("non-null const char*") {
const char* p = "bravo";
auto s = adaptString(p);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
CHECK(s.data() == p);
}
}
TEST_CASE("SizedRamString") {
SECTION("null") {
SizedRamString s = adaptString(static_cast<const char*>(0), 10);
SECTION("null const char* + size") {
auto s = adaptString(static_cast<const char*>(0), 10);
CHECK(s.isNull() == true);
CHECK(s.isStatic() == false);
}
SECTION("non-null") {
SizedRamString s = adaptString("bravo", 5);
SECTION("non-null const char* + size") {
auto s = adaptString("bravo", 5);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
}
}
TEST_CASE("FlashString") {
SECTION("null") {
FlashString s = adaptString(static_cast<const __FlashStringHelper*>(0));
SECTION("null Flash string") {
auto s = adaptString(static_cast<const __FlashStringHelper*>(0));
CHECK(s.isNull() == true);
CHECK(s.size() == 0);
CHECK(s.isStatic() == false);
}
SECTION("non-null") {
FlashString s = adaptString(F("bravo"));
SECTION("non-null Flash string") {
auto s = adaptString(F("bravo"));
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
}
}
TEST_CASE("std::string") {
std::string orig("bravo");
SizedRamString s = adaptString(orig);
SECTION("std::string") {
std::string orig("bravo");
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
}
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
}
TEST_CASE("Arduino String") {
::String orig("bravo");
SizedRamString s = adaptString(orig);
SECTION("Arduino String") {
::String orig("bravo");
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
}
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
}
TEST_CASE("custom_string") {
custom_string orig("bravo");
SizedRamString s = adaptString(orig);
SECTION("custom_string") {
custom_string orig("bravo");
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
}
SECTION("JsonString linked") {
JsonString orig("hello", true);
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == true);
}
SECTION("JsonString copied") {
JsonString orig("hello", false);
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isStatic() == false);
}
}
struct EmptyStruct {};
@ -97,6 +131,7 @@ TEST_CASE("IsString<T>") {
CHECK(IsString<::String>::value == true);
CHECK(IsString<::StringSumHelper>::value == true);
CHECK(IsString<const EmptyStruct*>::value == false);
CHECK(IsString<JsonString>::value == true);
}
TEST_CASE("stringCompare") {

View File

@ -211,6 +211,23 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_enum<bool>::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") {

View File

@ -8,4 +8,4 @@
struct custom_char_traits : std::char_traits<char> {};
typedef std::basic_string<char, custom_char_traits> custom_string;
using custom_string = std::basic_string<char, custom_char_traits>;

View File

@ -0,0 +1,55 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
template <typename T>
class Nullable {
public:
Nullable() : value_{} {}
Nullable(T value) : value_{value} {}
operator T() const {
return value_;
}
operator T&() {
return value_;
}
bool is_valid() const {
return value_ != invalid_value_;
}
T value() const {
return value_;
}
private:
T value_;
static T invalid_value_;
};
template <>
float Nullable<float>::invalid_value_ = std::numeric_limits<float>::lowest();
template <typename T>
void convertToJson(const Nullable<T>& src, JsonVariant dst) {
if (src.is_valid()) {
dst.set(src.value());
} else {
dst.clear();
}
}
TEST_CASE("Issue #2129") {
Nullable<float> nullable_value = Nullable<float>{123.4f};
JsonDocument doc;
doc["value"] = nullable_value;
REQUIRE(doc["value"].as<float>() == Approx(123.4f));
}

View File

@ -139,7 +139,8 @@ TEST_CASE("serialize MsgPack value") {
SECTION("str 32") {
std::string shortest(65536, '?');
checkVariant(shortest.c_str(), "\xDB\x00\x01\x00\x00"_s + shortest);
checkVariant(JsonString(shortest.c_str(), true), // force store by pointer
"\xDB\x00\x01\x00\x00"_s + shortest);
}
SECTION("serialized(const char*)") {

View File

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

View File

@ -1,13 +1,13 @@
{
"name": "ArduinoJson",
"keywords": "json, rest, http, web",
"description": "A simple and efficient JSON library for embedded C++. ⭐ 6690 stars on GitHub! Supports serialization, deserialization, MessagePack, streams, filtering, and more. Fully tested and documented.",
"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",
"repository": {
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "7.2.0",
"version": "7.3.0",
"authors": {
"name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr"

View File

@ -1,9 +1,9 @@
name=ArduinoJson
version=7.2.0
version=7.3.0
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.
paragraph=⭐ 6690 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
url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties
architectures=*

View File

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

View File

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

View File

@ -15,13 +15,18 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
public VariantOperators<ElementProxy<TUpstream>> {
friend class VariantAttorney;
friend class VariantRefBase<ElementProxy<TUpstream>>;
template <typename, typename>
friend class MemberProxy;
template <typename>
friend class ElementProxy;
public:
ElementProxy(TUpstream upstream, size_t index)
: upstream_(upstream), index_(index) {}
ElementProxy(const ElementProxy& src)
: upstream_(src.upstream_), index_(src.index_) {}
ElementProxy& operator=(const ElementProxy& src) {
this->set(src);
return *this;
@ -40,6 +45,11 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
}
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 {
return VariantAttorney::getResourceManager(upstream_);
}

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@
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>
struct Reader {
public:

View File

@ -24,14 +24,10 @@ struct first_or_void<T, Rest...> {
// A meta-function that returns true if T is a valid destination type for
// deserialize()
template <class T, class = void>
struct is_deserialize_destination : false_type {};
template <class T>
struct is_deserialize_destination<
T, enable_if_t<is_same<decltype(VariantAttorney::getResourceManager(
detail::declval<T&>())),
ResourceManager*>::value>> : true_type {};
using is_deserialize_destination =
bool_constant<is_base_of<JsonDocument, remove_cv_t<T>>::value ||
IsVariant<T>::value>;
template <typename TDestination>
inline void shrinkJsonDocument(TDestination&) {
@ -59,10 +55,11 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader,
return err;
}
template <template <typename> class TDeserializer, typename TDestination,
typename TStream, typename... Args,
typename = enable_if_t< // issue #1897
!is_integral<typename first_or_void<Args...>::type>::value>>
template <
template <typename> class TDeserializer, typename TDestination,
typename TStream, typename... Args,
enable_if_t< // issue #1897
!is_integral<typename first_or_void<Args...>::type>::value, int> = 0>
DeserializationError deserialize(TDestination&& dst, TStream&& input,
Args... args) {
return doDeserialize<TDeserializer>(
@ -72,7 +69,7 @@ DeserializationError deserialize(TDestination&& dst, TStream&& input,
template <template <typename> class TDeserializer, typename TDestination,
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,
Size inputSize, Args... args) {
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
template <typename T>
JsonDocument(
const T& src, Allocator* alloc = detail::DefaultAllocator::instance(),
detail::enable_if_t<detail::IsVariant<T>::value ||
detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value>* = 0)
template <typename T,
detail::enable_if_t<detail::IsVariant<T>::value ||
detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value,
int> = 0>
JsonDocument(const T& src,
Allocator* alloc = detail::DefaultAllocator::instance())
: JsonDocument(alloc) {
set(src);
}
@ -136,9 +137,18 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/
template <typename T>
detail::enable_if_t<!detail::is_base_of<JsonDocument, T>::value, bool> set(
const T& src) {
template <
typename T,
detail::enable_if_t<!detail::is_base_of<JsonDocument, T>::value, int> = 0>
bool set(const T& src) {
return to<JsonVariant>().set(src);
}
// Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/
template <typename TChar,
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
bool set(TChar* src) {
return to<JsonVariant>().set(src);
}
@ -160,64 +170,67 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// DEPRECATED: use obj[key].is<T>() instead
// 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")
detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
const TString& key) const {
bool containsKey(const TString& key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0;
}
// DEPRECATED: use obj[key].is<T>() instead
// 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")
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const {
bool containsKey(const TVariant& key) const {
return containsKey(key.template as<const char*>());
}
// Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString>
detail::enable_if_t<detail::IsString<TString>::value,
detail::MemberProxy<JsonDocument&, TString>>
operator[](const TString& key) {
return {*this, key};
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TString>> operator[](
const TString& key) {
return {*this, detail::adaptString(key)};
}
// Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar>
detail::enable_if_t<detail::IsString<TChar*>::value,
detail::MemberProxy<JsonDocument&, TChar*>>
operator[](TChar* key) {
return {*this, key};
template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
detail::MemberProxy<JsonDocument&, detail::AdaptedString<TChar*>> operator[](
TChar* key) {
return {*this, detail::adaptString(key)};
}
// Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString>
detail::enable_if_t<detail::IsString<TString>::value, JsonVariantConst>
operator[](const TString& key) const {
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
JsonVariantConst operator[](const TString& key) const {
return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_);
}
// Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar>
detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst>
operator[](TChar* key) const {
template <typename TChar,
detail::enable_if_t<detail::IsString<TChar*>::value &&
!detail::is_const<TChar>::value,
int> = 0>
JsonVariantConst operator[](TChar* key) const {
return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_);
}
// Gets or sets a root array's element.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename T>
detail::enable_if_t<detail::is_integral<T>::value,
detail::ElementProxy<JsonDocument&>>
operator[](T index) {
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
detail::ElementProxy<JsonDocument&> operator[](T index) {
return {*this, size_t(index)};
}
@ -229,11 +242,11 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TVariant>
detail::enable_if_t<detail::IsVariant<TVariant>::value, JsonVariantConst>
operator[](const TVariant& key) const {
if (key.template is<const char*>())
return operator[](key.template as<const char*>());
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
JsonVariantConst operator[](const TVariant& key) const {
if (key.template is<JsonString>())
return operator[](key.template as<JsonString>());
if (key.template is<size_t>())
return operator[](key.template as<size_t>());
return {};
@ -242,16 +255,18 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Appends a new (empty) element to the root array.
// Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename T>
detail::enable_if_t<!detail::is_same<T, JsonVariant>::value, T> add() {
template <typename T, detail::enable_if_t<
!detail::is_same<T, JsonVariant>::value, int> = 0>
T add() {
return add<JsonVariant>().to<T>();
}
// Appends a new (null) element to the root array.
// Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename T>
detail::enable_if_t<detail::is_same<T, JsonVariant>::value, T> add() {
template <typename T, detail::enable_if_t<
detail::is_same<T, JsonVariant>::value, int> = 0>
JsonVariant add() {
return JsonVariant(data_.addElement(&resources_), &resources_);
}
@ -264,41 +279,46 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/
template <typename TChar>
template <typename TChar,
detail::enable_if_t<!detail::is_const<TChar>::value, int> = 0>
bool add(TChar* value) {
return data_.addValue(value, &resources_);
}
// Removes an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename T>
detail::enable_if_t<detail::is_integral<T>::value> remove(T index) {
template <typename T,
detail::enable_if_t<detail::is_integral<T>::value, int> = 0>
void remove(T index) {
detail::VariantData::removeElement(getData(), size_t(index),
getResourceManager());
}
// Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TChar>
detail::enable_if_t<detail::IsString<TChar*>::value> remove(TChar* key) {
template <typename TChar,
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),
getResourceManager());
}
// Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TString>
detail::enable_if_t<detail::IsString<TString>::value> remove(
const TString& key) {
template <typename TString,
detail::enable_if_t<detail::IsString<TString>::value, int> = 0>
void remove(const TString& key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager());
}
// Removes a member of the root object or an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TVariant>
detail::enable_if_t<detail::IsVariant<TVariant>::value> remove(
const TVariant& key) {
template <typename TVariant,
detail::enable_if_t<detail::IsVariant<TVariant>::value, int> = 0>
void remove(const TVariant& key) {
if (key.template is<const char*>())
remove(key.template as<const char*>());
if (key.template is<size_t>())

View File

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

View File

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

View File

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

View File

@ -105,7 +105,7 @@ class TextFormatter {
template <typename T>
enable_if_t<is_signed<T>::value> writeInteger(T value) {
typedef make_unsigned_t<T> unsigned_type;
using unsigned_type = make_unsigned_t<T>;
unsigned_type unsigned_value;
if (value < 0) {
writeRaw('-');

View File

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

View File

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

View File

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

View File

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

View File

@ -20,7 +20,7 @@ struct FloatParts {
template <typename TFloat>
inline int16_t normalize(TFloat& value) {
typedef FloatTraits<TFloat> traits;
using traits = FloatTraits<TFloat>;
int16_t powersOf10 = 0;
int8_t index = sizeof(TFloat) == 8 ? 8 : 5;

View File

@ -21,12 +21,12 @@ struct FloatTraits {};
template <typename T>
struct FloatTraits<T, 8 /*64bits*/> {
typedef uint64_t mantissa_type;
using mantissa_type = uint64_t;
static const short mantissa_bits = 52;
static const mantissa_type mantissa_max =
(mantissa_type(1) << mantissa_bits) - 1;
typedef int16_t exponent_type;
using exponent_type = int16_t;
static const exponent_type exponent_max = 308;
static pgm_ptr<T> positiveBinaryPowersOfTen() {
@ -105,12 +105,12 @@ struct FloatTraits<T, 8 /*64bits*/> {
template <typename T>
struct FloatTraits<T, 4 /*32bits*/> {
typedef uint32_t mantissa_type;
using mantissa_type = uint32_t;
static const short mantissa_bits = 23;
static const mantissa_type mantissa_max =
(mantissa_type(1) << mantissa_bits) - 1;
typedef int8_t exponent_type;
using exponent_type = int8_t;
static const exponent_type exponent_max = 38;
static pgm_ptr<T> positiveBinaryPowersOfTen() {

View File

@ -10,9 +10,9 @@
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
#if ARDUINOJSON_USE_DOUBLE
typedef double JsonFloat;
using JsonFloat = double;
#else
typedef float JsonFloat;
using JsonFloat = float;
#endif
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -12,11 +12,11 @@
ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
#if ARDUINOJSON_USE_LONG_LONG
typedef int64_t JsonInteger;
typedef uint64_t JsonUInt;
using JsonInteger = int64_t;
using JsonUInt = uint64_t;
#else
typedef long JsonInteger;
typedef unsigned long JsonUInt;
using JsonInteger = long;
using JsonUInt = unsigned long;
#endif
ARDUINOJSON_END_PUBLIC_NAMESPACE

View File

@ -103,9 +103,9 @@ class Number {
};
inline Number parseNumber(const char* s) {
typedef FloatTraits<JsonFloat> traits;
typedef largest_type<traits::mantissa_type, JsonUInt> mantissa_t;
typedef traits::exponent_type exponent_t;
using traits = FloatTraits<JsonFloat>;
using mantissa_t = largest_type<traits::mantissa_type, JsonUInt>;
using exponent_t = traits::exponent_type;
ARDUINOJSON_ASSERT(s != 0);

View File

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

View File

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

View File

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

View File

@ -15,17 +15,17 @@ struct uint_;
template <>
struct uint_<8> {
typedef uint8_t type;
using type = uint8_t;
};
template <>
struct uint_<16> {
typedef uint16_t type;
using type = uint16_t;
};
template <>
struct uint_<32> {
typedef uint32_t type;
using type = uint32_t;
};
template <int Bits>

View File

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

View File

@ -10,12 +10,12 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <bool Condition, class TrueType, class FalseType>
struct conditional {
typedef TrueType type;
using type = TrueType;
};
template <class TrueType, class FalseType>
struct conditional<false, TrueType, FalseType> {
typedef FalseType type;
using type = FalseType;
};
template <bool Condition, class TrueType, class FalseType>

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

@ -14,7 +14,7 @@ struct enable_if {};
template <typename T>
struct enable_if<true, T> {
typedef T type;
using type = T;
};
template <bool Condition, typename T = void>

View File

@ -13,7 +13,10 @@ struct integral_constant {
static const T value = v;
};
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
template <bool B>
using bool_constant = integral_constant<bool, B>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -27,7 +27,7 @@ struct is_convertible {
static int probe(To);
static char probe(...);
static From& from_;
static const From& from_;
public:
static const bool value = sizeof(probe(from_)) == sizeof(int);

View File

@ -11,11 +11,11 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// A meta-function that return the type T without the const modifier
template <typename T>
struct remove_const {
typedef T type;
using type = T;
};
template <typename T>
struct remove_const<const T> {
typedef T type;
using type = T;
};
template <typename T>

View File

@ -10,19 +10,19 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename T>
struct remove_cv {
typedef T type;
using type = T;
};
template <typename T>
struct remove_cv<const T> {
typedef T type;
using type = T;
};
template <typename T>
struct remove_cv<volatile T> {
typedef T type;
using type = T;
};
template <typename T>
struct remove_cv<const volatile T> {
typedef T type;
using type = T;
};
template <typename T>

View File

@ -11,11 +11,11 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// A meta-function that return the type T without the reference modifier.
template <typename T>
struct remove_reference {
typedef T type;
using type = T;
};
template <typename T>
struct remove_reference<T&> {
typedef T type;
using type = T;
};
template <typename T>

View File

@ -10,7 +10,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename T>
struct type_identity {
typedef T type;
using type = T;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -8,7 +8,7 @@
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>
class Writer {
public:

View File

@ -34,7 +34,7 @@ class FlashString {
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.isNull());
ARDUINOJSON_ASSERT(!b.isNull());
@ -43,7 +43,7 @@ class FlashString {
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.isNull());
ARDUINOJSON_ASSERT(!b.isNull());
@ -63,7 +63,7 @@ class FlashString {
::memcpy_P(p, s.str_, n);
}
bool isLinked() const {
bool isStatic() const {
return false;
}
@ -74,7 +74,7 @@ class FlashString {
template <>
struct StringAdapter<const __FlashStringHelper*, void> {
typedef FlashString AdaptedString;
using AdaptedString = FlashString;
static AdaptedString adapt(const __FlashStringHelper* s) {
return AdaptedString(s, s ? strlen_P(reinterpret_cast<const char*>(s)) : 0);
@ -83,7 +83,7 @@ struct StringAdapter<const __FlashStringHelper*, void> {
template <>
struct SizedStringAdapter<const __FlashStringHelper*, void> {
typedef FlashString AdaptedString;
using AdaptedString = FlashString;
static AdaptedString adapt(const __FlashStringHelper* s, size_t n) {
return AdaptedString(s, n);

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> {
typedef JsonStringAdapter AdaptedString;
static AdaptedString adapt(const JsonString& s) {
return AdaptedString(s);
}
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -17,91 +17,19 @@ template <typename T>
struct IsChar
: integral_constant<bool, is_integral<T>::value && sizeof(T) == 1> {};
class ZeroTerminatedRamString {
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_;
}
friend int stringCompare(ZeroTerminatedRamString a,
ZeroTerminatedRamString b) {
ARDUINOJSON_ASSERT(!a.isNull());
ARDUINOJSON_ASSERT(!b.isNull());
return ::strcmp(a.str_, b.str_);
}
friend bool stringEquals(ZeroTerminatedRamString a,
ZeroTerminatedRamString b) {
return stringCompare(a, b) == 0;
}
bool isLinked() const {
return false;
}
protected:
const char* str_;
};
template <typename TChar>
struct StringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
typedef ZeroTerminatedRamString AdaptedString;
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>> {
typedef ZeroTerminatedRamString AdaptedString;
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> {
typedef StaticStringAdapter AdaptedString;
static AdaptedString adapt(const char* p) {
return AdaptedString(p);
}
};
class SizedRamString {
class RamString {
public:
static const size_t typeSortKey = 2;
#if ARDUINOJSON_SIZEOF_POINTER <= 2
static constexpr size_t sizeMask = size_t(-1) >> 1;
#else
static constexpr size_t sizeMask = size_t(-1);
#endif
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 {
return !str_;
@ -121,18 +49,55 @@ class SizedRamString {
return str_;
}
bool isLinked() const {
return false;
bool isStatic() const {
return static_;
}
protected:
const char* str_;
#if ARDUINOJSON_SIZEOF_POINTER <= 2
// Use a bitfield only on 8-bit microcontrollers
size_t size_ : sizeof(size_t) * 8 - 1;
bool static_ : 1;
#else
size_t size_;
bool static_;
#endif
};
template <typename TChar>
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>
struct SizedStringAdapter<TChar*, enable_if_t<IsChar<TChar>::value>> {
typedef SizedRamString AdaptedString;
using AdaptedString = RamString;
static AdaptedString adapt(const TChar* p, size_t n) {
return AdaptedString(reinterpret_cast<const char*>(p), n);

View File

@ -15,7 +15,7 @@ struct StringAdapter<
T,
enable_if_t<(string_traits<T>::has_cstr || string_traits<T>::has_data) &&
(string_traits<T>::has_length || string_traits<T>::has_size)>> {
typedef SizedRamString AdaptedString;
using AdaptedString = RamString;
static AdaptedString adapt(const T& 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 {};
template <typename T>
struct IsString<T, void_t<typename StringAdapter<T>::AdaptedString>>
struct IsString<T, void_t<typename StringAdapterFor<T>::AdaptedString>>
: true_type {};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

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

View File

@ -4,8 +4,17 @@
#pragma once
#include <ArduinoJson/Polyfills/utility.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
// a meta function that tells if the type is a string literal (const char[N])
template <typename T>
struct IsStringLiteral : false_type {};
template <size_t N>
struct IsStringLiteral<const char (&)[N]> : true_type {};
template <typename TString, typename Enable = void>
struct StringAdapter;
@ -13,18 +22,25 @@ template <typename TString, typename Enable = void>
struct SizedStringAdapter;
template <typename TString>
typename StringAdapter<TString>::AdaptedString adaptString(const TString& s) {
return StringAdapter<TString>::adapt(s);
using StringAdapterFor =
StringAdapter<conditional_t<IsStringLiteral<TString>::value, TString,
remove_cv_t<remove_reference_t<TString>>>>;
template <typename T>
using AdaptedString = typename StringAdapterFor<T>::AdaptedString;
template <typename TString>
AdaptedString<TString> adaptString(TString&& s) {
return StringAdapterFor<TString>::adapt(detail::forward<TString>(s));
}
template <typename TChar>
typename StringAdapter<TChar*>::AdaptedString adaptString(TChar* p) {
template <typename TChar, enable_if_t<!is_const<TChar>::value, int> = 0>
AdaptedString<TChar*> adaptString(TChar* p) {
return StringAdapter<TChar*>::adapt(p);
}
template <typename TChar>
typename SizedStringAdapter<TChar*>::AdaptedString adaptString(TChar* p,
size_t n) {
AdaptedString<TChar*> adaptString(TChar* p, size_t n) {
return SizedStringAdapter<TChar*>::adapt(p, n);
}

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
class JsonVariantCopier {
public:
typedef bool result_type;
using result_type = bool;
JsonVariantCopier(JsonVariant dst) : dst_(dst) {}

View File

@ -13,7 +13,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TResult>
struct JsonVariantVisitor {
typedef TResult result_type;
using result_type = TResult;
template <typename T>
TResult visit(const T&) {

View File

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

View File

@ -13,7 +13,7 @@ ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TResult>
struct VariantDataVisitor {
typedef TResult result_type;
using result_type = TResult;
template <typename T>
TResult visit(const T&) {

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More