Compare commits

...

28 Commits

Author SHA1 Message Date
61a5273aea Set version to 6.16.1 2020-08-04 10:33:38 +02:00
35a39b8d8f Fixed deserializeJson() that stopped reading after {} (fixes #1335) 2020-08-04 09:52:42 +02:00
96b6571352 Fixed use-of-uninitialized-value in encodeCodepoint() 2020-08-03 13:44:45 +02:00
74e7dd053f Added memory sanitizer to fuzzers 2020-08-03 13:44:45 +02:00
1f8636d762 Fixed build-arduino-package.sh to ignore .vs/ 2020-08-01 15:35:04 +02:00
e4cfa701d8 Fixed publish.sh to commit CMakeLists.txt 2020-08-01 15:34:27 +02:00
b85181a6db Set version to 6.16.0 2020-08-01 15:17:39 +02:00
6841b80466 Added JsonString::operator!= 2020-07-31 14:02:20 +02:00
298864bafe Set ARDUINOJSON_DECODE_UNICODE to 1 by default 2020-07-30 09:49:09 +02:00
4d7f03836c Added a test that removes an element during iteration (#1332) 2020-07-30 09:20:31 +02:00
c63eb80b95 Shortened the name of the private namespace using hexadecimal digits 2020-07-30 09:18:08 +02:00
0c0bf80074 Suppress IAR compiler warning. 2020-07-28 10:12:41 +02:00
144ff3b06e Updated the change log. Closes #1210 and closes #1314. 2020-07-26 16:09:03 +02:00
d6c50c3596 Reduced stack usage when compiled with -Og (issue #1210)
This saves 128 bytes on ESP8266
2020-07-26 14:51:58 +02:00
51b177ce47 Reduced stack usage when compiled with -Og (issue #1210)
This saves 112 bytes on ESP8266
2020-07-26 12:58:12 +02:00
824b7a25ca Reduced stack usage when compiled with -Og (issue #1210)
This saves 96 bytes on ESP8266
2020-07-26 12:25:13 +02:00
2223d40640 Reduced stack usage when compiler aggressively inlines deserializeJson()
This saves 64 bytes on the recursive part.
2020-07-26 12:25:13 +02:00
4df29fbac1 Fixed "linked" strings incorrectly marked as "owned" (fixes #1318) 2020-07-24 22:25:56 +02:00
6dc36125c2 Added ElementProxy::getOrAddElement() (fixes #1311) 2020-07-23 10:58:47 +02:00
40085609e2 Fixed copyArray() not working with MemberProxy and ElementProxy 2020-07-22 21:08:18 +02:00
ebb6d80092 Fixed copyArray() not working with String 2020-07-22 20:02:33 +02:00
764ff2cd53 Added string deduplication (closes #1303) 2020-07-21 20:15:31 +02:00
8ef226bcb8 Travis: Added Clang 9. Removed Xcode 7.3 2020-07-15 15:27:39 +02:00
63606c0985 Added json_fuzzer and msgpack_fuzzer in test suite 2020-07-15 12:50:00 +02:00
1600d39693 Refactored StringBuilder into StringStorage 2020-07-11 17:51:45 +02:00
04c59985a1 Inverted dependency between MemoryPool and string adapters.
Inserted a null after each raw string in the pool.
2020-07-08 09:38:27 +02:00
7e58347fbe Added comparisons between JsonVariants 2020-07-02 15:28:34 +02:00
0e794a28a1 Simplified implementation of comparison operators 2020-06-13 15:42:04 +02:00
96 changed files with 2694 additions and 1768 deletions

View File

@ -107,10 +107,14 @@ matrix:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-8']
packages: ['clang-8']
env: SCRIPT=test _CC=clang-8 _CXX=clang++-8
- addons:
apt:
sources:
- sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
packages: ['clang-9']
env: SCRIPT=test _CC=clang-9 _CXX=clang++-9
- env: SCRIPT=coverage
- os: osx
osx_image: xcode7.3
env: SCRIPT=test
- os: osx
osx_image: xcode8.3
env: SCRIPT=test
@ -124,11 +128,6 @@ matrix:
- env: SCRIPT=arduino VERSION=1.8.2 BOARD=arduino:samd:mkr1000
- env: SCRIPT=platformio BOARD=uno
- env: SCRIPT=platformio BOARD=esp01
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-6.0','llvm-6.0']
env: SCRIPT=fuzz CLANG=6.0
cache:
directories:
- "~/.platformio"

View File

@ -1,6 +1,23 @@
ArduinoJson: change log
=======================
v6.16.1 (2020-08-04)
-------
* Fixed `deserializeJson()` that stopped reading after `{}` (issue #1335)
v6.16.0 (2020-08-01)
-------
* Added comparisons (`>`, `>=`, `==`, `!=`, `<`, and `<=`) between `JsonVariant`s
* Added string deduplication (issue #1303)
* Added `JsonString::operator!=`
* Set `ARDUINOJSON_DECODE_UNICODE` to `1` by default
* Fixed `copyArray()` not working with `String`, `ElementProxy`, and `MemberProxy`
* Fixed error `getOrAddElement is not a member of ElementProxy` (issue #1311)
* Fixed excessive stack usage when compiled with `-Og` (issues #1210 and #1314)
* Fixed `Warning[Pa093]: implicit conversion from floating point to integer` on IAR compiler (PR #1328 by @stawiski)
v6.15.2 (2020-05-15)
-------

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.0)
project(ArduinoJson VERSION 6.15.1)
project(ArduinoJson VERSION 6.16.1)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)

View File

@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.15.2)](https://www.ardu-badge.com/ArduinoJson/6.15.2)
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.16.1)](https://www.ardu-badge.com/ArduinoJson/6.16.1)
[![Build Status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=6.x)](https://travis-ci.org/bblanchon/ArduinoJson)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
@ -31,6 +31,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
* [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/?utm_source=github&utm_medium=readme)
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme)
* Deduplicates strings
* Versatile
* [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
* Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme)

View File

@ -1,4 +1,4 @@
version: 6.15.2.{build}
version: 6.16.1.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019

View File

@ -1,26 +1,22 @@
#!/bin/bash -eux
ROOT_DIR=$(dirname $0)/../../
INCLUDE_DIR=${ROOT_DIR}/src/
FUZZING_DIR=${ROOT_DIR}/extras/fuzzing/
CXXFLAGS="-g -fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all"
fuzz() {
NAME="$1"
FUZZER="${NAME}_fuzzer"
FUZZER_CPP="${FUZZING_DIR}/${NAME}_fuzzer.cpp"
CORPUS_DIR="${FUZZING_DIR}/${NAME}_corpus"
SEED_CORPUS_DIR="${FUZZING_DIR}/${NAME}_seed_corpus"
export CC="clang-${CLANG}"
export CXX="clang++-${CLANG}"
cmake -DCMAKE_BUILD_TYPE=Debug .
clang++-${CLANG} ${CXXFLAGS} -o ${FUZZER} -I$INCLUDE_DIR ${FUZZER_CPP}
FUZZER_TARGET="${FUZZER}_fuzzer"
FUZZER_PATH="extras/fuzzing/${FUZZER_TARGET}"
CORPUS_DIR="${FUZZING_DIR}/${FUZZER}_corpus"
SEED_CORPUS_DIR="${FUZZING_DIR}/${FUZZER}_seed_corpus"
export ASAN_OPTIONS="detect_leaks=0"
export LLVM_PROFILE_FILE="${FUZZER}.profraw"
./${FUZZER} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=30 -timeout=1
cmake --build . --target $FUZZER_TARGET
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER}.profdata
llvm-cov-${CLANG} report ./${FUZZER} -instr-profile=${FUZZER}.profdata
}
export ASAN_OPTIONS="detect_leaks=0"
export LLVM_PROFILE_FILE="${FUZZER_TARGET}.profraw"
${FUZZER_PATH} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=60 -timeout=1
fuzz json
fuzz msgpack
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER_TARGET}.profdata
llvm-cov-${CLANG} report ./${FUZZER_PATH} -instr-profile=${FUZZER_TARGET}.profdata

View File

@ -6,18 +6,68 @@ if(MSVC)
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
endif()
add_executable(msgpack_fuzzer
add_executable(msgpack_reproducer
msgpack_fuzzer.cpp
fuzzer_main.cpp
reproducer.cpp
)
target_link_libraries(msgpack_fuzzer
target_link_libraries(msgpack_reproducer
ArduinoJson
)
add_executable(json_fuzzer
add_executable(json_reproducer
json_fuzzer.cpp
fuzzer_main.cpp
reproducer.cpp
)
target_link_libraries(json_fuzzer
target_link_libraries(json_reproducer
ArduinoJson
)
# Infer path of llvm-symbolizer from the path of clang
string(REPLACE "clang++" "llvm-symbolizer" LLVM_SYMBOLIZER ${CMAKE_CXX_COMPILER})
macro(add_fuzzer name mode)
set(FUZZER "${name}_${mode}_fuzzer")
set(CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_corpus")
set(SEED_CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_seed_corpus")
add_executable("${FUZZER}"
"${name}_fuzzer.cpp"
)
target_link_libraries("${FUZZER}"
ArduinoJson
)
set_target_properties("${FUZZER}"
PROPERTIES
COMPILE_FLAGS
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
LINK_FLAGS
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
)
add_test(
NAME
"${FUZZER}"
COMMAND
"${FUZZER}" "${CORPUS_DIR}" "${SEED_CORPUS_DIR}" -max_total_time=5 -timeout=1
)
set_tests_properties("${FUZZER}"
PROPERTIES
ENVIRONMENT
ASAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
ENVIRONMENT
LLVM_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
ENVIRONMENT
MSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
ENVIRONMENT
UBSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
)
endmacro()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
add_fuzzer(json address)
add_fuzzer(json memory)
add_fuzzer(json undefined)
add_fuzzer(msgpack address)
add_fuzzer(msgpack memory)
add_fuzzer(msgpack undefined)
endif()

View File

@ -10,6 +10,7 @@ rm -f $OUTPUT
# create zip
7z a $OUTPUT \
-xr!.vs \
ArduinoJson/CHANGELOG.md \
ArduinoJson/examples \
ArduinoJson/src \

View File

@ -41,7 +41,7 @@ update_version_in_source () {
}
commit_new_version () {
git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml
git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml CMakeLists.txt
git commit -m "Set version to $VERSION"
}

View File

@ -9,6 +9,7 @@ add_executable(ElementProxyTests
remove.cpp
set.cpp
size.cpp
subscript.cpp
)
add_test(ElementProxy ElementProxyTests)

View File

@ -10,19 +10,39 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::operator==()") {
DynamicJsonDocument doc(4096);
SECTION("same value") {
SECTION("1 vs 1") {
doc.add(1);
doc.add(1);
REQUIRE(doc[0] <= doc[1]);
REQUIRE(doc[0] == doc[1]);
REQUIRE(doc[0] >= doc[1]);
REQUIRE_FALSE(doc[0] != doc[1]);
REQUIRE_FALSE(doc[0] < doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
}
SECTION("different values") {
SECTION("1 vs 2") {
doc.add(1);
doc.add(2);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE(doc[0] != doc[1]);
REQUIRE(doc[0] < doc[1]);
REQUIRE(doc[0] <= doc[1]);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
REQUIRE_FALSE(doc[0] >= doc[1]);
}
SECTION("'abc' vs 'bcd'") {
doc.add("abc");
doc.add("bcd");
REQUIRE(doc[0] != doc[1]);
REQUIRE(doc[0] < doc[1]);
REQUIRE(doc[0] <= doc[1]);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
REQUIRE_FALSE(doc[0] >= doc[1]);
}
}

View File

@ -7,7 +7,7 @@
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::operator[]") {
TEST_CASE("ElementProxy::operator[]") {
DynamicJsonDocument doc(4096);
ElementProxy<JsonDocument&> ep = doc[1];

View File

@ -9,6 +9,9 @@
// Reproduces Arduino's String class
class String {
public:
String() {}
explicit String(const char* s) : _str(s) {}
String& operator+=(const char* rhs) {
_str += rhs;
return *this;

View File

@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") {
SECTION("should duplicate char*") {
array.add(const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
array.add(std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -6,32 +6,57 @@
#include <catch.hpp>
TEST_CASE("copyArray()") {
SECTION("1D -> JsonArray") {
SECTION("int[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, array);
REQUIRE(ok);
CHECK(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[1,2,3]") == json);
serializeJson(array, json);
CHECK(std::string("[1,2,3]") == json);
}
SECTION("1D -> JsonDocument") {
SECTION("std::string[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
char json[32];
std::string source[] = {"a", "b", "c"};
bool ok = copyArray(source, array);
CHECK(ok);
serializeJson(array, json);
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json);
}
SECTION("int[] -> JsonDocument") {
DynamicJsonDocument doc(4096);
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, doc);
REQUIRE(ok);
CHECK(ok);
serializeJson(doc, json, sizeof(json));
REQUIRE(std::string("[1,2,3]") == json);
serializeJson(doc, json);
CHECK(std::string("[1,2,3]") == json);
}
SECTION("1D -> JsonArray, but not enough memory") {
SECTION("int[] -> MemberProxy") {
DynamicJsonDocument doc(4096);
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, doc["data"]);
CHECK(ok);
serializeJson(doc, json);
CHECK(std::string("{\"data\":[1,2,3]}") == json);
}
SECTION("int[] -> JsonArray, but not enough memory") {
const size_t SIZE = JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
JsonArray array = doc.to<JsonArray>();
@ -41,36 +66,48 @@ TEST_CASE("copyArray()") {
bool ok = copyArray(source, array);
REQUIRE_FALSE(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[1,2]") == json);
serializeJson(array, json);
CHECK(std::string("[1,2]") == json);
}
SECTION("2D -> JsonArray") {
SECTION("int[][] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
bool ok = copyArray(source, array);
REQUIRE(ok);
CHECK(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json);
serializeJson(array, json);
CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
}
SECTION("2D -> JsonDocument") {
SECTION("int[][] -> MemberProxy") {
DynamicJsonDocument doc(4096);
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
bool ok = copyArray(source, doc["data"]);
CHECK(ok);
serializeJson(doc, json);
CHECK(std::string("{\"data\":[[1,2,3],[4,5,6]]}") == json);
}
SECTION("int[][] -> JsonDocument") {
DynamicJsonDocument doc(4096);
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
bool ok = copyArray(source, doc);
REQUIRE(ok);
CHECK(ok);
serializeJson(doc, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json);
serializeJson(doc, json);
CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
}
SECTION("2D -> JsonArray, but not enough memory") {
SECTION("int[][] -> JsonArray, but not enough memory") {
const size_t SIZE =
JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
@ -84,92 +121,159 @@ TEST_CASE("copyArray()") {
CAPTURE(doc.memoryUsage());
CHECK_FALSE(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5]]") == json);
serializeJson(array, json);
CHECK(std::string("[[1,2,3],[4,5]]") == json);
}
SECTION("JsonArray -> 1D, with more space than needed") {
SECTION("JsonArray -> int[], with more space than needed") {
DynamicJsonDocument doc(4096);
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
int destination[4] = {0};
size_t result = copyArray(array, destination);
REQUIRE(3 == result);
REQUIRE(1 == destination[0]);
REQUIRE(2 == destination[1]);
REQUIRE(3 == destination[2]);
REQUIRE(0 == destination[3]);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("JsonArray -> 1D, without enough space") {
SECTION("JsonArray -> int[], without enough space") {
DynamicJsonDocument doc(4096);
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
int destination[2] = {0};
size_t result = copyArray(array, destination);
REQUIRE(2 == result);
REQUIRE(1 == destination[0]);
REQUIRE(2 == destination[1]);
CHECK(2 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
}
SECTION("JsonDocument -> 1D") {
SECTION("JsonArray -> std::string[]") {
DynamicJsonDocument doc(4096);
char json[] = "[\"a\",\"b\",\"c\"]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
std::string destination[4];
size_t result = copyArray(array, destination);
CHECK(3 == result);
CHECK("a" == destination[0]);
CHECK("b" == destination[1]);
CHECK("c" == destination[2]);
CHECK("" == destination[3]);
}
SECTION("JsonDocument -> int[]") {
DynamicJsonDocument doc(4096);
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
int destination[4] = {0};
size_t result = copyArray(doc, destination);
REQUIRE(3 == result);
REQUIRE(1 == destination[0]);
REQUIRE(2 == destination[1]);
REQUIRE(3 == destination[2]);
REQUIRE(0 == destination[3]);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("JsonArray -> 2D") {
SECTION("MemberProxy -> int[]") {
DynamicJsonDocument doc(4096);
char json[] = "{\"data\":[1,2,3]}";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[4] = {0};
size_t result = copyArray(doc["data"], destination);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("ElementProxy -> int[]") {
DynamicJsonDocument doc(4096);
char json[] = "[[1,2,3]]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[4] = {0};
size_t result = copyArray(doc[0], destination);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("JsonArray -> int[][]") {
DynamicJsonDocument doc(4096);
char json[] = "[[1,2],[3],[4]]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
int destination[3][2] = {{0}};
copyArray(array, destination);
REQUIRE(1 == destination[0][0]);
REQUIRE(2 == destination[0][1]);
REQUIRE(3 == destination[1][0]);
REQUIRE(0 == destination[1][1]);
REQUIRE(4 == destination[2][0]);
REQUIRE(0 == destination[2][1]);
CHECK(1 == destination[0][0]);
CHECK(2 == destination[0][1]);
CHECK(3 == destination[1][0]);
CHECK(0 == destination[1][1]);
CHECK(4 == destination[2][0]);
CHECK(0 == destination[2][1]);
}
SECTION("JsonDocument -> 2D") {
SECTION("JsonDocument -> int[][]") {
DynamicJsonDocument doc(4096);
char json[] = "[[1,2],[3],[4]]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
int destination[3][2] = {{0}};
copyArray(doc, destination);
REQUIRE(1 == destination[0][0]);
REQUIRE(2 == destination[0][1]);
REQUIRE(3 == destination[1][0]);
REQUIRE(0 == destination[1][1]);
REQUIRE(4 == destination[2][0]);
REQUIRE(0 == destination[2][1]);
CHECK(1 == destination[0][0]);
CHECK(2 == destination[0][1]);
CHECK(3 == destination[1][0]);
CHECK(0 == destination[1][1]);
CHECK(4 == destination[2][0]);
CHECK(0 == destination[2][1]);
}
SECTION("MemberProxy -> int[][]") {
DynamicJsonDocument doc(4096);
char json[] = "{\"data\":[[1,2],[3],[4]]}";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[3][2] = {{0}};
copyArray(doc["data"], destination);
CHECK(1 == destination[0][0]);
CHECK(2 == destination[0][1]);
CHECK(3 == destination[1][0]);
CHECK(0 == destination[1][1]);
CHECK(4 == destination[2][0]);
CHECK(0 == destination[2][1]);
}
}

View File

@ -65,4 +65,15 @@ TEST_CASE("JsonArray::remove()") {
REQUIRE(_array[0] == 1);
REQUIRE(_array[1] == 2);
}
SECTION("In a loop") {
for (JsonArray::iterator it = _array.begin(); it != _array.end(); ++it) {
if (*it == 2)
_array.remove(it);
}
REQUIRE(2 == _array.size());
REQUIRE(_array[0] == 1);
REQUIRE(_array[1] == 3);
}
}

View File

@ -119,13 +119,13 @@ TEST_CASE("JsonArray::operator[]") {
SECTION("should duplicate char*") {
array[0] = const_cast<char*>("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
array[0] = std::string("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
deserializeJson(doc, " [ \"1234567\" ] ");
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(8) == doc.memoryUsage());
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(7) == doc.memoryUsage());
// note: we use a string of 8 bytes to be sure that the StaticMemoryPool
// will not insert bytes to enforce alignement
}

View File

@ -239,7 +239,7 @@ TEST_CASE("Filtering") {
10,
DeserializationError::Ok,
"[{\"example\":1},{\"example\":3}]",
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8
},
{
"[',2,3]",
@ -525,7 +525,7 @@ TEST_CASE("Filtering") {
10,
DeserializationError::InvalidInput,
"{}",
JSON_OBJECT_SIZE(0) + 8
JSON_OBJECT_SIZE(0)
},
{
// incomplete comment after key
@ -534,7 +534,7 @@ TEST_CASE("Filtering") {
10,
DeserializationError::IncompleteInput,
"{}",
JSON_OBJECT_SIZE(0) + 8
JSON_OBJECT_SIZE(0)
},
{
// invalid comment after colon
@ -730,20 +730,3 @@ TEST_CASE("Overloads") {
}
#endif
}
TEST_CASE("StringMover::reclaim()") {
StaticJsonDocument<200> filter;
filter["a"] = true;
filter["c"] = true;
char input[] = "{\"a\":1,\"b\":2,\"c\":1}";
StaticJsonDocument<200> doc;
deserializeJson(doc, input, DeserializationOption::Filter(filter));
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"c\":1}");
CHECK(input[0] == 'a');
CHECK(input[1] == 0);
CHECK(input[2] == 'c');
CHECK(input[3] == 0);
}

View File

@ -3,11 +3,27 @@
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include "CustomReader.hpp"
TEST_CASE("deserializeJson(char*)") {
StaticJsonDocument<1024> doc;
SECTION("should not duplicate strings") {
char input[] = "{\"hello\":\"world\"}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
CHECK(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
CHECK(doc.as<JsonVariant>().memoryUsage() ==
JSON_OBJECT_SIZE(1)); // issue #1318
}
}
TEST_CASE("deserializeJson(const std::string&)") {
DynamicJsonDocument doc(4096);

View File

@ -290,4 +290,10 @@ TEST_CASE("deserialize JSON object") {
REQUIRE(obj.size() == 0);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
}
SECTION("Issue #1335") {
std::string json("{\"a\":{},\"b\":{}}");
deserializeJson(doc, json);
CHECK(doc.as<std::string>() == json);
}
}

View File

@ -74,16 +74,16 @@ TEST_CASE("Invalid JSON string") {
}
}
TEST_CASE("Not enough room to duplicate the string") {
DynamicJsonDocument doc(JSON_OBJECT_SIZE(0));
TEST_CASE("Not enough room to save the key") {
DynamicJsonDocument doc(JSON_OBJECT_SIZE(1) + 8);
SECTION("Quoted string") {
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
REQUIRE(deserializeJson(doc, "{\"accuracy\":1}") ==
DeserializationError::NoMemory);
}
SECTION("Non-quoted string") {
REQUIRE(deserializeJson(doc, "{example:1}") ==
REQUIRE(deserializeJson(doc, "{accuracy:1}") ==
DeserializationError::NoMemory);
}
}

View File

@ -212,7 +212,7 @@ TEST_CASE("StaticJsonDocument") {
SECTION("garbageCollect()") {
StaticJsonDocument<256> doc;
doc[std::string("example")] = std::string("example");
doc[std::string("example")] = std::string("jukebox");
doc.remove("example");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);

View File

@ -94,8 +94,8 @@ TEST_CASE("BasicJsonDocument::shrinkToFit()") {
}
SECTION("owned raw") {
doc.set(serialized(std::string("[{},123]")));
testShrinkToFit(doc, "[{},123]", 8);
doc.set(serialized(std::string("[{},12]")));
testShrinkToFit(doc, "[{},12]", 8);
}
SECTION("linked key") {

View File

@ -107,43 +107,43 @@ TEST_CASE("JsonObject::operator[]") {
SECTION("should duplicate char* value") {
obj["hello"] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char* key") {
obj[const_cast<char*>("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char* key&value") {
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(5);
REQUIRE(expectedSize <= doc.memoryUsage());
}
SECTION("should duplicate std::string value") {
obj["hello"] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string key") {
obj[std::string("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string key&value") {
obj[std::string("hello")] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(5);
REQUIRE(expectedSize <= doc.memoryUsage());
}
SECTION("should duplicate a non-static JsonString key") {
obj[JsonString("hello", false)] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -5,460 +5,276 @@
#include <ArduinoJson.h>
#include <catch.hpp>
template <typename T>
void checkEquals(T a, T b) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
// Most code is already covered by arithmeticCompare.cpp.
// Here, we're just filling the holes
REQUIRE(b == variant);
REQUIRE(variant == b);
REQUIRE(b <= variant);
REQUIRE(variant <= b);
REQUIRE(b >= variant);
REQUIRE(variant >= b);
REQUIRE_FALSE(b != variant);
REQUIRE_FALSE(variant != b);
REQUIRE_FALSE(b > variant);
REQUIRE_FALSE(variant > b);
REQUIRE_FALSE(b < variant);
REQUIRE_FALSE(variant < b);
}
template <typename T>
void checkGreater(T a, T b) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE(variant > b);
REQUIRE(b < variant);
REQUIRE(variant != b);
REQUIRE(b != variant);
REQUIRE_FALSE(variant < b);
REQUIRE_FALSE(b > variant);
REQUIRE_FALSE(variant == b);
REQUIRE_FALSE(b == variant);
}
template <typename T>
void checkLower(T a, T b) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE(variant < b);
REQUIRE(b > variant);
REQUIRE(variant != b);
REQUIRE(b != variant);
REQUIRE_FALSE(variant > b);
REQUIRE_FALSE(b < variant);
REQUIRE_FALSE(variant == b);
REQUIRE_FALSE(b == variant);
}
template <typename T>
void checkComparisons(T low, T mid, T high) {
checkEquals(mid, mid);
checkGreater(mid, low);
checkLower(mid, high);
}
TEST_CASE("JsonVariant comparisons") {
static const char* null = 0;
SECTION("Double") {
checkComparisons<double>(123.44, 123.45, 123.46);
}
SECTION("Float") {
checkComparisons<float>(123.44f, 123.45f, 123.46f);
}
SECTION("SChar") {
checkComparisons<signed char>(122, 123, 124);
}
SECTION("SInt") {
checkComparisons<signed int>(122, 123, 124);
}
SECTION("SLong") {
checkComparisons<signed long>(122L, 123L, 124L);
}
SECTION("SShort") {
checkComparisons<signed short>(122, 123, 124);
}
SECTION("UChar") {
checkComparisons<unsigned char>(122, 123, 124);
}
SECTION("UInt") {
checkComparisons<unsigned int>(122, 123, 124);
}
SECTION("ULong") {
checkComparisons<unsigned long>(122L, 123L, 124L);
}
SECTION("UShort") {
checkComparisons<unsigned short>(122, 123, 124);
}
SECTION("null") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(null);
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
REQUIRE(variant == null);
REQUIRE_FALSE(variant != null);
REQUIRE(variant != "null");
REQUIRE_FALSE(variant == "null");
}
SECTION("StringLiteral") {
DynamicJsonDocument doc(4096);
deserializeJson(doc, "\"hello\"");
JsonVariant variant = doc.as<JsonVariant>();
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
REQUIRE(variant == "hello");
REQUIRE_FALSE(variant != "hello");
REQUIRE(variant != "world");
REQUIRE_FALSE(variant == "world");
REQUIRE(variant != null);
REQUIRE_FALSE(variant == null);
REQUIRE("hello" == variant);
REQUIRE_FALSE("hello" != variant);
REQUIRE("world" != variant);
REQUIRE_FALSE("world" == variant);
REQUIRE(null != variant);
REQUIRE_FALSE(null == variant);
}
SECTION("String") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set("hello");
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
REQUIRE(variant == std::string("hello"));
REQUIRE_FALSE(variant != std::string("hello"));
REQUIRE(variant != std::string("world"));
REQUIRE_FALSE(variant == std::string("world"));
REQUIRE(variant != null);
REQUIRE_FALSE(variant == null);
REQUIRE(std::string("hello") == variant);
REQUIRE_FALSE(std::string("hello") != variant);
REQUIRE(std::string("world") != variant);
REQUIRE_FALSE(std::string("world") == variant);
REQUIRE(null != variant);
REQUIRE_FALSE(null == variant);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA equals") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set("hello");
REQUIRE((vla == variant));
REQUIRE((variant == vla));
REQUIRE_FALSE((vla != variant));
REQUIRE_FALSE((variant != vla));
}
SECTION("VLA differs") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set("world");
REQUIRE((vla != variant));
REQUIRE((variant != vla));
REQUIRE_FALSE((vla == variant));
REQUIRE_FALSE((variant == vla));
}
#endif
DynamicJsonDocument doc1(4096), doc2(4096), doc3(4096);
JsonVariant variant1 = doc1.to<JsonVariant>();
JsonVariant variant2 = doc2.to<JsonVariant>();
JsonVariant variant3 = doc3.to<JsonVariant>();
SECTION("Variants containing integers") {
variant1.set(42);
variant2.set(42);
variant3.set(666);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing linked strings") {
// create two identical strings at different addresses
char hello1[] = "hello";
char hello2[] = "hello";
REQUIRE(hello1 != hello2);
variant1.set(hello1);
variant2.set(hello2);
variant3.set("world");
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing owned strings") {
variant1.set(std::string("hello"));
variant2.set(std::string("hello"));
variant3.set(std::string("world"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing linked raws") {
// create two identical strings at different addresses
char hello1[] = "hello";
char hello2[] = "hello";
REQUIRE(hello1 != hello2);
variant1.set(serialized(hello1));
variant2.set(serialized(hello2));
variant3.set(serialized("world"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing owned raws") {
variant1.set(serialized(std::string("hello")));
variant2.set(serialized(std::string("hello")));
variant3.set(serialized(std::string("world")));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing mixed strings (issue #1051)") {
variant1.set("hello");
variant2.set(std::string("hello"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant2 == variant1);
REQUIRE_FALSE(variant2 != variant1);
}
SECTION("Variants containing double") {
variant1.set(42.0);
variant2.set(42.0);
variant3.set(666.0);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("BoolInVariant") {
variant1.set(true);
variant2.set(true);
variant3.set(false);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("ArrayInVariant") {
JsonArray array1 = variant1.to<JsonArray>();
JsonArray array2 = variant2.to<JsonArray>();
array1.add(42);
array2.add(42);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("ObjectInVariant") {
JsonObject obj1 = variant1.to<JsonObject>();
JsonObject obj2 = variant2.to<JsonObject>();
obj1["hello"] = "world";
obj2["hello"] = "world";
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
}
class VariantComparisionFixture {
private:
TEST_CASE("Compare JsonVariant with value") {
StaticJsonDocument<256> doc;
JsonVariant variant;
JsonVariant a = doc.addElement();
public:
VariantComparisionFixture() : variant(doc.to<JsonVariant>()) {}
SECTION("null vs (char*)0") {
char* b = 0;
protected:
template <typename T>
void setValue(const T& value) {
variant.set(value);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
template <typename T>
void assertEqualsTo(const T& value) {
REQUIRE(variant == value);
REQUIRE(value == variant);
SECTION("42 vs 42") {
a.set(42);
int b = 42;
REQUIRE_FALSE(variant != value);
REQUIRE_FALSE(value != variant);
}
template <typename T>
void assertDiffersFrom(const T& value) {
REQUIRE(variant != value);
REQUIRE(value != variant);
REQUIRE_FALSE(variant == value);
REQUIRE_FALSE(value == variant);
}
template <typename T>
void assertGreaterThan(const T& value) {
REQUIRE((variant > value));
REQUIRE((variant >= value));
REQUIRE(value < variant);
REQUIRE(value <= variant);
REQUIRE_FALSE((variant < value));
REQUIRE_FALSE((variant <= value));
REQUIRE_FALSE(value > variant);
REQUIRE_FALSE(value >= variant);
}
template <typename T>
void assertLowerThan(const T& value) {
REQUIRE(variant < value);
REQUIRE(variant <= value);
REQUIRE(value > variant);
REQUIRE(value >= variant);
REQUIRE_FALSE(variant > value);
REQUIRE_FALSE(variant >= value);
REQUIRE_FALSE(value < variant);
REQUIRE_FALSE(value <= variant);
}
};
TEST_CASE_METHOD(VariantComparisionFixture,
"Compare variant with another type") {
SECTION("null") {
assertDiffersFrom(3);
assertDiffersFrom("world");
}
SECTION("string") {
setValue("hello");
assertEqualsTo("hello");
assertDiffersFrom(3);
assertDiffersFrom("world");
assertGreaterThan("helln");
assertLowerThan("hellp");
}
SECTION("positive integer") {
setValue(42);
assertEqualsTo(42);
assertDiffersFrom(43);
assertGreaterThan(41);
assertLowerThan(43);
assertDiffersFrom("world");
}
SECTION("negative integer") {
setValue(-42);
assertEqualsTo(-42);
assertDiffersFrom(42);
assertGreaterThan(-43);
assertLowerThan(-41);
assertDiffersFrom("world");
}
SECTION("double") {
setValue(42.0);
assertEqualsTo(42.0);
assertDiffersFrom(42.1);
assertGreaterThan(41.0);
assertLowerThan(43.0);
assertDiffersFrom("42.0");
}
SECTION("true") {
setValue(true);
assertEqualsTo(true);
assertDiffersFrom(false);
assertDiffersFrom(1);
assertDiffersFrom("true");
assertDiffersFrom(1.0);
assertGreaterThan(false);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
}
TEST_CASE("Compare JsonVariant with JsonVariant") {
StaticJsonDocument<256> doc;
JsonVariant a = doc.addElement();
JsonVariant b = doc.addElement();
SECTION("'abc' vs 'abc'") {
a.set("abc");
b.set("abc");
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("'abc' vs 'bcd'") {
a.set("abc");
b.set("bcd");
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("'bcd' vs 'abc'") {
a.set("bcd");
b.set("abc");
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("serialized('abc') vs serialized('abc')") {
a.set(serialized("abc"));
b.set(serialized("abc"));
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("serialized('abc') vs serialized('bcd')") {
a.set(serialized("abc"));
b.set(serialized("bcd"));
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("serialized('bcd') vs serialized('abc')") {
a.set(serialized("bcd"));
b.set(serialized("abc"));
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") {
a.set(false);
b.set(true);
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("false vs -1") {
a.set(false);
b.set(-1);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("null vs null") {
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42") {
a.set(42);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42.0") {
a.set(42);
b.set(42.0);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42.0 vs 42") {
a.set(42.0);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("-42 vs -42") {
a.set(-42);
b.set(-42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("-42 vs 42") {
a.set(-42);
b.set(42);
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("42 vs -42") {
a.set(42);
b.set(-42);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("42.0 vs -42") {
a.set(42.0);
b.set(-42);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("[1] vs [1]") {
a.add(1);
b.add(1);
CHECK(a <= b);
CHECK(a == b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("[1] vs [2]") {
a.add(1);
b.add(2);
CHECK(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("{x:1} vs {x:1}") {
a["x"] = 1;
b["x"] = 1;
CHECK(a <= b);
CHECK(a == b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("{x:1} vs {x:2}") {
a["x"] = 1;
b["x"] = 2;
CHECK(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
}

View File

@ -47,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
var1.set(str);
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
SECTION("stores std::string by copy") {
var1.set(std::string("hello!!"));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
SECTION("stores Serialized<const char*> by reference") {
var1.set(serialized("hello!!", JSON_STRING_SIZE(8)));
var1.set(serialized("hello!!", 8));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 0);
@ -69,18 +69,18 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
SECTION("stores Serialized<char*> by copy") {
char str[] = "hello!!";
var1.set(serialized(str, 8));
var1.set(serialized(str, 7));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
SECTION("stores Serialized<std::string> by copy") {
var1.set(serialized(std::string("hello!!!")));
var1.set(serialized(std::string("hello!!")));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
}

View File

@ -10,17 +10,39 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::operator==()") {
DynamicJsonDocument doc(4096);
SECTION("same values") {
doc["key1"] = "value";
doc["key2"] = "value";
REQUIRE(doc["key1"] == doc["key2"]);
REQUIRE_FALSE(doc["key1"] != doc["key2"]);
SECTION("1 vs 1") {
doc["a"] = 1;
doc["b"] = 1;
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE(doc["a"] == doc["b"]);
REQUIRE(doc["a"] >= doc["b"]);
REQUIRE_FALSE(doc["a"] != doc["b"]);
REQUIRE_FALSE(doc["a"] < doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
}
SECTION("different values") {
doc["key1"] = "value1";
doc["key2"] = "value2";
REQUIRE_FALSE(doc["key1"] == doc["key2"]);
REQUIRE(doc["key1"] != doc["key2"]);
SECTION("1 vs 2") {
doc["a"] = 1;
doc["b"] = 2;
REQUIRE(doc["a"] != doc["b"]);
REQUIRE(doc["a"] < doc["b"]);
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE_FALSE(doc["a"] == doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
REQUIRE_FALSE(doc["a"] >= doc["b"]);
}
SECTION("'abc' vs 'bcd'") {
doc["a"] = "abc";
doc["b"] = "bcd";
REQUIRE(doc["a"] != doc["b"]);
REQUIRE(doc["a"] < doc["b"]);
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE_FALSE(doc["a"] == doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
REQUIRE_FALSE(doc["a"] >= doc["b"]);
}
}

View File

@ -4,10 +4,10 @@
add_executable(MemoryPoolTests
allocVariant.cpp
allocString.cpp
clear.cpp
saveString.cpp
size.cpp
StringBuilder.cpp
StringCopier.cpp
)
add_test(MemoryPool MemoryPoolTests)

View File

@ -1,41 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StringBuilder") {
char buffer[4096];
SECTION("Works when buffer is big enough") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringBuilder str(&pool);
str.append("hello");
REQUIRE(str.complete() == std::string("hello"));
}
SECTION("Returns null when too small") {
MemoryPool pool(buffer, sizeof(void*));
StringBuilder str(&pool);
str.append("hello world!");
REQUIRE(str.complete() == 0);
}
SECTION("Increases size of memory pool") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringBuilder str(&pool);
str.append('h');
str.complete();
REQUIRE(JSON_STRING_SIZE(2) == pool.size());
}
}

View File

@ -0,0 +1,84 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/StringStorage/StringCopier.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StringCopier") {
char buffer[4096];
SECTION("Works when buffer is big enough") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringCopier str;
str.startString(&pool);
str.append("hello");
str.append('\0');
REQUIRE(str.isValid() == true);
REQUIRE(str.c_str() == std::string("hello"));
}
SECTION("Returns null when too small") {
MemoryPool pool(buffer, sizeof(void*));
StringCopier str;
str.startString(&pool);
str.append("hello world!");
REQUIRE(str.isValid() == false);
}
SECTION("Increases size of memory pool") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringCopier str;
str.startString(&pool);
str.append('h');
str.save(&pool);
REQUIRE(1 == pool.size());
}
}
static const char* addStringToPool(MemoryPool* pool, const char* s) {
StringCopier str;
str.startString(pool);
str.append(s);
str.append('\0');
return str.save(pool);
}
TEST_CASE("StringCopier::save() deduplicates strings") {
char buffer[4096];
MemoryPool pool(buffer, 4096);
SECTION("Basic") {
const char* s1 = addStringToPool(&pool, "hello");
const char* s2 = addStringToPool(&pool, "world");
const char* s3 = addStringToPool(&pool, "hello");
REQUIRE(s1 == s3);
REQUIRE(s2 != s3);
REQUIRE(pool.size() == 12);
}
SECTION("Requires terminator") {
const char* s1 = addStringToPool(&pool, "hello world");
const char* s2 = addStringToPool(&pool, "hello");
REQUIRE(s2 != s1);
REQUIRE(pool.size() == 12 + 6);
}
SECTION("Don't overrun") {
const char* s1 = addStringToPool(&pool, "hello world");
const char* s2 = addStringToPool(&pool, "wor");
REQUIRE(s2 != s1);
REQUIRE(pool.size() == 12 + 4);
}
}

View File

@ -1,67 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemoryPool::allocFrozenString()") {
const size_t poolCapacity = 64;
const size_t longestString = poolCapacity;
char buffer[poolCapacity];
MemoryPool pool(buffer, poolCapacity);
SECTION("Returns different addresses") {
char *a = pool.allocFrozenString(1);
char *b = pool.allocFrozenString(1);
REQUIRE(a != b);
}
SECTION("Returns NULL when full") {
void *p1 = pool.allocFrozenString(longestString);
REQUIRE(p1 != 0);
void *p2 = pool.allocFrozenString(1);
REQUIRE(p2 == 0);
}
SECTION("Returns NULL when pool is too small") {
void *p = pool.allocFrozenString(longestString + 1);
REQUIRE(0 == p);
}
SECTION("Returns NULL when buffer is NULL") {
MemoryPool pool2(0, poolCapacity);
REQUIRE(0 == pool2.allocFrozenString(2));
}
SECTION("Returns NULL when capacity is 0") {
MemoryPool pool2(buffer, 0);
REQUIRE(0 == pool2.allocFrozenString(2));
}
SECTION("Returns same address after clear()") {
void *a = pool.allocFrozenString(1);
pool.clear();
void *b = pool.allocFrozenString(1);
REQUIRE(a == b);
}
SECTION("Can use full capacity when fresh") {
void *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
SECTION("Can use full capacity after clear") {
pool.allocFrozenString(longestString);
pool.clear();
void *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
}

View File

@ -3,6 +3,7 @@
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
@ -21,8 +22,8 @@ TEST_CASE("MemoryPool::clear()") {
}
SECTION("Discards allocated strings") {
pool.allocFrozenString(10);
REQUIRE(pool.size() > 0);
pool.saveString(adaptString(const_cast<char *>("123456789")));
REQUIRE(pool.size() == 10);
pool.clear();

View File

@ -0,0 +1,81 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
static const char *saveString(MemoryPool &pool, const char *s) {
return pool.saveString(adaptString(const_cast<char *>(s)));
}
TEST_CASE("MemoryPool::saveString()") {
char buffer[32];
MemoryPool pool(buffer, 32);
SECTION("Duplicates different strings") {
const char *a = saveString(pool, "hello");
const char *b = saveString(pool, "world");
REQUIRE(a != b);
REQUIRE(pool.size() == 6 + 6);
}
SECTION("Deduplicates identical strings") {
const char *a = saveString(pool, "hello");
const char *b = saveString(pool, "hello");
REQUIRE(a == b);
REQUIRE(pool.size() == 6);
}
SECTION("Returns NULL when full") {
REQUIRE(pool.capacity() == 32);
const void *p1 = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
REQUIRE(p1 != 0);
REQUIRE(pool.size() == 32);
const void *p2 = saveString(pool, "b");
REQUIRE(p2 == 0);
}
SECTION("Returns NULL when pool is too small") {
const void *p = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
REQUIRE(0 == p);
}
SECTION("Returns NULL when buffer is NULL") {
MemoryPool pool2(0, 32);
REQUIRE(0 == saveString(pool2, "a"));
}
SECTION("Returns NULL when capacity is 0") {
MemoryPool pool2(buffer, 0);
REQUIRE(0 == saveString(pool2, "a"));
}
SECTION("Returns same address after clear()") {
const void *a = saveString(pool, "hello");
pool.clear();
const void *b = saveString(pool, "world");
REQUIRE(a == b);
}
SECTION("Can use full capacity when fresh") {
const void *a = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
REQUIRE(a != 0);
}
SECTION("Can use full capacity after clear") {
saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
pool.clear();
const void *a = saveString(pool, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
REQUIRE(a != 0);
}
}

View File

@ -22,29 +22,6 @@ TEST_CASE("MemoryPool::size()") {
REQUIRE(0 == pool.size());
}
SECTION("size() == capacity() after allocExpandableString()") {
pool.allocExpandableString();
REQUIRE(pool.size() == pool.capacity());
}
SECTION("Decreases after freezeString()") {
StringSlot a = pool.allocExpandableString();
pool.freezeString(a, 1);
REQUIRE(pool.size() == JSON_STRING_SIZE(1));
StringSlot b = pool.allocExpandableString();
pool.freezeString(b, 1);
REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(1));
}
SECTION("Increases after allocFrozenString()") {
pool.allocFrozenString(0);
REQUIRE(pool.size() == JSON_STRING_SIZE(0));
pool.allocFrozenString(0);
REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(0));
}
SECTION("Doesn't grow when memory pool is full") {
const size_t variantCount = sizeof(buffer) / sizeof(VariantSlot);

View File

@ -3,15 +3,17 @@
# MIT License
add_executable(MiscTests
arithmeticCompare.cpp
conflicts.cpp
FloatParts.cpp
JsonString.cpp
Readers.cpp
StringAdapters.cpp
StringWriter.cpp
TypeTraits.cpp
unsigned_char.cpp
Utf8.cpp
Utf16.cpp
Utf8.cpp
version.cpp
)

View File

@ -0,0 +1,60 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonString") {
SECTION("Default constructor creates a null JsonString") {
JsonString s;
CHECK(s.isNull() == true);
CHECK(s.c_str() == 0);
CHECK(s.isStatic() == true);
}
SECTION("Compare null with null") {
JsonString a, b;
CHECK(a == b);
CHECK_FALSE(a != b);
}
SECTION("Compare null with non-null") {
JsonString a(0), b("hello");
CHECK_FALSE(a == b);
CHECK(a != b);
}
SECTION("Compare non-null with null") {
JsonString a("hello"), b(0);
CHECK_FALSE(a == b);
CHECK(a != b);
}
SECTION("Compare different strings") {
JsonString a("hello"), b("world");
CHECK_FALSE(a == b);
CHECK(a != b);
}
SECTION("Compare identical by pointer") {
JsonString a("hello"), b("hello");
CHECK(a == b);
CHECK_FALSE(a != b);
}
SECTION("Compare identical by value") {
char s1[] = "hello";
char s2[] = "hello";
JsonString a(s1), b(s2);
CHECK(a == b);
CHECK_FALSE(a != b);
}
}

View File

@ -6,6 +6,7 @@
#include "progmem_emulation.hpp"
#include "weird_strcmp.hpp"
#include <ArduinoJson/Strings/ArduinoStringAdapter.hpp>
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
@ -114,6 +115,21 @@ TEST_CASE("std::string") {
CHECK(adapter.size() == 5);
}
TEST_CASE("Arduino String") {
::String str("bravo");
ArduinoStringAdapter adapter = adaptString(str);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
CHECK(adapter.compare("bravo") == 0);
CHECK(adapter.compare("charlie") < 0);
CHECK(adapter.equals("bravo"));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 5);
}
TEST_CASE("custom_string") {
custom_string str("bravo");
StlStringAdapter<custom_string> adapter = adaptString(str);
@ -145,4 +161,12 @@ TEST_CASE("IsString<T>") {
SECTION("const __FlashStringHelper*") {
CHECK(IsString<const __FlashStringHelper*>::value == true);
}
SECTION("const char*") {
CHECK(IsString<const char*>::value == true);
}
SECTION("const char[]") {
CHECK(IsString<const char[8]>::value == true);
}
}

View File

@ -29,6 +29,24 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_const<const char>::value == true);
}
SECTION("is_integral") {
CHECK(is_integral<double>::value == false);
CHECK(is_integral<float>::value == false);
CHECK(is_integral<bool>::value == true);
CHECK(is_integral<char>::value == true);
CHECK(is_integral<signed char>::value == true);
CHECK(is_integral<signed int>::value == true);
CHECK(is_integral<signed long>::value == true);
CHECK(is_integral<signed short>::value == true);
CHECK(is_integral<unsigned char>::value == true);
CHECK(is_integral<unsigned int>::value == true);
CHECK(is_integral<unsigned long>::value == true);
CHECK(is_integral<unsigned short>::value == true);
CHECK(is_integral<UInt>::value == true);
}
SECTION("is_signed") {
CHECK(is_signed<char>::value == true);
CHECK(is_signed<signed char>::value == true);

View File

@ -12,12 +12,14 @@ using namespace ARDUINOJSON_NAMESPACE;
static void testCodepoint(uint32_t codepoint, std::string expected) {
char buffer[4096];
MemoryPool pool(buffer, 4096);
StringBuilder str(&pool);
StringCopier str;
str.startString(&pool);
CAPTURE(codepoint);
Utf8::encodeCodepoint(codepoint, str);
REQUIRE(str.complete() == expected);
str.append('\0');
REQUIRE(str.c_str() == expected);
}
TEST_CASE("Utf8::encodeCodepoint()") {

View File

@ -0,0 +1,103 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Numbers/arithmeticCompare.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("arithmeticCompare()") {
SECTION("int vs uint8_t") {
CHECK((arithmeticCompare<int, uint8_t>(256, 1) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, uint8_t>(41, 42) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompare<int, uint8_t>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, uint8_t>(43, 42) == COMPARE_RESULT_GREATER));
}
SECTION("unsigned vs int") {
CHECK((arithmeticCompare<unsigned, int>(0, -1) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<unsigned, int>(42, 41) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<unsigned, int>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<unsigned, int>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("float vs int") {
CHECK((arithmeticCompare<float, int>(42, 41) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<float, int>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<float, int>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("int vs unsigned") {
CHECK((arithmeticCompare<int, unsigned>(-1, 0) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompare<int, unsigned>(0, 0) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, unsigned>(1, 0) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, unsigned>(42, 41) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, unsigned>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, unsigned>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("unsigned vs unsigned") {
CHECK((arithmeticCompare<unsigned, unsigned>(42, 41) ==
COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<unsigned, unsigned>(42, 42) ==
COMPARE_RESULT_EQUAL));
CHECK(
(arithmeticCompare<unsigned, unsigned>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("bool vs bool") {
CHECK(
(arithmeticCompare<bool, bool>(false, false) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, bool>(true, true) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, bool>(false, true) == COMPARE_RESULT_LESS));
CHECK(
(arithmeticCompare<bool, bool>(true, false) == COMPARE_RESULT_GREATER));
}
SECTION("bool vs int") {
CHECK((arithmeticCompare<bool, int>(false, -1) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<bool, int>(false, 0) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, int>(false, 1) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompare<bool, int>(true, 0) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<bool, int>(true, 1) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, int>(true, 2) == COMPARE_RESULT_LESS));
}
SECTION("bool vs int") {
CHECK((arithmeticCompare<int, bool>(0, false) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, bool>(1, true) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, bool>(1, false) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, bool>(0, true) == COMPARE_RESULT_LESS));
}
}
TEST_CASE("arithmeticCompareNegateLeft()") {
SECTION("unsigned vs int") {
CHECK((arithmeticCompareNegateLeft<int>(0, 1) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompareNegateLeft<int>(42, -41) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompareNegateLeft<int>(42, -42) == COMPARE_RESULT_EQUAL));
CHECK(
(arithmeticCompareNegateLeft<int>(42, -43) == COMPARE_RESULT_GREATER));
}
SECTION("unsigned vs unsigned") {
CHECK(
(arithmeticCompareNegateLeft<unsigned>(42, 42) == COMPARE_RESULT_LESS));
}
}
TEST_CASE("arithmeticCompareNegateRight()") {
SECTION("int vs unsigned") {
CHECK((arithmeticCompareNegateRight<int>(1, 0) == COMPARE_RESULT_GREATER));
CHECK(
(arithmeticCompareNegateRight<int>(-41, 42) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompareNegateRight<int>(-42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompareNegateRight<int>(-43, 42) == COMPARE_RESULT_LESS));
}
SECTION("unsigned vs unsigned") {
CHECK((arithmeticCompareNegateRight<unsigned>(42, 42) ==
COMPARE_RESULT_GREATER));
}
}

View File

@ -18,6 +18,8 @@ add_executable(MixedConfigurationTests
enable_nan_0.cpp
enable_nan_1.cpp
enable_progmem_1.cpp
enable_string_deduplication_0.cpp
enable_string_deduplication_1.cpp
use_double_0.cpp
use_double_1.cpp
use_long_long_0.cpp

View File

@ -0,0 +1,125 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include "progmem_emulation.hpp"
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 0
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
StaticJsonDocument<1024> doc;
SECTION("deserializeJson()") {
SECTION("Deduplicate values") {
deserializeJson(doc, "[\"example\",\"example\"]");
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("Deduplicate keys") {
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
CHECK(doc.memoryUsage() ==
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
}
SECTION("JsonDocument") {
SECTION("values") {
SECTION("std::string") {
doc.add(std::string("example"));
doc.add(std::string("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("char*") {
char value[] = "example";
doc.add(value);
doc.add(value);
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("Arduino String") {
doc.add(String("example"));
doc.add(String("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("Flash string") {
doc.add(F("example"));
doc.add(F("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
}
SECTION("keys") {
SECTION("std::string") {
doc[0][std::string("example")] = 1;
doc[1][std::string("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
SECTION("char*") {
char key[] = "example";
doc[0][key] = 1;
doc[1][key] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
SECTION("Arduino String") {
doc[0][String("example")] = 1;
doc[1][String("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
SECTION("Flash string") {
doc[0][F("example")] = 1;
doc[1][F("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
}
}
}

View File

@ -0,0 +1,124 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include "progmem_emulation.hpp"
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
StaticJsonDocument<1024> doc;
SECTION("deserializeJson()") {
SECTION("Deduplicate values") {
deserializeJson(doc, "[\"example\",\"example\"]");
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("Deduplicate keys") {
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
CHECK(doc.memoryUsage() ==
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
}
SECTION("JsonDocument") {
SECTION("values") {
SECTION("std::string") {
doc.add(std::string("example"));
doc.add(std::string("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("char*") {
char value[] = "example";
doc.add(value);
doc.add(value);
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("Arduino String") {
doc.add(String("example"));
doc.add(String("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("Flash string") {
doc.add(F("example"));
doc.add(F("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
}
SECTION("keys") {
SECTION("std::string") {
doc[0][std::string("example")] = 1;
doc[1][std::string("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
SECTION("char*") {
char key[] = "example";
doc[0][key] = 1;
doc[1][key] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
SECTION("Arduino String") {
doc[0][String("example")] = 1;
doc[1][String("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
SECTION("Flash string") {
doc[0][F("example")] = 1;
doc[1][F("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
}
}
}

View File

@ -7,9 +7,9 @@
template <size_t Capacity>
static void check(const char* input, DeserializationError expected) {
StaticJsonDocument<Capacity> variant;
StaticJsonDocument<Capacity> doc;
DeserializationError error = deserializeMsgPack(variant, input);
DeserializationError error = deserializeMsgPack(doc, input);
CAPTURE(input);
REQUIRE(error == expected);
@ -17,7 +17,7 @@ static void check(const char* input, DeserializationError expected) {
template <size_t Size>
static void checkString(const char* input, DeserializationError expected) {
check<JSON_STRING_SIZE(Size)>(input, expected);
check<Size>(input, expected);
}
TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {

View File

@ -8,17 +8,17 @@
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("Test uint32_t overflow") {
ParsedNumber<float, uint32_t> first =
parseNumber<float, uint32_t>("4294967295");
ParsedNumber<float, uint32_t> second =
parseNumber<float, uint32_t>("4294967296");
ParsedNumber<float, uint32_t> first, second;
parseNumber("4294967295", first);
parseNumber("4294967296", second);
REQUIRE(first.type() == uint8_t(VALUE_IS_POSITIVE_INTEGER));
REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT));
}
TEST_CASE("Invalid value") {
ParsedNumber<float, uint32_t> result = parseNumber<float, uint32_t>("6a3");
ParsedNumber<float, uint32_t> result;
parseNumber("6a3", result);
REQUIRE(result.type() == uint8_t(VALUE_IS_NULL));
}

View File

@ -7,7 +7,7 @@
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "6.15.2",
"version": "6.16.1",
"authors": {
"name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr"

View File

@ -1,5 +1,5 @@
name=ArduinoJson
version=6.15.2
version=6.16.1
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.

View File

@ -28,6 +28,7 @@
#include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp"
#include "ArduinoJson/Variant/VariantAsImpl.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp"

View File

@ -5,7 +5,8 @@
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Operators/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp>
#ifdef _MSC_VER
@ -17,6 +18,7 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TArray>
class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
public VariantShortcuts<ElementProxy<TArray> >,
public Visitable {
typedef ElementProxy<TArray> this_type;
@ -51,14 +53,6 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
return *this;
}
FORCE_INLINE bool operator==(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamElement()) == rhs;
}
FORCE_INLINE bool operator!=(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamElement()) != rhs;
}
FORCE_INLINE void clear() const {
getUpstreamElement().clear();
}
@ -141,6 +135,10 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
return getOrAddUpstreamElement().getElement(index);
}
VariantRef getOrAddElement(size_t index) const {
return getOrAddUpstreamElement().getOrAddElement(index);
}
FORCE_INLINE void remove(size_t index) const {
getUpstreamElement().remove(index);
}

View File

@ -10,8 +10,11 @@
namespace ARDUINOJSON_NAMESPACE {
// Copy a 1D array to a JsonArray
template <typename T, size_t N>
inline bool copyArray(T (&src)[N], ArrayRef dst) {
template <typename T, size_t N, typename TDestination>
inline typename enable_if<!is_array<T>::value &&
!is_base_of<JsonDocument, TDestination>::value,
bool>::type
copyArray(T (&src)[N], const TDestination& dst) {
return copyArray(src, N, dst);
}
@ -22,8 +25,11 @@ inline bool copyArray(T (&src)[N], JsonDocument& dst) {
}
// Copy a 1D array to a JsonArray
template <typename T>
inline bool copyArray(T* src, size_t len, ArrayRef dst) {
template <typename T, typename TDestination>
inline typename enable_if<!is_array<T>::value &&
!is_base_of<JsonDocument, TDestination>::value,
bool>::type
copyArray(T* src, size_t len, const TDestination& dst) {
bool ok = true;
for (size_t i = 0; i < len; i++) {
ok &= dst.add(src[i]);
@ -38,8 +44,10 @@ inline bool copyArray(T* src, size_t len, JsonDocument& dst) {
}
// Copy a 2D array to a JsonArray
template <typename T, size_t N1, size_t N2>
inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) {
template <typename T, size_t N1, size_t N2, typename TDestination>
inline typename enable_if<!is_base_of<JsonDocument, TDestination>::value,
bool>::type
copyArray(T (&src)[N1][N2], const TDestination& dst) {
bool ok = true;
for (size_t i = 0; i < N1; i++) {
ArrayRef nestedArray = dst.createNestedArray();
@ -56,42 +64,87 @@ inline bool copyArray(T (&src)[N1][N2], JsonDocument& dst) {
return copyArray(src, dst.to<ArrayRef>());
}
template <typename T>
class ArrayCopier1D {
public:
ArrayCopier1D(T* destination, size_t capacity)
: _destination(destination), _capacity(capacity), _size(0) {}
void visitArray(const CollectionData& array) {
VariantSlot* slot = array.head();
while (slot != 0 && _size < _capacity) {
_destination[_size++] = variantAs<T>(slot->data());
slot = slot->next();
}
}
void visitObject(const CollectionData&) {}
void visitFloat(Float) {}
void visitString(const char*) {}
void visitRawJson(const char*, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool) {}
void visitNull() {}
size_t result() const {
return _size;
}
private:
T* _destination;
size_t _capacity;
size_t _size;
};
template <typename T, size_t N1, size_t N2>
class ArrayCopier2D {
public:
ArrayCopier2D(T (*destination)[N1][N2]) : _destination(destination) {}
void visitArray(const CollectionData& array) {
VariantSlot* slot = array.head();
size_t n = 0;
while (slot != 0 && n < N1) {
ArrayCopier1D<T> copier((*_destination)[n++], N2);
variantAccept(slot->data(), copier);
slot = slot->next();
}
}
void visitObject(const CollectionData&) {}
void visitFloat(Float) {}
void visitString(const char*) {}
void visitRawJson(const char*, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool) {}
void visitNull() {}
private:
T (*_destination)[N1][N2];
size_t _capacity1, _capacity2;
};
// Copy a JsonArray to a 1D array
template <typename T, size_t N>
inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) {
template <typename TSource, typename T, size_t N>
inline typename enable_if<!is_array<T>::value, size_t>::type copyArray(
const TSource& src, T (&dst)[N]) {
return copyArray(src, dst, N);
}
// Copy a JsonDocument to a 1D array
template <typename T, size_t N>
inline size_t copyArray(const JsonDocument& src, T (&dst)[N]) {
return copyArray(src.as<ArrayConstRef>(), dst, N);
}
// Copy a JsonArray to a 1D array
template <typename T>
inline size_t copyArray(ArrayConstRef src, T* dst, size_t len) {
size_t i = 0;
for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < len;
++it)
dst[i++] = *it;
return i;
template <typename TSource, typename T>
inline size_t copyArray(const TSource& src, T* dst, size_t len) {
ArrayCopier1D<T> copier(dst, len);
src.accept(copier);
return copier.result();
}
// Copy a JsonArray to a 2D array
template <typename T, size_t N1, size_t N2>
inline void copyArray(ArrayConstRef src, T (&dst)[N1][N2]) {
size_t i = 0;
for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < N1;
++it) {
copyArray(it->as<ArrayConstRef>(), dst[i++]);
}
}
// Copy a JsonDocument to a 2D array
template <typename T, size_t N1, size_t N2>
inline void copyArray(const JsonDocument& src, T (&dst)[N1][N2]) {
copyArray(src.as<ArrayConstRef>(), dst);
template <typename TSource, typename T, size_t N1, size_t N2>
inline void copyArray(const TSource& src, T (&dst)[N1][N2]) {
ArrayCopier2D<T, N1, N2> copier(&dst);
src.accept(copier);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -9,6 +9,10 @@
namespace ARDUINOJSON_NAMESPACE {
inline bool variantEquals(const VariantData* a, const VariantData* b) {
return variantCompare(a, b) == COMPARE_RESULT_EQUAL;
}
inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) {
VariantSlot* slot = pool->allocVariant();
if (!slot)

View File

@ -164,7 +164,7 @@
// Convert unicode escape sequence (\u0123) to UTF-8
#ifndef ARDUINOJSON_DECODE_UNICODE
#define ARDUINOJSON_DECODE_UNICODE 0
#define ARDUINOJSON_DECODE_UNICODE 1
#endif
// Ignore comments in input
@ -215,6 +215,10 @@
#define ARDUINOJSON_TAB " "
#endif
#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
#endif
#ifndef ARDUINOJSON_STRING_BUFFER_SIZE
#define ARDUINOJSON_STRING_BUFFER_SIZE 32
#endif

View File

@ -32,9 +32,8 @@ deserialize(JsonDocument &doc, const TString &input, NestingLimit nestingLimit,
TFilter filter) {
Reader<TString> reader(input);
doc.clear();
return makeDeserializer<TDeserializer>(
doc.memoryPool(), reader,
makeStringStorage(doc.memoryPool(), input))
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
makeStringStorage(input))
.parse(doc.data(), filter, nestingLimit);
}
//
@ -48,9 +47,8 @@ DeserializationError deserialize(JsonDocument &doc, TChar *input,
TFilter filter) {
BoundedReader<TChar *> reader(input, inputSize);
doc.clear();
return makeDeserializer<TDeserializer>(
doc.memoryPool(), reader,
makeStringStorage(doc.memoryPool(), input))
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
makeStringStorage(input))
.parse(doc.data(), filter, nestingLimit);
}
//
@ -62,9 +60,8 @@ DeserializationError deserialize(JsonDocument &doc, TStream &input,
NestingLimit nestingLimit, TFilter filter) {
Reader<TStream> reader(input);
doc.clear();
return makeDeserializer<TDeserializer>(
doc.memoryPool(), reader,
makeStringStorage(doc.memoryPool(), input))
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
makeStringStorage(input))
.parse(doc.data(), filter, nestingLimit);
}

View File

@ -19,34 +19,25 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TReader, typename TStringStorage>
class JsonDeserializer {
typedef typename remove_reference<TStringStorage>::type::StringBuilder
StringBuilder;
struct StringOrError {
DeserializationError err;
const char *value;
StringOrError(DeserializationError e) : err(e) {}
StringOrError(DeserializationError::Code c) : err(c) {}
StringOrError(const char *s) : err(DeserializationError::Ok), value(s) {}
};
public:
JsonDeserializer(MemoryPool &pool, TReader reader,
TStringStorage stringStorage)
: _pool(&pool), _stringStorage(stringStorage), _latch(reader) {}
: _stringStorage(stringStorage),
_latch(reader),
_pool(&pool),
_error(DeserializationError::Ok) {}
template <typename TFilter>
DeserializationError parse(VariantData &variant, TFilter filter,
NestingLimit nestingLimit) {
DeserializationError err = parseVariant(variant, filter, nestingLimit);
parseVariant(variant, filter, nestingLimit);
if (!err && _latch.last() != 0 && !variant.isEnclosed()) {
if (!_error && _latch.last() != 0 && !variant.isEnclosed()) {
// We don't detect trailing characters earlier, so we need to check now
err = DeserializationError::InvalidInput;
_error = DeserializationError::InvalidInput;
}
return err;
return _error;
}
private:
@ -68,11 +59,10 @@ class JsonDeserializer {
}
template <typename TFilter>
DeserializationError parseVariant(VariantData &variant, TFilter filter,
NestingLimit nestingLimit) {
DeserializationError err = skipSpacesAndComments();
if (err)
return err;
bool parseVariant(VariantData &variant, TFilter filter,
NestingLimit nestingLimit) {
if (!skipSpacesAndComments())
return false;
switch (current()) {
case '[':
@ -102,10 +92,9 @@ class JsonDeserializer {
}
}
DeserializationError skipVariant(NestingLimit nestingLimit) {
DeserializationError err = skipSpacesAndComments();
if (err)
return err;
bool skipVariant(NestingLimit nestingLimit) {
if (!skipSpacesAndComments())
return false;
switch (current()) {
case '[':
@ -124,23 +113,24 @@ class JsonDeserializer {
}
template <typename TFilter>
DeserializationError parseArray(CollectionData &array, TFilter filter,
NestingLimit nestingLimit) {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool parseArray(CollectionData &array, TFilter filter,
NestingLimit nestingLimit) {
if (nestingLimit.reached()) {
_error = DeserializationError::TooDeep;
return false;
}
// Skip opening braket
ARDUINOJSON_ASSERT(current() == '[');
move();
// Skip spaces
DeserializationError err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// Empty array?
if (eat(']'))
return DeserializationError::Ok;
return true;
TFilter memberFilter = filter[0UL];
@ -149,35 +139,38 @@ class JsonDeserializer {
if (memberFilter.allow()) {
// Allocate slot in array
VariantData *value = array.addElement(_pool);
if (!value)
return DeserializationError::NoMemory;
if (!value) {
_error = DeserializationError::NoMemory;
return false;
}
// 1 - Parse value
err = parseVariant(*value, memberFilter, nestingLimit.decrement());
if (err)
return err;
if (!parseVariant(*value, memberFilter, nestingLimit.decrement()))
return false;
} else {
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
if (!skipVariant(nestingLimit.decrement()))
return false;
}
// 2 - Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// 3 - More values?
if (eat(']'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
return true;
if (!eat(',')) {
_error = DeserializationError::InvalidInput;
return false;
}
}
}
DeserializationError skipArray(NestingLimit nestingLimit) {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool skipArray(NestingLimit nestingLimit) {
if (nestingLimit.reached()) {
_error = DeserializationError::TooDeep;
return false;
}
// Skip opening braket
ARDUINOJSON_ASSERT(current() == '[');
@ -186,153 +179,162 @@ class JsonDeserializer {
// Read each value
for (;;) {
// 1 - Skip value
DeserializationError err = skipVariant(nestingLimit.decrement());
if (err)
return err;
if (!skipVariant(nestingLimit.decrement()))
return false;
// 2 - Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// 3 - More values?
if (eat(']'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
return true;
if (!eat(',')) {
_error = DeserializationError::InvalidInput;
return false;
}
}
}
template <typename TFilter>
DeserializationError parseObject(CollectionData &object, TFilter filter,
NestingLimit nestingLimit) {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool parseObject(CollectionData &object, TFilter filter,
NestingLimit nestingLimit) {
if (nestingLimit.reached()) {
_error = DeserializationError::TooDeep;
return false;
}
// Skip opening brace
ARDUINOJSON_ASSERT(current() == '{');
move();
// Skip spaces
DeserializationError err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// Empty object?
if (eat('}'))
return DeserializationError::Ok;
return true;
// Read each key value pair
for (;;) {
// Parse key
StringOrError key = parseKey();
err = key.err; // <- this trick saves 62 bytes on AVR
if (err)
return err;
if (!parseKey())
return false;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err; // Colon
if (!eat(':'))
return DeserializationError::InvalidInput;
if (!skipSpacesAndComments())
return false;
TFilter memberFilter = filter[key.value];
// Colon
if (!eat(':')) {
_error = DeserializationError::InvalidInput;
return false;
}
const char *key = _stringStorage.c_str();
TFilter memberFilter = filter[key];
if (memberFilter.allow()) {
VariantData *variant = object.getMember(adaptString(key.value));
VariantData *variant = object.getMember(adaptString(key));
if (!variant) {
// Save key in memory pool.
// This MUST be done before adding the slot.
key = _stringStorage.save(_pool);
// Allocate slot in object
VariantSlot *slot = object.addSlot(_pool);
if (!slot)
return DeserializationError::NoMemory;
if (!slot) {
_error = DeserializationError::NoMemory;
return false;
}
slot->setOwnedKey(make_not_null(key.value));
slot->setKey(key, typename TStringStorage::storage_policy());
variant = slot->data();
}
// Parse value
err = parseVariant(*variant, memberFilter, nestingLimit.decrement());
if (err)
return err;
if (!parseVariant(*variant, memberFilter, nestingLimit.decrement()))
return false;
} else {
_stringStorage.reclaim(key.value);
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
if (!skipVariant(nestingLimit.decrement()))
return false;
}
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// More keys/values?
if (eat('}'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
return true;
if (!eat(',')) {
_error = DeserializationError::InvalidInput;
return false;
}
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
}
}
DeserializationError skipObject(NestingLimit nestingLimit) {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool skipObject(NestingLimit nestingLimit) {
if (nestingLimit.reached()) {
_error = DeserializationError::TooDeep;
return false;
}
// Skip opening brace
ARDUINOJSON_ASSERT(current() == '{');
move();
// Skip spaces
DeserializationError err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// Empty object?
if (eat('}'))
return DeserializationError::Ok;
return true;
// Read each key value pair
for (;;) {
// Skip key
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
if (!skipVariant(nestingLimit.decrement()))
return false;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// Colon
if (!eat(':'))
return DeserializationError::InvalidInput;
if (!eat(':')) {
_error = DeserializationError::InvalidInput;
return false;
}
// Skip value
err = skipVariant(nestingLimit.decrement());
if (err)
return err;
if (!skipVariant(nestingLimit.decrement()))
return false;
// Skip spaces
err = skipSpacesAndComments();
if (err)
return err;
if (!skipSpacesAndComments())
return false;
// More keys/values?
if (eat('}'))
return DeserializationError::Ok;
if (!eat(','))
return DeserializationError::InvalidInput;
return true;
if (!eat(',')) {
_error = DeserializationError::InvalidInput;
return false;
}
}
}
StringOrError parseKey() {
bool parseKey() {
_stringStorage.startString(_pool);
if (isQuote(current())) {
return parseQuotedString();
} else {
@ -340,16 +342,17 @@ class JsonDeserializer {
}
}
DeserializationError parseStringValue(VariantData &variant) {
StringOrError result = parseQuotedString();
if (result.err)
return result.err;
variant.setOwnedString(make_not_null(result.value));
return DeserializationError::Ok;
bool parseStringValue(VariantData &variant) {
_stringStorage.startString(_pool);
if (!parseQuotedString())
return false;
const char *value = _stringStorage.save(_pool);
variant.setString(make_not_null(value),
typename TStringStorage::storage_policy());
return true;
}
StringOrError parseQuotedString() {
StringBuilder builder = _stringStorage.startString();
bool parseQuotedString() {
#if ARDUINOJSON_DECODE_UNICODE
Utf16::Codepoint codepoint;
#endif
@ -362,66 +365,82 @@ class JsonDeserializer {
if (c == stopChar)
break;
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\0') {
_error = DeserializationError::IncompleteInput;
return false;
}
if (c == '\\') {
c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\0') {
_error = DeserializationError::IncompleteInput;
return false;
}
if (c == 'u') {
#if ARDUINOJSON_DECODE_UNICODE
move();
uint16_t codeunit;
DeserializationError err = parseHex4(codeunit);
if (err)
return err;
if (!parseHex4(codeunit))
return false;
if (codepoint.append(codeunit))
Utf8::encodeCodepoint(codepoint.value(), builder);
Utf8::encodeCodepoint(codepoint.value(), _stringStorage);
continue;
#else
return DeserializationError::NotSupported;
_error = DeserializationError::NotSupported;
return false;
#endif
}
// replace char
c = EscapeSequence::unescapeChar(c);
if (c == '\0')
return DeserializationError::InvalidInput;
if (c == '\0') {
_error = DeserializationError::InvalidInput;
return false;
}
move();
}
builder.append(c);
_stringStorage.append(c);
}
const char *result = builder.complete();
if (!result)
return DeserializationError::NoMemory;
return result;
_stringStorage.append('\0');
if (!_stringStorage.isValid()) {
_error = DeserializationError::NoMemory;
return false;
}
return true;
}
StringOrError parseNonQuotedString() {
StringBuilder builder = _stringStorage.startString();
bool parseNonQuotedString() {
char c = current();
ARDUINOJSON_ASSERT(c);
if (canBeInNonQuotedString(c)) { // no quotes
do {
move();
builder.append(c);
_stringStorage.append(c);
c = current();
} while (canBeInNonQuotedString(c));
} else {
return DeserializationError::InvalidInput;
_error = DeserializationError::InvalidInput;
return false;
}
const char *result = builder.complete();
if (!result)
return DeserializationError::NoMemory;
return result;
_stringStorage.append('\0');
if (!_stringStorage.isValid()) {
_error = DeserializationError::NoMemory;
return false;
}
return true;
}
DeserializationError skipString() {
bool skipString() {
const char stopChar = current();
move();
@ -430,87 +449,104 @@ class JsonDeserializer {
move();
if (c == stopChar)
break;
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\0') {
_error = DeserializationError::IncompleteInput;
return false;
}
if (c == '\\') {
if (current() != '\0')
move();
}
}
return DeserializationError::Ok;
return true;
}
DeserializationError parseNumericValue(VariantData &result) {
char buffer[64];
bool parseNumericValue(VariantData &result) {
uint8_t n = 0;
char c = current();
while (canBeInNonQuotedString(c) && n < 63) {
move();
buffer[n++] = c;
_buffer[n++] = c;
c = current();
}
buffer[n] = 0;
_buffer[n] = 0;
c = buffer[0];
c = _buffer[0];
if (c == 't') { // true
result.setBoolean(true);
return n == 4 ? DeserializationError::Ok
: DeserializationError::IncompleteInput;
if (n != 4) {
_error = DeserializationError::IncompleteInput;
return false;
}
return true;
}
if (c == 'f') { // false
result.setBoolean(false);
return n == 5 ? DeserializationError::Ok
: DeserializationError::IncompleteInput;
if (n != 5) {
_error = DeserializationError::IncompleteInput;
return false;
}
return true;
}
if (c == 'n') { // null
// the variant is already null
return n == 4 ? DeserializationError::Ok
: DeserializationError::IncompleteInput;
if (n != 4) {
_error = DeserializationError::IncompleteInput;
return false;
}
return true;
}
ParsedNumber<Float, UInt> num = parseNumber<Float, UInt>(buffer);
ParsedNumber<Float, UInt> num;
parseNumber<Float, UInt>(_buffer, num);
switch (num.type()) {
case VALUE_IS_NEGATIVE_INTEGER:
result.setNegativeInteger(num.uintValue);
return DeserializationError::Ok;
return true;
case VALUE_IS_POSITIVE_INTEGER:
result.setPositiveInteger(num.uintValue);
return DeserializationError::Ok;
return true;
case VALUE_IS_FLOAT:
result.setFloat(num.floatValue);
return DeserializationError::Ok;
}
return true;
return DeserializationError::InvalidInput;
default:
_error = DeserializationError::InvalidInput;
return false;
}
}
DeserializationError skipNumericValue() {
bool skipNumericValue() {
char c = current();
while (canBeInNonQuotedString(c)) {
move();
c = current();
}
return DeserializationError::Ok;
return true;
}
DeserializationError parseHex4(uint16_t &result) {
bool parseHex4(uint16_t &result) {
result = 0;
for (uint8_t i = 0; i < 4; ++i) {
char digit = current();
if (!digit)
return DeserializationError::IncompleteInput;
if (!digit) {
_error = DeserializationError::IncompleteInput;
return false;
}
uint8_t value = decodeHex(digit);
if (value > 0x0F)
return DeserializationError::InvalidInput;
if (value > 0x0F) {
_error = DeserializationError::InvalidInput;
return false;
}
result = uint16_t((result << 4) | value);
move();
}
return DeserializationError::Ok;
return true;
}
static inline bool isBetween(char c, char min, char max) {
@ -533,12 +569,13 @@ class JsonDeserializer {
return uint8_t(c - 'A' + 10);
}
DeserializationError skipSpacesAndComments() {
bool skipSpacesAndComments() {
for (;;) {
switch (current()) {
// end of string
case '\0':
return DeserializationError::IncompleteInput;
_error = DeserializationError::IncompleteInput;
return false;
// spaces
case ' ':
@ -559,8 +596,10 @@ class JsonDeserializer {
bool wasStar = false;
for (;;) {
char c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\0') {
_error = DeserializationError::IncompleteInput;
return false;
}
if (c == '/' && wasStar) {
move();
break;
@ -577,8 +616,10 @@ class JsonDeserializer {
for (;;) {
move();
char c = current();
if (c == '\0')
return DeserializationError::IncompleteInput;
if (c == '\0') {
_error = DeserializationError::IncompleteInput;
return false;
}
if (c == '\n')
break;
}
@ -586,20 +627,25 @@ class JsonDeserializer {
// not a comment, just a '/'
default:
return DeserializationError::InvalidInput;
_error = DeserializationError::InvalidInput;
return false;
}
break;
#endif
default:
return DeserializationError::Ok;
return true;
}
}
}
MemoryPool *_pool;
TStringStorage _stringStorage;
Latch<TReader> _latch;
MemoryPool *_pool;
char _buffer[64]; // using a member instead of a local variable because it
// ended in the recursive path after compiler inlined the
// code
DeserializationError _error;
};
// deserializeJson(JsonDocument&, const std::string&, ...)

View File

@ -31,6 +31,8 @@ inline bool isLowSurrogate(uint16_t codeunit) {
class Codepoint {
public:
Codepoint() : _highSurrogate(0) {}
bool append(uint16_t codeunit) {
if (isHighSurrogate(codeunit)) {
_highSurrogate = codeunit & 0x3FF;

View File

@ -51,32 +51,43 @@ class MemoryPool {
return allocRight<VariantSlot>();
}
char* allocFrozenString(size_t n) {
if (!canAlloc(n))
template <typename TAdaptedString>
const char* saveString(const TAdaptedString& str) {
if (str.isNull())
return 0;
char* s = _left;
_left += n;
checkInvariants();
return s;
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
const char* existingCopy = findString(str.begin());
if (existingCopy)
return existingCopy;
#endif
size_t n = str.size();
char* newCopy = allocString(n + 1);
if (newCopy) {
str.copyTo(newCopy, n);
newCopy[n] = 0; // force null-terminator
}
return newCopy;
}
StringSlot allocExpandableString() {
StringSlot s;
s.value = _left;
s.size = size_t(_right - _left);
_left = _right;
checkInvariants();
return s;
void getFreeZone(char** zoneStart, size_t* zoneSize) const {
*zoneStart = _left;
*zoneSize = size_t(_right - _left);
}
void freezeString(StringSlot& s, size_t newSize) {
_left -= (s.size - newSize);
s.size = newSize;
checkInvariants();
}
const char* saveStringFromFreeZone(size_t len) {
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
const char* dup = findString(_left);
if (dup)
return dup;
#endif
void reclaimLastString(const char* s) {
_left = const_cast<char*>(s);
const char* str = _left;
_left += len;
checkInvariants();
return str;
}
void clear() {
@ -92,18 +103,6 @@ class MemoryPool {
return _begin <= p && p < _end;
}
template <typename T>
T* allocRight() {
return reinterpret_cast<T*>(allocRight(sizeof(T)));
}
void* allocRight(size_t bytes) {
if (!canAlloc(bytes))
return 0;
_right -= bytes;
return _right;
}
// Workaround for missing placement new
void* operator new(size_t, void* p) {
return p;
@ -155,6 +154,46 @@ class MemoryPool {
ARDUINOJSON_ASSERT(isAligned(_right));
}
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
template <typename TIterator>
const char* findString(TIterator str) {
for (char* next = _begin; next < _left; ++next) {
char* begin = next;
// try to match
for (TIterator it = str; *it == *next; ++it) {
if (*next++ == 0)
return begin;
}
// jump to next terminator
while (*next) ++next;
}
return 0;
}
#endif
char* allocString(size_t n) {
if (!canAlloc(n))
return 0;
char* s = _left;
_left += n;
checkInvariants();
return s;
}
template <typename T>
T* allocRight() {
return reinterpret_cast<T*>(allocRight(sizeof(T)));
}
void* allocRight(size_t bytes) {
if (!canAlloc(bytes))
return 0;
_right -= bytes;
return _right;
}
char *_begin, *_left, *_right, *_end;
};

View File

@ -1,51 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
namespace ARDUINOJSON_NAMESPACE {
class StringBuilder {
public:
explicit StringBuilder(MemoryPool* parent) : _parent(parent), _size(0) {
_slot = _parent->allocExpandableString();
}
void append(const char* s) {
while (*s) append(*s++);
}
void append(const char* s, size_t n) {
while (n-- > 0) append(*s++);
}
void append(char c) {
if (!_slot.value)
return;
if (_size >= _slot.size) {
_slot.value = 0;
return;
}
_slot.value[_size++] = c;
}
char* complete() {
append('\0');
if (_slot.value) {
_parent->freezeString(_slot, _size);
}
return _slot.value;
}
private:
MemoryPool* _parent;
size_t _size;
StringSlot _slot;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -8,7 +8,7 @@
#include <ArduinoJson/Namespace.hpp>
#define JSON_STRING_SIZE(SIZE) (SIZE)
#define JSON_STRING_SIZE(SIZE) (SIZE + 1)
namespace ARDUINOJSON_NAMESPACE {

View File

@ -15,34 +15,37 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TReader, typename TStringStorage>
class MsgPackDeserializer {
typedef typename remove_reference<TStringStorage>::type::StringBuilder
StringBuilder;
public:
MsgPackDeserializer(MemoryPool &pool, TReader reader,
TStringStorage stringStorage)
: _pool(&pool), _reader(reader), _stringStorage(stringStorage) {}
: _pool(&pool),
_reader(reader),
_stringStorage(stringStorage),
_error(DeserializationError::Ok) {}
// TODO: add support for filter
DeserializationError parse(VariantData &variant, AllowAllFilter,
NestingLimit nestingLimit) {
return parse(variant, nestingLimit);
parseVariant(variant, nestingLimit);
return _error;
}
DeserializationError parse(VariantData &variant, NestingLimit nestingLimit) {
private:
bool parseVariant(VariantData &variant, NestingLimit nestingLimit) {
uint8_t code;
if (!readByte(code))
return DeserializationError::IncompleteInput;
if (!readByte(code)) {
_error = DeserializationError::IncompleteInput;
return false;
}
if ((code & 0x80) == 0) {
variant.setUnsignedInteger(code);
return DeserializationError::Ok;
return true;
}
if ((code & 0xe0) == 0xe0) {
// TODO: add setNegativeInteger()
variant.setSignedInteger(static_cast<int8_t>(code));
return DeserializationError::Ok;
return true;
}
if ((code & 0xe0) == 0xa0) {
@ -60,15 +63,15 @@ class MsgPackDeserializer {
switch (code) {
case 0xc0:
// already null
return DeserializationError::Ok;
return true;
case 0xc2:
variant.setBoolean(false);
return DeserializationError::Ok;
return true;
case 0xc3:
variant.setBoolean(true);
return DeserializationError::Ok;
return true;
case 0xcc:
return readInteger<uint8_t>(variant);
@ -79,11 +82,9 @@ class MsgPackDeserializer {
case 0xce:
return readInteger<uint32_t>(variant);
case 0xcf:
#if ARDUINOJSON_USE_LONG_LONG
case 0xcf:
return readInteger<uint64_t>(variant);
#else
return DeserializationError::NotSupported;
#endif
case 0xd0:
@ -95,11 +96,9 @@ class MsgPackDeserializer {
case 0xd2:
return readInteger<int32_t>(variant);
case 0xd3:
#if ARDUINOJSON_USE_LONG_LONG
case 0xd3:
return readInteger<int64_t>(variant);
#else
return DeserializationError::NotSupported;
#endif
case 0xca:
@ -130,7 +129,8 @@ class MsgPackDeserializer {
return readObject<uint32_t>(variant.toObject(), nestingLimit);
default:
return DeserializationError::NotSupported;
_error = DeserializationError::NotSupported;
return false;
}
}
@ -140,14 +140,19 @@ class MsgPackDeserializer {
bool readByte(uint8_t &value) {
int c = _reader.read();
if (c < 0)
if (c < 0) {
_error = DeserializationError::IncompleteInput;
return false;
}
value = static_cast<uint8_t>(c);
return true;
}
bool readBytes(uint8_t *p, size_t n) {
return _reader.readBytes(reinterpret_cast<char *>(p), n) == n;
if (_reader.readBytes(reinterpret_cast<char *>(p), n) == n)
return true;
_error = DeserializationError::IncompleteInput;
return false;
}
template <typename T>
@ -155,14 +160,6 @@ class MsgPackDeserializer {
return readBytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
}
template <typename T>
T readInteger() {
T value;
readBytes(value);
fixEndianess(value);
return value;
}
template <typename T>
bool readInteger(T &value) {
if (!readBytes(value))
@ -172,152 +169,159 @@ class MsgPackDeserializer {
}
template <typename T>
DeserializationError readInteger(VariantData &variant) {
bool readInteger(VariantData &variant) {
T value;
if (!readInteger(value))
return DeserializationError::IncompleteInput;
return false;
variant.setInteger(value);
return DeserializationError::Ok;
return true;
}
template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat(
typename enable_if<sizeof(T) == 4, bool>::type readFloat(
VariantData &variant) {
T value;
if (!readBytes(value))
return DeserializationError::IncompleteInput;
return false;
fixEndianess(value);
variant.setFloat(value);
return DeserializationError::Ok;
return true;
}
template <typename T>
typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble(
typename enable_if<sizeof(T) == 8, bool>::type readDouble(
VariantData &variant) {
T value;
if (!readBytes(value))
return DeserializationError::IncompleteInput;
return false;
fixEndianess(value);
variant.setFloat(value);
return DeserializationError::Ok;
return true;
}
template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble(
typename enable_if<sizeof(T) == 4, bool>::type readDouble(
VariantData &variant) {
uint8_t i[8]; // input is 8 bytes
T value; // output is 4 bytes
uint8_t *o = reinterpret_cast<uint8_t *>(&value);
if (!readBytes(i, 8))
return DeserializationError::IncompleteInput;
return false;
doubleToFloat(i, o);
fixEndianess(value);
variant.setFloat(value);
return DeserializationError::Ok;
return true;
}
template <typename T>
DeserializationError readString(VariantData &variant) {
bool readString(VariantData &variant) {
T size;
if (!readInteger(size))
return DeserializationError::IncompleteInput;
return false;
return readString(variant, size);
}
template <typename T>
DeserializationError readString(const char *&str) {
bool readString(const char *&str) {
T size;
if (!readInteger(size))
return DeserializationError::IncompleteInput;
return false;
return readString(str, size);
}
DeserializationError readString(VariantData &variant, size_t n) {
bool readString(VariantData &variant, size_t n) {
const char *s = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
DeserializationError err = readString(s, n);
if (!err)
variant.setOwnedString(make_not_null(s));
return err;
if (!readString(s, n))
return false;
variant.setString(make_not_null(s),
typename TStringStorage::storage_policy());
return true;
}
DeserializationError readString(const char *&result, size_t n) {
StringBuilder builder = _stringStorage.startString();
bool readString(const char *&result, size_t n) {
_stringStorage.startString(_pool);
for (; n; --n) {
uint8_t c;
if (!readBytes(c))
return DeserializationError::IncompleteInput;
builder.append(static_cast<char>(c));
return false;
_stringStorage.append(static_cast<char>(c));
}
result = builder.complete();
if (!result)
return DeserializationError::NoMemory;
return DeserializationError::Ok;
_stringStorage.append('\0');
if (!_stringStorage.isValid()) {
_error = DeserializationError::NoMemory;
return false;
}
result = _stringStorage.save(_pool);
return true;
}
template <typename TSize>
DeserializationError readArray(CollectionData &array,
NestingLimit nestingLimit) {
bool readArray(CollectionData &array, NestingLimit nestingLimit) {
TSize size;
if (!readInteger(size))
return DeserializationError::IncompleteInput;
return false;
return readArray(array, size, nestingLimit);
}
DeserializationError readArray(CollectionData &array, size_t n,
NestingLimit nestingLimit) {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool readArray(CollectionData &array, size_t n, NestingLimit nestingLimit) {
if (nestingLimit.reached()) {
_error = DeserializationError::TooDeep;
return false;
}
for (; n; --n) {
VariantData *value = array.addElement(_pool);
if (!value)
return DeserializationError::NoMemory;
if (!value) {
_error = DeserializationError::NoMemory;
return false;
}
DeserializationError err = parse(*value, nestingLimit.decrement());
if (err)
return err;
if (!parseVariant(*value, nestingLimit.decrement()))
return false;
}
return DeserializationError::Ok;
return true;
}
template <typename TSize>
DeserializationError readObject(CollectionData &object,
NestingLimit nestingLimit) {
bool readObject(CollectionData &object, NestingLimit nestingLimit) {
TSize size;
if (!readInteger(size))
return DeserializationError::IncompleteInput;
return false;
return readObject(object, size, nestingLimit);
}
DeserializationError readObject(CollectionData &object, size_t n,
NestingLimit nestingLimit) {
if (nestingLimit.reached())
return DeserializationError::TooDeep;
bool readObject(CollectionData &object, size_t n, NestingLimit nestingLimit) {
if (nestingLimit.reached()) {
_error = DeserializationError::TooDeep;
return false;
}
for (; n; --n) {
VariantSlot *slot = object.addSlot(_pool);
if (!slot)
return DeserializationError::NoMemory;
if (!slot) {
_error = DeserializationError::NoMemory;
return false;
}
const char *key = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
DeserializationError err = parseKey(key);
if (err)
return err;
slot->setOwnedKey(make_not_null(key));
if (!parseKey(key))
return false;
err = parse(*slot->data(), nestingLimit.decrement());
if (err)
return err;
slot->setKey(key, typename TStringStorage::storage_policy());
if (!parseVariant(*slot->data(), nestingLimit.decrement()))
return false;
}
return DeserializationError::Ok;
return true;
}
DeserializationError parseKey(const char *&key) {
bool parseKey(const char *&key) {
uint8_t code;
if (!readByte(code))
return DeserializationError::IncompleteInput;
return false;
if ((code & 0xe0) == 0xa0)
return readString(key, code & 0x1f);
@ -333,13 +337,15 @@ class MsgPackDeserializer {
return readString<uint32_t>(key);
default:
return DeserializationError::NotSupported;
_error = DeserializationError::NotSupported;
return false;
}
}
MemoryPool *_pool;
TReader _reader;
TStringStorage _stringStorage;
DeserializationError _error;
};
template <typename TInput>

View File

@ -9,23 +9,41 @@
#ifndef ARDUINOJSON_NAMESPACE
#define ARDUINOJSON_DO_CONCAT(A, B) A##B
#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_DO_CONCAT(A, B)
#define ARDUINOJSON_HEX_DIGIT_0000() 0
#define ARDUINOJSON_HEX_DIGIT_0001() 1
#define ARDUINOJSON_HEX_DIGIT_0010() 2
#define ARDUINOJSON_HEX_DIGIT_0011() 3
#define ARDUINOJSON_HEX_DIGIT_0100() 4
#define ARDUINOJSON_HEX_DIGIT_0101() 5
#define ARDUINOJSON_HEX_DIGIT_0110() 6
#define ARDUINOJSON_HEX_DIGIT_0111() 7
#define ARDUINOJSON_HEX_DIGIT_1000() 8
#define ARDUINOJSON_HEX_DIGIT_1001() 9
#define ARDUINOJSON_HEX_DIGIT_1010() A
#define ARDUINOJSON_HEX_DIGIT_1011() B
#define ARDUINOJSON_HEX_DIGIT_1100() C
#define ARDUINOJSON_HEX_DIGIT_1101() D
#define ARDUINOJSON_HEX_DIGIT_1110() E
#define ARDUINOJSON_HEX_DIGIT_1111() F
#define ARDUINOJSON_HEX_DIGIT_(A, B, C, D) ARDUINOJSON_HEX_DIGIT_##A##B##C##D()
#define ARDUINOJSON_HEX_DIGIT(A, B, C, D) ARDUINOJSON_HEX_DIGIT_(A, B, C, D)
#define ARDUINOJSON_CONCAT_(A, B) A##B
#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B)
#define ARDUINOJSON_CONCAT4(A, B, C, D) \
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D))
#define ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, H) \
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \
ARDUINOJSON_CONCAT4(E, F, G, H))
#define ARDUINOJSON_CONCAT12(A, B, C, D, E, F, G, H, I, J, K, L) \
ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, \
ARDUINOJSON_CONCAT4(H, I, J, ARDUINOJSON_CONCAT2(K, L)))
#define ARDUINOJSON_NAMESPACE \
ARDUINOJSON_CONCAT12( \
ArduinoJson, ARDUINOJSON_VERSION_MAJOR, ARDUINOJSON_VERSION_MINOR, \
ARDUINOJSON_VERSION_REVISION, _, ARDUINOJSON_USE_LONG_LONG, \
ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_DECODE_UNICODE, \
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_ENABLE_COMMENTS)
#define ARDUINOJSON_NAMESPACE \
ARDUINOJSON_CONCAT4( \
ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \
ARDUINOJSON_VERSION_MINOR, \
ARDUINOJSON_VERSION_REVISION), \
_, \
ARDUINOJSON_HEX_DIGIT(0, ARDUINOJSON_USE_LONG_LONG, \
ARDUINOJSON_USE_DOUBLE, \
ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \
ARDUINOJSON_HEX_DIGIT( \
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE))
#endif

View File

@ -0,0 +1,121 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Numbers/Integer.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
namespace ARDUINOJSON_NAMESPACE {
enum CompareResult {
COMPARE_RESULT_DIFFER = 0,
COMPARE_RESULT_EQUAL = 1,
COMPARE_RESULT_GREATER = 2,
COMPARE_RESULT_LESS = 4,
COMPARE_RESULT_GREATER_OR_EQUAL = 3,
COMPARE_RESULT_LESS_OR_EQUAL = 5
};
template <typename T>
CompareResult arithmeticCompare(const T &lhs, const T &rhs) {
if (lhs < rhs)
return COMPARE_RESULT_LESS;
else if (lhs > rhs)
return COMPARE_RESULT_GREATER;
else
return COMPARE_RESULT_EQUAL;
}
template <typename T1, typename T2>
CompareResult arithmeticCompare(
const T1 &lhs, const T2 &rhs,
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
sizeof(T1) < sizeof(T2),
int // Using int instead of void to avoid C2572 on
// Visual Studio 2012, 2013, and 2015
>::type * = 0) {
return arithmeticCompare<T2>(static_cast<T2>(lhs), rhs);
}
template <typename T1, typename T2>
CompareResult arithmeticCompare(
const T1 &lhs, const T2 &rhs,
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
sizeof(T2) < sizeof(T1)>::type * = 0) {
return arithmeticCompare<T1>(lhs, static_cast<T1>(rhs));
}
template <typename T1, typename T2>
CompareResult arithmeticCompare(
const T1 &lhs, const T2 &rhs,
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
is_signed<T1>::value == is_signed<T2>::value &&
sizeof(T2) == sizeof(T1)>::type * = 0) {
return arithmeticCompare<T1>(lhs, static_cast<T1>(rhs));
}
template <typename T1, typename T2>
CompareResult arithmeticCompare(
const T1 &lhs, const T2 &rhs,
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
is_unsigned<T1>::value && is_signed<T2>::value &&
sizeof(T2) == sizeof(T1)>::type * = 0) {
if (rhs < 0)
return COMPARE_RESULT_GREATER;
return arithmeticCompare<T1>(lhs, static_cast<T1>(rhs));
}
template <typename T1, typename T2>
CompareResult arithmeticCompare(
const T1 &lhs, const T2 &rhs,
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
is_signed<T1>::value && is_unsigned<T2>::value &&
sizeof(T2) == sizeof(T1)>::type * = 0) {
if (lhs < 0)
return COMPARE_RESULT_LESS;
return arithmeticCompare<T2>(static_cast<T2>(lhs), rhs);
}
template <typename T1, typename T2>
CompareResult arithmeticCompare(
const T1 &lhs, const T2 &rhs,
typename enable_if<is_floating_point<T1>::value ||
is_floating_point<T2>::value>::type * = 0) {
return arithmeticCompare<double>(static_cast<double>(lhs),
static_cast<double>(rhs));
}
template <typename T2>
CompareResult arithmeticCompareNegateLeft(
UInt, const T2 &, typename enable_if<is_unsigned<T2>::value>::type * = 0) {
return COMPARE_RESULT_LESS;
}
template <typename T2>
CompareResult arithmeticCompareNegateLeft(
UInt lhs, const T2 &rhs,
typename enable_if<is_signed<T2>::value>::type * = 0) {
if (rhs > 0)
return COMPARE_RESULT_LESS;
return arithmeticCompare(-rhs, static_cast<T2>(lhs));
}
template <typename T1>
CompareResult arithmeticCompareNegateRight(
const T1 &, UInt, typename enable_if<is_unsigned<T1>::value>::type * = 0) {
return COMPARE_RESULT_GREATER;
}
template <typename T1>
CompareResult arithmeticCompareNegateRight(
const T1 &lhs, UInt rhs,
typename enable_if<is_signed<T1>::value>::type * = 0) {
if (lhs > 0)
return COMPARE_RESULT_GREATER;
return arithmeticCompare(static_cast<T1>(rhs), -lhs);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -13,6 +13,8 @@ template <typename T>
inline T parseFloat(const char* s) {
// try to reuse the same parameters as JsonDeserializer
typedef typename choose_largest<Float, T>::type TFloat;
return parseNumber<TFloat, UInt>(s).template as<T>();
ParsedNumber<TFloat, UInt> value;
parseNumber(s, value);
return value.template as<T>();
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -14,6 +14,8 @@ T parseInteger(const char *s) {
// try to reuse the same parameters as JsonDeserializer
typedef typename choose_largest<UInt, typename make_unsigned<T>::type>::type
TUInt;
return parseNumber<Float, TUInt>(s).template as<T>();
ParsedNumber<Float, TUInt> value;
parseNumber(s, value);
return value.template as<T>();
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -16,14 +16,18 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TFloat, typename TUInt>
struct ParsedNumber {
ParsedNumber() : uintValue(0), floatValue(0), _type(VALUE_IS_NULL) {}
ParsedNumber() : _type(VALUE_IS_NULL) {}
ParsedNumber(TUInt value, bool is_negative)
: uintValue(value),
floatValue(TFloat(value)),
_type(uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER
: VALUE_IS_POSITIVE_INTEGER)) {}
ParsedNumber(TFloat value) : floatValue(value), _type(VALUE_IS_FLOAT) {}
void setInteger(TUInt value, bool is_negative) {
uintValue = value;
_type = uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER
: VALUE_IS_POSITIVE_INTEGER);
}
void setFloat(TFloat value) {
floatValue = value;
_type = VALUE_IS_FLOAT;
}
template <typename T>
T as() const {
@ -43,21 +47,22 @@ struct ParsedNumber {
return _type;
}
TUInt uintValue;
TFloat floatValue;
union {
TUInt uintValue;
TFloat floatValue;
};
uint8_t _type;
};
}; // namespace ARDUINOJSON_NAMESPACE
template <typename A, typename B>
struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {};
template <typename TFloat, typename TUInt>
inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
inline void parseNumber(const char* s, ParsedNumber<TFloat, TUInt>& result) {
typedef FloatTraits<TFloat> traits;
typedef typename choose_largest<typename traits::mantissa_type, TUInt>::type
mantissa_t;
typedef typename traits::exponent_type exponent_t;
typedef ParsedNumber<TFloat, TUInt> return_type;
ARDUINOJSON_ASSERT(s != 0);
@ -73,17 +78,22 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
}
#if ARDUINOJSON_ENABLE_NAN
if (*s == 'n' || *s == 'N')
return traits::nan();
if (*s == 'n' || *s == 'N') {
result.setFloat(traits::nan());
return;
}
#endif
#if ARDUINOJSON_ENABLE_INFINITY
if (*s == 'i' || *s == 'I')
return is_negative ? -traits::inf() : traits::inf();
if (*s == 'i' || *s == 'I') {
result.setFloat(is_negative ? -traits::inf() : traits::inf());
return;
}
#endif
if (!isdigit(*s) && *s != '.')
return return_type();
return;
mantissa_t mantissa = 0;
exponent_t exponent_offset = 0;
@ -100,8 +110,10 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
s++;
}
if (*s == '\0')
return return_type(TUInt(mantissa), is_negative);
if (*s == '\0') {
result.setInteger(TUInt(mantissa), is_negative);
return;
}
// avoid mantissa overflow
while (mantissa > traits::mantissa_max) {
@ -141,9 +153,10 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
exponent = exponent * 10 + (*s - '0');
if (exponent + exponent_offset > traits::exponent_max) {
if (negative_exponent)
return is_negative ? -0.0f : 0.0f;
result.setFloat(is_negative ? -0.0f : 0.0f);
else
return is_negative ? -traits::inf() : traits::inf();
result.setFloat(is_negative ? -traits::inf() : traits::inf());
return;
}
s++;
}
@ -154,10 +167,11 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
// we should be at the end of the string, otherwise it's an error
if (*s != '\0')
return return_type();
return;
TFloat result = traits::make_float(static_cast<TFloat>(mantissa), exponent);
TFloat final_result =
traits::make_float(static_cast<TFloat>(mantissa), exponent);
return is_negative ? -result : result;
result.setFloat(is_negative ? -final_result : final_result);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -5,9 +5,10 @@
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Operators/VariantOperators.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantRef.hpp>
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp>
#ifdef _MSC_VER
@ -19,6 +20,7 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TObject, typename TStringRef>
class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
public VariantShortcuts<MemberProxy<TObject, TStringRef> >,
public Visitable {
typedef MemberProxy<TObject, TStringRef> this_type;
@ -54,14 +56,6 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
return *this;
}
FORCE_INLINE bool operator==(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamMember()) == rhs;
}
FORCE_INLINE bool operator!=(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamMember()) != rhs;
}
FORCE_INLINE void clear() const {
getUpstreamMember().clear();
}

View File

@ -1,255 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Misc/Visitable.hpp>
#include <ArduinoJson/Numbers/Float.hpp>
#include <ArduinoJson/Numbers/Integer.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/IsString.hpp>
namespace ARDUINOJSON_NAMESPACE {
class CollectionData;
template <typename T, typename Enable = void>
struct Comparer;
template <typename T>
struct Comparer<T, typename enable_if<IsString<T>::value>::type> {
T rhs;
int result;
explicit Comparer(T value) : rhs(value), result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float) {}
void visitString(const char *lhs) {
result = -adaptString(rhs).compare(lhs);
}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool) {}
void visitNull() {
result = adaptString(rhs).compare(NULL);
}
};
template <typename T>
typename enable_if<is_signed<T>::value, int>::type sign(const T &value) {
return value < 0 ? -1 : value > 0 ? 1 : 0;
}
template <typename T>
typename enable_if<is_unsigned<T>::value, int>::type sign(const T &value) {
return value > 0 ? 1 : 0;
}
template <typename T>
struct Comparer<T, typename enable_if<is_integral<T>::value ||
is_floating_point<T>::value>::type> {
T rhs;
int result;
explicit Comparer(T value) : rhs(value), result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float lhs) {
result = sign(lhs - static_cast<Float>(rhs));
}
void visitString(const char *) {}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt lhs) {
result = -sign(static_cast<T>(lhs) + rhs);
}
void visitPositiveInteger(UInt lhs) {
result = static_cast<T>(lhs) < rhs ? -1 : static_cast<T>(lhs) > rhs ? 1 : 0;
}
void visitBoolean(bool) {}
void visitNull() {}
};
template <>
struct Comparer<bool, void> {
bool rhs;
int result;
explicit Comparer(bool value) : rhs(value), result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float) {}
void visitString(const char *) {}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool lhs) {
result = static_cast<int>(lhs - rhs);
}
void visitNull() {}
};
#if ARDUINOJSON_HAS_NULLPTR
template <>
struct Comparer<decltype(nullptr), void> {
int result;
explicit Comparer(decltype(nullptr)) : result(1) {}
void visitArray(const CollectionData &) {}
void visitObject(const CollectionData &) {}
void visitFloat(Float) {}
void visitString(const char *) {}
void visitRawJson(const char *, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool) {}
void visitNull() {
result = 0;
}
};
#endif
template <typename TVariant>
class VariantComparisons {
private:
template <typename T>
static int compare(TVariant lhs, const T &rhs) {
Comparer<T> comparer(rhs);
lhs.accept(comparer);
return comparer.result;
}
public:
// value == TVariant
template <typename T>
friend bool operator==(T *lhs, TVariant rhs) {
return compare(rhs, lhs) == 0;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
const T &lhs, TVariant rhs) {
return compare(rhs, lhs) == 0;
}
// TVariant == value
template <typename T>
friend bool operator==(TVariant lhs, T *rhs) {
return compare(lhs, rhs) == 0;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
TVariant lhs, const T &rhs) {
return compare(lhs, rhs) == 0;
}
// value != TVariant
template <typename T>
friend bool operator!=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) != 0;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator!=(
const T &lhs, TVariant rhs) {
return compare(rhs, lhs) != 0;
}
// TVariant != value
template <typename T>
friend bool operator!=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) != 0;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator!=(
TVariant lhs, const T &rhs) {
return compare(lhs, rhs) != 0;
}
// value < TVariant
template <typename T>
friend bool operator<(T *lhs, TVariant rhs) {
return compare(rhs, lhs) > 0;
}
template <typename T>
friend bool operator<(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) > 0;
}
// TVariant < value
template <typename T>
friend bool operator<(TVariant lhs, T *rhs) {
return compare(lhs, rhs) < 0;
}
template <typename T>
friend bool operator<(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) < 0;
}
// value <= TVariant
template <typename T>
friend bool operator<=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) >= 0;
}
template <typename T>
friend bool operator<=(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) >= 0;
}
// TVariant <= value
template <typename T>
friend bool operator<=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) <= 0;
}
template <typename T>
friend bool operator<=(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) <= 0;
}
// value > TVariant
template <typename T>
friend bool operator>(T *lhs, TVariant rhs) {
return compare(rhs, lhs) < 0;
}
template <typename T>
friend bool operator>(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) < 0;
}
// TVariant > value
template <typename T>
friend bool operator>(TVariant lhs, T *rhs) {
return compare(lhs, rhs) > 0;
}
template <typename T>
friend bool operator>(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) > 0;
}
// value >= TVariant
template <typename T>
friend bool operator>=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) <= 0;
}
template <typename T>
friend bool operator>=(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) <= 0;
}
// TVariant >= value
template <typename T>
friend bool operator>=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) >= 0;
}
template <typename T>
friend bool operator>=(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) >= 0;
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -1,17 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Operators/VariantComparisons.hpp>
#include <ArduinoJson/Operators/VariantOr.hpp>
#include <ArduinoJson/Operators/VariantShortcuts.hpp>
namespace ARDUINOJSON_NAMESPACE {
template <typename TImpl>
class VariantOperators : public VariantComparisons<TImpl>,
public VariantOr<TImpl>,
public VariantShortcuts<TImpl> {};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -1,37 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantAs.hpp>
namespace ARDUINOJSON_NAMESPACE {
template <typename TImpl>
class VariantOr {
public:
// Returns the default value if the VariantRef is undefined of incompatible
template <typename T>
T operator|(const T &defaultValue) const {
if (impl()->template is<T>())
return impl()->template as<T>();
else
return defaultValue;
}
// Returns the default value if the VariantRef is undefined of incompatible
// Special case for string: null is treated as undefined
const char *operator|(const char *defaultValue) const {
const char *value = impl()->template as<const char *>();
return value ? value : defaultValue;
}
private:
const TImpl *impl() const {
return static_cast<const TImpl *>(this);
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -12,6 +12,11 @@
#pragma warning(disable : 4244)
#endif
#ifdef __ICCARM__
// Suppress IAR Compiler Warning[Pa093]: implicit conversion from floating point to integer
#pragma diag_suppress=Pa093
#endif
namespace ARDUINOJSON_NAMESPACE {
template <typename From, typename To>
@ -32,3 +37,7 @@ struct is_convertible {
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef __ICCARM__
#pragma diag_default=Pa093
#endif

View File

@ -16,8 +16,7 @@ template <typename T>
struct is_enum {
static const bool value = is_convertible<T, int>::value &&
!is_class<T>::value && !is_integral<T>::value &&
!is_floating_point<T>::value &&
!is_same<T, bool>::value;
!is_floating_point<T>::value;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -25,9 +25,7 @@ struct is_integral {
is_same<T, signed __int64>::value ||
is_same<T, unsigned __int64>::value ||
#endif
is_same<T, char>::value;
// CAUTION: differs from std::is_integral as it doesn't include bool
is_same<T, char>::value || is_same<T, bool>::value;
};
template <typename T>

View File

@ -5,25 +5,54 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
namespace ARDUINOJSON_NAMESPACE {
class StringCopier {
public:
typedef ARDUINOJSON_NAMESPACE::StringBuilder StringBuilder;
StringCopier(MemoryPool* pool) : _pool(pool) {}
StringBuilder startString() {
return StringBuilder(_pool);
void startString(MemoryPool* pool) {
pool->getFreeZone(&_ptr, &_capacity);
_size = 0;
}
void reclaim(const char* s) {
_pool->reclaimLastString(s);
const char* save(MemoryPool* pool) {
ARDUINOJSON_ASSERT(_ptr);
return pool->saveStringFromFreeZone(_size);
}
void append(const char* s) {
while (*s) append(*s++);
}
void append(const char* s, size_t n) {
while (n-- > 0) append(*s++);
}
void append(char c) {
if (!_ptr)
return;
if (_size >= _capacity) {
_ptr = 0;
return;
}
_ptr[_size++] = c;
}
bool isValid() {
return _ptr != 0;
}
const char* c_str() {
return _ptr;
}
typedef storage_policies::store_by_copy storage_policy;
private:
MemoryPool* _pool;
char* _ptr;
size_t _size;
size_t _capacity;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -5,41 +5,38 @@
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
namespace ARDUINOJSON_NAMESPACE {
class StringMover {
public:
class StringBuilder {
public:
StringBuilder(char** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
StringMover(char* ptr) : _writePtr(ptr) {}
void append(char c) {
*(*_writePtr)++ = char(c);
}
char* complete() const {
*(*_writePtr)++ = 0;
return _startPtr;
}
private:
char** _writePtr;
char* _startPtr;
};
StringMover(char* ptr) : _ptr(ptr) {}
StringBuilder startString() {
return StringBuilder(&_ptr);
void startString(MemoryPool*) {
_startPtr = _writePtr;
}
// recover memory from last string
void reclaim(const char* str) {
_ptr = const_cast<char*>(str);
const char* save(MemoryPool*) const {
return _startPtr;
}
void append(char c) {
*_writePtr++ = c;
}
bool isValid() const {
return true;
}
const char* c_str() const {
return _startPtr;
}
typedef storage_policies::store_by_address storage_policy;
private:
char* _ptr;
char* _writePtr;
char* _startPtr;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -13,8 +13,8 @@ template <typename TInput, typename Enable = void>
struct StringStorage {
typedef StringCopier type;
static type create(MemoryPool& pool, TInput&) {
return type(&pool);
static type create(TInput&) {
return type();
}
};
@ -23,20 +23,18 @@ struct StringStorage<TChar*,
typename enable_if<!is_const<TChar>::value>::type> {
typedef StringMover type;
static type create(MemoryPool&, TChar* input) {
static type create(TChar* input) {
return type(reinterpret_cast<char*>(input));
}
};
template <typename TInput>
typename StringStorage<TInput>::type makeStringStorage(MemoryPool& pool,
TInput& input) {
return StringStorage<TInput>::create(pool, input);
typename StringStorage<TInput>::type makeStringStorage(TInput& input) {
return StringStorage<TInput>::create(input);
}
template <typename TChar>
typename StringStorage<TChar*>::type makeStringStorage(MemoryPool& pool,
TChar* input) {
return StringStorage<TChar*>::create(pool, input);
typename StringStorage<TChar*>::type makeStringStorage(TChar* input) {
return StringStorage<TChar*>::create(input);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -16,14 +16,8 @@ class ArduinoStringAdapter {
public:
ArduinoStringAdapter(const ::String& str) : _str(&str) {}
char* save(MemoryPool* pool) const {
if (isNull())
return NULL;
size_t n = _str->length() + 1;
char* dup = pool->allocFrozenString(n);
if (dup)
memcpy(dup, _str->c_str(), n);
return dup;
void copyTo(char* p, size_t n) const {
memcpy(p, _str->c_str(), n);
}
bool isNull() const {
@ -45,7 +39,11 @@ class ArduinoStringAdapter {
return _str->length();
}
typedef storage_policy::store_by_copy storage_policy;
const char* begin() const {
return _str->c_str();
}
typedef storage_policies::store_by_copy storage_policy;
private:
const ::String* _str;

View File

@ -39,12 +39,22 @@ class ConstRamStringAdapter {
return _str;
}
typedef storage_policy::store_by_address storage_policy;
const char* begin() const {
return _str;
}
typedef storage_policies::store_by_address storage_policy;
protected:
const char* _str;
};
template <>
struct IsString<const char*> : true_type {};
template <int N>
struct IsString<const char[N]> : true_type {};
inline ConstRamStringAdapter adaptString(const char* str) {
return ConstRamStringAdapter(str);
}

View File

@ -4,8 +4,8 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Polyfills/pgmspace.hpp>
#include <ArduinoJson/Strings/FlashStringIterator.hpp>
#include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
@ -33,14 +33,8 @@ class FlashStringAdapter {
return !_str;
}
char* save(MemoryPool* pool) const {
if (!_str)
return NULL;
size_t n = size() + 1; // copy the terminator
char* dup = pool->allocFrozenString(n);
if (dup)
memcpy_P(dup, reinterpret_cast<const char*>(_str), n);
return dup;
void copyTo(char* p, size_t n) const {
memcpy_P(p, reinterpret_cast<const char*>(_str), n);
}
size_t size() const {
@ -49,7 +43,11 @@ class FlashStringAdapter {
return strlen_P(reinterpret_cast<const char*>(_str));
}
typedef storage_policy::store_by_copy storage_policy;
FlashStringIterator begin() const {
return FlashStringIterator(_str);
}
typedef storage_policies::store_by_copy storage_policy;
private:
const __FlashStringHelper* _str;

View File

@ -0,0 +1,44 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
namespace ARDUINOJSON_NAMESPACE {
class FlashStringIterator {
public:
explicit FlashStringIterator(const __FlashStringHelper* ptr)
: _ptr(reinterpret_cast<const char*>(ptr)) {}
explicit FlashStringIterator(const char* ptr) : _ptr(ptr) {}
FlashStringIterator operator+(ptrdiff_t d) const {
return FlashStringIterator(_ptr + d);
}
ptrdiff_t operator-(FlashStringIterator other) const {
return _ptr - other._ptr;
}
FlashStringIterator operator++(int) {
return FlashStringIterator(_ptr++);
}
FlashStringIterator operator++() {
return FlashStringIterator(++_ptr);
}
bool operator!=(FlashStringIterator other) const {
return _ptr != other._ptr;
}
char operator*() const {
return char(pgm_read_byte(_ptr));
}
private:
const char* _ptr;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -4,7 +4,6 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
#include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
@ -15,17 +14,11 @@ class RamStringAdapter : public ConstRamStringAdapter {
public:
RamStringAdapter(const char* str) : ConstRamStringAdapter(str) {}
char* save(MemoryPool* pool) const {
if (!_str)
return NULL;
size_t n = size() + 1;
char* dup = pool->allocFrozenString(n);
if (dup)
memcpy(dup, _str, n);
return dup;
void copyTo(char* p, size_t n) const {
memcpy(p, _str, n);
}
typedef ARDUINOJSON_NAMESPACE::storage_policy::store_by_copy storage_policy;
typedef ARDUINOJSON_NAMESPACE::storage_policies::store_by_copy storage_policy;
};
template <typename TChar>

View File

@ -5,6 +5,7 @@
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Strings/FlashStringIterator.hpp>
#include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
@ -33,20 +34,19 @@ class SizedFlashStringAdapter {
return !_str;
}
char* save(MemoryPool* pool) const {
if (!_str)
return NULL;
char* dup = pool->allocFrozenString(_size);
if (dup)
memcpy_P(dup, reinterpret_cast<const char*>(_str), _size);
return dup;
void copyTo(char* p, size_t n) const {
memcpy_P(p, reinterpret_cast<const char*>(_str), n);
}
size_t size() const {
return _size;
}
typedef storage_policy::store_by_copy storage_policy;
FlashStringIterator begin() const {
return FlashStringIterator(_str);
}
typedef storage_policies::store_by_copy storage_policy;
private:
const __FlashStringHelper* _str;

View File

@ -28,20 +28,19 @@ class SizedRamStringAdapter {
return !_str;
}
char* save(MemoryPool* pool) const {
if (!_str)
return NULL;
char* dup = pool->allocFrozenString(_size);
if (dup)
memcpy(dup, _str, _size);
return dup;
void copyTo(char* p, size_t n) const {
memcpy(p, _str, n);
}
size_t size() const {
return _size;
}
typedef storage_policy::store_by_copy storage_policy;
const char* begin() const {
return _str;
}
typedef storage_policies::store_by_copy storage_policy;
private:
const char* _str;

View File

@ -4,7 +4,6 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
@ -18,12 +17,8 @@ class StlStringAdapter {
public:
StlStringAdapter(const TString& str) : _str(&str) {}
char* save(MemoryPool* pool) const {
size_t n = _str->length() + 1;
char* dup = pool->allocFrozenString(n);
if (dup)
memcpy(dup, _str->c_str(), n);
return dup;
void copyTo(char* p, size_t n) const {
memcpy(p, _str->c_str(), n);
}
bool isNull() const {
@ -46,7 +41,11 @@ class StlStringAdapter {
return _str->size();
}
typedef storage_policy::store_by_copy storage_policy;
const char* begin() const {
return _str->c_str();
}
typedef storage_policies::store_by_copy storage_policy;
private:
const TString* _str;

View File

@ -6,10 +6,10 @@
namespace ARDUINOJSON_NAMESPACE {
namespace storage_policy {
namespace storage_policies {
struct store_by_address {};
struct store_by_copy {};
struct decide_at_runtime {};
} // namespace storage_policy
} // namespace storage_policies
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -38,7 +38,15 @@ class String {
return strcmp(lhs._data, rhs._data) == 0;
}
typedef storage_policy::decide_at_runtime storage_policy;
friend bool operator!=(String lhs, String rhs) {
if (lhs._data == rhs._data)
return false;
if (!lhs._data)
return true;
if (!rhs._data)
return true;
return strcmp(lhs._data, rhs._data) != 0;
}
private:
const char* _data;
@ -54,11 +62,7 @@ class StringAdapter : public RamStringAdapter {
return _isStatic;
}
const char* save(MemoryPool* pool) const {
if (_isStatic)
return data();
return RamStringAdapter::save(pool);
}
typedef storage_policies::decide_at_runtime storage_policy;
private:
bool _isStatic;

View File

@ -18,30 +18,30 @@ inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) {
template <typename TAdaptedString>
inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool,
storage_policy::decide_at_runtime) {
storage_policies::decide_at_runtime) {
if (key.isStatic()) {
return slotSetKey(var, key, pool, storage_policy::store_by_address());
return slotSetKey(var, key, pool, storage_policies::store_by_address());
} else {
return slotSetKey(var, key, pool, storage_policy::store_by_copy());
return slotSetKey(var, key, pool, storage_policies::store_by_copy());
}
}
template <typename TAdaptedString>
inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool*,
storage_policy::store_by_address) {
storage_policies::store_by_address) {
ARDUINOJSON_ASSERT(var);
var->setLinkedKey(make_not_null(key.data()));
var->setKey(key.data(), storage_policies::store_by_address());
return true;
}
template <typename TAdaptedString>
inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool,
storage_policy::store_by_copy) {
const char* dup = key.save(pool);
storage_policies::store_by_copy) {
const char* dup = pool->saveString(key);
if (!dup)
return false;
ARDUINOJSON_ASSERT(var);
var->setOwnedKey(make_not_null(dup));
var->setKey(dup, storage_policies::store_by_copy());
return true;
}

View File

@ -53,8 +53,9 @@ struct VariantConstAs<ArrayRef> {
// ---
template <typename T>
inline typename enable_if<is_integral<T>::value, T>::type variantAs(
const VariantData* data) {
inline typename enable_if<is_integral<T>::value && !is_same<bool, T>::value,
T>::type
variantAs(const VariantData* data) {
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
return data != 0 ? data->asIntegral<T>() : T(0);
}

View File

@ -0,0 +1,241 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Misc/Visitable.hpp>
#include <ArduinoJson/Numbers/arithmeticCompare.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/IsString.hpp>
namespace ARDUINOJSON_NAMESPACE {
class CollectionData;
struct ComparerBase {
CompareResult result;
ComparerBase() : result(COMPARE_RESULT_DIFFER) {}
void visitArray(const CollectionData &) {}
void visitBoolean(bool) {}
void visitFloat(Float) {}
void visitNegativeInteger(UInt) {}
void visitNull() {}
void visitObject(const CollectionData &) {}
void visitPositiveInteger(UInt) {}
void visitRawJson(const char *, size_t) {}
void visitString(const char *) {}
};
template <typename T, typename Enable = void>
struct Comparer;
template <typename T>
struct Comparer<T, typename enable_if<IsString<T>::value>::type>
: ComparerBase {
T rhs;
explicit Comparer(T value) : rhs(value) {}
void visitString(const char *lhs) {
int i = adaptString(rhs).compare(lhs);
if (i < 0)
result = COMPARE_RESULT_GREATER;
else if (i > 0)
result = COMPARE_RESULT_LESS;
else
result = COMPARE_RESULT_EQUAL;
}
void visitNull() {
if (adaptString(rhs).isNull())
result = COMPARE_RESULT_EQUAL;
}
};
template <typename T>
struct Comparer<T, typename enable_if<is_integral<T>::value ||
is_floating_point<T>::value>::type>
: ComparerBase {
T rhs;
explicit Comparer(T value) : rhs(value) {}
void visitFloat(Float lhs) {
result = arithmeticCompare(lhs, rhs);
}
void visitNegativeInteger(UInt lhs) {
result = arithmeticCompareNegateLeft(lhs, rhs);
}
void visitPositiveInteger(UInt lhs) {
result = arithmeticCompare(lhs, rhs);
}
void visitBoolean(bool lhs) {
visitPositiveInteger(static_cast<UInt>(lhs));
}
};
struct NullComparer : ComparerBase {
void visitNull() {
result = COMPARE_RESULT_EQUAL;
}
};
#if ARDUINOJSON_HAS_NULLPTR
template <>
struct Comparer<decltype(nullptr), void> : NullComparer {
explicit Comparer(decltype(nullptr)) : NullComparer() {}
};
#endif
struct ArrayComparer : ComparerBase {
const CollectionData *_rhs;
explicit ArrayComparer(const CollectionData &rhs) : _rhs(&rhs) {}
void visitArray(const CollectionData &lhs) {
if (lhs.equalsArray(*_rhs))
result = COMPARE_RESULT_EQUAL;
}
};
struct NegativeIntegerComparer : ComparerBase {
UInt _rhs;
explicit NegativeIntegerComparer(UInt rhs) : _rhs(rhs) {}
void visitFloat(Float lhs) {
result = arithmeticCompareNegateRight(lhs, _rhs);
}
void visitNegativeInteger(UInt lhs) {
result = arithmeticCompare(_rhs, lhs);
}
void visitPositiveInteger(UInt) {
result = COMPARE_RESULT_GREATER;
}
void visitBoolean(bool) {
result = COMPARE_RESULT_GREATER;
}
};
struct ObjectComparer : ComparerBase {
const CollectionData *_rhs;
explicit ObjectComparer(const CollectionData &rhs) : _rhs(&rhs) {}
void visitObject(const CollectionData &lhs) {
if (lhs.equalsObject(*_rhs))
result = COMPARE_RESULT_EQUAL;
}
};
struct RawComparer : ComparerBase {
const char *_rhsData;
size_t _rhsSize;
explicit RawComparer(const char *rhsData, size_t rhsSize)
: _rhsData(rhsData), _rhsSize(rhsSize) {}
void visitRawJson(const char *lhsData, size_t lhsSize) {
size_t size = _rhsSize < lhsSize ? _rhsSize : lhsSize;
int n = memcmp(lhsData, _rhsData, size);
if (n < 0)
result = COMPARE_RESULT_LESS;
else if (n > 0)
result = COMPARE_RESULT_GREATER;
else
result = COMPARE_RESULT_EQUAL;
}
};
template <typename T>
struct Comparer<T, typename enable_if<IsVisitable<T>::value>::type>
: ComparerBase {
T rhs;
explicit Comparer(T value) : rhs(value) {}
void visitArray(const CollectionData &lhs) {
ArrayComparer comparer(lhs);
accept(comparer);
}
void visitObject(const CollectionData &lhs) {
ObjectComparer comparer(lhs);
accept(comparer);
}
void visitFloat(Float lhs) {
Comparer<Float> comparer(lhs);
accept(comparer);
}
void visitString(const char *lhs) {
Comparer<const char *> comparer(lhs);
accept(comparer);
}
void visitRawJson(const char *lhsData, size_t lhsSize) {
RawComparer comparer(lhsData, lhsSize);
accept(comparer);
}
void visitNegativeInteger(UInt lhs) {
NegativeIntegerComparer comparer(lhs);
accept(comparer);
}
void visitPositiveInteger(UInt lhs) {
Comparer<UInt> comparer(lhs);
accept(comparer);
}
void visitBoolean(bool lhs) {
Comparer<bool> comparer(lhs);
accept(comparer);
}
void visitNull() {
NullComparer comparer;
accept(comparer);
}
private:
template <typename TComparer>
void accept(TComparer &comparer) {
rhs.accept(comparer);
switch (comparer.result) {
case COMPARE_RESULT_GREATER:
result = COMPARE_RESULT_LESS;
break;
case COMPARE_RESULT_LESS:
result = COMPARE_RESULT_GREATER;
break;
default:
result = comparer.result;
break;
}
}
};
template <typename T1, typename T2>
CompareResult compare(const T1 &lhs, const T2 &rhs) {
Comparer<T2> comparer(rhs);
lhs.accept(comparer);
return comparer.result;
}
inline int variantCompare(const VariantData *a, const VariantData *b) {
return compare(VariantConstRef(a), VariantConstRef(b));
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -4,6 +4,7 @@
#pragma once
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Polyfills/gsl/not_null.hpp>
@ -100,7 +101,7 @@ class VariantData {
case VALUE_IS_OBJECT:
return toObject().copyFrom(src._content.asCollection, pool);
case VALUE_IS_OWNED_STRING:
return setOwnedString(RamStringAdapter(src._content.asString), pool);
return setString(RamStringAdapter(src._content.asString), pool);
case VALUE_IS_OWNED_RAW:
return setOwnedRaw(
serialized(src._content.asRaw.data, src._content.asRaw.size), pool);
@ -111,42 +112,6 @@ class VariantData {
}
}
bool equals(const VariantData &other) const {
// Check that variant have the same type, but ignore string ownership
if ((type() | VALUE_IS_OWNED) != (other.type() | VALUE_IS_OWNED))
return false;
switch (type()) {
case VALUE_IS_LINKED_STRING:
case VALUE_IS_OWNED_STRING:
return !strcmp(_content.asString, other._content.asString);
case VALUE_IS_LINKED_RAW:
case VALUE_IS_OWNED_RAW:
return _content.asRaw.size == other._content.asRaw.size &&
!memcmp(_content.asRaw.data, other._content.asRaw.data,
_content.asRaw.size);
case VALUE_IS_BOOLEAN:
case VALUE_IS_POSITIVE_INTEGER:
case VALUE_IS_NEGATIVE_INTEGER:
return _content.asInteger == other._content.asInteger;
case VALUE_IS_ARRAY:
return _content.asCollection.equalsArray(other._content.asCollection);
case VALUE_IS_OBJECT:
return _content.asCollection.equalsObject(other._content.asCollection);
case VALUE_IS_FLOAT:
return _content.asFloat == other._content.asFloat;
case VALUE_IS_NULL:
default:
return true;
}
}
bool isArray() const {
return (_flags & VALUE_IS_ARRAY) != 0;
}
@ -227,7 +192,7 @@ class VariantData {
template <typename T>
bool setOwnedRaw(SerializedValue<T> value, MemoryPool *pool) {
char *dup = adaptString(value.data(), value.size()).save(pool);
const char *dup = pool->saveString(adaptString(value.data(), value.size()));
if (dup) {
setType(VALUE_IS_OWNED_RAW);
_content.asRaw.data = dup;
@ -273,27 +238,24 @@ class VariantData {
_content.asInteger = value;
}
void setLinkedString(const char *value) {
if (value) {
setType(VALUE_IS_LINKED_STRING);
_content.asString = value;
} else {
setType(VALUE_IS_NULL);
}
}
void setNull() {
setType(VALUE_IS_NULL);
}
void setOwnedString(not_null<const char *> s) {
void setString(not_null<const char *> s, storage_policies::store_by_copy) {
setType(VALUE_IS_OWNED_STRING);
_content.asString = s.get();
}
bool setOwnedString(const char *s) {
void setString(not_null<const char *> s, storage_policies::store_by_address) {
setType(VALUE_IS_LINKED_STRING);
_content.asString = s.get();
}
template <typename TStoragePolicy>
bool setString(const char *s, TStoragePolicy storage_policy) {
if (s) {
setOwnedString(make_not_null(s));
setString(make_not_null(s), storage_policy);
return true;
} else {
setType(VALUE_IS_NULL);
@ -301,9 +263,31 @@ class VariantData {
}
}
template <typename T>
bool setOwnedString(T value, MemoryPool *pool) {
return setOwnedString(value.save(pool));
template <typename TAdaptedString>
bool setString(TAdaptedString value, MemoryPool *pool) {
return setString(value, pool, typename TAdaptedString::storage_policy());
}
template <typename TAdaptedString>
inline bool setString(TAdaptedString value, MemoryPool *pool,
storage_policies::decide_at_runtime) {
if (value.isStatic())
return setString(value, pool, storage_policies::store_by_address());
else
return setString(value, pool, storage_policies::store_by_copy());
}
template <typename TAdaptedString>
inline bool setString(TAdaptedString value, MemoryPool *,
storage_policies::store_by_address) {
return setString(value.data(), storage_policies::store_by_address());
}
template <typename TAdaptedString>
inline bool setString(TAdaptedString value, MemoryPool *pool,
storage_policies::store_by_copy) {
return setString(pool->saveString(value),
storage_policies::store_by_copy());
}
CollectionData &toArray() {

View File

@ -40,13 +40,7 @@ inline bool variantCopyFrom(VariantData *dst, const VariantData *src,
return dst->copyFrom(*src, pool);
}
inline bool variantEquals(const VariantData *a, const VariantData *b) {
if (a == b)
return true;
if (!a || !b)
return false;
return a->equals(*b);
}
inline int variantCompare(const VariantData *a, const VariantData *b);
inline bool variantIsArray(const VariantData *var) {
return var && var->isArray();
@ -105,29 +99,18 @@ inline bool variantSetOwnedRaw(VariantData *var, SerializedValue<T> value,
return var != 0 && var->setOwnedRaw(value, pool);
}
inline bool variantSetLinkedString(VariantData *var, const char *value) {
if (!var)
return false;
var->setLinkedString(value);
return true;
}
inline void variantSetNull(VariantData *var) {
if (!var)
return;
var->setNull();
}
inline bool variantSetOwnedString(VariantData *var, char *value) {
template <typename TAdaptedString>
inline bool variantSetString(VariantData *var, TAdaptedString value,
MemoryPool *pool) {
if (!var)
return false;
var->setOwnedString(value);
return true;
}
template <typename T>
inline bool variantSetOwnedString(VariantData *var, T value, MemoryPool *pool) {
return var != 0 && var->setOwnedString(value, pool);
return var->setString(value, pool);
}
template <typename T>

View File

@ -0,0 +1,163 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Misc/Visitable.hpp>
#include <ArduinoJson/Numbers/arithmeticCompare.hpp>
#include <ArduinoJson/Polyfills/attributes.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Variant/VariantAs.hpp>
namespace ARDUINOJSON_NAMESPACE {
template <typename T1, typename T2>
CompareResult compare(const T1 &lhs, const T2 &rhs); // VariantCompare.cpp
template <typename TVariant>
struct VariantOperators {
// Returns the default value if the VariantRef is undefined of incompatible
template <typename T>
friend T operator|(const TVariant &variant, const T &defaultValue) {
if (variant.template is<T>())
return variant.template as<T>();
else
return defaultValue;
}
// Returns the default value if the VariantRef is undefined of incompatible
// Special case for string: null is treated as undefined
friend const char *operator|(const TVariant &variant,
const char *defaultValue) {
const char *value = variant.template as<const char *>();
return value ? value : defaultValue;
}
// value == TVariant
template <typename T>
friend bool operator==(T *lhs, TVariant rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_EQUAL;
}
template <typename T>
friend bool operator==(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_EQUAL;
}
// TVariant == value
template <typename T>
friend bool operator==(TVariant lhs, T *rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_EQUAL;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
TVariant lhs, const T &rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_EQUAL;
}
// value != TVariant
template <typename T>
friend bool operator!=(T *lhs, TVariant rhs) {
return compare(rhs, lhs) != COMPARE_RESULT_EQUAL;
}
template <typename T>
friend bool operator!=(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) != COMPARE_RESULT_EQUAL;
}
// TVariant != value
template <typename T>
friend bool operator!=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) != COMPARE_RESULT_EQUAL;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator!=(
TVariant lhs, const T &rhs) {
return compare(lhs, rhs) != COMPARE_RESULT_EQUAL;
}
// value < TVariant
template <typename T>
friend bool operator<(T *lhs, TVariant rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_GREATER;
}
template <typename T>
friend bool operator<(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_GREATER;
}
// TVariant < value
template <typename T>
friend bool operator<(TVariant lhs, T *rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_LESS;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator<(
TVariant lhs, const T &rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_LESS;
}
// value <= TVariant
template <typename T>
friend bool operator<=(T *lhs, TVariant rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
}
template <typename T>
friend bool operator<=(const T &lhs, TVariant rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
}
// TVariant <= value
template <typename T>
friend bool operator<=(TVariant lhs, T *rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator<=(
TVariant lhs, const T &rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
}
// value > TVariant
template <typename T>
friend bool operator>(T *lhs, TVariant rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_LESS;
}
template <typename T>
friend bool operator>(const T &lhs, TVariant rhs) {
return compare(rhs, lhs) == COMPARE_RESULT_LESS;
}
// TVariant > value
template <typename T>
friend bool operator>(TVariant lhs, T *rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_GREATER;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator>(
TVariant lhs, const T &rhs) {
return compare(lhs, rhs) == COMPARE_RESULT_GREATER;
}
// value >= TVariant
template <typename T>
friend bool operator>=(T *lhs, TVariant rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
}
template <typename T>
friend bool operator>=(const T &lhs, TVariant rhs) {
return (compare(rhs, lhs) & COMPARE_RESULT_LESS_OR_EQUAL) != 0;
}
// TVariant >= value
template <typename T>
friend bool operator>=(TVariant lhs, T *rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
}
template <typename T>
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator>=(
TVariant lhs, const T &rhs) {
return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -9,11 +9,13 @@
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Misc/Visitable.hpp>
#include <ArduinoJson/Operators/VariantOperators.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantAs.hpp>
#include <ArduinoJson/Variant/VariantFunctions.hpp>
#include <ArduinoJson/Variant/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantRef.hpp>
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
namespace ARDUINOJSON_NAMESPACE {
@ -41,8 +43,10 @@ class VariantRefBase {
// bool is<unsigned int>() const;
// bool is<unsigned long>() const;
template <typename T>
FORCE_INLINE typename enable_if<is_integral<T>::value, bool>::type is()
const {
FORCE_INLINE
typename enable_if<is_integral<T>::value && !is_same<bool, T>::value,
bool>::type
is() const {
return variantIsInteger<T>(_data);
}
//
@ -141,6 +145,7 @@ class VariantRefBase {
// - a reference to a ArrayRef or ObjectRef
class VariantRef : public VariantRefBase<VariantData>,
public VariantOperators<VariantRef>,
public VariantShortcuts<VariantRef>,
public Visitable {
typedef VariantRefBase<VariantData> base_type;
friend class VariantConstRef;
@ -183,7 +188,8 @@ class VariantRef : public VariantRefBase<VariantData>,
// set(unsigned long)
template <typename T>
FORCE_INLINE bool set(
T value, typename enable_if<is_integral<T>::value>::type * = 0) const {
T value, typename enable_if<is_integral<T>::value &&
!is_same<bool, T>::value>::type * = 0) const {
return variantSetInteger<T>(_data, value);
}
@ -208,20 +214,15 @@ class VariantRef : public VariantRefBase<VariantData>,
FORCE_INLINE bool set(
const T &value,
typename enable_if<IsString<T>::value>::type * = 0) const {
return variantSetOwnedString(_data, adaptString(value), _pool);
return variantSetString(_data, adaptString(value), _pool);
}
// set(char*)
// set(const __FlashStringHelper*)
// set(const char*)
template <typename T>
FORCE_INLINE bool set(
T *value, typename enable_if<IsString<T *>::value>::type * = 0) const {
return variantSetOwnedString(_data, adaptString(value), _pool);
}
// set(const char*);
FORCE_INLINE bool set(const char *value) const {
return variantSetLinkedString(_data, value);
return variantSetString(_data, adaptString(value), _pool);
}
// set(VariantRef)
@ -242,6 +243,14 @@ class VariantRef : public VariantRefBase<VariantData>,
return variantSetInteger(_data, static_cast<Integer>(value));
}
#if ARDUINOJSON_HAS_NULLPTR
// set(nullptr_t)
FORCE_INLINE bool set(decltype(nullptr)) const {
variantSetNull(_data);
return true;
}
#endif
template <typename T>
FORCE_INLINE typename VariantAs<T>::type as() const {
return variantAs<typename VariantAs<T>::type>(_data, _pool);
@ -257,14 +266,6 @@ class VariantRef : public VariantRefBase<VariantData>,
variantAccept(_data, visitor);
}
FORCE_INLINE bool operator==(VariantRef lhs) const {
return variantEquals(_data, lhs._data);
}
FORCE_INLINE bool operator!=(VariantRef lhs) const {
return !variantEquals(_data, lhs._data);
}
// Change the type of the variant
//
// ArrayRef to<ArrayRef>()
@ -336,6 +337,7 @@ class VariantRef : public VariantRefBase<VariantData>,
class VariantConstRef : public VariantRefBase<const VariantData>,
public VariantOperators<VariantConstRef>,
public VariantShortcuts<VariantConstRef>,
public Visitable {
typedef VariantRefBase<const VariantData> base_type;
friend class VariantRef;
@ -401,13 +403,5 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
operator[](TChar *key) const {
return getMember(key);
}
FORCE_INLINE bool operator==(VariantConstRef lhs) const {
return variantEquals(_data, lhs._data);
}
FORCE_INLINE bool operator!=(VariantConstRef lhs) const {
return !variantEquals(_data, lhs._data);
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -4,12 +4,13 @@
#pragma once
#include <stdint.h> // int8_t, int16_t
#include <ArduinoJson/Polyfills/gsl/not_null.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>
#include <stdint.h> // int8_t, int16_t
namespace ARDUINOJSON_NAMESPACE {
typedef conditional<sizeof(void*) <= 2, int8_t, int16_t>::type VariantSlotDiff;
@ -69,14 +70,16 @@ class VariantSlot {
_next = VariantSlotDiff(slot - this);
}
void setOwnedKey(not_null<const char*> k) {
void setKey(const char* k, storage_policies::store_by_copy) {
ARDUINOJSON_ASSERT(k != NULL);
_flags |= KEY_IS_OWNED;
_key = k.get();
_key = k;
}
void setLinkedKey(not_null<const char*> k) {
void setKey(const char* k, storage_policies::store_by_address) {
ARDUINOJSON_ASSERT(k != NULL);
_flags &= VALUE_MASK;
_key = k.get();
_key = k;
}
const char* key() const {

View File

@ -4,7 +4,7 @@
#pragma once
#define ARDUINOJSON_VERSION "6.15.2"
#define ARDUINOJSON_VERSION "6.16.1"
#define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 15
#define ARDUINOJSON_VERSION_REVISION 2
#define ARDUINOJSON_VERSION_MINOR 16
#define ARDUINOJSON_VERSION_REVISION 1