Update ArduinoJSON, esp-nimble-cpp, Arduino Core, ESP-IDF (#448)

* ArduinoJSON 7.1.0

* Update nimble and arduino core

* Update nuki_ble
This commit is contained in:
iranl
2024-08-11 11:20:31 +02:00
committed by GitHub
parent 4af90cbc79
commit 9d55c2173d
216 changed files with 6437 additions and 5705 deletions

View File

@@ -1,6 +1,21 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
v7.1.0 (2024-06-27)
------
* Add `ARDUINOJSON_STRING_LENGTH_SIZE` to the namespace name
* Add support for MsgPack binary (PR #2078 by @Sanae6)
* Add support for MsgPack extension
* Make string support even more generic (PR #2084 by @d-a-v)
* Optimize `deserializeMsgPack()`
* Allow using a `JsonVariant` as a key or index (issue #2080)
Note: works only for reading, not for writing
* Support `ElementProxy` and `MemberProxy` in `JsonDocument`'s constructor
* Don't add partial objects when allocation fails (issue #2081)
* Read MsgPack's 64-bit integers even if `ARDUINOJSON_USE_LONG_LONG` is `0`
(they are set to `null` if they don't fit in a `long`)
v7.0.4 (2024-03-12) v7.0.4 (2024-03-12)
------ ------

View File

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

46
lib/ArduinoJson/README Normal file
View File

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

View File

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

View File

@@ -1,3 +1,7 @@
if(NOT DEFINED COVERAGE)
set(COVERAGE OFF)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
add_compile_options( add_compile_options(
-pedantic -pedantic
@@ -30,7 +34,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
endif() endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8) AND(NOT ${COVERAGE})) if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9) AND(NOT ${COVERAGE}))
add_compile_options(-g -Og) add_compile_options(-g -Og)
else() # GCC 4.8 else() # GCC 4.8
add_compile_options( add_compile_options(

View File

@@ -52,7 +52,16 @@ macro(add_fuzzer name)
) )
endmacro() endmacro()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6) # Needs Clang 6+ to compile
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6)
if(DEFINED ENV{GITHUB_ACTIONS} AND CMAKE_CXX_COMPILER_VERSION MATCHES "^11\\.")
# Clang 11 fails on GitHub Actions with the following error:
# > ERROR: UndefinedBehaviorSanitizer failed to allocate 0x0 (0) bytes of SetAlternateSignalStack (error code: 22)
# > Sanitizer CHECK failed: /build/llvm-toolchain-11-mnvtwk/llvm-toolchain-11-11.1.0/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp:54 ((0 && "unable to mmap")) != (0) (0, 0)
message(WARNING "Fuzzing is disabled on GitHub Actions to workaround a bug in Clang 11")
return()
endif()
add_fuzzer(json) add_fuzzer(json)
add_fuzzer(msgpack) add_fuzzer(msgpack)
endif() endif()

View File

@@ -12,6 +12,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
#if !ARDUINOJSON_ENABLE_STRING_VIEW #if !ARDUINOJSON_ENABLE_STRING_VIEW
# error ARDUINOJSON_ENABLE_STRING_VIEW must be set to 1 # error ARDUINOJSON_ENABLE_STRING_VIEW must be set to 1
@@ -92,7 +93,7 @@ TEST_CASE("string_view") {
} }
SECTION("String containing NUL") { SECTION("String containing NUL") {
doc.set(std::string("hello\0world", 11)); doc.set("hello\0world"_s);
REQUIRE(doc.as<std::string_view>().size() == 11); REQUIRE(doc.as<std::string_view>().size() == 11);
REQUIRE(doc.as<std::string_view>() == std::string_view("hello\0world", 11)); REQUIRE(doc.as<std::string_view>() == std::string_view("hello\0world", 11));
} }

View File

@@ -7,6 +7,8 @@
#include <string> #include <string>
#include "Literals.hpp"
TEST_CASE("JsonDocument::createNestedArray()") { TEST_CASE("JsonDocument::createNestedArray()") {
JsonDocument doc; JsonDocument doc;
@@ -23,7 +25,7 @@ TEST_CASE("JsonDocument::createNestedArray()") {
} }
SECTION("createNestedArray(std::string)") { SECTION("createNestedArray(std::string)") {
JsonArray array = doc.createNestedArray(std::string("key")); JsonArray array = doc.createNestedArray("key"_s);
array.add(42); array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}"); REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
} }
@@ -59,7 +61,7 @@ TEST_CASE("JsonObject::createNestedArray()") {
} }
SECTION("createNestedArray(std::string)") { SECTION("createNestedArray(std::string)") {
JsonArray array = object.createNestedArray(std::string("key")); JsonArray array = object.createNestedArray("key"_s);
array.add(42); array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}"); REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
} }
@@ -93,7 +95,7 @@ TEST_CASE("JsonVariant::createNestedArray()") {
} }
SECTION("createNestedArray(std::string)") { SECTION("createNestedArray(std::string)") {
JsonArray array = variant.createNestedArray(std::string("key")); JsonArray array = variant.createNestedArray("key"_s);
array.add(42); array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}"); REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
} }

View File

@@ -7,6 +7,8 @@
#include <string> #include <string>
#include "Literals.hpp"
TEST_CASE("JsonDocument::createNestedObject()") { TEST_CASE("JsonDocument::createNestedObject()") {
JsonDocument doc; JsonDocument doc;
@@ -23,7 +25,7 @@ TEST_CASE("JsonDocument::createNestedObject()") {
} }
SECTION("createNestedObject(std::string)") { SECTION("createNestedObject(std::string)") {
JsonObject object = doc.createNestedObject(std::string("key")); JsonObject object = doc.createNestedObject("key"_s);
object["hello"] = "world"; object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}"); REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
} }
@@ -59,7 +61,7 @@ TEST_CASE("JsonObject::createNestedObject()") {
} }
SECTION("createNestedObject(std::string)") { SECTION("createNestedObject(std::string)") {
JsonObject nestedObject = object.createNestedObject(std::string("key")); JsonObject nestedObject = object.createNestedObject("key"_s);
nestedObject["hello"] = "world"; nestedObject["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}"); REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
} }
@@ -93,7 +95,7 @@ TEST_CASE("JsonVariant::createNestedObject()") {
} }
SECTION("createNestedObject(std::string)") { SECTION("createNestedObject(std::string)") {
JsonObject object = variant.createNestedObject(std::string("key")); JsonObject object = variant.createNestedObject("key"_s);
object["hello"] = "world"; object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}"); REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
} }

View File

@@ -10,6 +10,8 @@
#include <sstream> #include <sstream>
namespace {
struct FailingAllocator : ArduinoJson::Allocator { struct FailingAllocator : ArduinoJson::Allocator {
static FailingAllocator* instance() { static FailingAllocator* instance() {
static FailingAllocator allocator; static FailingAllocator allocator;
@@ -54,31 +56,31 @@ class AllocatorLogEntry {
inline AllocatorLogEntry Allocate(size_t s) { inline AllocatorLogEntry Allocate(size_t s) {
char buffer[32]; char buffer[32];
sprintf(buffer, "allocate(%zu)", s); snprintf(buffer, sizeof(buffer), "allocate(%zu)", s);
return AllocatorLogEntry(buffer); return AllocatorLogEntry(buffer);
} }
inline AllocatorLogEntry AllocateFail(size_t s) { inline AllocatorLogEntry AllocateFail(size_t s) {
char buffer[32]; char buffer[32];
sprintf(buffer, "allocate(%zu) -> nullptr", s); snprintf(buffer, sizeof(buffer), "allocate(%zu) -> nullptr", s);
return AllocatorLogEntry(buffer); return AllocatorLogEntry(buffer);
} }
inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) { inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) {
char buffer[32]; char buffer[32];
sprintf(buffer, "reallocate(%zu, %zu)", s1, s2); snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu)", s1, s2);
return AllocatorLogEntry(buffer); return AllocatorLogEntry(buffer);
} }
inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) { inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) {
char buffer[32]; char buffer[32];
sprintf(buffer, "reallocate(%zu, %zu) -> nullptr", s1, s2); snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu) -> nullptr", s1, s2);
return AllocatorLogEntry(buffer); return AllocatorLogEntry(buffer);
} }
inline AllocatorLogEntry Deallocate(size_t s) { inline AllocatorLogEntry Deallocate(size_t s) {
char buffer[32]; char buffer[32];
sprintf(buffer, "deallocate(%zu)", s); snprintf(buffer, sizeof(buffer), "deallocate(%zu)", s);
return AllocatorLogEntry(buffer); return AllocatorLogEntry(buffer);
} }
@@ -260,6 +262,7 @@ class TimebombAllocator : public ArduinoJson::Allocator {
size_t countdown_ = 0; size_t countdown_ = 0;
Allocator* upstream_; Allocator* upstream_;
}; };
} // namespace
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) { inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
return sizeof(ArduinoJson::detail::VariantPool) * n; return sizeof(ArduinoJson::detail::VariantPool) * n;

View File

@@ -0,0 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <string>
// the space before _s is required by GCC 4.8
inline std::string operator"" _s(const char* str, size_t len) {
return std::string(str, len);
}

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
@@ -51,7 +52,7 @@ TEST_CASE("JsonArray::add(T)") {
array.add(vla); array.add(vla);
REQUIRE(std::string("world") == array[0]); REQUIRE("world"_s == array[0]);
} }
#endif #endif
@@ -115,7 +116,7 @@ TEST_CASE("JsonArray::add(T)") {
} }
SECTION("should duplicate std::string") { SECTION("should duplicate std::string") {
array.add(std::string("world")); array.add("world"_s);
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString("world")), Allocate(sizeofString("world")),
@@ -139,7 +140,7 @@ TEST_CASE("JsonArray::add(T)") {
} }
SECTION("should duplicate serialized(std::string)") { SECTION("should duplicate serialized(std::string)") {
array.add(serialized(std::string("{}"))); array.add(serialized("{}"_s));
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString("{}")), Allocate(sizeofString("{}")),
@@ -147,7 +148,7 @@ TEST_CASE("JsonArray::add(T)") {
} }
SECTION("should duplicate serialized(std::string)") { SECTION("should duplicate serialized(std::string)") {
array.add(serialized(std::string("\0XX", 3))); array.add(serialized("\0XX"_s));
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString(" XX")), Allocate(sizeofString(" XX")),
@@ -179,3 +180,52 @@ TEST_CASE("JsonArray::add<T>()") {
REQUIRE(doc.as<std::string>() == "[42]"); REQUIRE(doc.as<std::string>() == "[42]");
} }
} }
TEST_CASE("JsonObject::add(JsonObject) ") {
JsonDocument doc1;
doc1["key1"_s] = "value1"_s;
TimebombAllocator allocator(10);
SpyingAllocator spy(&allocator);
JsonDocument doc2(&spy);
JsonArray array = doc2.to<JsonArray>();
SECTION("success") {
bool result = array.add(doc1.as<JsonObject>());
REQUIRE(result == true);
REQUIRE(doc2.as<std::string>() == "[{\"key1\":\"value1\"}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("key1")),
Allocate(sizeofString("value1")),
});
}
SECTION("partial failure") { // issue #2081
allocator.setCountdown(2);
bool result = array.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("key1")),
AllocateFail(sizeofString("value1")),
Deallocate(sizeofString("key1")),
});
}
SECTION("complete failure") {
allocator.setCountdown(0);
bool result = array.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
}

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("copyArray()") { TEST_CASE("copyArray()") {
SECTION("int[] -> JsonArray") { SECTION("int[] -> JsonArray") {
@@ -18,7 +19,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(array, json); serializeJson(array, json);
CHECK(std::string("[1,2,3]") == json); CHECK("[1,2,3]"_s == json);
} }
SECTION("std::string[] -> JsonArray") { SECTION("std::string[] -> JsonArray") {
@@ -31,7 +32,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(array, json); serializeJson(array, json);
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json); CHECK("[\"a\",\"b\",\"c\"]"_s == json);
} }
SECTION("const char*[] -> JsonArray") { SECTION("const char*[] -> JsonArray") {
@@ -44,7 +45,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(array, json); serializeJson(array, json);
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json); CHECK("[\"a\",\"b\",\"c\"]"_s == json);
} }
SECTION("const char[][] -> JsonArray") { SECTION("const char[][] -> JsonArray") {
@@ -57,7 +58,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(array, json); serializeJson(array, json);
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json); CHECK("[\"a\",\"b\",\"c\"]"_s == json);
} }
SECTION("const char[][] -> JsonDocument") { SECTION("const char[][] -> JsonDocument") {
@@ -69,7 +70,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(doc, json); serializeJson(doc, json);
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json); CHECK("[\"a\",\"b\",\"c\"]"_s == json);
} }
SECTION("const char[][] -> MemberProxy") { SECTION("const char[][] -> MemberProxy") {
@@ -81,7 +82,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(doc, json); serializeJson(doc, json);
CHECK(std::string("{\"data\":[\"a\",\"b\",\"c\"]}") == json); CHECK("{\"data\":[\"a\",\"b\",\"c\"]}"_s == json);
} }
SECTION("int[] -> JsonDocument") { SECTION("int[] -> JsonDocument") {
@@ -93,7 +94,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(doc, json); serializeJson(doc, json);
CHECK(std::string("[1,2,3]") == json); CHECK("[1,2,3]"_s == json);
} }
SECTION("int[] -> MemberProxy") { SECTION("int[] -> MemberProxy") {
@@ -105,7 +106,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(doc, json); serializeJson(doc, json);
CHECK(std::string("{\"data\":[1,2,3]}") == json); CHECK("{\"data\":[1,2,3]}"_s == json);
} }
SECTION("int[] -> JsonArray, but not enough memory") { SECTION("int[] -> JsonArray, but not enough memory") {
@@ -127,7 +128,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(array, json); serializeJson(array, json);
CHECK(std::string("[[1,2,3],[4,5,6]]") == json); CHECK("[[1,2,3],[4,5,6]]"_s == json);
} }
SECTION("int[][] -> MemberProxy") { SECTION("int[][] -> MemberProxy") {
@@ -139,7 +140,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(doc, json); serializeJson(doc, json);
CHECK(std::string("{\"data\":[[1,2,3],[4,5,6]]}") == json); CHECK("{\"data\":[[1,2,3],[4,5,6]]}"_s == json);
} }
SECTION("int[][] -> JsonDocument") { SECTION("int[][] -> JsonDocument") {
@@ -151,7 +152,7 @@ TEST_CASE("copyArray()") {
CHECK(ok); CHECK(ok);
serializeJson(doc, json); serializeJson(doc, json);
CHECK(std::string("[[1,2,3],[4,5,6]]") == json); CHECK("[[1,2,3],[4,5,6]]"_s == json);
} }
SECTION("int[][] -> JsonArray, but not enough memory") { SECTION("int[][] -> JsonArray, but not enough memory") {
@@ -223,9 +224,9 @@ TEST_CASE("copyArray()") {
size_t result = copyArray(array, destination); size_t result = copyArray(array, destination);
CHECK(3 == result); CHECK(3 == result);
CHECK(std::string("a12345") == destination[0]); CHECK("a12345"_s == destination[0]);
CHECK(std::string("b123456") == destination[1]); CHECK("b123456"_s == destination[1]);
CHECK(std::string("c123456") == destination[2]); // truncated CHECK("c123456"_s == destination[2]); // truncated
CHECK(std::string("") == destination[3]); CHECK(std::string("") == destination[3]);
} }

View File

@@ -88,6 +88,15 @@ TEST_CASE("JsonArray::remove()") {
JsonArray unboundArray; JsonArray unboundArray;
unboundArray.remove(unboundArray.begin()); unboundArray.remove(unboundArray.begin());
} }
SECTION("use JsonVariant as index") {
array.remove(array[3]); // no effect with null variant
array.remove(array[0]); // remove element at index 1
REQUIRE(2 == array.size());
REQUIRE(array[0] == 1);
REQUIRE(array[1] == 3);
}
} }
TEST_CASE("Removed elements are recycled") { TEST_CASE("Removed elements are recycled") {

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
static void eraseString(std::string& str) { static void eraseString(std::string& str) {
char* p = const_cast<char*>(str.c_str()); char* p = const_cast<char*>(str.c_str());
while (*p) while (*p)
@@ -19,7 +21,7 @@ TEST_CASE("std::string") {
std::string value("hello"); std::string value("hello");
array.add(value); array.add(value);
eraseString(value); eraseString(value);
REQUIRE(std::string("hello") == array[0]); REQUIRE("hello"_s == array[0]);
} }
SECTION("operator[]") { SECTION("operator[]") {
@@ -27,6 +29,6 @@ TEST_CASE("std::string") {
array.add("hello"); array.add("hello");
array[0] = value; array[0] = value;
eraseString(value); eraseString(value);
REQUIRE(std::string("world") == array[0]); REQUIRE("world"_s == array[0]);
} }
} }

View File

@@ -7,6 +7,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonArray::operator[]") { TEST_CASE("JsonArray::operator[]") {
SpyingAllocator spy; SpyingAllocator spy;
@@ -129,7 +130,7 @@ TEST_CASE("JsonArray::operator[]") {
} }
SECTION("should duplicate std::string") { SECTION("should duplicate std::string") {
array[0] = std::string("world"); array[0] = "world"_s;
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString("world")), Allocate(sizeofString("world")),
@@ -150,7 +151,7 @@ TEST_CASE("JsonArray::operator[]") {
array.add("hello"); array.add("hello");
array[0].set(vla); array[0].set(vla);
REQUIRE(std::string("world") == array[0]); REQUIRE("world"_s == array[0]);
} }
SECTION("operator=(VLA)") { SECTION("operator=(VLA)") {
@@ -161,7 +162,16 @@ TEST_CASE("JsonArray::operator[]") {
array.add("hello"); array.add("hello");
array[0] = vla; array[0] = vla;
REQUIRE(std::string("world") == array[0]); REQUIRE("world"_s == array[0]);
} }
#endif #endif
SECTION("Use a JsonVariant as index") {
array[0] = 1;
array[1] = 2;
array[2] = 3;
REQUIRE(array[array[1]] == 3);
REQUIRE(array[array[3]] == nullptr);
}
} }

View File

@@ -13,8 +13,15 @@ TEST_CASE("JsonArrayConst::operator[]") {
doc.add(2); doc.add(2);
doc.add(3); doc.add(3);
SECTION("int") {
REQUIRE(1 == arr[0].as<int>()); REQUIRE(1 == arr[0].as<int>());
REQUIRE(2 == arr[1].as<int>()); REQUIRE(2 == arr[1].as<int>());
REQUIRE(3 == arr[2].as<int>()); REQUIRE(3 == arr[2].as<int>());
REQUIRE(0 == arr[3].as<int>()); REQUIRE(0 == arr[3].as<int>());
} }
SECTION("JsonVariant") {
REQUIRE(2 == arr[arr[0]].as<int>());
REQUIRE(0 == arr[arr[3]].as<int>());
}
}

View File

@@ -8,6 +8,7 @@
#include <string> #include <string>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -15,7 +16,7 @@ using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserializeJson(JsonDocument&)") { TEST_CASE("deserializeJson(JsonDocument&)") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc.add(std::string("hello")); doc.add("hello"_s);
spy.clearLog(); spy.clearLog();
auto err = deserializeJson(doc, "[42]"); auto err = deserializeJson(doc, "[42]");
@@ -34,7 +35,7 @@ TEST_CASE("deserializeJson(JsonVariant)") {
SECTION("variant is bound") { SECTION("variant is bound") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc.add(std::string("hello")); doc.add("hello"_s);
spy.clearLog(); spy.clearLog();
JsonVariant variant = doc[0]; JsonVariant variant = doc[0];
@@ -60,7 +61,7 @@ TEST_CASE("deserializeJson(JsonVariant)") {
TEST_CASE("deserializeJson(ElementProxy)") { TEST_CASE("deserializeJson(ElementProxy)") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc.add(std::string("hello")); doc.add("hello"_s);
spy.clearLog(); spy.clearLog();
SECTION("element already exists") { SECTION("element already exists") {
@@ -85,7 +86,7 @@ TEST_CASE("deserializeJson(ElementProxy)") {
TEST_CASE("deserializeJson(MemberProxy)") { TEST_CASE("deserializeJson(MemberProxy)") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc[std::string("hello")] = std::string("world"); doc["hello"_s] = "world"_s;
spy.clearLog(); spy.clearLog();
SECTION("member already exists") { SECTION("member already exists") {

View File

@@ -10,6 +10,7 @@
#include <string> #include <string>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -732,7 +733,7 @@ TEST_CASE("Overloads") {
} }
SECTION("const std::string&, Filter") { SECTION("const std::string&, Filter") {
deserializeJson(doc, std::string("{}"), Filter(filter)); deserializeJson(doc, "{}"_s, Filter(filter));
} }
SECTION("std::istream&, Filter") { SECTION("std::istream&, Filter") {
@@ -760,7 +761,7 @@ TEST_CASE("Overloads") {
} }
SECTION("const std::string&, Filter, NestingLimit") { SECTION("const std::string&, Filter, NestingLimit") {
deserializeJson(doc, std::string("{}"), Filter(filter), NestingLimit(5)); deserializeJson(doc, "{}"_s, Filter(filter), NestingLimit(5));
} }
SECTION("std::istream&, Filter, NestingLimit") { SECTION("std::istream&, Filter, NestingLimit") {
@@ -788,7 +789,7 @@ TEST_CASE("Overloads") {
} }
SECTION("const std::string&, NestingLimit, Filter") { SECTION("const std::string&, NestingLimit, Filter") {
deserializeJson(doc, std::string("{}"), NestingLimit(5), Filter(filter)); deserializeJson(doc, "{}"_s, NestingLimit(5), Filter(filter));
} }
SECTION("std::istream&, NestingLimit, Filter") { SECTION("std::istream&, NestingLimit, Filter") {

View File

@@ -9,6 +9,7 @@
#include "Allocators.hpp" #include "Allocators.hpp"
#include "CustomReader.hpp" #include "CustomReader.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -69,7 +70,7 @@ TEST_CASE("deserializeJson(const std::string&)") {
} }
SECTION("should accept temporary string") { SECTION("should accept temporary string") {
DeserializationError err = deserializeJson(doc, std::string("[42]")); DeserializationError err = deserializeJson(doc, "[42]"_s);
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
} }
@@ -82,7 +83,7 @@ TEST_CASE("deserializeJson(const std::string&)") {
JsonArray array = doc.as<JsonArray>(); JsonArray array = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
REQUIRE(std::string("hello") == array[0]); REQUIRE("hello"_s == array[0]);
} }
} }
@@ -108,7 +109,7 @@ TEST_CASE("deserializeJson(std::istream&)") {
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == obj.size()); REQUIRE(1 == obj.size());
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
SECTION("Should not read after the closing brace of an empty object") { SECTION("Should not read after the closing brace of an empty object") {

View File

@@ -7,6 +7,8 @@
#include <sstream> #include <sstream>
#include "Literals.hpp"
#define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression); #define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
#define SHOULD_FAIL(expression) \ #define SHOULD_FAIL(expression) \
REQUIRE(DeserializationError::TooDeep == expression); REQUIRE(DeserializationError::TooDeep == expression);
@@ -63,23 +65,23 @@ TEST_CASE("JsonDeserializer nesting") {
SECTION("Input = std::string") { SECTION("Input = std::string") {
SECTION("limit = 0") { SECTION("limit = 0") {
DeserializationOption::NestingLimit nesting(0); DeserializationOption::NestingLimit nesting(0);
SHOULD_WORK(deserializeJson(doc, std::string("\"toto\""), nesting)); SHOULD_WORK(deserializeJson(doc, "\"toto\""_s, nesting));
SHOULD_WORK(deserializeJson(doc, std::string("123"), nesting)); SHOULD_WORK(deserializeJson(doc, "123"_s, nesting));
SHOULD_WORK(deserializeJson(doc, std::string("true"), nesting)); SHOULD_WORK(deserializeJson(doc, "true"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("[]"), nesting)); SHOULD_FAIL(deserializeJson(doc, "[]"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("{}"), nesting)); SHOULD_FAIL(deserializeJson(doc, "{}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("[\"toto\"]"), nesting)); SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("{\"toto\":1}"), nesting)); SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
} }
SECTION("limit = 1") { SECTION("limit = 1") {
DeserializationOption::NestingLimit nesting(1); DeserializationOption::NestingLimit nesting(1);
SHOULD_WORK(deserializeJson(doc, std::string("[\"toto\"]"), nesting)); SHOULD_WORK(deserializeJson(doc, "[\"toto\"]"_s, nesting));
SHOULD_WORK(deserializeJson(doc, std::string("{\"toto\":1}"), nesting)); SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("{\"toto\":{}}"), nesting)); SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("{\"toto\":[]}"), nesting)); SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("[[\"toto\"]]"), nesting)); SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, std::string("[{\"toto\":1}]"), nesting)); SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]"_s, nesting));
} }
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
typedef ArduinoJson::detail::ElementProxy<JsonDocument&> ElementProxy; typedef ArduinoJson::detail::ElementProxy<JsonDocument&> ElementProxy;
TEST_CASE("ElementProxy::add()") { TEST_CASE("ElementProxy::add()") {
@@ -121,7 +123,7 @@ TEST_CASE("ElementProxy::remove()") {
ep["a"] = 1; ep["a"] = 1;
ep["b"] = 2; ep["b"] = 2;
ep.remove(std::string("b")); ep.remove("b"_s);
REQUIRE(ep.as<std::string>() == "{\"a\":1}"); REQUIRE(ep.as<std::string>() == "{\"a\":1}");
} }

View File

@@ -9,6 +9,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -106,8 +107,8 @@ TEST_CASE("MemberProxy::containsKey()") {
SECTION("containsKey(std::string)") { SECTION("containsKey(std::string)") {
mp["key"] = "value"; mp["key"] = "value";
REQUIRE(mp.containsKey(std::string("key")) == true); REQUIRE(mp.containsKey("key"_s) == true);
REQUIRE(mp.containsKey(std::string("key")) == true); REQUIRE(mp.containsKey("key"_s) == true);
} }
} }
@@ -117,8 +118,8 @@ TEST_CASE("MemberProxy::operator|()") {
SECTION("const char*") { SECTION("const char*") {
doc["a"] = "hello"; doc["a"] = "hello";
REQUIRE((doc["a"] | "world") == std::string("hello")); REQUIRE((doc["a"] | "world") == "hello"_s);
REQUIRE((doc["b"] | "world") == std::string("world")); REQUIRE((doc["b"] | "world") == "world"_s);
} }
SECTION("Issue #1411") { SECTION("Issue #1411") {
@@ -128,7 +129,7 @@ TEST_CASE("MemberProxy::operator|()") {
// to trigger the bug // to trigger the bug
const char* sensor = doc["sensor"] | test; // "gps" const char* sensor = doc["sensor"] | test; // "gps"
REQUIRE(sensor == std::string("gps")); REQUIRE(sensor == "gps"_s);
} }
SECTION("Issue #1415") { SECTION("Issue #1415") {
@@ -170,7 +171,7 @@ TEST_CASE("MemberProxy::remove()") {
mp["a"] = 1; mp["a"] = 1;
mp["b"] = 2; mp["b"] = 2;
mp.remove(std::string("b")); mp.remove("b"_s);
REQUIRE(mp.as<std::string>() == "{\"a\":1}"); REQUIRE(mp.as<std::string>() == "{\"a\":1}");
} }
@@ -286,8 +287,8 @@ TEST_CASE("Deduplicate keys") {
JsonDocument doc(&spy); JsonDocument doc(&spy);
SECTION("std::string") { SECTION("std::string") {
doc[0][std::string("example")] = 1; doc[0]["example"_s] = 1;
doc[1][std::string("example")] = 2; doc[1]["example"_s] = 2;
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str(); const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str(); const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
@@ -351,7 +352,7 @@ TEST_CASE("MemberProxy under memory constraints") {
SECTION("key allocation fails") { SECTION("key allocation fails") {
killswitch.on(); killswitch.on();
doc[std::string("hello")] = "world"; doc["hello"_s] = "world";
REQUIRE(doc.is<JsonObject>()); REQUIRE(doc.is<JsonObject>());
REQUIRE(doc.size() == 0); REQUIRE(doc.size() == 0);

View File

@@ -9,6 +9,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
@@ -35,8 +36,8 @@ TEST_CASE("JsonDocument::add(T)") {
} }
SECTION("std::string") { SECTION("std::string") {
doc.add(std::string("example")); doc.add("example"_s);
doc.add(std::string("example")); doc.add("example"_s);
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>()); CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
@@ -90,15 +91,57 @@ TEST_CASE("JsonDocument::add<T>()") {
REQUIRE(doc.as<std::string>() == "[[1,2]]"); REQUIRE(doc.as<std::string>() == "[[1,2]]");
} }
SECTION("JsonObject") {
JsonObject object = doc.add<JsonObject>();
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("JsonVariant") { SECTION("JsonVariant") {
JsonVariant variant = doc.add<JsonVariant>(); JsonVariant variant = doc.add<JsonVariant>();
variant.set(42); variant.set(42);
REQUIRE(doc.as<std::string>() == "[42]"); REQUIRE(doc.as<std::string>() == "[42]");
} }
} }
TEST_CASE("JsonObject::add(JsonObject) ") {
JsonDocument doc1;
doc1["hello"_s] = "world"_s;
TimebombAllocator allocator(10);
SpyingAllocator spy(&allocator);
JsonDocument doc2(&spy);
SECTION("success") {
bool result = doc2.add(doc1.as<JsonObject>());
REQUIRE(result == true);
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
});
}
SECTION("partial failure") { // issue #2081
allocator.setCountdown(2);
bool result = doc2.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
AllocateFail(sizeofString("world")),
Deallocate(sizeofString("hello")),
});
}
SECTION("complete failure") {
allocator.setCountdown(0);
bool result = doc2.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
}

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument assignment") { TEST_CASE("JsonDocument assignment") {
SpyingAllocator spyingAllocator; SpyingAllocator spyingAllocator;
@@ -62,7 +63,7 @@ TEST_CASE("JsonDocument assignment") {
SECTION("Move assign") { SECTION("Move assign") {
{ {
JsonDocument doc1(&spyingAllocator); JsonDocument doc1(&spyingAllocator);
doc1[std::string("hello")] = std::string("world"); doc1["hello"_s] = "world"_s;
JsonDocument doc2(&spyingAllocator); JsonDocument doc2(&spyingAllocator);
doc2 = std::move(doc1); doc2 = std::move(doc1);

View File

@@ -9,6 +9,7 @@
#include <string> #include <string>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::clear()") { TEST_CASE("JsonDocument::clear()") {
SpyingAllocator spy; SpyingAllocator spy;
@@ -22,7 +23,7 @@ TEST_CASE("JsonDocument::clear()") {
} }
SECTION("releases resources") { SECTION("releases resources") {
doc[std::string("hello")] = std::string("world"); doc["hello"_s] = "world"_s;
spy.clearLog(); spy.clearLog();
doc.clear(); doc.clear();

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::addPadding; using ArduinoJson::detail::addPadding;
@@ -20,7 +21,7 @@ TEST_CASE("JsonDocument constructor") {
SECTION("JsonDocument(const JsonDocument&)") { SECTION("JsonDocument(const JsonDocument&)") {
{ {
JsonDocument doc1(&spyingAllocator); JsonDocument doc1(&spyingAllocator);
doc1.set(std::string("The size of this string is 32!!")); doc1.set("The size of this string is 32!!"_s);
JsonDocument doc2(doc1); JsonDocument doc2(doc1);
@@ -38,7 +39,7 @@ TEST_CASE("JsonDocument constructor") {
SECTION("JsonDocument(JsonDocument&&)") { SECTION("JsonDocument(JsonDocument&&)") {
{ {
JsonDocument doc1(&spyingAllocator); JsonDocument doc1(&spyingAllocator);
doc1.set(std::string("The size of this string is 32!!")); doc1.set("The size of this string is 32!!"_s);
JsonDocument doc2(std::move(doc1)); JsonDocument doc2(std::move(doc1));
@@ -117,4 +118,31 @@ TEST_CASE("JsonDocument constructor") {
REQUIRE(doc2.as<std::string>() == "hello"); REQUIRE(doc2.as<std::string>() == "hello");
} }
SECTION("JsonDocument(JsonVariantConst)") {
JsonDocument doc1;
deserializeJson(doc1, "\"hello\"");
JsonDocument doc2(doc1.as<JsonVariantConst>());
REQUIRE(doc2.as<std::string>() == "hello");
}
SECTION("JsonDocument(ElementProxy)") {
JsonDocument doc1;
deserializeJson(doc1, "[\"hello\",\"world\"]");
JsonDocument doc2(doc1[1]);
REQUIRE(doc2.as<std::string>() == "world");
}
SECTION("JsonDocument(MemberProxy)") {
JsonDocument doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(doc1["hello"]);
REQUIRE(doc2.as<std::string>() == "world");
}
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::containsKey()") { TEST_CASE("JsonDocument::containsKey()") {
JsonDocument doc; JsonDocument doc;
@@ -23,7 +25,7 @@ TEST_CASE("JsonDocument::containsKey()") {
SECTION("returns true when key is a std::string") { SECTION("returns true when key is a std::string") {
doc["hello"] = "world"; doc["hello"] = "world";
REQUIRE(doc.containsKey(std::string("hello")) == true); REQUIRE(doc.containsKey("hello"_s) == true);
} }
SECTION("returns false on object") { SECTION("returns false on object") {
@@ -41,4 +43,12 @@ TEST_CASE("JsonDocument::containsKey()") {
SECTION("returns false on null") { SECTION("returns false on null") {
REQUIRE(doc.containsKey("hello") == false); REQUIRE(doc.containsKey("hello") == false);
} }
SECTION("support JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
} }

View File

@@ -2,6 +2,8 @@
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("Issue #1120") { TEST_CASE("Issue #1120") {
JsonDocument doc; JsonDocument doc;
constexpr char str[] = constexpr char str[] =
@@ -10,12 +12,12 @@ TEST_CASE("Issue #1120") {
SECTION("MemberProxy<std::string>::isNull()") { SECTION("MemberProxy<std::string>::isNull()") {
SECTION("returns false") { SECTION("returns false") {
auto value = doc[std::string("contents")]; auto value = doc["contents"_s];
CHECK(value.isNull() == false); CHECK(value.isNull() == false);
} }
SECTION("returns true") { SECTION("returns true") {
auto value = doc[std::string("zontents")]; auto value = doc["zontents"_s];
CHECK(value.isNull() == true); CHECK(value.isNull() == true);
} }
} }
@@ -46,12 +48,12 @@ TEST_CASE("Issue #1120") {
SECTION("MemberProxy<ElementProxy<MemberProxy>, std::string>::isNull()") { SECTION("MemberProxy<ElementProxy<MemberProxy>, std::string>::isNull()") {
SECTION("returns false") { SECTION("returns false") {
auto value = doc["contents"][1][std::string("module")]; auto value = doc["contents"][1]["module"_s];
CHECK(value.isNull() == false); CHECK(value.isNull() == false);
} }
SECTION("returns true") { SECTION("returns true") {
auto value = doc["contents"][1][std::string("zodule")]; auto value = doc["contents"][1]["zodule"_s];
CHECK(value.isNull() == true); CHECK(value.isNull() == true);
} }
} }

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::overflowed()") { TEST_CASE("JsonDocument::overflowed()") {
TimebombAllocator timebomb(10); TimebombAllocator timebomb(10);
@@ -30,13 +31,13 @@ TEST_CASE("JsonDocument::overflowed()") {
SECTION("returns true after a failed string copy") { SECTION("returns true after a failed string copy") {
timebomb.setCountdown(0); timebomb.setCountdown(0);
doc.add(std::string("example")); doc.add("example"_s);
CHECK(doc.overflowed() == true); CHECK(doc.overflowed() == true);
} }
SECTION("returns false after a successful string copy") { SECTION("returns false after a successful string copy") {
timebomb.setCountdown(3); timebomb.setCountdown(3);
doc.add(std::string("example")); doc.add("example"_s);
CHECK(doc.overflowed() == false); CHECK(doc.overflowed() == false);
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::remove()") { TEST_CASE("JsonDocument::remove()") {
JsonDocument doc; JsonDocument doc;
@@ -31,7 +33,7 @@ TEST_CASE("JsonDocument::remove()") {
doc["a"] = 1; doc["a"] = 1;
doc["b"] = 2; doc["b"] = 2;
doc.remove(std::string("b")); doc.remove("b"_s);
REQUIRE(doc.as<std::string>() == "{\"a\":1}"); REQUIRE(doc.as<std::string>() == "{\"a\":1}");
} }
@@ -49,4 +51,25 @@ TEST_CASE("JsonDocument::remove()") {
REQUIRE(doc.as<std::string>() == "{\"a\":1}"); REQUIRE(doc.as<std::string>() == "{\"a\":1}");
} }
#endif #endif
SECTION("remove(JsonVariant) from object") {
doc["a"] = 1;
doc["b"] = 2;
doc["c"] = "b";
doc.remove(doc["c"]);
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"c\":\"b\"}");
}
SECTION("remove(JsonVariant) from array") {
doc[0] = 3;
doc[1] = 2;
doc[2] = 1;
doc.remove(doc[2]);
doc.remove(doc[3]); // noop
REQUIRE(doc.as<std::string>() == "[3,1]");
}
} }

View File

@@ -9,6 +9,7 @@
#include <string> #include <string>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -78,7 +79,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
} }
SECTION("owned string") { SECTION("owned string") {
doc.set(std::string("abcdefg")); doc.set("abcdefg"_s);
REQUIRE(doc.as<std::string>() == "abcdefg"); REQUIRE(doc.as<std::string>() == "abcdefg");
doc.shrinkToFit(); doc.shrinkToFit();
@@ -114,7 +115,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
} }
SECTION("owned key") { SECTION("owned key") {
doc[std::string("abcdefg")] = 42; doc["abcdefg"_s] = 42;
doc.shrinkToFit(); doc.shrinkToFit();
@@ -141,7 +142,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
} }
SECTION("owned string in array") { SECTION("owned string in array") {
doc.add(std::string("abcdefg")); doc.add("abcdefg"_s);
doc.shrinkToFit(); doc.shrinkToFit();
@@ -168,7 +169,7 @@ TEST_CASE("JsonDocument::shrinkToFit()") {
} }
SECTION("owned string in object") { SECTION("owned string in object") {
doc["key"] = std::string("abcdefg"); doc["key"] = "abcdefg"_s;
doc.shrinkToFit(); doc.shrinkToFit();

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::operator[]") { TEST_CASE("JsonDocument::operator[]") {
JsonDocument doc; JsonDocument doc;
const JsonDocument& cdoc = doc; const JsonDocument& cdoc = doc;
@@ -18,22 +20,36 @@ TEST_CASE("JsonDocument::operator[]") {
} }
SECTION("std::string") { SECTION("std::string") {
REQUIRE(doc[std::string("hello")] == "world"); REQUIRE(doc["hello"_s] == "world");
REQUIRE(cdoc[std::string("hello")] == "world"); REQUIRE(cdoc["hello"_s] == "world");
}
SECTION("JsonVariant") {
doc["key"] = "hello";
REQUIRE(doc[doc["key"]] == "world");
REQUIRE(cdoc[cdoc["key"]] == "world");
} }
SECTION("supports operator|") { SECTION("supports operator|") {
REQUIRE((doc["hello"] | "nope") == std::string("world")); REQUIRE((doc["hello"] | "nope") == "world"_s);
REQUIRE((doc["world"] | "nope") == std::string("nope")); REQUIRE((doc["world"] | "nope") == "nope"_s);
} }
} }
SECTION("array") { SECTION("array") {
deserializeJson(doc, "[\"hello\",\"world\"]"); deserializeJson(doc, "[\"hello\",\"world\"]");
SECTION("int") {
REQUIRE(doc[1] == "world"); REQUIRE(doc[1] == "world");
REQUIRE(cdoc[1] == "world"); REQUIRE(cdoc[1] == "world");
} }
SECTION("JsonVariant") {
doc[2] = 1;
REQUIRE(doc[doc[2]] == "world");
REQUIRE(cdoc[doc[2]] == "world");
}
}
} }
TEST_CASE("JsonDocument automatically promotes to object") { TEST_CASE("JsonDocument automatically promotes to object") {

View File

@@ -6,12 +6,12 @@ add_executable(JsonObjectTests
clear.cpp clear.cpp
compare.cpp compare.cpp
containsKey.cpp containsKey.cpp
copy.cpp
equals.cpp equals.cpp
isNull.cpp isNull.cpp
iterator.cpp iterator.cpp
nesting.cpp nesting.cpp
remove.cpp remove.cpp
set.cpp
size.cpp size.cpp
std_string.cpp std_string.cpp
subscript.cpp subscript.cpp

View File

@@ -30,4 +30,10 @@ TEST_CASE("JsonObject::containsKey()") {
REQUIRE(true == obj.containsKey(vla)); REQUIRE(true == obj.containsKey(vla));
} }
#endif #endif
SECTION("key is a JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
} }

View File

@@ -80,4 +80,10 @@ TEST_CASE("JsonObject::remove()") {
JsonObject unboundObject; JsonObject unboundObject;
unboundObject.remove(unboundObject.begin()); unboundObject.remove(unboundObject.begin());
} }
SECTION("remove(JsonVariant)") {
obj["key"] = "b";
obj.remove(obj["key"]);
REQUIRE("{\"a\":0,\"c\":2,\"key\":\"b\"}" == doc.as<std::string>());
}
} }

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonObject::set()") { TEST_CASE("JsonObject::set()") {
SpyingAllocator spy; SpyingAllocator spy;
@@ -22,20 +23,20 @@ TEST_CASE("JsonObject::set()") {
bool success = obj2.set(obj1); bool success = obj2.set(obj1);
REQUIRE(success == true); REQUIRE(success == true);
REQUIRE(obj2["hello"] == std::string("world")); REQUIRE(obj2["hello"] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
}); });
} }
SECTION("copy local string value") { SECTION("copy local string value") {
obj1["hello"] = std::string("world"); obj1["hello"] = "world"_s;
spy.clearLog(); spy.clearLog();
bool success = obj2.set(obj1); bool success = obj2.set(obj1);
REQUIRE(success == true); REQUIRE(success == true);
REQUIRE(obj2["hello"] == std::string("world")); REQUIRE(obj2["hello"] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString("world")), Allocate(sizeofString("world")),
@@ -43,13 +44,13 @@ TEST_CASE("JsonObject::set()") {
} }
SECTION("copy local key") { SECTION("copy local key") {
obj1[std::string("hello")] = "world"; obj1["hello"_s] = "world";
spy.clearLog(); spy.clearLog();
bool success = obj2.set(obj1); bool success = obj2.set(obj1);
REQUIRE(success == true); REQUIRE(success == true);
REQUIRE(obj2["hello"] == std::string("world")); REQUIRE(obj2["hello"] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")), Allocate(sizeofString("hello")),
Allocate(sizeofPool()), Allocate(sizeofPool()),
@@ -63,7 +64,7 @@ TEST_CASE("JsonObject::set()") {
bool success = obj2.set(obj1); bool success = obj2.set(obj1);
REQUIRE(success == true); REQUIRE(success == true);
REQUIRE(obj2["hello"] == std::string("world")); REQUIRE(obj2["hello"] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")), Allocate(sizeofString("hello")),
Allocate(sizeofPool()), Allocate(sizeofPool()),
@@ -78,7 +79,7 @@ TEST_CASE("JsonObject::set()") {
bool success = obj2.set(obj1); bool success = obj2.set(obj1);
REQUIRE(success == true); REQUIRE(success == true);
REQUIRE(obj2["hello"] == std::string("world")); REQUIRE(obj2["hello"] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")), Allocate(sizeofString("hello")),
Allocate(sizeofPool()), Allocate(sizeofPool()),
@@ -91,7 +92,7 @@ TEST_CASE("JsonObject::set()") {
obj2.set(static_cast<JsonObjectConst>(obj1)); obj2.set(static_cast<JsonObjectConst>(obj1));
REQUIRE(obj2["hello"] == std::string("world")); REQUIRE(obj2["hello"] == "world"_s);
} }
SECTION("copy fails in the middle of an object") { SECTION("copy fails in the middle of an object") {
@@ -99,8 +100,8 @@ TEST_CASE("JsonObject::set()") {
JsonDocument doc3(&timebomb); JsonDocument doc3(&timebomb);
JsonObject obj3 = doc3.to<JsonObject>(); JsonObject obj3 = doc3.to<JsonObject>();
obj1[std::string("a")] = 1; obj1["a"_s] = 1;
obj1[std::string("b")] = 2; obj1["b"_s] = 2;
bool success = obj3.set(obj1); bool success = obj3.set(obj1);
@@ -113,12 +114,12 @@ TEST_CASE("JsonObject::set()") {
JsonDocument doc3(&timebomb); JsonDocument doc3(&timebomb);
JsonObject obj3 = doc3.to<JsonObject>(); JsonObject obj3 = doc3.to<JsonObject>();
obj1["hello"][0] = std::string("world"); obj1["hello"][0] = "world"_s;
bool success = obj3.set(obj1); bool success = obj3.set(obj1);
REQUIRE(success == false); REQUIRE(success == false);
REQUIRE(doc3.as<std::string>() == "{\"hello\":[null]}"); REQUIRE(doc3.as<std::string>() == "{\"hello\":[]}");
} }
SECTION("destination is null") { SECTION("destination is null") {

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
static void eraseString(std::string& str) { static void eraseString(std::string& str) {
char* p = const_cast<char*>(str.c_str()); char* p = const_cast<char*>(str.c_str());
while (*p) while (*p)
@@ -20,7 +22,7 @@ TEST_CASE("std::string") {
deserializeJson(doc, json); deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
REQUIRE(std::string("value") == obj[std::string("key")]); REQUIRE("value"_s == obj["key"_s]);
} }
SECTION("operator[] const") { SECTION("operator[] const") {
@@ -29,21 +31,21 @@ TEST_CASE("std::string") {
deserializeJson(doc, json); deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
REQUIRE(std::string("value") == obj[std::string("key")]); REQUIRE("value"_s == obj["key"_s]);
} }
SECTION("containsKey()") { SECTION("containsKey()") {
char json[] = "{\"key\":\"value\"}"; char json[] = "{\"key\":\"value\"}";
deserializeJson(doc, json); deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
REQUIRE(true == obj.containsKey(std::string("key"))); REQUIRE(true == obj.containsKey("key"_s));
} }
SECTION("remove()") { SECTION("remove()") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
obj["key"] = "value"; obj["key"] = "value";
obj.remove(std::string("key")); obj.remove("key"_s);
REQUIRE(0 == obj.size()); REQUIRE(0 == obj.size());
} }
@@ -53,7 +55,7 @@ TEST_CASE("std::string") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
obj[key] = "world"; obj[key] = "world";
eraseString(key); eraseString(key);
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
SECTION("operator[], set value") { SECTION("operator[], set value") {
@@ -61,6 +63,6 @@ TEST_CASE("std::string") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
obj["hello"] = value; obj["hello"] = value;
eraseString(value); eraseString(value);
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
} }

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonObject::operator[]") { TEST_CASE("JsonObject::operator[]") {
SpyingAllocator spy; SpyingAllocator spy;
@@ -50,7 +51,7 @@ TEST_CASE("JsonObject::operator[]") {
REQUIRE(true == obj["hello"].is<const char*>()); REQUIRE(true == obj["hello"].is<const char*>());
REQUIRE(false == obj["hello"].is<long>()); REQUIRE(false == obj["hello"].is<long>());
REQUIRE(std::string("h3110") == obj["hello"].as<const char*>()); REQUIRE("h3110"_s == obj["hello"].as<const char*>());
} }
SECTION("array") { SECTION("array") {
@@ -132,7 +133,7 @@ TEST_CASE("JsonObject::operator[]") {
} }
SECTION("should duplicate std::string value") { SECTION("should duplicate std::string value") {
obj["hello"] = std::string("world"); obj["hello"] = "world"_s;
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
Allocate(sizeofString("world")), Allocate(sizeofString("world")),
@@ -140,7 +141,7 @@ TEST_CASE("JsonObject::operator[]") {
} }
SECTION("should duplicate std::string key") { SECTION("should duplicate std::string key") {
obj[std::string("hello")] = "world"; obj["hello"_s] = "world";
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")), Allocate(sizeofString("hello")),
Allocate(sizeofPool()), Allocate(sizeofPool()),
@@ -148,7 +149,7 @@ TEST_CASE("JsonObject::operator[]") {
} }
SECTION("should duplicate std::string key&value") { SECTION("should duplicate std::string key&value") {
obj[std::string("hello")] = std::string("world"); obj["hello"_s] = "world"_s;
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")), Allocate(sizeofString("hello")),
Allocate(sizeofPool()), Allocate(sizeofPool()),
@@ -197,7 +198,7 @@ TEST_CASE("JsonObject::operator[]") {
obj[vla] = "world"; obj[vla] = "world";
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
SECTION("obj[str] = VLA") { // issue #416 SECTION("obj[str] = VLA") { // issue #416
@@ -207,7 +208,7 @@ TEST_CASE("JsonObject::operator[]") {
obj["hello"] = vla; obj["hello"] = vla;
REQUIRE(std::string("world") == obj["hello"].as<const char*>()); REQUIRE("world"_s == obj["hello"].as<const char*>());
} }
SECTION("obj.set(VLA, str)") { SECTION("obj.set(VLA, str)") {
@@ -217,7 +218,7 @@ TEST_CASE("JsonObject::operator[]") {
obj[vla] = "world"; obj[vla] = "world";
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
SECTION("obj.set(str, VLA)") { SECTION("obj.set(str, VLA)") {
@@ -227,7 +228,7 @@ TEST_CASE("JsonObject::operator[]") {
obj["hello"].set(vla); obj["hello"].set(vla);
REQUIRE(std::string("world") == obj["hello"].as<const char*>()); REQUIRE("world"_s == obj["hello"].as<const char*>());
} }
SECTION("obj[VLA]") { SECTION("obj[VLA]") {
@@ -238,7 +239,7 @@ TEST_CASE("JsonObject::operator[]") {
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
obj = doc.as<JsonObject>(); obj = doc.as<JsonObject>();
REQUIRE(std::string("world") == obj[vla]); REQUIRE("world"_s == obj[vla]);
} }
#endif #endif
@@ -249,4 +250,12 @@ TEST_CASE("JsonObject::operator[]") {
REQUIRE(true == obj["hello"]["world"].is<int>()); REQUIRE(true == obj["hello"]["world"].is<int>());
REQUIRE(false == obj["hello"]["world"].is<bool>()); REQUIRE(false == obj["hello"]["world"].is<bool>());
} }
SECTION("JsonVariant") {
obj["hello"] = "world";
doc["key"] = "hello";
REQUIRE(obj[obj["key"]] == "world");
REQUIRE(obj[obj["foo"]] == nullptr);
}
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonObjectConst::containsKey()") { TEST_CASE("JsonObjectConst::containsKey()") {
JsonDocument doc; JsonDocument doc;
doc["hello"] = 42; doc["hello"] = 42;
@@ -16,8 +18,8 @@ TEST_CASE("JsonObjectConst::containsKey()") {
} }
SECTION("supports std::string") { SECTION("supports std::string") {
REQUIRE(false == obj.containsKey(std::string("world"))); REQUIRE(false == obj.containsKey("world"_s));
REQUIRE(true == obj.containsKey(std::string("hello"))); REQUIRE(true == obj.containsKey("hello"_s));
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
@@ -29,4 +31,10 @@ TEST_CASE("JsonObjectConst::containsKey()") {
REQUIRE(true == obj.containsKey(vla)); REQUIRE(true == obj.containsKey(vla));
} }
#endif #endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
} }

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonObjectConst::operator[]") { TEST_CASE("JsonObjectConst::operator[]") {
JsonDocument doc; JsonDocument doc;
@@ -17,7 +18,7 @@ TEST_CASE("JsonObjectConst::operator[]") {
} }
SECTION("supports std::string") { SECTION("supports std::string") {
REQUIRE(obj[std::string("hello")] == "world"); // issue #2019 REQUIRE(obj["hello"_s] == "world"); // issue #2019
} }
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \ #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@@ -27,7 +28,13 @@ TEST_CASE("JsonObjectConst::operator[]") {
char vla[i]; char vla[i];
strcpy(vla, "hello"); strcpy(vla, "hello");
REQUIRE(std::string("world") == obj[vla]); REQUIRE("world"_s == obj[vla]);
} }
#endif #endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(obj[obj["key"]] == "world");
REQUIRE(obj[obj["foo"]] == nullptr);
}
} }

View File

@@ -6,6 +6,8 @@
#include <catch.hpp> #include <catch.hpp>
#include <limits> #include <limits>
#include "Literals.hpp"
template <typename T> template <typename T>
void check(T value, const std::string& expected) { void check(T value, const std::string& expected) {
JsonDocument doc; JsonDocument doc;
@@ -30,42 +32,42 @@ TEST_CASE("serializeJson(JsonVariant)") {
} }
SECTION("string") { SECTION("string") {
check(std::string("hello"), "\"hello\""); check("hello"_s, "\"hello\"");
SECTION("Escape quotation mark") { SECTION("Escape quotation mark") {
check(std::string("hello \"world\""), "\"hello \\\"world\\\"\""); check("hello \"world\""_s, "\"hello \\\"world\\\"\"");
} }
SECTION("Escape reverse solidus") { SECTION("Escape reverse solidus") {
check(std::string("hello\\world"), "\"hello\\\\world\""); check("hello\\world"_s, "\"hello\\\\world\"");
} }
SECTION("Don't escape solidus") { SECTION("Don't escape solidus") {
check(std::string("fifty/fifty"), "\"fifty/fifty\""); check("fifty/fifty"_s, "\"fifty/fifty\"");
} }
SECTION("Escape backspace") { SECTION("Escape backspace") {
check(std::string("hello\bworld"), "\"hello\\bworld\""); check("hello\bworld"_s, "\"hello\\bworld\"");
} }
SECTION("Escape formfeed") { SECTION("Escape formfeed") {
check(std::string("hello\fworld"), "\"hello\\fworld\""); check("hello\fworld"_s, "\"hello\\fworld\"");
} }
SECTION("Escape linefeed") { SECTION("Escape linefeed") {
check(std::string("hello\nworld"), "\"hello\\nworld\""); check("hello\nworld"_s, "\"hello\\nworld\"");
} }
SECTION("Escape carriage return") { SECTION("Escape carriage return") {
check(std::string("hello\rworld"), "\"hello\\rworld\""); check("hello\rworld"_s, "\"hello\\rworld\"");
} }
SECTION("Escape tab") { SECTION("Escape tab") {
check(std::string("hello\tworld"), "\"hello\\tworld\""); check("hello\tworld"_s, "\"hello\\tworld\"");
} }
SECTION("NUL char") { SECTION("NUL char") {
check(std::string("hello\0world", 11), "\"hello\\u0000world\""); check("hello\0world"_s, "\"hello\\u0000world\"");
} }
} }
@@ -74,7 +76,7 @@ TEST_CASE("serializeJson(JsonVariant)") {
} }
SECTION("SerializedValue<std::string>") { SECTION("SerializedValue<std::string>") {
check(serialized(std::string("[1,2]")), "[1,2]"); check(serialized("[1,2]"_s), "[1,2]");
} }
SECTION("Double") { SECTION("Double") {

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("serialize JsonArray to std::string") { TEST_CASE("serialize JsonArray to std::string") {
JsonDocument doc; JsonDocument doc;
JsonArray array = doc.to<JsonArray>(); JsonArray array = doc.to<JsonArray>();
@@ -48,7 +50,7 @@ TEST_CASE("serialize JsonObject to std::string") {
TEST_CASE("serialize an std::string containing a NUL") { TEST_CASE("serialize an std::string containing a NUL") {
JsonDocument doc; JsonDocument doc;
doc.set(std::string("hello\0world", 11)); doc.set("hello\0world"_s);
std::string json = "erase me"; std::string json = "erase me";
serializeJson(doc, json); serializeJson(doc, json);

View File

@@ -6,6 +6,9 @@
#include <stdint.h> #include <stdint.h>
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonVariant::add(T)") { TEST_CASE("JsonVariant::add(T)") {
JsonDocument doc; JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>(); JsonVariant var = doc.to<JsonVariant>();
@@ -23,7 +26,7 @@ TEST_CASE("JsonVariant::add(T)") {
} }
SECTION("add std::string to new variant") { SECTION("add std::string to new variant") {
var.add(std::string("hello")); var.add("hello"_s);
REQUIRE(var.as<std::string>() == "[\"hello\"]"); REQUIRE(var.as<std::string>() == "[\"hello\"]");
} }
@@ -56,15 +59,58 @@ TEST_CASE("JsonVariant::add<T>()") {
REQUIRE(doc.as<std::string>() == "[[1,2]]"); REQUIRE(doc.as<std::string>() == "[[1,2]]");
} }
SECTION("JsonObject") {
JsonObject object = var.add<JsonObject>();
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("JsonVariant") { SECTION("JsonVariant") {
JsonVariant variant = var.add<JsonVariant>(); JsonVariant variant = var.add<JsonVariant>();
variant.set(42); variant.set(42);
REQUIRE(doc.as<std::string>() == "[42]"); REQUIRE(doc.as<std::string>() == "[42]");
} }
} }
TEST_CASE("JsonObject::add(JsonObject) ") {
JsonDocument doc1;
doc1["hello"_s] = "world"_s;
TimebombAllocator allocator(10);
SpyingAllocator spy(&allocator);
JsonDocument doc2(&spy);
JsonVariant variant = doc2.to<JsonVariant>();
SECTION("success") {
bool result = variant.add(doc1.as<JsonObject>());
REQUIRE(result == true);
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
});
}
SECTION("partial failure") { // issue #2081
allocator.setCountdown(2);
bool result = variant.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
AllocateFail(sizeofString("world")),
Deallocate(sizeofString("hello")),
});
}
SECTION("complete failure") {
allocator.setCountdown(0);
bool result = variant.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
}

View File

@@ -6,6 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
namespace my { namespace my {
using ArduinoJson::detail::isinf; using ArduinoJson::detail::isinf;
} // namespace my } // namespace my
@@ -25,6 +27,8 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(0 == variant.as<const char*>()); REQUIRE(0 == variant.as<const char*>());
REQUIRE("null" == variant.as<std::string>()); REQUIRE("null" == variant.as<std::string>());
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
} }
SECTION("set(4.2)") { SECTION("set(4.2)") {
@@ -36,6 +40,8 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long>() == 4L); REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<unsigned>() == 4U); REQUIRE(variant.as<unsigned>() == 4U);
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
} }
SECTION("set(0.0)") { SECTION("set(0.0)") {
@@ -44,6 +50,8 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<bool>() == false); REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<long>() == 0L); REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
} }
SECTION("set(false)") { SECTION("set(false)") {
@@ -54,6 +62,8 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long>() == 0L); REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<std::string>() == "false"); REQUIRE(variant.as<std::string>() == "false");
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
} }
SECTION("set(true)") { SECTION("set(true)") {
@@ -64,6 +74,8 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long>() == 1L); REQUIRE(variant.as<long>() == 1L);
REQUIRE(variant.as<std::string>() == "true"); REQUIRE(variant.as<std::string>() == "true");
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
} }
SECTION("set(42)") { SECTION("set(42)") {
@@ -75,6 +87,8 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<unsigned int>() == 42U); // issue #1601 REQUIRE(variant.as<unsigned int>() == 42U); // issue #1601
REQUIRE(variant.as<std::string>() == "42"); REQUIRE(variant.as<std::string>() == "42");
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
} }
SECTION("set(42L)") { SECTION("set(42L)") {
@@ -144,20 +158,20 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<bool>() == true); REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<long>() == 0L); REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<const char*>() == std::string("hello")); REQUIRE(variant.as<const char*>() == "hello"_s);
REQUIRE(variant.as<const char*>() == std::string("hello")); REQUIRE(variant.as<const char*>() == "hello"_s);
REQUIRE(variant.as<std::string>() == std::string("hello")); REQUIRE(variant.as<std::string>() == "hello"_s);
REQUIRE(variant.as<JsonString>() == "hello"); REQUIRE(variant.as<JsonString>() == "hello");
} }
SECTION("set(std::string(\"4.2\"))") { SECTION("set(std::string(\"4.2\"))") {
variant.set(std::string("4.2")); variant.set("4.2"_s);
REQUIRE(variant.as<bool>() == true); REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<long>() == 4L); REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<double>() == 4.2); REQUIRE(variant.as<double>() == 4.2);
REQUIRE(variant.as<const char*>() == std::string("4.2")); REQUIRE(variant.as<const char*>() == "4.2"_s);
REQUIRE(variant.as<std::string>() == std::string("4.2")); REQUIRE(variant.as<std::string>() == "4.2"_s);
REQUIRE(variant.as<JsonString>() == "4.2"); REQUIRE(variant.as<JsonString>() == "4.2");
REQUIRE(variant.as<JsonString>().isLinked() == false); REQUIRE(variant.as<JsonString>().isLinked() == false);
} }
@@ -199,6 +213,13 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<JsonString>().isNull()); REQUIRE(variant.as<JsonString>().isNull());
} }
SECTION("set(serialized(\"hello\"))") {
variant.set(serialized("hello"));
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("to<JsonObject>()") { SECTION("to<JsonObject>()") {
JsonObject obj = variant.to<JsonObject>(); JsonObject obj = variant.to<JsonObject>();
obj["key"] = "value"; obj["key"] = "value";
@@ -208,13 +229,13 @@ TEST_CASE("JsonVariant::as()") {
} }
SECTION("as<std::string>()") { SECTION("as<std::string>()") {
REQUIRE(variant.as<std::string>() == std::string("{\"key\":\"value\"}")); REQUIRE(variant.as<std::string>() == "{\"key\":\"value\"}"_s);
} }
SECTION("ObjectAsJsonObject") { SECTION("ObjectAsJsonObject") {
JsonObject o = variant.as<JsonObject>(); JsonObject o = variant.as<JsonObject>();
REQUIRE(o.size() == 1); REQUIRE(o.size() == 1);
REQUIRE(o["key"] == std::string("value")); REQUIRE(o["key"] == "value"_s);
} }
} }
@@ -228,7 +249,7 @@ TEST_CASE("JsonVariant::as()") {
} }
SECTION("as<std::string>()") { SECTION("as<std::string>()") {
REQUIRE(variant.as<std::string>() == std::string("[4,2]")); REQUIRE(variant.as<std::string>() == "[4,2]"_s);
} }
SECTION("as<JsonArray>()") { SECTION("as<JsonArray>()") {
@@ -245,7 +266,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long long>() == -9223372036854775807 - 1); REQUIRE(variant.as<long long>() == -9223372036854775807 - 1);
} }
SECTION("Biggerst int64 positive") { SECTION("Biggest int64 positive") {
variant.set("9223372036854775807"); variant.set("9223372036854775807");
REQUIRE(variant.as<long long>() == 9223372036854775807); REQUIRE(variant.as<long long>() == 9223372036854775807);
} }

View File

@@ -7,6 +7,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonVariant::clear()") { TEST_CASE("JsonVariant::clear()") {
SpyingAllocator spy; SpyingAllocator spy;
@@ -28,7 +29,7 @@ TEST_CASE("JsonVariant::clear()") {
} }
SECTION("releases owned string") { SECTION("releases owned string") {
var.set(std::string("hello")); var.set("hello"_s);
var.clear(); var.clear();
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(spy.log() == AllocatorLog{

View File

@@ -113,6 +113,42 @@ TEST_CASE("Compare JsonVariant with JsonVariant") {
CHECK_FALSE(a == b); CHECK_FALSE(a == b);
} }
SECTION("MsgPackBinary('abc') vs MsgPackBinary('abc')") {
a.set(MsgPackBinary("abc", 4));
b.set(MsgPackBinary("abc", 4));
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("MsgPackBinary('abc') vs MsgPackBinary('bcd')") {
a.set(MsgPackBinary("abc", 4));
b.set(MsgPackBinary("bcd", 4));
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("MsgPackBinary('bcd') vs MsgPackBinary('abc')") {
a.set(MsgPackBinary("bcd", 4));
b.set(MsgPackBinary("abc", 4));
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("false vs true") { SECTION("false vs true") {
a.set(false); a.set(false);
b.set(true); b.set(true);

View File

@@ -6,6 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariant::containsKey()") { TEST_CASE("JsonVariant::containsKey()") {
JsonDocument doc; JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>(); JsonVariant var = doc.to<JsonVariant>();
@@ -20,7 +22,15 @@ TEST_CASE("JsonVariant::containsKey()") {
SECTION("containsKey(std::string)") { SECTION("containsKey(std::string)") {
var["hello"] = "world"; var["hello"] = "world";
REQUIRE(var.containsKey(std::string("hello")) == true); REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey(std::string("world")) == false); REQUIRE(var.containsKey("world"_s) == false);
}
SECTION("containsKey(JsonVariant)") {
var["hello"] = "world";
var["key"] = "hello";
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
} }
} }

View File

@@ -140,15 +140,3 @@ TEST_CASE("Custom converter with specialization") {
REQUIRE(doc["value"]["imag"] == 3); REQUIRE(doc["value"]["imag"] == 3);
} }
} }
TEST_CASE("ConverterNeedsWriteableRef") {
using namespace ArduinoJson::detail;
CHECK(ConverterNeedsWriteableRef<int>::value == false);
CHECK(ConverterNeedsWriteableRef<float>::value == false);
CHECK(ConverterNeedsWriteableRef<JsonVariant>::value == true);
CHECK(ConverterNeedsWriteableRef<JsonVariantConst>::value == false);
CHECK(ConverterNeedsWriteableRef<JsonObject>::value == true);
CHECK(ConverterNeedsWriteableRef<JsonObjectConst>::value == false);
CHECK(ConverterNeedsWriteableRef<JsonArray>::value == true);
CHECK(ConverterNeedsWriteableRef<JsonArrayConst>::value == false);
}

View File

@@ -6,6 +6,8 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonVariant::set(JsonVariant)") { TEST_CASE("JsonVariant::set(JsonVariant)") {
KillswitchAllocator killswitch; KillswitchAllocator killswitch;
SpyingAllocator spyingAllocator(&killswitch); SpyingAllocator spyingAllocator(&killswitch);
@@ -72,7 +74,7 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
} }
SECTION("stores std::string by copy") { SECTION("stores std::string by copy") {
var1.set(std::string("hello!!")); var1.set("hello!!"_s);
spyingAllocator.clearLog(); spyingAllocator.clearLog();
var2.set(var1); var2.set(var1);
@@ -106,7 +108,7 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
} }
SECTION("stores Serialized<std::string> by copy") { SECTION("stores Serialized<std::string> by copy") {
var1.set(serialized(std::string("hello!!"))); var1.set(serialized("hello!!"_s));
spyingAllocator.clearLog(); spyingAllocator.clearLog();
var2.set(var1); var2.set(var1);
@@ -117,7 +119,7 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
} }
SECTION("fails gracefully if raw string allocation fails") { SECTION("fails gracefully if raw string allocation fails") {
var1.set(serialized(std::string("hello!!"))); var1.set(serialized("hello!!"_s));
killswitch.on(); killswitch.on();
spyingAllocator.clearLog(); spyingAllocator.clearLog();

View File

@@ -7,6 +7,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
@@ -15,9 +16,9 @@ TEST_CASE("JsonVariant::remove(int)") {
JsonDocument doc(&spy); JsonDocument doc(&spy);
SECTION("release top level strings") { SECTION("release top level strings") {
doc.add(std::string("hello")); doc.add("hello"_s);
doc.add(std::string("hello")); doc.add("hello"_s);
doc.add(std::string("world")); doc.add("world"_s);
JsonVariant var = doc.as<JsonVariant>(); JsonVariant var = doc.as<JsonVariant>();
REQUIRE(var.as<std::string>() == "[\"hello\",\"hello\",\"world\"]"); REQUIRE(var.as<std::string>() == "[\"hello\",\"hello\",\"world\"]");
@@ -43,7 +44,7 @@ TEST_CASE("JsonVariant::remove(int)") {
} }
SECTION("release strings in nested array") { SECTION("release strings in nested array") {
doc[0][0] = std::string("hello"); doc[0][0] = "hello"_s;
JsonVariant var = doc.as<JsonVariant>(); JsonVariant var = doc.as<JsonVariant>();
REQUIRE(var.as<std::string>() == "[[\"hello\"]]"); REQUIRE(var.as<std::string>() == "[[\"hello\"]]");
@@ -77,7 +78,34 @@ TEST_CASE("JsonVariant::remove(std::string)") {
var["a"] = 1; var["a"] = 1;
var["b"] = 2; var["b"] = 2;
var.remove(std::string("b")); var.remove("b"_s);
REQUIRE(var.as<std::string>() == "{\"a\":1}"); REQUIRE(var.as<std::string>() == "{\"a\":1}");
} }
TEST_CASE("JsonVariant::remove(JsonVariant) from object") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var["a"] = "a";
var["b"] = 2;
var["c"] = "b";
var.remove(var["c"]);
REQUIRE(var.as<std::string>() == "{\"a\":\"a\",\"c\":\"b\"}");
}
TEST_CASE("JsonVariant::remove(JsonVariant) from array") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var[0] = 3;
var[1] = 2;
var[2] = 1;
var.remove(var[2]);
var.remove(var[3]); // noop
REQUIRE(var.as<std::string>() == "[3,1]");
}

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -137,14 +138,14 @@ TEST_CASE("JsonVariant::set() with not enough memory") {
JsonVariant v = doc.to<JsonVariant>(); JsonVariant v = doc.to<JsonVariant>();
SECTION("std::string") { SECTION("std::string") {
bool result = v.set(std::string("hello world!!")); bool result = v.set("hello world!!"_s);
REQUIRE(result == false); REQUIRE(result == false);
REQUIRE(v.isNull()); REQUIRE(v.isNull());
} }
SECTION("Serialized<std::string>") { SECTION("Serialized<std::string>") {
bool result = v.set(serialized(std::string("hello world!!"))); bool result = v.set(serialized("hello world!!"_s));
REQUIRE(result == false); REQUIRE(result == false);
REQUIRE(v.isNull()); REQUIRE(v.isNull());
@@ -178,7 +179,7 @@ TEST_CASE("JsonVariant::set(JsonDocument)") {
TEST_CASE("JsonVariant::set() releases the previous value") { TEST_CASE("JsonVariant::set() releases the previous value") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc["hello"] = std::string("world"); doc["hello"] = "world"_s;
spy.clearLog(); spy.clearLog();
JsonVariant v = doc["hello"]; JsonVariant v = doc["hello"];

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariant::operator[]") { TEST_CASE("JsonVariant::operator[]") {
JsonDocument doc; JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>(); JsonVariant var = doc.to<JsonVariant>();
@@ -31,9 +33,9 @@ TEST_CASE("JsonVariant::operator[]") {
REQUIRE(2 == var.size()); REQUIRE(2 == var.size());
var[0].as<std::string>(); var[0].as<std::string>();
// REQUIRE(std::string("element at index 0") == ); // REQUIRE("element at index 0"_s == );
REQUIRE(std::string("element at index 1") == var[1]); REQUIRE("element at index 1"_s == var[1]);
REQUIRE(std::string("element at index 0") == REQUIRE("element at index 0"_s ==
var[static_cast<unsigned char>(0)]); // issue #381 var[static_cast<unsigned char>(0)]); // issue #381
REQUIRE(var[666].isNull()); REQUIRE(var[666].isNull());
REQUIRE(var[3].isNull()); REQUIRE(var[3].isNull());
@@ -46,7 +48,7 @@ TEST_CASE("JsonVariant::operator[]") {
var[1] = "world"; var[1] = "world";
REQUIRE(var.size() == 2); REQUIRE(var.size() == 2);
REQUIRE(std::string("world") == var[1]); REQUIRE("world"_s == var[1]);
} }
SECTION("set value in a nested object") { SECTION("set value in a nested object") {
@@ -56,7 +58,7 @@ TEST_CASE("JsonVariant::operator[]") {
REQUIRE(1 == var.size()); REQUIRE(1 == var.size());
REQUIRE(1 == var[0].size()); REQUIRE(1 == var[0].size());
REQUIRE(std::string("world") == var[0]["hello"]); REQUIRE("world"_s == var[0]["hello"]);
} }
SECTION("variant[0] when variant contains an integer") { SECTION("variant[0] when variant contains an integer") {
@@ -67,6 +69,15 @@ TEST_CASE("JsonVariant::operator[]") {
REQUIRE(var.is<int>()); REQUIRE(var.is<int>());
REQUIRE(var.as<int>() == 123); REQUIRE(var.as<int>() == 123);
} }
SECTION("use JsonVariant as index") {
array.add("A");
array.add("B");
array.add(1);
REQUIRE(var[var[2]] == "B");
REQUIRE(var[var[3]].isNull());
}
} }
SECTION("The JsonVariant is a JsonObject") { SECTION("The JsonVariant is a JsonObject") {
@@ -77,8 +88,8 @@ TEST_CASE("JsonVariant::operator[]") {
object["b"] = "element at key \"b\""; object["b"] = "element at key \"b\"";
REQUIRE(2 == var.size()); REQUIRE(2 == var.size());
REQUIRE(std::string("element at key \"a\"") == var["a"]); REQUIRE("element at key \"a\""_s == var["a"]);
REQUIRE(std::string("element at key \"b\"") == var["b"]); REQUIRE("element at key \"b\""_s == var["b"]);
REQUIRE(var["c"].isNull()); REQUIRE(var["c"].isNull());
REQUIRE(var[0].isNull()); REQUIRE(var[0].isNull());
} }
@@ -87,7 +98,7 @@ TEST_CASE("JsonVariant::operator[]") {
var["hello"] = "world"; var["hello"] = "world";
REQUIRE(1 == var.size()); REQUIRE(1 == var.size());
REQUIRE(std::string("world") == var["hello"]); REQUIRE("world"_s == var["hello"]);
} }
SECTION("set value, key is a char[]") { SECTION("set value, key is a char[]") {
@@ -96,13 +107,22 @@ TEST_CASE("JsonVariant::operator[]") {
key[0] = '!'; // make sure the key is duplicated key[0] = '!'; // make sure the key is duplicated
REQUIRE(1 == var.size()); REQUIRE(1 == var.size());
REQUIRE(std::string("world") == var["hello"]); REQUIRE("world"_s == var["hello"]);
} }
SECTION("var[key].to<JsonArray>()") { SECTION("var[key].to<JsonArray>()") {
JsonArray arr = var["hello"].to<JsonArray>(); JsonArray arr = var["hello"].to<JsonArray>();
REQUIRE(arr.isNull() == false); REQUIRE(arr.isNull() == false);
} }
SECTION("use JsonVariant as key") {
object["a"] = "a";
object["b"] = "b";
object["c"] = "b";
REQUIRE(var[var["c"]] == "b");
REQUIRE(var[var["d"]].isNull());
}
} }
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \ #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@@ -115,7 +135,7 @@ TEST_CASE("JsonVariant::operator[]") {
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
JsonVariant variant = doc.as<JsonVariant>(); JsonVariant variant = doc.as<JsonVariant>();
REQUIRE(std::string("world") == variant[vla]); REQUIRE("world"_s == variant[vla]);
} }
SECTION("key is a VLA, const JsonVariant") { SECTION("key is a VLA, const JsonVariant") {
@@ -126,7 +146,7 @@ TEST_CASE("JsonVariant::operator[]") {
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
const JsonVariant variant = doc.as<JsonVariant>(); const JsonVariant variant = doc.as<JsonVariant>();
REQUIRE(std::string("world") == variant[vla]); REQUIRE("world"_s == variant[vla]);
} }
#endif #endif
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("Unbound JsonVariant") { TEST_CASE("Unbound JsonVariant") {
JsonVariant variant; JsonVariant variant;
@@ -21,6 +23,10 @@ TEST_CASE("Unbound JsonVariant") {
CHECK(variant.as<JsonObject>().isNull()); CHECK(variant.as<JsonObject>().isNull());
CHECK(variant.as<JsonObjectConst>().isNull()); CHECK(variant.as<JsonObjectConst>().isNull());
CHECK(variant.as<JsonString>().isNull()); CHECK(variant.as<JsonString>().isNull());
CHECK(variant.as<MsgPackBinary>().data() == nullptr);
CHECK(variant.as<MsgPackBinary>().size() == 0);
CHECK(variant.as<MsgPackExtension>().data() == nullptr);
CHECK(variant.as<MsgPackExtension>().size() == 0);
} }
SECTION("is<T>()") { SECTION("is<T>()") {
@@ -44,8 +50,10 @@ TEST_CASE("Unbound JsonVariant") {
CHECK_FALSE(variant.set(42L)); CHECK_FALSE(variant.set(42L));
CHECK_FALSE(variant.set(42U)); CHECK_FALSE(variant.set(42U));
CHECK_FALSE(variant.set(serialized("42"))); CHECK_FALSE(variant.set(serialized("42")));
CHECK_FALSE(variant.set(serialized(std::string("42")))); CHECK_FALSE(variant.set(serialized("42"_s)));
CHECK_FALSE(variant.set(true)); CHECK_FALSE(variant.set(true));
CHECK_FALSE(variant.set(MsgPackBinary("hello", 5)));
CHECK_FALSE(variant.set(MsgPackExtension(1, "hello", 5)));
} }
SECTION("add()") { SECTION("add()") {
@@ -62,7 +70,7 @@ TEST_CASE("Unbound JsonVariant") {
CHECK(variant["key"].isNull()); CHECK(variant["key"].isNull());
CHECK_FALSE(variant[0].set(1)); CHECK_FALSE(variant[0].set(1));
CHECK_FALSE(variant["key"].set(1)); CHECK_FALSE(variant["key"].set(1));
CHECK_FALSE(variant[std::string("key")].set(1)); CHECK_FALSE(variant["key"_s].set(1));
} }
SECTION("containsKey()") { SECTION("containsKey()") {

View File

@@ -6,6 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariantConst::as<T>()") { TEST_CASE("JsonVariantConst::as<T>()") {
JsonDocument doc; JsonDocument doc;
JsonVariantConst var = doc.to<JsonVariant>(); JsonVariantConst var = doc.to<JsonVariant>();
@@ -14,6 +16,27 @@ TEST_CASE("JsonVariantConst::as<T>()") {
REQUIRE(var.as<bool>() == true); REQUIRE(var.as<bool>() == true);
REQUIRE(var.as<long>() == 0L); REQUIRE(var.as<long>() == 0L);
REQUIRE(var.as<const char*>() == std::string("hello")); REQUIRE(var.as<const char*>() == "hello"_s);
REQUIRE(var.as<std::string>() == std::string("hello")); REQUIRE(var.as<std::string>() == "hello"_s);
}
TEST_CASE("Invalid conversions") {
using namespace ArduinoJson::detail;
JsonVariantConst variant;
CHECK(is_same<decltype(variant.as<int>()), int>::value);
CHECK(is_same<decltype(variant.as<float>()), float>::value);
CHECK(is_same<decltype(variant.as<JsonVariantConst>()),
JsonVariantConst>::value);
CHECK(
is_same<decltype(variant.as<JsonObjectConst>()), JsonObjectConst>::value);
CHECK(is_same<decltype(variant.as<JsonArrayConst>()), JsonArrayConst>::value);
CHECK(is_same<decltype(variant.as<JsonVariant>()),
InvalidConversion<JsonVariantConst, JsonVariant>>::value);
CHECK(is_same<decltype(variant.as<JsonObject>()),
InvalidConversion<JsonVariantConst, JsonObject>>::value);
CHECK(is_same<decltype(variant.as<JsonArray>()),
InvalidConversion<JsonVariantConst, JsonArray>>::value);
} }

View File

@@ -6,6 +6,8 @@
#include <stdint.h> #include <stdint.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariantConst::containsKey()") { TEST_CASE("JsonVariantConst::containsKey()") {
JsonDocument doc; JsonDocument doc;
doc["hello"] = "world"; doc["hello"] = "world";
@@ -17,8 +19,8 @@ TEST_CASE("JsonVariantConst::containsKey()") {
} }
SECTION("support std::string") { SECTION("support std::string") {
REQUIRE(var.containsKey(std::string("hello")) == true); REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey(std::string("world")) == false); REQUIRE(var.containsKey("world"_s) == false);
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
@@ -30,4 +32,10 @@ TEST_CASE("JsonVariantConst::containsKey()") {
REQUIRE(true == var.containsKey(vla)); REQUIRE(true == var.containsKey(vla));
} }
#endif #endif
SECTION("support JsonVariant") {
doc["key"] = "hello";
REQUIRE(var.containsKey(var["key"]) == true);
REQUIRE(var.containsKey(var["foo"]) == false);
}
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariantConst::operator[]") { TEST_CASE("JsonVariantConst::operator[]") {
JsonDocument doc; JsonDocument doc;
JsonVariantConst var = doc.to<JsonVariant>(); JsonVariantConst var = doc.to<JsonVariant>();
@@ -27,31 +29,41 @@ TEST_CASE("JsonVariantConst::operator[]") {
array.add("A"); array.add("A");
array.add("B"); array.add("B");
REQUIRE(std::string("A") == var[0]); SECTION("int") {
REQUIRE(std::string("B") == var[1]); REQUIRE("A"_s == var[0]);
REQUIRE(std::string("A") == REQUIRE("B"_s == var[1]);
var[static_cast<unsigned char>(0)]); // issue #381 REQUIRE("A"_s == var[static_cast<unsigned char>(0)]); // issue #381
REQUIRE(var[666].isNull()); REQUIRE(var[666].isNull());
REQUIRE(var[3].isNull()); REQUIRE(var[3].isNull());
}
SECTION("const char*") {
REQUIRE(var["0"].isNull()); REQUIRE(var["0"].isNull());
} }
SECTION("JsonVariant") {
array.add(1);
REQUIRE(var[var[2]] == "B"_s);
REQUIRE(var[var[3]].isNull());
}
}
SECTION("object") { SECTION("object") {
JsonObject object = doc.to<JsonObject>(); JsonObject object = doc.to<JsonObject>();
object["a"] = "A"; object["a"] = "A";
object["b"] = "B"; object["b"] = "B";
SECTION("supports const char*") { SECTION("supports const char*") {
REQUIRE(std::string("A") == var["a"]); REQUIRE("A"_s == var["a"]);
REQUIRE(std::string("B") == var["b"]); REQUIRE("B"_s == var["b"]);
REQUIRE(var["c"].isNull()); REQUIRE(var["c"].isNull());
REQUIRE(var[0].isNull()); REQUIRE(var[0].isNull());
} }
SECTION("supports std::string") { SECTION("supports std::string") {
REQUIRE(std::string("A") == var[std::string("a")]); REQUIRE("A"_s == var["a"_s]);
REQUIRE(std::string("B") == var[std::string("b")]); REQUIRE("B"_s == var["b"_s]);
REQUIRE(var[std::string("c")].isNull()); REQUIRE(var["c"_s].isNull());
} }
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \ #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
@@ -61,8 +73,14 @@ TEST_CASE("JsonVariantConst::operator[]") {
char vla[i]; char vla[i];
strcpy(vla, "a"); strcpy(vla, "a");
REQUIRE(std::string("A") == var[vla]); REQUIRE("A"_s == var[vla]);
} }
#endif #endif
SECTION("supports JsonVariant") {
object["c"] = "b";
REQUIRE(var[var["c"]] == "B");
REQUIRE(var[var["d"]].isNull());
}
} }
} }

View File

@@ -9,6 +9,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
#include "custom_string.hpp" #include "custom_string.hpp"
using namespace ArduinoJson::detail; using namespace ArduinoJson::detail;
@@ -36,13 +37,13 @@ void common_tests(StringWriter& writer, const String& output) {
SECTION("OneString") { SECTION("OneString") {
REQUIRE(4 == print(writer, "ABCD")); REQUIRE(4 == print(writer, "ABCD"));
REQUIRE(std::string("ABCD") == output); REQUIRE("ABCD"_s == output);
} }
SECTION("TwoStrings") { SECTION("TwoStrings") {
REQUIRE(4 == print(writer, "ABCD")); REQUIRE(4 == print(writer, "ABCD"));
REQUIRE(4 == print(writer, "EFGH")); REQUIRE(4 == print(writer, "EFGH"));
REQUIRE(std::string("ABCDEFGH") == output); REQUIRE("ABCDEFGH"_s == output);
} }
} }

View File

@@ -58,5 +58,10 @@
// issue #1914 // issue #1914
#define V7 7 #define V7 7
// STM32, Mbed, Particle
#define A0 16
#define A1 17
#define A2 18
// catch.hpp mutes several warnings, this file also allows to detect them // catch.hpp mutes several warnings, this file also allows to detect them
#include "ArduinoJson.h" #include "ArduinoJson.h"

View File

@@ -7,6 +7,5 @@
#include <string> #include <string>
struct custom_char_traits : std::char_traits<char> {}; struct custom_char_traits : std::char_traits<char> {};
struct custom_allocator : std::allocator<char> {};
typedef std::basic_string<char, custom_char_traits, custom_allocator> typedef std::basic_string<char, custom_char_traits> custom_string;
custom_string;

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
#if defined(__clang__) #if defined(__clang__)
# define CONFLICTS_WITH_BUILTIN_OPERATOR # define CONFLICTS_WITH_BUILTIN_OPERATOR
#endif #endif
@@ -111,7 +113,7 @@ TEST_CASE("unsigned char[]") {
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
JsonVariant variant = doc.as<JsonVariant>(); JsonVariant variant = doc.as<JsonVariant>();
REQUIRE(std::string("world") == variant[key]); REQUIRE("world"_s == variant[key]);
} }
#endif #endif
@@ -122,7 +124,7 @@ TEST_CASE("unsigned char[]") {
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
const JsonVariant variant = doc.as<JsonVariant>(); const JsonVariant variant = doc.as<JsonVariant>();
REQUIRE(std::string("world") == variant[key]); REQUIRE("world"_s == variant[key]);
} }
#endif #endif
@@ -160,7 +162,7 @@ TEST_CASE("unsigned char[]") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
obj[key] = "world"; obj[key] = "world";
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
SECTION("JsonObject::operator[] const") { SECTION("JsonObject::operator[] const") {
@@ -170,7 +172,7 @@ TEST_CASE("unsigned char[]") {
deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{\"hello\":\"world\"}");
JsonObject obj = doc.as<JsonObject>(); JsonObject obj = doc.as<JsonObject>();
REQUIRE(std::string("world") == obj[key]); REQUIRE("world"_s == obj[key]);
} }
#endif #endif
@@ -203,7 +205,7 @@ TEST_CASE("unsigned char[]") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
obj["hello"] = value; obj["hello"] = value;
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
SECTION("set()") { SECTION("set()") {
@@ -213,7 +215,7 @@ TEST_CASE("unsigned char[]") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
obj["hello"].set(value); obj["hello"].set(value);
REQUIRE(std::string("world") == obj["hello"]); REQUIRE("world"_s == obj["hello"]);
} }
} }
@@ -225,7 +227,7 @@ TEST_CASE("unsigned char[]") {
JsonArray arr = doc.to<JsonArray>(); JsonArray arr = doc.to<JsonArray>();
arr.add(value); arr.add(value);
REQUIRE(std::string("world") == arr[0]); REQUIRE("world"_s == arr[0]);
} }
} }
@@ -238,7 +240,7 @@ TEST_CASE("unsigned char[]") {
arr.add("hello"); arr.add("hello");
arr[0].set(value); arr[0].set(value);
REQUIRE(std::string("world") == arr[0]); REQUIRE("world"_s == arr[0]);
} }
SECTION("operator=") { SECTION("operator=") {
@@ -249,7 +251,7 @@ TEST_CASE("unsigned char[]") {
arr.add("hello"); arr.add("hello");
arr[0] = value; arr[0] = value;
REQUIRE(std::string("world") == arr[0]); REQUIRE("world"_s == arr[0]);
} }
} }
} }

View File

@@ -15,6 +15,9 @@ add_executable(MixedConfigurationTests
enable_nan_1.cpp enable_nan_1.cpp
enable_progmem_1.cpp enable_progmem_1.cpp
issue1707.cpp issue1707.cpp
string_length_size_1.cpp
string_length_size_2.cpp
string_length_size_4.cpp
use_double_0.cpp use_double_0.cpp
use_double_1.cpp use_double_1.cpp
use_long_long_0.cpp use_long_long_0.cpp

View File

@@ -0,0 +1,131 @@
#define ARDUINOJSON_STRING_LENGTH_SIZE 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Literals.hpp"
TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 1") {
JsonDocument doc;
SECTION("set(std::string)") {
SECTION("returns true if len <= 255") {
auto result = doc.set(std::string(255, '?'));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
SECTION("returns false if len >= 256") {
auto result = doc.set(std::string(256, '?'));
REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}
SECTION("set(MsgPackBinary)") {
SECTION("returns true if size <= 253") {
auto str = std::string(253, '?');
auto result = doc.set(MsgPackBinary(str.data(), str.size()));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
SECTION("returns false if size >= 254") {
auto str = std::string(254, '?');
auto result = doc.set(MsgPackBinary(str.data(), str.size()));
REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}
SECTION("set(MsgPackExtension)") {
SECTION("returns true if size <= 252") {
auto str = std::string(252, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
SECTION("returns false if size >= 253") {
auto str = std::string(253, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));
REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}
SECTION("deserializeJson()") {
SECTION("returns Ok if string length <= 255") {
auto input = "\"" + std::string(255, '?') + "\"";
auto err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if string length >= 256") {
auto input = "\"" + std::string(256, '?') + "\"";
auto err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
}
SECTION("deserializeMsgPack()") {
SECTION("returns Ok if string length <= 255") {
auto input = "\xd9\xff" + std::string(255, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if string length >= 256") {
auto input = "\xda\x01\x00"_s + std::string(256, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("returns Ok if binary size <= 253") {
auto input = "\xc4\xfd" + std::string(253, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if binary size >= 254") {
auto input = "\xc4\xfe" + std::string(254, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("returns Ok if extension size <= 252") {
auto input = "\xc7\xfc\x01" + std::string(252, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if binary size >= 253") {
auto input = "\xc7\xfd\x01" + std::string(253, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
}
}

View File

@@ -0,0 +1,140 @@
#define ARDUINOJSON_STRING_LENGTH_SIZE 2
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Literals.hpp"
TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 2") {
JsonDocument doc;
SECTION("set(std::string)") {
SECTION("returns true if len <= 65535") {
auto result = doc.set(std::string(65535, '?'));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
SECTION("returns false if len >= 65536") {
auto result = doc.set(std::string(65536, '?'));
REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}
SECTION("set(MsgPackBinary)") {
SECTION("returns true if size <= 65532") {
auto str = std::string(65532, '?');
auto result = doc.set(MsgPackBinary(str.data(), str.size()));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
SECTION("returns false if size >= 65533") {
auto str = std::string(65533, '?');
auto result = doc.set(MsgPackBinary(str.data(), str.size()));
REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}
SECTION("set(MsgPackExtension)") {
SECTION("returns true if size <= 65531") {
auto str = std::string(65531, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
SECTION("returns false if size >= 65532") {
auto str = std::string(65532, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));
REQUIRE(result == false);
REQUIRE(doc.overflowed() == true);
}
}
SECTION("deserializeJson()") {
SECTION("returns Ok if string length <= 65535") {
auto input = "\"" + std::string(65535, '?') + "\"";
auto err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if string length >= 65536") {
auto input = "\"" + std::string(65536, '?') + "\"";
auto err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
}
SECTION("deserializeMsgPack()") {
SECTION("returns Ok if string length <= 65535") {
auto input = "\xda\xff\xff" + std::string(65535, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if string length >= 65536") {
auto input = "\xdb\x00\x01\x00\x00"_s + std::string(65536, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("returns Ok if binary size <= 65532") {
auto input = "\xc5\xff\xfc" + std::string(65532, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if binary size >= 65534") {
auto input = "\xc5\xff\xfd" + std::string(65534, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
// https://oss-fuzz.com/testcase?key=5354792971993088
SECTION("doesn't overflow if binary size == 0xFFFF") {
auto input = "\xc5\xff\xff"_s;
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("returns Ok if extension size <= 65531") {
auto input = "\xc8\xff\xfb\x01" + std::string(65531, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns NoMemory if extension size >= 65532") {
auto input = "\xc8\xff\xfc\x01" + std::string(65532, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
}
}

View File

@@ -0,0 +1,146 @@
#define ARDUINOJSON_STRING_LENGTH_SIZE 4
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Literals.hpp"
TEST_CASE("ARDUINOJSON_STRING_LENGTH_SIZE == 4") {
JsonDocument doc;
SECTION("set(std::string)") {
SECTION("returns true if string length >= 65536") {
auto result = doc.set(std::string(65536, '?'));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
}
SECTION("set(MsgPackBinary)") {
SECTION("returns true if size >= 65536") {
auto str = std::string(65536, '?');
auto result = doc.set(MsgPackBinary(str.data(), str.size()));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
}
SECTION("set(MsgPackExtension)") {
SECTION("returns true if size >= 65532") {
auto str = std::string(65532, '?');
auto result = doc.set(MsgPackExtension(1, str.data(), str.size()));
REQUIRE(result == true);
REQUIRE(doc.overflowed() == false);
}
}
SECTION("deserializeJson()") {
SECTION("returns Ok if string length >= 65536") {
auto input = "\"" + std::string(65536, '?') + "\"";
auto err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
}
SECTION("deserializeMsgPack()") {
SECTION("returns Ok if string size >= 65536") {
auto input = "\xda\xff\xff" + std::string(65536, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns Ok if binary size >= 65536") {
auto input = "\xc5\xff\xff" + std::string(65536, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("returns Ok if extension size >= 65532") {
auto input = "\xc8\xff\xfb\x01" + std::string(65532, '?');
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
// https://oss-fuzz.com/testcase?key=5354792971993088
SECTION("doesn't overflow if binary size == 0xFFFFFFFF") {
auto input = "\xc6\xff\xff\xff\xff"_s;
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("doesn't overflow if string size == 0xFFFFFFFF") {
auto input = "\xdb\xff\xff\xff\xff???????????????????"_s;
auto err = deserializeMsgPack(doc, input);
REQUIRE(err != DeserializationError::Ok);
}
}
SECTION("bin 32 deserialization") {
auto str = std::string(65536, '?');
auto input = "\xc6\x00\x01\x00\x00"_s + str;
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackBinary>());
auto binary = doc.as<MsgPackBinary>();
REQUIRE(binary.size() == 65536);
REQUIRE(binary.data() != nullptr);
REQUIRE(std::string(reinterpret_cast<const char*>(binary.data()),
binary.size()) == str);
}
SECTION("bin 32 serialization") {
auto str = std::string(65536, '?');
doc.set(MsgPackBinary(str.data(), str.size()));
std::string output;
auto result = serializeMsgPack(doc, output);
REQUIRE(result == 5 + str.size());
REQUIRE(output == "\xc6\x00\x01\x00\x00"_s + str);
}
SECTION("ext 32 deserialization") {
auto str = std::string(65536, '?');
auto input = "\xc9\x00\x01\x00\x00\x2a"_s + str;
auto err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto value = doc.as<MsgPackExtension>();
REQUIRE(value.type() == 42);
REQUIRE(value.size() == 65536);
REQUIRE(value.data() != nullptr);
REQUIRE(std::string(reinterpret_cast<const char*>(value.data()),
value.size()) == str);
}
SECTION("ext 32 serialization") {
auto str = std::string(65536, '?');
doc.set(MsgPackExtension(42, str.data(), str.size()));
std::string output;
auto result = serializeMsgPack(doc, output);
REQUIRE(result == 6 + str.size());
REQUIRE(output == "\xc9\x00\x01\x00\x00\x2a"_s + str);
}
}

View File

@@ -3,9 +3,12 @@
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") { TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") {
JsonDocument doc; JsonDocument doc;
SECTION("smoke test") {
doc["A"] = 42; doc["A"] = 42;
doc["B"] = 84; doc["B"] = 84;
@@ -14,3 +17,26 @@ TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") {
REQUIRE(json == "{\"A\":42,\"B\":84}"); REQUIRE(json == "{\"A\":42,\"B\":84}");
} }
SECTION("deserializeMsgPack()") {
SECTION("cf 00 00 00 00 ff ff ff ff") {
auto err =
deserializeMsgPack(doc, "\xcf\x00\x00\x00\x00\xff\xff\xff\xff"_s);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<uint32_t>() == 0xFFFFFFFF);
}
SECTION("cf 00 00 00 01 00 00 00 00") {
auto err =
deserializeMsgPack(doc, "\xcf\x00\x00\x00\x01\x00\x00\x00\x00"_s);
REQUIRE(err == DeserializationError::Ok);
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
REQUIRE(doc.as<JsonInteger>() == 0x100000000);
#else
REQUIRE(doc.isNull());
#endif
}
}
}

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
template <typename T> template <typename T>
static void checkValue(const char* input, T expected) { static void checkValue(const char* input, T expected) {
@@ -123,21 +124,171 @@ TEST_CASE("deserialize MsgPack value") {
SECTION("fixstr") { SECTION("fixstr") {
checkValue<std::string>("\xA0", std::string("")); checkValue<std::string>("\xA0", std::string(""));
checkValue<std::string>("\xABhello world", std::string("hello world")); checkValue<std::string>("\xABhello world", "hello world"_s);
checkValue<std::string>("\xBFhello world hello world hello !", checkValue<std::string>("\xBFhello world hello world hello !",
std::string("hello world hello world hello !")); "hello world hello world hello !"_s);
} }
SECTION("str 8") { SECTION("str 8") {
checkValue<std::string>("\xd9\x05hello", std::string("hello")); checkValue<std::string>("\xd9\x05hello", "hello"_s);
} }
SECTION("str 16") { SECTION("str 16") {
checkValue<std::string>("\xda\x00\x05hello", std::string("hello")); checkValue<std::string>("\xda\x00\x05hello", "hello"_s);
} }
SECTION("str 32") { SECTION("str 32") {
checkValue<std::string>("\xdb\x00\x00\x00\x05hello", std::string("hello")); checkValue<std::string>("\xdb\x00\x00\x00\x05hello", "hello"_s);
}
SECTION("bin 8") {
JsonDocument doc;
DeserializationError error = deserializeMsgPack(doc, "\xc4\x01?");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackBinary>());
auto binary = doc.as<MsgPackBinary>();
REQUIRE(binary.size() == 1);
REQUIRE(binary.data() != nullptr);
REQUIRE(reinterpret_cast<const char*>(binary.data())[0] == '?');
}
SECTION("bin 16") {
JsonDocument doc;
auto str = std::string(256, '?');
auto input = "\xc5\x01\x00"_s + str;
DeserializationError error = deserializeMsgPack(doc, input);
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackBinary>());
auto binary = doc.as<MsgPackBinary>();
REQUIRE(binary.size() == 0x100);
REQUIRE(binary.data() != nullptr);
REQUIRE(std::string(reinterpret_cast<const char*>(binary.data()),
binary.size()) == str);
}
SECTION("fixext 1") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xd4\x01\x02");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 1);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 2);
}
SECTION("fixext 2") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xd5\x01\x02\x03");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 2);
REQUIRE(data[1] == 3);
}
SECTION("fixext 4") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xd6\x01\x02\x03\x04\x05");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 4);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 2);
REQUIRE(data[1] == 3);
REQUIRE(data[2] == 4);
REQUIRE(data[3] == 5);
}
SECTION("fixext 8") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xd7\x01????????");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 8);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == '?');
REQUIRE(data[7] == '?');
}
SECTION("fixext 16") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xd8\x01?????????????????");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 16);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == '?');
REQUIRE(data[15] == '?');
}
SECTION("ext 8") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xc7\x02\x01\x03\x04");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 3);
REQUIRE(data[1] == 4);
}
SECTION("ext 16") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xc8\x00\x02\x01\x03\x04");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 3);
REQUIRE(data[1] == 4);
}
SECTION("ext 32") {
JsonDocument doc;
auto error = deserializeMsgPack(doc, "\xc9\x00\x00\x00\x02\x01\x03\x04");
REQUIRE(error == DeserializationError::Ok);
REQUIRE(doc.is<MsgPackExtension>());
auto ext = doc.as<MsgPackExtension>();
REQUIRE(ext.type() == 1);
REQUIRE(ext.size() == 2);
auto data = reinterpret_cast<const uint8_t*>(ext.data());
REQUIRE(data[0] == 3);
REQUIRE(data[1] == 4);
} }
} }
@@ -198,12 +349,12 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
checkError(0, "\x80", DeserializationError::Ok); checkError(0, "\x80", DeserializationError::Ok);
} }
SECTION("{H:1}") { SECTION("{H:1}") {
checkError(0, "\x81\xA1H\x01", DeserializationError::NoMemory); checkError(1, "\x81\xA1H\x01", DeserializationError::NoMemory);
checkError(3, "\x81\xA1H\x01", DeserializationError::Ok); checkError(2, "\x81\xA1H\x01", DeserializationError::Ok);
} }
SECTION("{H:1,W:2}") { SECTION("{H:1,W:2}") {
checkError(3, "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory); checkError(2, "\x82\xA1H\x01\xA1W\x02", DeserializationError::NoMemory);
checkError(5, "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok); checkError(3, "\x82\xA1H\x01\xA1W\x02", DeserializationError::Ok);
} }
} }
@@ -212,13 +363,13 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
checkError(0, "\xDE\x00\x00", DeserializationError::Ok); checkError(0, "\xDE\x00\x00", DeserializationError::Ok);
} }
SECTION("{H:1}") { SECTION("{H:1}") {
checkError(2, "\xDE\x00\x01\xA1H\x01", DeserializationError::NoMemory); checkError(1, "\xDE\x00\x01\xA1H\x01", DeserializationError::NoMemory);
checkError(3, "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok); checkError(2, "\xDE\x00\x01\xA1H\x01", DeserializationError::Ok);
} }
SECTION("{H:1,W:2}") { SECTION("{H:1,W:2}") {
checkError(3, "\xDE\x00\x02\xA1H\x01\xA1W\x02", checkError(2, "\xDE\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory); DeserializationError::NoMemory);
checkError(5, "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok); checkError(3, "\xDE\x00\x02\xA1H\x01\xA1W\x02", DeserializationError::Ok);
} }
} }
@@ -227,14 +378,14 @@ TEST_CASE("deserializeMsgPack() under memory constaints") {
checkError(0, "\xDF\x00\x00\x00\x00", DeserializationError::Ok); checkError(0, "\xDF\x00\x00\x00\x00", DeserializationError::Ok);
} }
SECTION("{H:1}") { SECTION("{H:1}") {
checkError(2, "\xDF\x00\x00\x00\x01\xA1H\x01", checkError(1, "\xDF\x00\x00\x00\x01\xA1H\x01",
DeserializationError::NoMemory); DeserializationError::NoMemory);
checkError(3, "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok); checkError(2, "\xDF\x00\x00\x00\x01\xA1H\x01", DeserializationError::Ok);
} }
SECTION("{H:1,W:2}") { SECTION("{H:1,W:2}") {
checkError(3, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", checkError(2, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::NoMemory); DeserializationError::NoMemory);
checkError(5, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02", checkError(3, "\xDF\x00\x00\x00\x02\xA1H\x01\xA1W\x02",
DeserializationError::Ok); DeserializationError::Ok);
} }
} }

View File

@@ -8,6 +8,7 @@
#include <string> #include <string>
#include "Allocators.hpp" #include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray; using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -15,7 +16,7 @@ using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserializeMsgPack(JsonDocument&)") { TEST_CASE("deserializeMsgPack(JsonDocument&)") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc.add(std::string("hello")); doc.add("hello"_s);
spy.clearLog(); spy.clearLog();
auto err = deserializeMsgPack(doc, "\x91\x2A"); auto err = deserializeMsgPack(doc, "\x91\x2A");
@@ -34,7 +35,7 @@ TEST_CASE("deserializeMsgPack(JsonVariant)") {
SECTION("variant is bound") { SECTION("variant is bound") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc.add(std::string("hello")); doc.add("hello"_s);
spy.clearLog(); spy.clearLog();
JsonVariant variant = doc[0]; JsonVariant variant = doc[0];
@@ -60,7 +61,7 @@ TEST_CASE("deserializeMsgPack(JsonVariant)") {
TEST_CASE("deserializeMsgPack(ElementProxy)") { TEST_CASE("deserializeMsgPack(ElementProxy)") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc.add(std::string("hello")); doc.add("hello"_s);
spy.clearLog(); spy.clearLog();
SECTION("element already exists") { SECTION("element already exists") {
@@ -85,7 +86,7 @@ TEST_CASE("deserializeMsgPack(ElementProxy)") {
TEST_CASE("deserializeMsgPack(MemberProxy)") { TEST_CASE("deserializeMsgPack(MemberProxy)") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
doc[std::string("hello")] = std::string("world"); doc["hello"_s] = "world"_s;
spy.clearLog(); spy.clearLog();
SECTION("member already exists") { SECTION("member already exists") {

View File

@@ -13,7 +13,7 @@ static void check(const char* input, T expected) {
uint8_t* f = reinterpret_cast<uint8_t*>(&actual); uint8_t* f = reinterpret_cast<uint8_t*>(&actual);
const uint8_t* d = reinterpret_cast<const uint8_t*>(input); const uint8_t* d = reinterpret_cast<const uint8_t*>(input);
doubleToFloat(d, f); doubleToFloat(d, f);
fixEndianess(actual); fixEndianness(actual);
CHECK(actual == expected); CHECK(actual == expected);
} }

View File

@@ -7,6 +7,8 @@
#include <sstream> #include <sstream>
#include "Allocators.hpp"
TEST_CASE("deserializeMsgPack() returns InvalidInput") { TEST_CASE("deserializeMsgPack() returns InvalidInput") {
JsonDocument doc; JsonDocument doc;
@@ -182,84 +184,18 @@ TEST_CASE("deserializeMsgPack() returns IncompleteInput") {
} }
} }
static std::string msgPackToJson(const char* input, size_t inputSize) { TEST_CASE(
JsonDocument doc; "deserializeMsgPack() returns NoMemory when string allocation fails") {
auto err = deserializeMsgPack(doc, input, inputSize); TimebombAllocator allocator(0);
REQUIRE(err == DeserializationError::Ok); JsonDocument doc(&allocator);
return doc.as<std::string>();
SECTION("fixstr") {
DeserializationError err = deserializeMsgPack(doc, "\xA5hello", 9);
REQUIRE(err == DeserializationError::NoMemory);
} }
TEST_CASE("deserializeMsgPack() replaces unsupported types by null") {
SECTION("bin 8") { SECTION("bin 8") {
REQUIRE(msgPackToJson("\x92\xc4\x01X\x2A", 5) == "[null,42]"); DeserializationError err = deserializeMsgPack(doc, "\xC4\x01X", 3);
}
SECTION("bin 16") {
REQUIRE(msgPackToJson("\x92\xc5\x00\x01X\x2A", 6) == "[null,42]");
}
SECTION("bin 32") {
REQUIRE(msgPackToJson("\x92\xc6\x00\x00\x00\x01X\x2A", 8) == "[null,42]");
}
SECTION("ext 8") {
REQUIRE(msgPackToJson("\x92\xc7\x01\x01\x01\x2A", 6) == "[null,42]");
}
SECTION("ext 16") {
REQUIRE(msgPackToJson("\x92\xc8\x00\x01\x01\x01\x2A", 7) == "[null,42]");
}
SECTION("ext 32") {
REQUIRE(msgPackToJson("\x92\xc9\x00\x00\x00\x01\x01\x01\x2A", 9) ==
"[null,42]");
}
SECTION("fixext 1") {
REQUIRE(msgPackToJson("\x92\xd4\x01\x01\x2A", 5) == "[null,42]");
}
SECTION("fixext 2") {
REQUIRE(msgPackToJson("\x92\xd5\x01\x01\x02\x2A", 6) == "[null,42]");
}
SECTION("fixext 4") {
REQUIRE(msgPackToJson("\x92\xd6\x01\x01\x02\x03\x04\x2A", 8) ==
"[null,42]");
}
SECTION("fixext 8") {
REQUIRE(msgPackToJson("\x92\xd7\x01\x01\x02\x03\x04\x05\x06\x07\x08\x2A",
12) == "[null,42]");
}
SECTION("fixext 16") {
REQUIRE(msgPackToJson("\x92\xd8\x01\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A"
"\x0B\x0C\x0D\x0E"
"\x0F\x10\x2A",
20) == "[null,42]");
}
}
TEST_CASE("deserializeMsgPack() returns NoMemory is string length overflows") {
JsonDocument doc;
auto maxLength = ArduinoJson::detail::StringNode::maxLength;
SECTION("max length should succeed") {
auto len = maxLength;
std::string prefix = {'\xdb', char(len >> 24), char(len >> 16),
char(len >> 8), char(len)};
auto err = deserializeMsgPack(doc, prefix + std::string(len, 'a'));
REQUIRE(err == DeserializationError::Ok);
}
SECTION("one above max length should fail") {
auto len = maxLength + 1;
std::string prefix = {'\xdb', char(len >> 24), char(len >> 16),
char(len >> 8), char(len)};
auto err = deserializeMsgPack(doc, prefix + std::string(len, 'a'));
REQUIRE(err == DeserializationError::NoMemory); REQUIRE(err == DeserializationError::NoMemory);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "CustomReader.hpp" #include "CustomReader.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject; using ArduinoJson::detail::sizeofObject;
@@ -21,8 +22,7 @@ TEST_CASE("deserializeMsgPack(const std::string&)") {
} }
SECTION("should accept temporary string") { SECTION("should accept temporary string") {
DeserializationError err = DeserializationError err = deserializeMsgPack(doc, "\x92\x01\x02"_s);
deserializeMsgPack(doc, std::string("\x92\x01\x02"));
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
} }
@@ -35,12 +35,11 @@ TEST_CASE("deserializeMsgPack(const std::string&)") {
JsonArray array = doc.as<JsonArray>(); JsonArray array = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
REQUIRE(std::string("hello") == array[0]); REQUIRE("hello"_s == array[0]);
} }
SECTION("should accept a zero in input") { SECTION("should accept a zero in input") {
DeserializationError err = DeserializationError err = deserializeMsgPack(doc, "\x92\x00\x02"_s);
deserializeMsgPack(doc, std::string("\x92\x00\x02", 3));
REQUIRE(err == DeserializationError::Ok); REQUIRE(err == DeserializationError::Ok);
JsonArray arr = doc.as<JsonArray>(); JsonArray arr = doc.as<JsonArray>();
@@ -53,7 +52,7 @@ TEST_CASE("deserializeMsgPack(std::istream&)") {
JsonDocument doc; JsonDocument doc;
SECTION("should accept a zero in input") { SECTION("should accept a zero in input") {
std::istringstream input(std::string("\x92\x00\x02", 3)); std::istringstream input("\x92\x00\x02"_s);
DeserializationError err = deserializeMsgPack(doc, input); DeserializationError err = deserializeMsgPack(doc, input);

View File

@@ -7,6 +7,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
static void check(const JsonArray array, const char* expected_data, static void check(const JsonArray array, const char* expected_data,
size_t expected_len) { size_t expected_len) {
std::string expected(expected_data, expected_data + expected_len); std::string expected(expected_data, expected_data + expected_len);
@@ -57,7 +59,6 @@ TEST_CASE("serialize MsgPack array") {
array.add(nil); array.add(nil);
REQUIRE(array.size() == 65536); REQUIRE(array.size() == 65536);
check(array, check(array, "\xDD\x00\x01\x00\x00"_s + std::string(65536, '\xc0'));
std::string("\xDD\x00\x01\x00\x00", 5) + std::string(65536, '\xc0'));
} }
} }

View File

@@ -6,6 +6,8 @@
#include <stdio.h> #include <stdio.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
static void check(const JsonObject object, const char* expected_data, static void check(const JsonObject object, const char* expected_data,
size_t expected_len) { size_t expected_len) {
std::string expected(expected_data, expected_data + expected_len); std::string expected(expected_data, expected_data + expected_len);
@@ -44,7 +46,7 @@ TEST_CASE("serialize MsgPack object") {
SECTION("map 16") { SECTION("map 16") {
for (int i = 0; i < 16; ++i) { for (int i = 0; i < 16; ++i) {
char key[16]; char key[16];
sprintf(key, "i%X", i); snprintf(key, sizeof(key), "i%X", i);
object[key] = i; object[key] = i;
} }
@@ -60,7 +62,7 @@ TEST_CASE("serialize MsgPack object") {
// //
// for (int i = 0; i < 65536; ++i) { // for (int i = 0; i < 65536; ++i) {
// char kv[16]; // char kv[16];
// sprintf(kv, "%04x", i); // snprintf(kv, sizeof(kv), "%04x", i);
// object[kv] = kv; // object[kv] = kv;
// expected += '\xA4'; // expected += '\xA4';
// expected += kv; // expected += kv;
@@ -77,7 +79,7 @@ TEST_CASE("serialize MsgPack object") {
} }
SECTION("serialized(std::string)") { SECTION("serialized(std::string)") {
object["hello"] = serialized(std::string("\xDB\x00\x01\x00\x00", 5)); object["hello"] = serialized("\xDB\x00\x01\x00\x00"_s);
check(object, "\x81\xA5hello\xDB\x00\x01\x00\x00"); check(object, "\x81\xA5hello\xDB\x00\x01\x00\x00");
} }
} }

View File

@@ -5,6 +5,8 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <catch.hpp> #include <catch.hpp>
#include "Literals.hpp"
template <typename T> template <typename T>
static void checkVariant(T value, const char* expected_data, static void checkVariant(T value, const char* expected_data,
size_t expected_len) { size_t expected_len) {
@@ -129,16 +131,15 @@ TEST_CASE("serialize MsgPack value") {
SECTION("str 16") { SECTION("str 16") {
std::string shortest(256, '?'); std::string shortest(256, '?');
checkVariant(shortest.c_str(), std::string("\xDA\x01\x00", 3) + shortest); checkVariant(shortest.c_str(), "\xDA\x01\x00"_s + shortest);
std::string longest(65535, '?'); std::string longest(65535, '?');
checkVariant(longest.c_str(), std::string("\xDA\xFF\xFF", 3) + longest); checkVariant(longest.c_str(), "\xDA\xFF\xFF"_s + longest);
} }
SECTION("str 32") { SECTION("str 32") {
std::string shortest(65536, '?'); std::string shortest(65536, '?');
checkVariant(shortest.c_str(), checkVariant(shortest.c_str(), "\xDB\x00\x01\x00\x00"_s + shortest);
std::string("\xDB\x00\x01\x00\x00", 5) + shortest);
} }
SECTION("serialized(const char*)") { SECTION("serialized(const char*)") {
@@ -146,6 +147,56 @@ TEST_CASE("serialize MsgPack value") {
checkVariant(serialized("\xDB\x00\x01\x00\x00", 5), "\xDB\x00\x01\x00\x00"); checkVariant(serialized("\xDB\x00\x01\x00\x00", 5), "\xDB\x00\x01\x00\x00");
} }
SECTION("bin 8") {
checkVariant(MsgPackBinary("?", 1), "\xC4\x01?");
}
SECTION("bin 16") {
auto str = std::string(256, '?');
checkVariant(MsgPackBinary(str.data(), str.size()), "\xC5\x01\x00"_s + str);
}
// bin 32 is tested in string_length_size_4.cpp
SECTION("fixext 1") {
checkVariant(MsgPackExtension(1, "\x02", 1), "\xD4\x01\x02");
}
SECTION("fixext 2") {
checkVariant(MsgPackExtension(1, "\x03\x04", 2), "\xD5\x01\x03\x04");
}
SECTION("fixext 4") {
checkVariant(MsgPackExtension(1, "\x05\x06\x07\x08", 4),
"\xD6\x01\x05\x06\x07\x08");
}
SECTION("fixext 8") {
checkVariant(MsgPackExtension(1, "????????", 8), "\xD7\x01????????");
}
SECTION("fixext 16") {
checkVariant(MsgPackExtension(1, "????????????????", 16),
"\xD8\x01????????????????");
}
SECTION("ext 8") {
checkVariant(MsgPackExtension(2, "???", 3), "\xC7\x03\x02???");
checkVariant(MsgPackExtension(2, "?????", 5), "\xC7\x05\x02?????");
checkVariant(MsgPackExtension(2, "???????", 7), "\xC7\x07\x02???????");
checkVariant(MsgPackExtension(2, "?????????", 9), "\xC7\x09\x02?????????");
checkVariant(MsgPackExtension(2, "???????????????", 15),
"\xC7\x0F\x02???????????????");
checkVariant(MsgPackExtension(2, "?????????????????", 17),
"\xC7\x11\x02?????????????????");
}
SECTION("ext 16") {
auto str = std::string(256, '?');
checkVariant(MsgPackExtension(2, str.data(), str.size()),
"\xC8\x01\x00\x02"_s + str);
}
SECTION("serialize round double as integer") { // Issue #1718 SECTION("serialize round double as integer") { // Issue #1718
checkVariant(-32768.0, "\xD1\x80\x00"); checkVariant(-32768.0, "\xD1\x80\x00");
checkVariant(-129.0, "\xD1\xFF\x7F"); checkVariant(-129.0, "\xD1\xFF\x7F");

View File

@@ -3,6 +3,7 @@
// MIT License // MIT License
#include <ArduinoJson/Memory/StringBuilder.hpp> #include <ArduinoJson/Memory/StringBuilder.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"

View File

@@ -2,9 +2,7 @@
// Copyright © 2014-2024, Benoit BLANCHON // Copyright © 2014-2024, Benoit BLANCHON
// MIT License // MIT License
#include <ArduinoJson/Memory/Alignment.hpp> #include <ArduinoJson.hpp>
#include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <catch.hpp> #include <catch.hpp>
#include "Allocators.hpp" #include "Allocators.hpp"

View File

@@ -3,6 +3,7 @@
// MIT License // MIT License
#include <ArduinoJson/Memory/ResourceManager.hpp> #include <ArduinoJson/Memory/ResourceManager.hpp>
#include <ArduinoJson/Memory/VariantPoolImpl.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp> #include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp> #include <catch.hpp>

View File

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

View File

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

View File

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

View File

@@ -36,6 +36,7 @@
#include "ArduinoJson/Array/ElementProxy.hpp" #include "ArduinoJson/Array/ElementProxy.hpp"
#include "ArduinoJson/Array/Utilities.hpp" #include "ArduinoJson/Array/Utilities.hpp"
#include "ArduinoJson/Collection/CollectionImpl.hpp" #include "ArduinoJson/Collection/CollectionImpl.hpp"
#include "ArduinoJson/Memory/ResourceManagerImpl.hpp"
#include "ArduinoJson/Memory/VariantPoolImpl.hpp" #include "ArduinoJson/Memory/VariantPoolImpl.hpp"
#include "ArduinoJson/Object/MemberProxy.hpp" #include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp" #include "ArduinoJson/Object/ObjectImpl.hpp"
@@ -47,7 +48,9 @@
#include "ArduinoJson/Json/JsonDeserializer.hpp" #include "ArduinoJson/Json/JsonDeserializer.hpp"
#include "ArduinoJson/Json/JsonSerializer.hpp" #include "ArduinoJson/Json/JsonSerializer.hpp"
#include "ArduinoJson/Json/PrettyJsonSerializer.hpp" #include "ArduinoJson/Json/PrettyJsonSerializer.hpp"
#include "ArduinoJson/MsgPack/MsgPackBinary.hpp"
#include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp" #include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp"
#include "ArduinoJson/MsgPack/MsgPackExtension.hpp"
#include "ArduinoJson/MsgPack/MsgPackSerializer.hpp" #include "ArduinoJson/MsgPack/MsgPackSerializer.hpp"
#include "ArduinoJson/compatibility.hpp" #include "ArduinoJson/compatibility.hpp"

View File

@@ -20,6 +20,17 @@ class ArrayData : public CollectionData {
return array->addElement(resources); return array->addElement(resources);
} }
template <typename T>
bool addValue(T&& value, ResourceManager* resources);
template <typename T>
static bool addValue(ArrayData* array, T&& value,
ResourceManager* resources) {
if (!array)
return false;
return array->addValue(value, resources);
}
VariantData* getOrAddElement(size_t index, ResourceManager* resources); VariantData* getOrAddElement(size_t index, ResourceManager* resources);
VariantData* getElement(size_t index, const ResourceManager* resources) const; VariantData* getElement(size_t index, const ResourceManager* resources) const;

View File

@@ -47,4 +47,19 @@ inline void ArrayData::removeElement(size_t index, ResourceManager* resources) {
remove(at(index, resources), resources); remove(at(index, resources), resources);
} }
template <typename T>
inline bool ArrayData::addValue(T&& value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(resources != nullptr);
auto slot = resources->allocSlot();
if (!slot)
return false;
JsonVariant variant(slot->data(), resources);
if (!variant.set(detail::forward<T>(value))) {
resources->freeSlot(slot);
return false;
}
addSlot(slot, resources);
return true;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -44,8 +44,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T>
typename detail::enable_if<!detail::is_same<T, JsonVariant>::value, T>::type detail::enable_if_t<!detail::is_same<T, JsonVariant>::value, T> add() const {
add() const {
return add<JsonVariant>().to<T>(); return add<JsonVariant>().to<T>();
} }
@@ -53,8 +52,7 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T>
typename detail::enable_if<detail::is_same<T, JsonVariant>::value, T>::type detail::enable_if_t<detail::is_same<T, JsonVariant>::value, T> add() const {
add() const {
return JsonVariant(detail::ArrayData::addElement(data_, resources_), return JsonVariant(detail::ArrayData::addElement(data_, resources_),
resources_); resources_);
} }
@@ -63,14 +61,14 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T>
bool add(const T& value) const { bool add(const T& value) const {
return add<JsonVariant>().set(value); return detail::ArrayData::addValue(data_, value, resources_);
} }
// Appends a value to the array. // Appends a value to the array.
// https://arduinojson.org/v7/api/jsonarray/add/ // https://arduinojson.org/v7/api/jsonarray/add/
template <typename T> template <typename T>
bool add(T* value) const { bool add(T* value) const {
return add<JsonVariant>().set(value); return detail::ArrayData::addValue(data_, value, resources_);
} }
// Returns an iterator to the first element of the array. // Returns an iterator to the first element of the array.
@@ -114,6 +112,15 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
detail::ArrayData::removeElement(data_, index, resources_); detail::ArrayData::removeElement(data_, index, resources_);
} }
// 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 {
if (variant.template is<size_t>())
remove(variant.template as<size_t>());
}
// Removes all the elements of the array. // Removes all the elements of the array.
// https://arduinojson.org/v7/api/jsonarray/clear/ // https://arduinojson.org/v7/api/jsonarray/clear/
void clear() const { void clear() const {
@@ -122,8 +129,23 @@ class JsonArray : public detail::VariantOperators<JsonArray> {
// Gets or sets the element at the specified index. // Gets or sets the element at the specified index.
// https://arduinojson.org/v7/api/jsonarray/subscript/ // https://arduinojson.org/v7/api/jsonarray/subscript/
detail::ElementProxy<JsonArray> operator[](size_t index) const { template <typename T>
return {*this, index}; detail::enable_if_t<detail::is_integral<T>::value,
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 {
if (variant.template is<size_t>())
return operator[](variant.template as<size_t>());
else
return {*this, size_t(-1)};
} }
operator JsonVariantConst() const { operator JsonVariantConst() const {

View File

@@ -36,7 +36,7 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
} }
// Creates an unbound reference. // Creates an unbound reference.
JsonArrayConst() : data_(0) {} JsonArrayConst() : data_(0), resources_(0) {}
// INTERNAL USE ONLY // INTERNAL USE ONLY
JsonArrayConst(const detail::ArrayData* data, JsonArrayConst(const detail::ArrayData* data,
@@ -45,9 +45,23 @@ class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
// Returns the element at the specified index. // Returns the element at the specified index.
// https://arduinojson.org/v7/api/jsonarrayconst/subscript/ // https://arduinojson.org/v7/api/jsonarrayconst/subscript/
JsonVariantConst operator[](size_t index) const { template <typename T>
detail::enable_if_t<detail::is_integral<T>::value, JsonVariantConst>
operator[](T index) const {
return JsonVariantConst( return JsonVariantConst(
detail::ArrayData::getElement(data_, index, resources_), resources_); detail::ArrayData::getElement(data_, size_t(index), resources_),
resources_);
}
// 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 {
if (variant.template is<size_t>())
return operator[](variant.template as<size_t>());
else
return JsonVariantConst();
} }
operator JsonVariantConst() const { operator JsonVariantConst() const {

View File

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

View File

@@ -111,12 +111,13 @@ class CollectionData {
return head_; return head_;
} }
void addSlot(SlotWithId slot, ResourceManager* resources);
protected: protected:
iterator addSlot(ResourceManager*); iterator addSlot(ResourceManager*);
private: private:
SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const; SlotWithId getPreviousSlot(VariantSlot*, const ResourceManager*) const;
void releaseSlot(SlotWithId, ResourceManager*);
}; };
inline const VariantData* collectionToVariant( inline const VariantData* collectionToVariant(

View File

@@ -63,13 +63,25 @@ inline CollectionData::iterator CollectionData::addSlot(
return iterator(slot, slot.id()); return iterator(slot, slot.id());
} }
inline void CollectionData::addSlot(SlotWithId slot,
ResourceManager* resources) {
if (tail_ != NULL_SLOT) {
auto tail = resources->getSlot(tail_);
tail->setNext(slot.id());
tail_ = slot.id();
} else {
head_ = slot.id();
tail_ = slot.id();
}
}
inline void CollectionData::clear(ResourceManager* resources) { inline void CollectionData::clear(ResourceManager* resources) {
auto next = head_; auto next = head_;
while (next != NULL_SLOT) { while (next != NULL_SLOT) {
auto currId = next; auto currId = next;
auto slot = resources->getSlot(next); auto slot = resources->getSlot(next);
next = slot->next(); next = slot->next();
releaseSlot(SlotWithId(slot, currId), resources); resources->freeSlot(SlotWithId(slot, currId));
} }
head_ = NULL_SLOT; head_ = NULL_SLOT;
@@ -102,7 +114,7 @@ inline void CollectionData::remove(iterator it, ResourceManager* resources) {
head_ = next; head_ = next;
if (next == NULL_SLOT) if (next == NULL_SLOT)
tail_ = prev.id(); tail_ = prev.id();
releaseSlot({it.slot_, it.currentId_}, resources); resources->freeSlot({it.slot_, it.currentId_});
} }
inline size_t CollectionData::nesting(const ResourceManager* resources) const { inline size_t CollectionData::nesting(const ResourceManager* resources) const {
@@ -122,12 +134,4 @@ inline size_t CollectionData::size(const ResourceManager* resources) const {
return count; return count;
} }
inline void CollectionData::releaseSlot(SlotWithId slot,
ResourceManager* resources) {
if (slot->ownsKey())
resources->dereferenceString(slot->key());
slot->data()->setNull(resources);
resources->freeSlot(slot);
}
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -93,11 +93,14 @@
// https://arduinojson.org/v7/config/slot_id_size/ // https://arduinojson.org/v7/config/slot_id_size/
#ifndef ARDUINOJSON_SLOT_ID_SIZE #ifndef ARDUINOJSON_SLOT_ID_SIZE
# if ARDUINOJSON_SIZEOF_POINTER <= 2 # if ARDUINOJSON_SIZEOF_POINTER <= 2
# define ARDUINOJSON_SLOT_ID_SIZE 1 // up to 255 slots // 8-bit and 16-bit archs => up to 255 slots
# define ARDUINOJSON_SLOT_ID_SIZE 1
# elif ARDUINOJSON_SIZEOF_POINTER == 4 # elif ARDUINOJSON_SIZEOF_POINTER == 4
# define ARDUINOJSON_SLOT_ID_SIZE 2 // up to 65535 slots // 32-bit arch => up to 65535 slots
# define ARDUINOJSON_SLOT_ID_SIZE 2
# else # else
# define ARDUINOJSON_SLOT_ID_SIZE 4 // up to 4294967295 slots // 64-bit arch => up to 4294967295 slots
# define ARDUINOJSON_SLOT_ID_SIZE 4
# endif # endif
#endif #endif

View File

@@ -62,9 +62,8 @@ ARDUINOJSON_END_PRIVATE_NAMESPACE
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TInput> template <typename TInput>
Reader<typename remove_reference<TInput>::type> makeReader(TInput&& input) { Reader<remove_reference_t<TInput>> makeReader(TInput&& input) {
return Reader<typename remove_reference<TInput>::type>{ return Reader<remove_reference_t<TInput>>{detail::forward<TInput>(input)};
detail::forward<TInput>(input)};
} }
template <typename TChar> template <typename TChar>

View File

@@ -9,8 +9,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TSource> template <typename TSource>
struct Reader<TSource, struct Reader<TSource, enable_if_t<is_base_of<Stream, TSource>::value>> {
typename enable_if<is_base_of<Stream, TSource>::value>::type> {
public: public:
explicit Reader(Stream& stream) : stream_(&stream) {} explicit Reader(Stream& stream) : stream_(&stream) {}

View File

@@ -9,8 +9,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TSource> template <typename TSource>
struct Reader<TSource, struct Reader<TSource, enable_if_t<is_base_of<::String, TSource>::value>>
typename enable_if<is_base_of<::String, TSource>::value>::type>
: BoundedReader<const char*> { : BoundedReader<const char*> {
explicit Reader(const ::String& s) explicit Reader(const ::String& s)
: BoundedReader<const char*>(s.c_str(), s.length()) {} : BoundedReader<const char*>(s.c_str(), s.length()) {}

View File

@@ -4,6 +4,8 @@
#pragma once #pragma once
#include <ArduinoJson/Polyfills/type_traits.hpp>
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TIterator> template <typename TIterator>
@@ -29,13 +31,8 @@ class IteratorReader {
} }
}; };
template <typename T>
struct void_ {
typedef void type;
};
template <typename TSource> template <typename TSource>
struct Reader<TSource, typename void_<typename TSource::const_iterator>::type> struct Reader<TSource, void_t<typename TSource::const_iterator>>
: IteratorReader<typename TSource::const_iterator> { : IteratorReader<typename TSource::const_iterator> {
explicit Reader(const TSource& source) explicit Reader(const TSource& source)
: IteratorReader<typename TSource::const_iterator>(source.begin(), : IteratorReader<typename TSource::const_iterator>(source.begin(),

View File

@@ -19,8 +19,7 @@ template <typename T>
struct IsCharOrVoid<const T> : IsCharOrVoid<T> {}; struct IsCharOrVoid<const T> : IsCharOrVoid<T> {};
template <typename TSource> template <typename TSource>
struct Reader<TSource*, struct Reader<TSource*, enable_if_t<IsCharOrVoid<TSource>::value>> {
typename enable_if<IsCharOrVoid<TSource>::value>::type> {
const char* ptr_; const char* ptr_;
public: public:
@@ -39,8 +38,7 @@ struct Reader<TSource*,
}; };
template <typename TSource> template <typename TSource>
struct BoundedReader<TSource*, struct BoundedReader<TSource*, enable_if_t<IsCharOrVoid<TSource>::value>>
typename enable_if<IsCharOrVoid<TSource>::value>::type>
: public IteratorReader<const char*> { : public IteratorReader<const char*> {
public: public:
explicit BoundedReader(const void* ptr, size_t len) explicit BoundedReader(const void* ptr, size_t len)

View File

@@ -9,8 +9,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TSource> template <typename TSource>
struct Reader<TSource, typename enable_if< struct Reader<TSource, enable_if_t<is_base_of<std::istream, TSource>::value>> {
is_base_of<std::istream, TSource>::value>::type> {
public: public:
explicit Reader(std::istream& stream) : stream_(&stream) {} explicit Reader(std::istream& stream) : stream_(&stream) {}

View File

@@ -10,7 +10,7 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
template <typename TVariant> template <typename TVariant>
struct Reader<TVariant, typename enable_if<IsVariant<TVariant>::value>::type> struct Reader<TVariant, enable_if_t<IsVariant<TVariant>::value>>
: Reader<char*, void> { : Reader<char*, void> {
explicit Reader(const TVariant& x) explicit Reader(const TVariant& x)
: Reader<char*, void>(x.template as<const char*>()) {} : Reader<char*, void>(x.template as<const char*>()) {}

View File

@@ -29,10 +29,9 @@ struct is_deserialize_destination : false_type {};
template <class T> template <class T>
struct is_deserialize_destination< struct is_deserialize_destination<
T, typename enable_if<is_same<decltype(VariantAttorney::getResourceManager( T, enable_if_t<is_same<decltype(VariantAttorney::getResourceManager(
detail::declval<T&>())), detail::declval<T&>())),
ResourceManager*>::value>::type> : true_type { ResourceManager*>::value>> : true_type {};
};
template <typename TDestination> template <typename TDestination>
inline void shrinkJsonDocument(TDestination&) { inline void shrinkJsonDocument(TDestination&) {
@@ -62,8 +61,8 @@ DeserializationError doDeserialize(TDestination&& dst, TReader reader,
template <template <typename> class TDeserializer, typename TDestination, template <template <typename> class TDeserializer, typename TDestination,
typename TStream, typename... Args, typename TStream, typename... Args,
typename = typename enable_if< // issue #1897 typename = enable_if_t< // issue #1897
!is_integral<typename first_or_void<Args...>::type>::value>::type> !is_integral<typename first_or_void<Args...>::type>::value>>
DeserializationError deserialize(TDestination&& dst, TStream&& input, DeserializationError deserialize(TDestination&& dst, TStream&& input,
Args... args) { Args... args) {
return doDeserialize<TDeserializer>( return doDeserialize<TDeserializer>(
@@ -73,7 +72,7 @@ DeserializationError deserialize(TDestination&& dst, TStream&& input,
template <template <typename> class TDeserializer, typename TDestination, template <template <typename> class TDeserializer, typename TDestination,
typename TChar, typename Size, typename... Args, typename TChar, typename Size, typename... Args,
typename = typename enable_if<is_integral<Size>::value>::type> typename = enable_if_t<is_integral<Size>::value>>
DeserializationError deserialize(TDestination&& dst, TChar* input, DeserializationError deserialize(TDestination&& dst, TChar* input,
Size inputSize, Args... args) { Size inputSize, Args... args) {
return doDeserialize<TDeserializer>(dst, makeReader(input, size_t(inputSize)), return doDeserialize<TDeserializer>(dst, makeReader(input, size_t(inputSize)),

View File

@@ -37,15 +37,13 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Construct from variant, array, or object // Construct from variant, array, or object
template <typename T> template <typename T>
JsonDocument(const T& src, JsonDocument(
Allocator* alloc = detail::DefaultAllocator::instance(), const T& src, Allocator* alloc = detail::DefaultAllocator::instance(),
typename detail::enable_if< detail::enable_if_t<detail::IsVariant<T>::value ||
detail::is_same<T, JsonVariant>::value ||
detail::is_same<T, JsonVariantConst>::value ||
detail::is_same<T, JsonArray>::value || detail::is_same<T, JsonArray>::value ||
detail::is_same<T, JsonArrayConst>::value || detail::is_same<T, JsonArrayConst>::value ||
detail::is_same<T, JsonObject>::value || detail::is_same<T, JsonObject>::value ||
detail::is_same<T, JsonObjectConst>::value>::type* = 0) detail::is_same<T, JsonObjectConst>::value>* = 0)
: JsonDocument(alloc) { : JsonDocument(alloc) {
set(src); set(src);
} }
@@ -139,9 +137,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Replaces the root with the specified value. // Replaces the root with the specified value.
// https://arduinojson.org/v7/api/jsondocument/set/ // https://arduinojson.org/v7/api/jsondocument/set/
template <typename T> template <typename T>
typename detail::enable_if<!detail::is_base_of<JsonDocument, T>::value, detail::enable_if_t<!detail::is_base_of<JsonDocument, T>::value, bool> set(
bool>::type const T& src) {
set(const T& src) {
return to<JsonVariant>().set(src); return to<JsonVariant>().set(src);
} }
@@ -163,15 +160,24 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Returns true if the root object contains the specified key. // Returns true if the root object contains the specified key.
// https://arduinojson.org/v7/api/jsondocument/containskey/ // https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TString> template <typename TString>
bool containsKey(const TString& key) const { detail::enable_if_t<detail::IsString<TString>::value, bool> containsKey(
const TString& key) const {
return data_.getMember(detail::adaptString(key), &resources_) != 0; return data_.getMember(detail::adaptString(key), &resources_) != 0;
} }
// Returns true if the root object contains the specified key.
// https://arduinojson.org/v7/api/jsondocument/containskey/
template <typename TVariant>
detail::enable_if_t<detail::IsVariant<TVariant>::value, bool> containsKey(
const TVariant& key) const {
return containsKey(key.template as<const char*>());
}
// Gets or sets a root object's member. // Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString> template <typename TString>
typename detail::enable_if<detail::IsString<TString>::value, detail::enable_if_t<detail::IsString<TString>::value,
detail::MemberProxy<JsonDocument&, TString>>::type detail::MemberProxy<JsonDocument&, TString>>
operator[](const TString& key) { operator[](const TString& key) {
return {*this, key}; return {*this, key};
} }
@@ -179,8 +185,8 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets or sets a root object's member. // Gets or sets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar> template <typename TChar>
typename detail::enable_if<detail::IsString<TChar*>::value, detail::enable_if_t<detail::IsString<TChar*>::value,
detail::MemberProxy<JsonDocument&, TChar*>>::type detail::MemberProxy<JsonDocument&, TChar*>>
operator[](TChar* key) { operator[](TChar* key) {
return {*this, key}; return {*this, key};
} }
@@ -188,8 +194,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets a root object's member. // Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TString> template <typename TString>
typename detail::enable_if<detail::IsString<TString>::value, detail::enable_if_t<detail::IsString<TString>::value, JsonVariantConst>
JsonVariantConst>::type
operator[](const TString& key) const { operator[](const TString& key) const {
return JsonVariantConst( return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_); data_.getMember(detail::adaptString(key), &resources_), &resources_);
@@ -198,8 +203,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets a root object's member. // Gets a root object's member.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
template <typename TChar> template <typename TChar>
typename detail::enable_if<detail::IsString<TChar*>::value, detail::enable_if_t<detail::IsString<TChar*>::value, JsonVariantConst>
JsonVariantConst>::type
operator[](TChar* key) const { operator[](TChar* key) const {
return JsonVariantConst( return JsonVariantConst(
data_.getMember(detail::adaptString(key), &resources_), &resources_); data_.getMember(detail::adaptString(key), &resources_), &resources_);
@@ -207,8 +211,11 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Gets or sets a root array's element. // Gets or sets a root array's element.
// https://arduinojson.org/v7/api/jsondocument/subscript/ // https://arduinojson.org/v7/api/jsondocument/subscript/
detail::ElementProxy<JsonDocument&> operator[](size_t index) { template <typename T>
return {*this, index}; detail::enable_if_t<detail::is_integral<T>::value,
detail::ElementProxy<JsonDocument&>>
operator[](T index) {
return {*this, size_t(index)};
} }
// Gets a root array's member. // Gets a root array's member.
@@ -217,12 +224,23 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
return JsonVariantConst(data_.getElement(index, &resources_), &resources_); return JsonVariantConst(data_.getElement(index, &resources_), &resources_);
} }
// 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*>());
if (key.template is<size_t>())
return operator[](key.template as<size_t>());
return {};
}
// Appends a new (empty) element to the root array. // Appends a new (empty) element to the root array.
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename T> template <typename T>
typename detail::enable_if<!detail::is_same<T, JsonVariant>::value, T>::type detail::enable_if_t<!detail::is_same<T, JsonVariant>::value, T> add() {
add() {
return add<JsonVariant>().to<T>(); return add<JsonVariant>().to<T>();
} }
@@ -230,8 +248,7 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Returns a reference to the new element. // Returns a reference to the new element.
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename T> template <typename T>
typename detail::enable_if<detail::is_same<T, JsonVariant>::value, T>::type detail::enable_if_t<detail::is_same<T, JsonVariant>::value, T> add() {
add() {
return JsonVariant(data_.addElement(&resources_), &resources_); return JsonVariant(data_.addElement(&resources_), &resources_);
} }
@@ -239,27 +256,28 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename TValue> template <typename TValue>
bool add(const TValue& value) { bool add(const TValue& value) {
return add<JsonVariant>().set(value); return data_.addValue(value, &resources_);
} }
// Appends a value to the root array. // Appends a value to the root array.
// https://arduinojson.org/v7/api/jsondocument/add/ // https://arduinojson.org/v7/api/jsondocument/add/
template <typename TChar> template <typename TChar>
bool add(TChar* value) { bool add(TChar* value) {
return add<JsonVariant>().set(value); return data_.addValue(value, &resources_);
} }
// Removes an element of the root array. // Removes an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
void remove(size_t index) { template <typename T>
detail::VariantData::removeElement(getData(), index, getResourceManager()); detail::enable_if_t<detail::is_integral<T>::value> remove(T index) {
detail::VariantData::removeElement(getData(), size_t(index),
getResourceManager());
} }
// Removes a member of the root object. // Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TChar> template <typename TChar>
typename detail::enable_if<detail::IsString<TChar*>::value>::type remove( detail::enable_if_t<detail::IsString<TChar*>::value> remove(TChar* key) {
TChar* key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key), detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager()); getResourceManager());
} }
@@ -267,13 +285,23 @@ class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
// Removes a member of the root object. // Removes a member of the root object.
// https://arduinojson.org/v7/api/jsondocument/remove/ // https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TString> template <typename TString>
detail::enable_if_t<detail::IsString<TString>::value> remove(
typename detail::enable_if<detail::IsString<TString>::value>::type remove(
const TString& key) { const TString& key) {
detail::VariantData::removeMember(getData(), detail::adaptString(key), detail::VariantData::removeMember(getData(), detail::adaptString(key),
getResourceManager()); getResourceManager());
} }
// Removes a member of the root object or an element of the root array.
// https://arduinojson.org/v7/api/jsondocument/remove/
template <typename TVariant>
detail::enable_if_t<detail::IsVariant<TVariant>::value> remove(
const TVariant& key) {
if (key.template is<const char*>())
remove(key.template as<const char*>());
if (key.template is<size_t>())
remove(key.template as<size_t>());
}
operator JsonVariant() { operator JsonVariant() {
return getVariant(); return getVariant();
} }

View File

@@ -671,9 +671,8 @@ ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
// Parses a JSON input, filters, and puts the result in a JsonDocument. // Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/json/deserializejson/ // https://arduinojson.org/v7/api/json/deserializejson/
template <typename TDestination, typename... Args> template <typename TDestination, typename... Args>
typename detail::enable_if< detail::enable_if_t<detail::is_deserialize_destination<TDestination>::value,
detail::is_deserialize_destination<TDestination>::value, DeserializationError>
DeserializationError>::type
deserializeJson(TDestination&& dst, Args&&... args) { deserializeJson(TDestination&& dst, Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst), return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),
@@ -683,9 +682,8 @@ deserializeJson(TDestination&& dst, Args&&... args) {
// Parses a JSON input, filters, and puts the result in a JsonDocument. // Parses a JSON input, filters, and puts the result in a JsonDocument.
// https://arduinojson.org/v7/api/json/deserializejson/ // https://arduinojson.org/v7/api/json/deserializejson/
template <typename TDestination, typename TChar, typename... Args> template <typename TDestination, typename TChar, typename... Args>
typename detail::enable_if< detail::enable_if_t<detail::is_deserialize_destination<TDestination>::value,
detail::is_deserialize_destination<TDestination>::value, DeserializationError>
DeserializationError>::type
deserializeJson(TDestination&& dst, TChar* input, Args&&... args) { deserializeJson(TDestination&& dst, TChar* input, Args&&... args) {
using namespace detail; using namespace detail;
return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst), return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst),

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