mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-27 01:07:30 +02:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
1360b6a396 | |||
16e51b83ab | |||
aa7cc5351c | |||
30da920135 | |||
bcdf5b7e52 | |||
c711fe592d | |||
9d5c1b3742 | |||
6b26cd0977 | |||
10ec0f21b0 | |||
ff66182dc6 | |||
d02ce2f1bb | |||
2664a2d0da | |||
1f7350658e | |||
41132b701b | |||
712005219c | |||
0bd17aff8a | |||
f3f44d7812 | |||
8385d5fa3a | |||
726f8be341 | |||
fee029b86e | |||
c3504ddf0a | |||
6a878ee444 | |||
c4ec2ba88f | |||
c907ca6e5d | |||
8993a093e9 | |||
d04669d0cc | |||
05fc136915 | |||
8d37939086 | |||
6d2ad4539f | |||
5ab53f42b2 | |||
f448e805e9 | |||
67aa8efd5a | |||
61a5273aea | |||
35a39b8d8f | |||
96b6571352 | |||
74e7dd053f | |||
1f8636d762 | |||
e4cfa701d8 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -10,3 +10,7 @@
|
||||
/extras/fuzzing/*_fuzzer.options
|
||||
/extras/fuzzing/*_fuzzer_seed_corpus.zip
|
||||
.vs/
|
||||
|
||||
# Used by CI for Particle
|
||||
/src/*.ino
|
||||
/project.properties
|
||||
|
35
.travis.yml
35
.travis.yml
@ -21,17 +21,17 @@ matrix:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.8']
|
||||
env: SCRIPT=test _CC=gcc-4.8 _CXX=g++-4.8 SANITIZE=address
|
||||
env: SCRIPT=test _CC=gcc-4.8 _CXX=g++-4.8
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-4.9']
|
||||
env: SCRIPT=test _CC=gcc-4.9 _CXX=g++-4.9 SANITIZE=leak
|
||||
env: SCRIPT=test _CC=gcc-4.9 _CXX=g++-4.9
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-5']
|
||||
env: SCRIPT=test _CC=gcc-5 _CXX=g++-5 # SANITIZE=undefined
|
||||
env: SCRIPT=test _CC=gcc-5 _CXX=g++-5
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
@ -41,17 +41,17 @@ matrix:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-7']
|
||||
env: SCRIPT=test _CC=gcc-7 _CXX=g++-7
|
||||
env: SCRIPT=test _CC=gcc-7 _CXX=g++-7 CXXFLAGS="-fsanitize=leak"
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-8']
|
||||
env: SCRIPT=test _CC=gcc-8 _CXX=g++-8
|
||||
env: SCRIPT=test _CC=gcc-8 _CXX=g++-8 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fuse-ld=gold"
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['g++-9']
|
||||
env: SCRIPT=test _CC=gcc-9 _CXX=g++-9
|
||||
env: SCRIPT=test _CC=gcc-9 _CXX=g++-9 CXXFLAGS="-fsanitize=address"
|
||||
- addons:
|
||||
apt:
|
||||
packages: ['g++-arm-linux-gnueabihf']
|
||||
@ -61,12 +61,12 @@ matrix:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5']
|
||||
packages: ['clang-3.5']
|
||||
env: SCRIPT=test _CC=clang-3.5 _CXX=clang++-3.5 SANITIZE=address
|
||||
env: SCRIPT=test _CC=clang-3.5 _CXX=clang++-3.5 CXXFLAGS="-fsanitize=address"
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.6']
|
||||
packages: ['clang-3.6']
|
||||
env: SCRIPT=test _CC=clang-3.6 _CXX=clang++-3.6 SANITIZE=leak
|
||||
env: SCRIPT=test _CC=clang-3.6 _CXX=clang++-3.6 CXXFLAGS="-fsanitize=leak"
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.7']
|
||||
@ -76,7 +76,7 @@ matrix:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.8']
|
||||
packages: ['clang-3.8']
|
||||
env: SCRIPT=test _CC=clang-3.8 _CXX=clang++-3.8 SANITIZE=undefined
|
||||
env: SCRIPT=test _CC=clang-3.8 _CXX=clang++-3.8 CXXFLAGS="-fsanitize=undefined"
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-3.9']
|
||||
@ -123,25 +123,12 @@ matrix:
|
||||
env: SCRIPT=test
|
||||
- os: osx
|
||||
osx_image: xcode10
|
||||
env: SCRIPT=test SANITIZE=address
|
||||
env: SCRIPT=test CXXFLAGS="-fsanitize=address"
|
||||
- env: SCRIPT=arduino VERSION=1.6.7 BOARD=arduino:avr:uno
|
||||
- env: SCRIPT=arduino VERSION=1.8.2 BOARD=arduino:samd:mkr1000
|
||||
- env: SCRIPT=platformio BOARD=uno
|
||||
- env: SCRIPT=platformio BOARD=esp01
|
||||
- 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','llvm-9']
|
||||
env: SCRIPT=fuzz CLANG=9 FUZZER=json
|
||||
- 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','llvm-9']
|
||||
env: SCRIPT=fuzz CLANG=9 FUZZER=msgpack
|
||||
- env: SCRIPT=particle BOARD=argon
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,12 +1,45 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v6.17.2 (2020-11-14)
|
||||
-------
|
||||
|
||||
* Fixed invalid conversion error in `operator|(JsonVariant, char*)` (issue #1432)
|
||||
* Changed the default value of `ARDUINOJSON_ENABLE_PROGMEM` (issue #1433).
|
||||
It now checks that the `pgm_read_XXX` macros are defined before enabling `PROGMEM`.
|
||||
|
||||
v6.17.1 (2020-11-07)
|
||||
-------
|
||||
|
||||
* Fixed error `ambiguous overload for 'operator|'` (issue #1411)
|
||||
* Fixed `operator|(MemberProxy, JsonObject)` (issue #1415)
|
||||
* Allowed more than 32767 values in non-embedded mode (issue #1414)
|
||||
|
||||
v6.17.0 (2020-10-19)
|
||||
-------
|
||||
|
||||
* Added a build failure when nullptr is defined as a macro (issue #1355)
|
||||
* Added `JsonDocument::overflowed()` which tells if the memory pool was too small (issue #1358)
|
||||
* Added `DeserializationError::EmptyInput` which tells if the input was empty
|
||||
* Added `DeserializationError::f_str()` which returns a `const __FlashStringHelper*` (issue #846)
|
||||
* Added `operator|(JsonVariantConst, JsonVariantConst)`
|
||||
* Added filtering for MessagePack (issue #1298, PR #1394 by Luca Passarella)
|
||||
* Moved float convertion tables to PROGMEM
|
||||
* Fixed `JsonVariant::set((char*)0)` which returned false instead of true (issue #1368)
|
||||
* Fixed error `No such file or directory #include <WString.h>` (issue #1381)
|
||||
|
||||
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!=`
|
||||
* Added wildcard key (`*`) for filters (issue #1309)
|
||||
* 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)
|
||||
|
@ -2,9 +2,9 @@
|
||||
# Copyright Benoit Blanchon 2014-2020
|
||||
# MIT License
|
||||
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
project(ArduinoJson VERSION 6.15.1)
|
||||
project(ArduinoJson VERSION 6.17.2)
|
||||
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
include(CTest)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.16.0)
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.17.2)
|
||||
[](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
|
||||
[](https://travis-ci.org/bblanchon/ArduinoJson)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: 6.16.0.{build}
|
||||
version: 6.17.2.{build}
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
|
@ -82,7 +82,7 @@ void setup() {
|
||||
DeserializationError error = deserializeJson(doc, client);
|
||||
if (error) {
|
||||
Serial.print(F("deserializeJson() failed: "));
|
||||
Serial.println(error.c_str());
|
||||
Serial.println(error.f_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ void setup() {
|
||||
// Test if parsing succeeds.
|
||||
if (error) {
|
||||
Serial.print(F("deserializeJson() failed: "));
|
||||
Serial.println(error.c_str());
|
||||
Serial.println(error.f_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ void setup() {
|
||||
// Test if parsing succeeded.
|
||||
if (error) {
|
||||
Serial.print("deserializeMsgPack() failed: ");
|
||||
Serial.println(error.c_str());
|
||||
Serial.println(error.f_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8)
|
||||
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8) AND (NOT ${COVERAGE}))
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
@ -70,7 +70,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0)
|
||||
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0) AND (NOT ${COVERAGE}))
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
@ -78,7 +78,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0)
|
||||
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0) AND (NOT ${COVERAGE}))
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
|
12
extras/ci/particle.sh
Executable file
12
extras/ci/particle.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
cd "$(dirname "$0")/../../"
|
||||
|
||||
npm install -g particle-cli
|
||||
|
||||
particle login -t "${PARTICLE_TOKEN}"
|
||||
|
||||
cp extras/particle/src/smocktest.ino src/
|
||||
cp extras/particle/project.properties ./
|
||||
|
||||
particle compile "$BOARD"
|
@ -3,8 +3,6 @@
|
||||
export CC="$_CC"
|
||||
export CXX="$_CXX"
|
||||
|
||||
[ -n "$SANITIZE" ] && export CXXFLAGS="-fsanitize=$SANITIZE"
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug .
|
||||
cmake --build .
|
||||
ctest --output-on-failure .
|
||||
|
@ -22,22 +22,25 @@ target_link_libraries(json_reproducer
|
||||
ArduinoJson
|
||||
)
|
||||
|
||||
macro(add_fuzzer name)
|
||||
set(FUZZER "${name}_fuzzer")
|
||||
# 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}"
|
||||
"${FUZZER}.cpp"
|
||||
"${name}_fuzzer.cpp"
|
||||
)
|
||||
target_link_libraries("${FUZZER}"
|
||||
ArduinoJson
|
||||
)
|
||||
set_target_properties("${FUZZER}"
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS
|
||||
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all"
|
||||
COMPILE_FLAGS
|
||||
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
|
||||
LINK_FLAGS
|
||||
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all"
|
||||
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
|
||||
)
|
||||
|
||||
add_test(
|
||||
@ -46,9 +49,29 @@ macro(add_fuzzer name)
|
||||
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_EQUAL 6)
|
||||
add_fuzzer(json)
|
||||
add_fuzzer(msgpack)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
|
||||
add_fuzzer(json address)
|
||||
add_fuzzer(json undefined)
|
||||
add_fuzzer(msgpack address)
|
||||
add_fuzzer(msgpack undefined)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7)
|
||||
# We're getting false positive with Clang 6
|
||||
add_fuzzer(json memory)
|
||||
add_fuzzer(msgpack memory)
|
||||
endif()
|
||||
|
1
extras/particle/project.properties
Normal file
1
extras/particle/project.properties
Normal file
@ -0,0 +1 @@
|
||||
name=ArduinoJsonCI
|
5
extras/particle/src/smocktest.ino
Normal file
5
extras/particle/src/smocktest.ino
Normal file
@ -0,0 +1,5 @@
|
||||
#include "ArduinoJson.h"
|
||||
|
||||
void setup() {}
|
||||
|
||||
void loop() {}
|
@ -10,6 +10,7 @@ rm -f $OUTPUT
|
||||
|
||||
# create zip
|
||||
7z a $OUTPUT \
|
||||
-xr!.vs \
|
||||
ArduinoJson/CHANGELOG.md \
|
||||
ArduinoJson/examples \
|
||||
ArduinoJson/src \
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ add_subdirectory(catch)
|
||||
link_libraries(ArduinoJson catch)
|
||||
|
||||
include_directories(Helpers)
|
||||
add_subdirectory(ElementProxy)
|
||||
add_subdirectory(FailingBuilds)
|
||||
add_subdirectory(IntegrationTests)
|
||||
add_subdirectory(JsonArray)
|
||||
@ -16,7 +15,6 @@ add_subdirectory(JsonDocument)
|
||||
add_subdirectory(JsonObject)
|
||||
add_subdirectory(JsonSerializer)
|
||||
add_subdirectory(JsonVariant)
|
||||
add_subdirectory(MemberProxy)
|
||||
add_subdirectory(MemoryPool)
|
||||
add_subdirectory(Misc)
|
||||
add_subdirectory(MixedConfiguration)
|
||||
|
@ -1,15 +0,0 @@
|
||||
# ArduinoJson - arduinojson.org
|
||||
# Copyright Benoit Blanchon 2014-2020
|
||||
# MIT License
|
||||
|
||||
add_executable(ElementProxyTests
|
||||
add.cpp
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
add_test(ElementProxy ElementProxyTests)
|
@ -1,34 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::add()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("add(int)") {
|
||||
ep.add(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
|
||||
SECTION("add(const char*)") {
|
||||
ep.add("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
|
||||
}
|
||||
|
||||
SECTION("set(char[])") {
|
||||
char s[] = "world";
|
||||
ep.add(s);
|
||||
strcpy(s, "!!!!!");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::clear()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("size goes back to zero") {
|
||||
ep.add(42);
|
||||
ep.clear();
|
||||
|
||||
REQUIRE(ep.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("isNull() return true") {
|
||||
ep.add("hello");
|
||||
ep.clear();
|
||||
|
||||
REQUIRE(ep.isNull() == true);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::operator==()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
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("1 vs 2") {
|
||||
doc.add(1);
|
||||
doc.add(2);
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::remove()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("remove(int)") {
|
||||
ep.add(1);
|
||||
ep.add(2);
|
||||
ep.add(3);
|
||||
|
||||
ep.remove(1);
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "[1,3]");
|
||||
}
|
||||
|
||||
SECTION("remove(const char *)") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
|
||||
ep.remove("a");
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "{\"b\":2}");
|
||||
}
|
||||
|
||||
SECTION("remove(std::string)") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
|
||||
ep.remove(std::string("b"));
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("remove(vla)") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
|
||||
int i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "b");
|
||||
ep.remove(vla);
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::set()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("set(int)") {
|
||||
ep.set(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
}
|
||||
|
||||
SECTION("set(const char*)") {
|
||||
ep.set("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[\"world\"]");
|
||||
}
|
||||
|
||||
SECTION("set(char[])") {
|
||||
char s[] = "world";
|
||||
ep.set(s);
|
||||
strcpy(s, "!!!!!");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[\"world\"]");
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::size()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("returns 0") {
|
||||
REQUIRE(ep.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("as an array, returns 2") {
|
||||
ep.add(1);
|
||||
ep.add(2);
|
||||
REQUIRE(ep.size() == 2);
|
||||
}
|
||||
|
||||
SECTION("as an object, returns 2") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
REQUIRE(ep.size() == 2);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::operator[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
ElementProxy<JsonDocument&> ep = doc[1];
|
||||
|
||||
SECTION("set member") {
|
||||
ep["world"] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[null,{\"world\":42}]");
|
||||
}
|
||||
|
||||
SECTION("set element") {
|
||||
ep[2] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[null,[null,null,42]]");
|
||||
}
|
||||
}
|
8
extras/tests/Helpers/Arduino.h
Normal file
8
extras/tests/Helpers/Arduino.h
Normal file
@ -0,0 +1,8 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/Stream.h"
|
||||
#include "api/String.h"
|
@ -5,6 +5,8 @@
|
||||
#include <stdint.h> // uint8_t
|
||||
#include <string.h> // strcmp, strlen...
|
||||
|
||||
#define PROGMEM
|
||||
|
||||
class __FlashStringHelper;
|
||||
|
||||
inline const void* convertPtrToFlash(const void* s) {
|
||||
@ -15,9 +17,26 @@ inline const void* convertFlashToPtr(const void* s) {
|
||||
return reinterpret_cast<const char*>(s) - 42;
|
||||
}
|
||||
|
||||
#define F(X) reinterpret_cast<const __FlashStringHelper*>(convertPtrToFlash(X))
|
||||
#define FC(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
|
||||
#define PSTR(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
|
||||
#define F(X) reinterpret_cast<const __FlashStringHelper*>(PSTR(X))
|
||||
|
||||
inline uint8_t pgm_read_byte(const void* p) {
|
||||
return *reinterpret_cast<const uint8_t*>(convertFlashToPtr(p));
|
||||
}
|
||||
|
||||
inline void* pgm_read_ptr(const void* p) {
|
||||
return *reinterpret_cast<void* const*>(convertFlashToPtr(p));
|
||||
}
|
||||
|
||||
inline float pgm_read_float(const void* p) {
|
||||
return *reinterpret_cast<const float*>(convertFlashToPtr(p));
|
||||
}
|
||||
|
||||
inline uint32_t pgm_read_dword(const void* p) {
|
||||
return *reinterpret_cast<const uint32_t*>(convertFlashToPtr(p));
|
||||
}
|
||||
|
||||
#define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \
|
||||
static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = value; \
|
||||
static type const* name = reinterpret_cast<type const*>( \
|
||||
convertPtrToFlash(ARDUINOJSON_CONCAT2(name, _progmem)));
|
||||
|
@ -30,20 +30,22 @@ void testBoolification(DeserializationError error, bool expected) {
|
||||
TEST_CASE("DeserializationError") {
|
||||
SECTION("c_str()") {
|
||||
TEST_STRINGIFICATION(Ok);
|
||||
TEST_STRINGIFICATION(TooDeep);
|
||||
TEST_STRINGIFICATION(NoMemory);
|
||||
TEST_STRINGIFICATION(InvalidInput);
|
||||
TEST_STRINGIFICATION(EmptyInput);
|
||||
TEST_STRINGIFICATION(IncompleteInput);
|
||||
TEST_STRINGIFICATION(InvalidInput);
|
||||
TEST_STRINGIFICATION(NoMemory);
|
||||
TEST_STRINGIFICATION(NotSupported);
|
||||
TEST_STRINGIFICATION(TooDeep);
|
||||
}
|
||||
|
||||
SECTION("as boolean") {
|
||||
TEST_BOOLIFICATION(Ok, false);
|
||||
TEST_BOOLIFICATION(TooDeep, true);
|
||||
TEST_BOOLIFICATION(NoMemory, true);
|
||||
TEST_BOOLIFICATION(InvalidInput, true);
|
||||
TEST_BOOLIFICATION(EmptyInput, true);
|
||||
TEST_BOOLIFICATION(IncompleteInput, true);
|
||||
TEST_BOOLIFICATION(InvalidInput, true);
|
||||
TEST_BOOLIFICATION(NoMemory, true);
|
||||
TEST_BOOLIFICATION(NotSupported, true);
|
||||
TEST_BOOLIFICATION(TooDeep, true);
|
||||
}
|
||||
|
||||
SECTION("ostream DeserializationError") {
|
||||
@ -58,13 +60,6 @@ TEST_CASE("DeserializationError") {
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("out of range") {
|
||||
int code = 666;
|
||||
DeserializationError err(
|
||||
*reinterpret_cast<DeserializationError::Code*>(&code));
|
||||
REQUIRE(err.c_str() == std::string("???"));
|
||||
}
|
||||
|
||||
SECTION("switch") {
|
||||
DeserializationError err = DeserializationError::InvalidInput;
|
||||
switch (err.code()) {
|
||||
|
@ -62,6 +62,15 @@ TEST_CASE("Filtering") {
|
||||
"null",
|
||||
0
|
||||
},
|
||||
{
|
||||
// Member is a string, but filter wants an array
|
||||
"{\"example\":\"example\"}",
|
||||
"{\"example\":[true]}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":null}",
|
||||
JSON_OBJECT_SIZE(1) + 8
|
||||
},
|
||||
{
|
||||
// Input is an array, but filter wants an object
|
||||
"[\"hello\",\"world\"]",
|
||||
@ -214,6 +223,15 @@ TEST_CASE("Filtering") {
|
||||
"{\"example\":{\"outcome\":42}}",
|
||||
2 * JSON_OBJECT_SIZE(1) + 16
|
||||
},
|
||||
{
|
||||
// wildcard
|
||||
"{\"example\":{\"type\":\"int\",\"outcome\":42}}",
|
||||
"{\"*\":{\"outcome\":true}}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":{\"outcome\":42}}",
|
||||
2 * JSON_OBJECT_SIZE(1) + 16
|
||||
},
|
||||
{
|
||||
// only the first element of array counts
|
||||
"[1,2,3]",
|
||||
|
@ -27,7 +27,13 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
|
||||
SECTION("Empty input") {
|
||||
DeserializationError err = deserializeJson(doc, "");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Only spaces") {
|
||||
DeserializationError err = deserializeJson(doc, " \t\n\r");
|
||||
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("issue #628") {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,11 @@ add_executable(JsonDocumentTests
|
||||
containsKey.cpp
|
||||
createNested.cpp
|
||||
DynamicJsonDocument.cpp
|
||||
ElementProxy.cpp
|
||||
isNull.cpp
|
||||
MemberProxy.cpp
|
||||
nesting.cpp
|
||||
overflowed.cpp
|
||||
remove.cpp
|
||||
shrinkToFit.cpp
|
||||
size.cpp
|
||||
|
206
extras/tests/JsonDocument/ElementProxy.cpp
Normal file
206
extras/tests/JsonDocument/ElementProxy.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("ElementProxy::add()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("add(int)") {
|
||||
ep.add(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
|
||||
SECTION("add(const char*)") {
|
||||
ep.add("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
|
||||
}
|
||||
|
||||
SECTION("set(char[])") {
|
||||
char s[] = "world";
|
||||
ep.add(s);
|
||||
strcpy(s, "!!!!!");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::clear()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("size goes back to zero") {
|
||||
ep.add(42);
|
||||
ep.clear();
|
||||
|
||||
REQUIRE(ep.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("isNull() return true") {
|
||||
ep.add("hello");
|
||||
ep.clear();
|
||||
|
||||
REQUIRE(ep.isNull() == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::operator==()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
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("1 vs 2") {
|
||||
doc.add(1);
|
||||
doc.add(2);
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::remove()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("remove(int)") {
|
||||
ep.add(1);
|
||||
ep.add(2);
|
||||
ep.add(3);
|
||||
|
||||
ep.remove(1);
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "[1,3]");
|
||||
}
|
||||
|
||||
SECTION("remove(const char *)") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
|
||||
ep.remove("a");
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "{\"b\":2}");
|
||||
}
|
||||
|
||||
SECTION("remove(std::string)") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
|
||||
ep.remove(std::string("b"));
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("remove(vla)") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
|
||||
int i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "b");
|
||||
ep.remove(vla);
|
||||
|
||||
REQUIRE(ep.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::set()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("set(int)") {
|
||||
ep.set(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
}
|
||||
|
||||
SECTION("set(const char*)") {
|
||||
ep.set("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[\"world\"]");
|
||||
}
|
||||
|
||||
SECTION("set(char[])") {
|
||||
char s[] = "world";
|
||||
ep.set(s);
|
||||
strcpy(s, "!!!!!");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[\"world\"]");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::size()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc.addElement();
|
||||
ElementProxy<JsonDocument&> ep = doc[0];
|
||||
|
||||
SECTION("returns 0") {
|
||||
REQUIRE(ep.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("as an array, returns 2") {
|
||||
ep.add(1);
|
||||
ep.add(2);
|
||||
REQUIRE(ep.size() == 2);
|
||||
}
|
||||
|
||||
SECTION("as an object, returns 2") {
|
||||
ep["a"] = 1;
|
||||
ep["b"] = 2;
|
||||
REQUIRE(ep.size() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::operator[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
ElementProxy<JsonDocument&> ep = doc[1];
|
||||
|
||||
SECTION("set member") {
|
||||
ep["world"] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[null,{\"world\":42}]");
|
||||
}
|
||||
|
||||
SECTION("set element") {
|
||||
ep[2] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[null,[null,null,42]]");
|
||||
}
|
||||
}
|
247
extras/tests/JsonDocument/MemberProxy.cpp
Normal file
247
extras/tests/JsonDocument/MemberProxy.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::add()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("add(int)") {
|
||||
mp.add(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("add(const char*)") {
|
||||
mp.add("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::clear()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("size goes back to zero") {
|
||||
mp.add(42);
|
||||
mp.clear();
|
||||
|
||||
REQUIRE(mp.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("isNull() return true") {
|
||||
mp.add("hello");
|
||||
mp.clear();
|
||||
|
||||
REQUIRE(mp.isNull() == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::operator==()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
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("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"]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::containsKey()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey(std::string("key")) == true);
|
||||
REQUIRE(mp.containsKey(std::string("key")) == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::operator|()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
SECTION("const char*") {
|
||||
doc["a"] = "hello";
|
||||
|
||||
REQUIRE((doc["a"] | "world") == std::string("hello"));
|
||||
REQUIRE((doc["b"] | "world") == std::string("world"));
|
||||
}
|
||||
|
||||
SECTION("Issue #1411") {
|
||||
doc["sensor"] = "gps";
|
||||
|
||||
const char *test = "test"; // <- the literal must be captured in a variable
|
||||
// to trigger the bug
|
||||
const char *sensor = doc["sensor"] | test; // "gps"
|
||||
|
||||
REQUIRE(sensor == std::string("gps"));
|
||||
}
|
||||
|
||||
SECTION("Issue #1415") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["hello"] = "world";
|
||||
|
||||
StaticJsonDocument<0> emptyDoc;
|
||||
JsonObject anotherObject = object["hello"] | emptyDoc.to<JsonObject>();
|
||||
|
||||
REQUIRE(anotherObject.isNull() == false);
|
||||
REQUIRE(anotherObject.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::remove()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("remove(int)") {
|
||||
mp.add(1);
|
||||
mp.add(2);
|
||||
mp.add(3);
|
||||
|
||||
mp.remove(1);
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "[1,3]");
|
||||
}
|
||||
|
||||
SECTION("remove(const char *)") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
mp.remove("a");
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "{\"b\":2}");
|
||||
}
|
||||
|
||||
SECTION("remove(std::string)") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
mp.remove(std::string("b"));
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("remove(vla)") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
int i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "b");
|
||||
mp.remove(vla);
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::set()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("set(int)") {
|
||||
mp.set(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":42}");
|
||||
}
|
||||
|
||||
SECTION("set(const char*)") {
|
||||
mp.set("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
|
||||
SECTION("set(char[])") { // issue #1191
|
||||
char s[] = "world";
|
||||
mp.set(s);
|
||||
strcpy(s, "!!!!!");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::size()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("returns 0") {
|
||||
REQUIRE(mp.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("as an array, return 2") {
|
||||
mp.add(1);
|
||||
mp.add(2);
|
||||
|
||||
REQUIRE(mp.size() == 2);
|
||||
}
|
||||
|
||||
SECTION("as an object, return 2") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
REQUIRE(mp.size() == 2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::operator[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument &, const char *> mp = doc["hello"];
|
||||
|
||||
SECTION("set member") {
|
||||
mp["world"] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":{\"world\":42}}");
|
||||
}
|
||||
|
||||
SECTION("set element") {
|
||||
mp[2] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[null,null,42]}");
|
||||
}
|
||||
}
|
79
extras/tests/JsonDocument/overflowed.cpp
Normal file
79
extras/tests/JsonDocument/overflowed.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonDocument::overflowed()") {
|
||||
SECTION("returns false on a fresh object") {
|
||||
StaticJsonDocument<0> doc;
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
|
||||
SECTION("returns true after a failed insertion") {
|
||||
StaticJsonDocument<0> doc;
|
||||
doc.add(0);
|
||||
CHECK(doc.overflowed() == true);
|
||||
}
|
||||
|
||||
SECTION("returns false after successful insertion") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
|
||||
doc.add(0);
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
|
||||
SECTION("returns true after a failed string copy") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
|
||||
doc.add(std::string("example"));
|
||||
CHECK(doc.overflowed() == true);
|
||||
}
|
||||
|
||||
SECTION("returns false after a successful string copy") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1) + 8> doc;
|
||||
doc.add(std::string("example"));
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
|
||||
SECTION("returns true after a failed deserialization") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
|
||||
deserializeJson(doc, "[\"example\"]");
|
||||
CHECK(doc.overflowed() == true);
|
||||
}
|
||||
|
||||
SECTION("returns false after a successful deserialization") {
|
||||
StaticJsonDocument<JSON_ARRAY_SIZE(1) + 8> doc;
|
||||
deserializeJson(doc, "[\"example\"]");
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
|
||||
SECTION("returns false after clear()") {
|
||||
StaticJsonDocument<0> doc;
|
||||
doc.add(0);
|
||||
doc.clear();
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
|
||||
SECTION("remains false after shrinkToFit()") {
|
||||
DynamicJsonDocument doc(JSON_ARRAY_SIZE(1));
|
||||
doc.add(0);
|
||||
doc.shrinkToFit();
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
|
||||
SECTION("remains true after shrinkToFit()") {
|
||||
DynamicJsonDocument doc(JSON_ARRAY_SIZE(1));
|
||||
doc.add(0);
|
||||
doc.add(0);
|
||||
doc.shrinkToFit();
|
||||
CHECK(doc.overflowed() == true);
|
||||
}
|
||||
|
||||
SECTION("return false after garbageCollect()") {
|
||||
DynamicJsonDocument doc(JSON_ARRAY_SIZE(1));
|
||||
doc.add(0);
|
||||
doc.add(0);
|
||||
doc.garbageCollect();
|
||||
CHECK(doc.overflowed() == false);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
TEST_CASE("JsonVariant::operator|()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
JsonVariant variant = doc["value"].to<JsonVariant>();
|
||||
|
||||
SECTION("undefined") {
|
||||
SECTION("undefined | const char*") {
|
||||
@ -24,6 +24,27 @@ TEST_CASE("JsonVariant::operator|()") {
|
||||
bool result = variant | true;
|
||||
REQUIRE(result == true);
|
||||
}
|
||||
|
||||
SECTION("undefined | ElementProxy") {
|
||||
doc["array"][0] = 42;
|
||||
|
||||
JsonVariantConst result = variant | doc["array"][0];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
|
||||
SECTION("undefined | MemberProxy") {
|
||||
doc["other"] = 42;
|
||||
|
||||
JsonVariantConst result = variant | doc["other"];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
|
||||
SECTION("ElementProxy | ElementProxy") {
|
||||
doc["array"][0] = 42;
|
||||
|
||||
JsonVariantConst result = doc["array"][1] | doc["array"][0];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("null") {
|
||||
@ -43,6 +64,20 @@ TEST_CASE("JsonVariant::operator|()") {
|
||||
bool result = variant | true;
|
||||
REQUIRE(result == true);
|
||||
}
|
||||
|
||||
SECTION("null | ElementProxy") {
|
||||
doc["array"][0] = 42;
|
||||
|
||||
JsonVariantConst result = variant | doc["array"][0];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
|
||||
SECTION("null | MemberProxy") {
|
||||
doc["other"] = 42;
|
||||
|
||||
JsonVariantConst result = variant | doc["other"];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("int | const char*") {
|
||||
@ -57,6 +92,20 @@ TEST_CASE("JsonVariant::operator|()") {
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
|
||||
SECTION("int | ElementProxy") {
|
||||
variant.set(42);
|
||||
doc["array"][0] = 666;
|
||||
JsonVariantConst result = variant | doc["array"][0];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
|
||||
SECTION("int | MemberProxy") {
|
||||
variant.set(42);
|
||||
doc["other"] = 666;
|
||||
JsonVariantConst result = variant | doc["other"];
|
||||
REQUIRE(result == 42);
|
||||
}
|
||||
|
||||
SECTION("int | int") {
|
||||
variant.set(0);
|
||||
int result = variant | 666;
|
||||
@ -88,6 +137,20 @@ TEST_CASE("JsonVariant::operator|()") {
|
||||
REQUIRE(result == "not default");
|
||||
}
|
||||
|
||||
SECTION("const char* | char*") {
|
||||
char dflt[] = "default";
|
||||
variant.set("not default");
|
||||
std::string result = variant | dflt;
|
||||
REQUIRE(result == "not default");
|
||||
}
|
||||
|
||||
SECTION("int | char*") {
|
||||
char dflt[] = "default";
|
||||
variant.set(42);
|
||||
std::string result = variant | dflt;
|
||||
REQUIRE(result == "default");
|
||||
}
|
||||
|
||||
SECTION("const char* | int") {
|
||||
variant.set("not default");
|
||||
int result = variant | 42;
|
||||
|
@ -7,115 +7,150 @@
|
||||
|
||||
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
|
||||
|
||||
TEST_CASE("JsonVariant and strings") {
|
||||
TEST_CASE("JsonVariant::set() when there is enough memory") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("stores const char* by reference") {
|
||||
SECTION("const char*") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(static_cast<const char *>(str));
|
||||
bool result = variant.set(static_cast<const char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "world");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "world"); // stores by pointer
|
||||
}
|
||||
|
||||
SECTION("stores char* by copy") {
|
||||
char str[16];
|
||||
SECTION("(const char*)0") {
|
||||
bool result = variant.set(static_cast<const char *>(0));
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.isNull());
|
||||
}
|
||||
|
||||
SECTION("stores unsigned char* by copy") {
|
||||
SECTION("char*") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(reinterpret_cast<unsigned char *>(str));
|
||||
bool result = variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
}
|
||||
|
||||
SECTION("stores signed char* by copy") {
|
||||
SECTION("(char*)0") {
|
||||
bool result = variant.set(static_cast<char *>(0));
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.isNull());
|
||||
}
|
||||
|
||||
SECTION("unsigned char*") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(reinterpret_cast<signed char *>(str));
|
||||
bool result = variant.set(reinterpret_cast<unsigned char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
}
|
||||
|
||||
SECTION("signed char*") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
bool result = variant.set(reinterpret_cast<signed char *>(str));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("stores VLA by copy") {
|
||||
SECTION("VLA") {
|
||||
int n = 16;
|
||||
char str[n];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(str);
|
||||
bool result = variant.set(str);
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("stores std::string by copy") {
|
||||
SECTION("std::string") {
|
||||
std::string str;
|
||||
|
||||
str = "hello";
|
||||
variant.set(str);
|
||||
bool result = variant.set(str);
|
||||
str.replace(0, 5, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
}
|
||||
|
||||
SECTION("stores static JsonString by reference") {
|
||||
SECTION("static JsonString") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(JsonString(str, true));
|
||||
bool result = variant.set(JsonString(str, true));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "world");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "world"); // stores by pointer
|
||||
}
|
||||
|
||||
SECTION("stores non-static JsonString by copy") {
|
||||
SECTION("non-static JsonString") {
|
||||
char str[16];
|
||||
|
||||
strcpy(str, "hello");
|
||||
variant.set(JsonString(str, false));
|
||||
bool result = variant.set(JsonString(str, false));
|
||||
strcpy(str, "world");
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant == "hello"); // stores by copy
|
||||
}
|
||||
|
||||
SECTION("stores an enum as an integer") {
|
||||
SECTION("enum") {
|
||||
ErrorCode code = ERROR_10;
|
||||
|
||||
variant.set(code);
|
||||
bool result = variant.set(code);
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(variant.is<int>() == true);
|
||||
REQUIRE(variant.as<int>() == 10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant with not enough memory") {
|
||||
TEST_CASE("JsonVariant::set() with not enough memory") {
|
||||
StaticJsonDocument<1> doc;
|
||||
|
||||
JsonVariant v = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("std::string") {
|
||||
v.set(std::string("hello world!!"));
|
||||
bool result = v.set(std::string("hello world!!"));
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("Serialized<std::string>") {
|
||||
v.set(serialized(std::string("hello world!!")));
|
||||
bool result = v.set(serialized(std::string("hello world!!")));
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
char s[] = "hello world!!";
|
||||
bool result = v.set(s);
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(v.isNull());
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
# ArduinoJson - arduinojson.org
|
||||
# Copyright Benoit Blanchon 2014-2020
|
||||
# MIT License
|
||||
|
||||
add_executable(MemberProxyTests
|
||||
add.cpp
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
containsKey.cpp
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
add_test(MemberProxy MemberProxyTests)
|
@ -1,25 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::add()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("add(int)") {
|
||||
mp.add(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("add(const char*)") {
|
||||
mp.add("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::clear()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("size goes back to zero") {
|
||||
mp.add(42);
|
||||
mp.clear();
|
||||
|
||||
REQUIRE(mp.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("isNull() return true") {
|
||||
mp.add("hello");
|
||||
mp.clear();
|
||||
|
||||
REQUIRE(mp.isNull() == true);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::operator==()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
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("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"]);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::containsKey()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey(std::string("key")) == true);
|
||||
REQUIRE(mp.containsKey(std::string("key")) == true);
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::remove()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("remove(int)") {
|
||||
mp.add(1);
|
||||
mp.add(2);
|
||||
mp.add(3);
|
||||
|
||||
mp.remove(1);
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "[1,3]");
|
||||
}
|
||||
|
||||
SECTION("remove(const char *)") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
mp.remove("a");
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "{\"b\":2}");
|
||||
}
|
||||
|
||||
SECTION("remove(std::string)") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
mp.remove(std::string("b"));
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("remove(vla)") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
int i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "b");
|
||||
mp.remove(vla);
|
||||
|
||||
REQUIRE(mp.as<std::string>() == "{\"a\":1}");
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::set()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("set(int)") {
|
||||
mp.set(42);
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":42}");
|
||||
}
|
||||
|
||||
SECTION("set(const char*)") {
|
||||
mp.set("world");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
|
||||
SECTION("set(char[])") { // issue #1191
|
||||
char s[] = "world";
|
||||
mp.set(s);
|
||||
strcpy(s, "!!!!!");
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::size()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("returns 0") {
|
||||
REQUIRE(mp.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("as an array, return 2") {
|
||||
mp.add(1);
|
||||
mp.add(2);
|
||||
|
||||
REQUIRE(mp.size() == 2);
|
||||
}
|
||||
|
||||
SECTION("as an object, return 2") {
|
||||
mp["a"] = 1;
|
||||
mp["b"] = 2;
|
||||
|
||||
REQUIRE(mp.size() == 2);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::operator[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
|
||||
|
||||
SECTION("set member") {
|
||||
mp["world"] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":{\"world\":42}}");
|
||||
}
|
||||
|
||||
SECTION("set element") {
|
||||
mp[2] = 42;
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[null,null,42]}");
|
||||
}
|
||||
}
|
@ -12,9 +12,9 @@ TEST_CASE("StringCopier") {
|
||||
|
||||
SECTION("Works when buffer is big enough") {
|
||||
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
|
||||
StringCopier str;
|
||||
StringCopier str(pool);
|
||||
|
||||
str.startString(&pool);
|
||||
str.startString();
|
||||
str.append("hello");
|
||||
str.append('\0');
|
||||
|
||||
@ -24,9 +24,9 @@ TEST_CASE("StringCopier") {
|
||||
|
||||
SECTION("Returns null when too small") {
|
||||
MemoryPool pool(buffer, sizeof(void*));
|
||||
StringCopier str;
|
||||
StringCopier str(pool);
|
||||
|
||||
str.startString(&pool);
|
||||
str.startString();
|
||||
str.append("hello world!");
|
||||
|
||||
REQUIRE(str.isValid() == false);
|
||||
@ -34,22 +34,22 @@ TEST_CASE("StringCopier") {
|
||||
|
||||
SECTION("Increases size of memory pool") {
|
||||
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
|
||||
StringCopier str;
|
||||
StringCopier str(pool);
|
||||
|
||||
str.startString(&pool);
|
||||
str.startString();
|
||||
str.append('h');
|
||||
str.save(&pool);
|
||||
str.save();
|
||||
|
||||
REQUIRE(1 == pool.size());
|
||||
}
|
||||
}
|
||||
|
||||
static const char* addStringToPool(MemoryPool* pool, const char* s) {
|
||||
StringCopier str;
|
||||
str.startString(pool);
|
||||
static const char* addStringToPool(MemoryPool& pool, const char* s) {
|
||||
StringCopier str(pool);
|
||||
str.startString();
|
||||
str.append(s);
|
||||
str.append('\0');
|
||||
return str.save(pool);
|
||||
return str.save();
|
||||
}
|
||||
|
||||
TEST_CASE("StringCopier::save() deduplicates strings") {
|
||||
@ -57,9 +57,9 @@ TEST_CASE("StringCopier::save() deduplicates strings") {
|
||||
MemoryPool pool(buffer, 4096);
|
||||
|
||||
SECTION("Basic") {
|
||||
const char* s1 = addStringToPool(&pool, "hello");
|
||||
const char* s2 = addStringToPool(&pool, "world");
|
||||
const char* s3 = addStringToPool(&pool, "hello");
|
||||
const char* s1 = addStringToPool(pool, "hello");
|
||||
const char* s2 = addStringToPool(pool, "world");
|
||||
const char* s3 = addStringToPool(pool, "hello");
|
||||
|
||||
REQUIRE(s1 == s3);
|
||||
REQUIRE(s2 != s3);
|
||||
@ -67,16 +67,16 @@ TEST_CASE("StringCopier::save() deduplicates strings") {
|
||||
}
|
||||
|
||||
SECTION("Requires terminator") {
|
||||
const char* s1 = addStringToPool(&pool, "hello world");
|
||||
const char* s2 = addStringToPool(&pool, "hello");
|
||||
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");
|
||||
const char* s1 = addStringToPool(pool, "hello world");
|
||||
const char* s2 = addStringToPool(pool, "wor");
|
||||
|
||||
REQUIRE(s2 != s1);
|
||||
REQUIRE(pool.size() == 12 + 4);
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/StlStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/StdStringAdapter.hpp>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
@ -102,7 +102,7 @@ TEST_CASE("FlashStringAdapter") {
|
||||
|
||||
TEST_CASE("std::string") {
|
||||
std::string str("bravo");
|
||||
StlStringAdapter<std::string> adapter = adaptString(str);
|
||||
StdStringAdapter<std::string> adapter = adaptString(str);
|
||||
|
||||
CHECK(adapter.compare(NULL) > 0);
|
||||
CHECK(adapter.compare("alpha") > 0);
|
||||
@ -132,7 +132,7 @@ TEST_CASE("Arduino String") {
|
||||
|
||||
TEST_CASE("custom_string") {
|
||||
custom_string str("bravo");
|
||||
StlStringAdapter<custom_string> adapter = adaptString(str);
|
||||
StdStringAdapter<custom_string> adapter = adaptString(str);
|
||||
|
||||
CHECK(adapter.compare(NULL) > 0);
|
||||
CHECK(adapter.compare("alpha") > 0);
|
||||
|
@ -12,8 +12,8 @@ using namespace ARDUINOJSON_NAMESPACE;
|
||||
static void testCodepoint(uint32_t codepoint, std::string expected) {
|
||||
char buffer[4096];
|
||||
MemoryPool pool(buffer, 4096);
|
||||
StringCopier str;
|
||||
str.startString(&pool);
|
||||
StringCopier str(pool);
|
||||
str.startString();
|
||||
|
||||
CAPTURE(codepoint);
|
||||
Utf8::encodeCodepoint(codepoint, str);
|
||||
|
@ -373,16 +373,22 @@ TEST_CASE("Comments in objects") {
|
||||
TEST_CASE("Comments alone") {
|
||||
DynamicJsonDocument doc(2048);
|
||||
|
||||
SECTION("Just a trailing comment") {
|
||||
SECTION("Just a trailing comment with no line break") {
|
||||
DeserializationError err = deserializeJson(doc, "// comment");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("Just a trailing comment with no a break") {
|
||||
DeserializationError err = deserializeJson(doc, "// comment\n");
|
||||
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Just a block comment") {
|
||||
DeserializationError err = deserializeJson(doc, "/*comment*/");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Just a slash") {
|
||||
|
@ -53,33 +53,33 @@ TEST_CASE("Flash strings") {
|
||||
}
|
||||
|
||||
TEST_CASE("strlen_P") {
|
||||
CHECK(strlen_P(FC("")) == 0);
|
||||
CHECK(strlen_P(FC("a")) == 1);
|
||||
CHECK(strlen_P(FC("ac")) == 2);
|
||||
CHECK(strlen_P(PSTR("")) == 0);
|
||||
CHECK(strlen_P(PSTR("a")) == 1);
|
||||
CHECK(strlen_P(PSTR("ac")) == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("strncmp_P") {
|
||||
CHECK(strncmp_P("a", FC("b"), 0) == 0);
|
||||
CHECK(strncmp_P("a", FC("b"), 1) == -1);
|
||||
CHECK(strncmp_P("b", FC("a"), 1) == 1);
|
||||
CHECK(strncmp_P("a", FC("a"), 0) == 0);
|
||||
CHECK(strncmp_P("a", FC("b"), 2) == -1);
|
||||
CHECK(strncmp_P("b", FC("a"), 2) == 1);
|
||||
CHECK(strncmp_P("a", FC("a"), 2) == 0);
|
||||
CHECK(strncmp_P("a", PSTR("b"), 0) == 0);
|
||||
CHECK(strncmp_P("a", PSTR("b"), 1) == -1);
|
||||
CHECK(strncmp_P("b", PSTR("a"), 1) == 1);
|
||||
CHECK(strncmp_P("a", PSTR("a"), 0) == 0);
|
||||
CHECK(strncmp_P("a", PSTR("b"), 2) == -1);
|
||||
CHECK(strncmp_P("b", PSTR("a"), 2) == 1);
|
||||
CHECK(strncmp_P("a", PSTR("a"), 2) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("strcmp_P") {
|
||||
CHECK(strcmp_P("a", FC("b")) == -1);
|
||||
CHECK(strcmp_P("b", FC("a")) == 1);
|
||||
CHECK(strcmp_P("a", FC("a")) == 0);
|
||||
CHECK(strcmp_P("aa", FC("ab")) == -1);
|
||||
CHECK(strcmp_P("ab", FC("aa")) == 1);
|
||||
CHECK(strcmp_P("aa", FC("aa")) == 0);
|
||||
CHECK(strcmp_P("a", PSTR("b")) == -1);
|
||||
CHECK(strcmp_P("b", PSTR("a")) == 1);
|
||||
CHECK(strcmp_P("a", PSTR("a")) == 0);
|
||||
CHECK(strcmp_P("aa", PSTR("ab")) == -1);
|
||||
CHECK(strcmp_P("ab", PSTR("aa")) == 1);
|
||||
CHECK(strcmp_P("aa", PSTR("aa")) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("memcpy_P") {
|
||||
char dst[4];
|
||||
CHECK(memcpy_P(dst, FC("ABC"), 4) == dst);
|
||||
CHECK(memcpy_P(dst, PSTR("ABC"), 4) == dst);
|
||||
CHECK(dst[0] == 'A');
|
||||
CHECK(dst[1] == 'B');
|
||||
CHECK(dst[2] == 'C');
|
||||
@ -165,3 +165,22 @@ TEST_CASE("Reader<const __FlashStringHelper*>") {
|
||||
REQUIRE(buffer[6] == 'g');
|
||||
}
|
||||
}
|
||||
|
||||
static void testStringification(DeserializationError error,
|
||||
std::string expected) {
|
||||
const __FlashStringHelper* s = error.f_str();
|
||||
CHECK(reinterpret_cast<const char*>(convertFlashToPtr(s)) == expected);
|
||||
}
|
||||
|
||||
#define TEST_STRINGIFICATION(symbol) \
|
||||
testStringification(DeserializationError::symbol, #symbol)
|
||||
|
||||
TEST_CASE("DeserializationError::f_str()") {
|
||||
TEST_STRINGIFICATION(Ok);
|
||||
TEST_STRINGIFICATION(EmptyInput);
|
||||
TEST_STRINGIFICATION(IncompleteInput);
|
||||
TEST_STRINGIFICATION(InvalidInput);
|
||||
TEST_STRINGIFICATION(NoMemory);
|
||||
TEST_STRINGIFICATION(NotSupported);
|
||||
TEST_STRINGIFICATION(TooDeep);
|
||||
}
|
||||
|
@ -8,8 +8,10 @@ add_executable(MsgPackDeserializerTests
|
||||
deserializeStaticVariant.cpp
|
||||
deserializeVariant.cpp
|
||||
doubleToFloat.cpp
|
||||
filter.cpp
|
||||
incompleteInput.cpp
|
||||
input_types.cpp
|
||||
misc.cpp
|
||||
nestingLimit.cpp
|
||||
notSupported.cpp
|
||||
)
|
||||
|
1119
extras/tests/MsgPackDeserializer/filter.cpp
Normal file
1119
extras/tests/MsgPackDeserializer/filter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
24
extras/tests/MsgPackDeserializer/misc.cpp
Normal file
24
extras/tests/MsgPackDeserializer/misc.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("deserializeMsgPack() returns EmptyInput") {
|
||||
StaticJsonDocument<100> doc;
|
||||
|
||||
SECTION("from sized buffer") {
|
||||
DeserializationError err = deserializeMsgPack(doc, "", 0);
|
||||
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("from stream") {
|
||||
std::istringstream input("");
|
||||
|
||||
DeserializationError err = deserializeMsgPack(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
add_executable(NumbersTests
|
||||
parseFloat.cpp
|
||||
parseDouble.cpp
|
||||
parseInteger.cpp
|
||||
parseNumber.cpp
|
||||
)
|
||||
|
97
extras/tests/Numbers/parseDouble.cpp
Normal file
97
extras/tests/Numbers/parseDouble.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_DOUBLE 1
|
||||
#define ARDUINOJSON_ENABLE_NAN 1
|
||||
#define ARDUINOJSON_ENABLE_INFINITY 1
|
||||
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
void checkDouble(const char* input, double expected) {
|
||||
CAPTURE(input);
|
||||
REQUIRE(parseNumber<double>(input) == Approx(expected));
|
||||
}
|
||||
|
||||
void checkDoubleNaN(const char* input) {
|
||||
CAPTURE(input);
|
||||
double result = parseNumber<double>(input);
|
||||
REQUIRE(result != result);
|
||||
}
|
||||
|
||||
void checkDoubleInf(const char* input, bool negative) {
|
||||
CAPTURE(input);
|
||||
double x = parseNumber<double>(input);
|
||||
if (negative)
|
||||
REQUIRE(x < 0);
|
||||
else
|
||||
REQUIRE(x > 0);
|
||||
REQUIRE(x == x); // not a NaN
|
||||
REQUIRE(x * 2 == x); // a property of infinity
|
||||
}
|
||||
|
||||
TEST_CASE("parseNumber<double>()") {
|
||||
SECTION("Short_NoExponent") {
|
||||
checkDouble("3.14", 3.14);
|
||||
checkDouble("-3.14", -3.14);
|
||||
checkDouble("+3.14", +3.14);
|
||||
}
|
||||
|
||||
SECTION("Short_NoDot") {
|
||||
checkDouble("1E+308", 1E+308);
|
||||
checkDouble("-1E+308", -1E+308);
|
||||
checkDouble("+1E-308", +1E-308);
|
||||
checkDouble("+1e+308", +1e+308);
|
||||
checkDouble("-1e-308", -1e-308);
|
||||
}
|
||||
|
||||
SECTION("Max") {
|
||||
checkDouble(".017976931348623147e+310", 1.7976931348623147e+308);
|
||||
checkDouble(".17976931348623147e+309", 1.7976931348623147e+308);
|
||||
checkDouble("1.7976931348623147e+308", 1.7976931348623147e+308);
|
||||
checkDouble("17.976931348623147e+307", 1.7976931348623147e+308);
|
||||
checkDouble("179.76931348623147e+306", 1.7976931348623147e+308);
|
||||
}
|
||||
|
||||
SECTION("Min") {
|
||||
checkDouble(".022250738585072014e-306", 2.2250738585072014e-308);
|
||||
checkDouble(".22250738585072014e-307", 2.2250738585072014e-308);
|
||||
checkDouble("2.2250738585072014e-308", 2.2250738585072014e-308);
|
||||
checkDouble("22.250738585072014e-309", 2.2250738585072014e-308);
|
||||
checkDouble("222.50738585072014e-310", 2.2250738585072014e-308);
|
||||
}
|
||||
|
||||
SECTION("VeryLong") {
|
||||
checkDouble("0.00000000000000000000000000000001", 1e-32);
|
||||
checkDouble("100000000000000000000000000000000.0", 1e+32);
|
||||
checkDouble(
|
||||
"100000000000000000000000000000000.00000000000000000000000000000",
|
||||
1e+32);
|
||||
}
|
||||
|
||||
SECTION("MantissaTooLongToFit") {
|
||||
checkDouble("0.179769313486231571111111111111", 0.17976931348623157);
|
||||
checkDouble("17976931348623157.11111111111111", 17976931348623157.0);
|
||||
checkDouble("1797693.134862315711111111111111", 1797693.1348623157);
|
||||
|
||||
checkDouble("-0.179769313486231571111111111111", -0.17976931348623157);
|
||||
checkDouble("-17976931348623157.11111111111111", -17976931348623157.0);
|
||||
checkDouble("-1797693.134862315711111111111111", -1797693.1348623157);
|
||||
}
|
||||
|
||||
SECTION("ExponentTooBig") {
|
||||
checkDoubleInf("1e309", false);
|
||||
checkDoubleInf("-1e309", true);
|
||||
checkDoubleInf("1e65535", false);
|
||||
checkDouble("1e-65535", 0.0);
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
checkDoubleNaN("NaN");
|
||||
checkDoubleNaN("nan");
|
||||
}
|
||||
}
|
@ -6,28 +6,26 @@
|
||||
#define ARDUINOJSON_ENABLE_NAN 1
|
||||
#define ARDUINOJSON_ENABLE_INFINITY 1
|
||||
|
||||
#include <ArduinoJson/Numbers/parseFloat.hpp>
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
template <typename T>
|
||||
void checkFloat(const char* input, T expected) {
|
||||
void checkFloat(const char* input, float expected) {
|
||||
CAPTURE(input);
|
||||
REQUIRE(parseFloat<T>(input) == Approx(expected));
|
||||
REQUIRE(parseNumber<float>(input) == Approx(expected));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkNaN(const char* input) {
|
||||
void checkFloatNaN(const char* input) {
|
||||
CAPTURE(input);
|
||||
T result = parseFloat<T>(input);
|
||||
float result = parseNumber<float>(input);
|
||||
REQUIRE(result != result);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkInf(const char* input, bool negative) {
|
||||
void checkFloatInf(const char* input, bool negative) {
|
||||
CAPTURE(input);
|
||||
T x = parseFloat<T>(input);
|
||||
float x = parseNumber<float>(input);
|
||||
if (negative)
|
||||
REQUIRE(x < 0);
|
||||
else
|
||||
@ -36,137 +34,69 @@ void checkInf(const char* input, bool negative) {
|
||||
REQUIRE(x * 2 == x); // a property of infinity
|
||||
}
|
||||
|
||||
TEST_CASE("parseFloat<float>()") {
|
||||
TEST_CASE("parseNumber<float>()") {
|
||||
SECTION("Float_Short_NoExponent") {
|
||||
checkFloat<float>("3.14", 3.14f);
|
||||
checkFloat<float>("-3.14", -3.14f);
|
||||
checkFloat<float>("+3.14", +3.14f);
|
||||
checkFloat("3.14", 3.14f);
|
||||
checkFloat("-3.14", -3.14f);
|
||||
checkFloat("+3.14", +3.14f);
|
||||
}
|
||||
|
||||
SECTION("Short_NoDot") {
|
||||
checkFloat<float>("1E+38", 1E+38f);
|
||||
checkFloat<float>("-1E+38", -1E+38f);
|
||||
checkFloat<float>("+1E-38", +1E-38f);
|
||||
checkFloat<float>("+1e+38", +1e+38f);
|
||||
checkFloat<float>("-1e-38", -1e-38f);
|
||||
checkFloat("1E+38", 1E+38f);
|
||||
checkFloat("-1E+38", -1E+38f);
|
||||
checkFloat("+1E-38", +1E-38f);
|
||||
checkFloat("+1e+38", +1e+38f);
|
||||
checkFloat("-1e-38", -1e-38f);
|
||||
}
|
||||
|
||||
SECTION("Max") {
|
||||
checkFloat<float>("340.2823e+36", 3.402823e+38f);
|
||||
checkFloat<float>("34.02823e+37", 3.402823e+38f);
|
||||
checkFloat<float>("3.402823e+38", 3.402823e+38f);
|
||||
checkFloat<float>("0.3402823e+39", 3.402823e+38f);
|
||||
checkFloat<float>("0.03402823e+40", 3.402823e+38f);
|
||||
checkFloat<float>("0.003402823e+41", 3.402823e+38f);
|
||||
checkFloat("340.2823e+36", 3.402823e+38f);
|
||||
checkFloat("34.02823e+37", 3.402823e+38f);
|
||||
checkFloat("3.402823e+38", 3.402823e+38f);
|
||||
checkFloat("0.3402823e+39", 3.402823e+38f);
|
||||
checkFloat("0.03402823e+40", 3.402823e+38f);
|
||||
checkFloat("0.003402823e+41", 3.402823e+38f);
|
||||
}
|
||||
|
||||
SECTION("VeryLong") {
|
||||
checkFloat<float>("0.00000000000000000000000000000001", 1e-32f);
|
||||
checkFloat<float>("100000000000000000000000000000000.0", 1e+32f);
|
||||
checkFloat<float>(
|
||||
checkFloat("0.00000000000000000000000000000001", 1e-32f);
|
||||
checkFloat("100000000000000000000000000000000.0", 1e+32f);
|
||||
checkFloat(
|
||||
"100000000000000000000000000000000.00000000000000000000000000000",
|
||||
1e+32f);
|
||||
}
|
||||
|
||||
SECTION("MantissaTooLongToFit") {
|
||||
checkFloat<float>("0.340282346638528861111111111111", 0.34028234663852886f);
|
||||
checkFloat<float>("34028234663852886.11111111111111", 34028234663852886.0f);
|
||||
checkFloat<float>("34028234.66385288611111111111111", 34028234.663852886f);
|
||||
checkFloat("0.340282346638528861111111111111", 0.34028234663852886f);
|
||||
checkFloat("34028234663852886.11111111111111", 34028234663852886.0f);
|
||||
checkFloat("34028234.66385288611111111111111", 34028234.663852886f);
|
||||
|
||||
checkFloat<float>("-0.340282346638528861111111111111",
|
||||
-0.34028234663852886f);
|
||||
checkFloat<float>("-34028234663852886.11111111111111",
|
||||
-34028234663852886.0f);
|
||||
checkFloat<float>("-34028234.66385288611111111111111",
|
||||
-34028234.663852886f);
|
||||
checkFloat("-0.340282346638528861111111111111", -0.34028234663852886f);
|
||||
checkFloat("-34028234663852886.11111111111111", -34028234663852886.0f);
|
||||
checkFloat("-34028234.66385288611111111111111", -34028234.663852886f);
|
||||
}
|
||||
|
||||
SECTION("ExponentTooBig") {
|
||||
checkInf<float>("1e39", false);
|
||||
checkInf<float>("-1e39", true);
|
||||
checkInf<float>("1e255", false);
|
||||
checkFloat<float>("1e-255", 0.0f);
|
||||
checkFloatInf("1e39", false);
|
||||
checkFloatInf("-1e39", true);
|
||||
checkFloatInf("1e255", false);
|
||||
checkFloat("1e-255", 0.0f);
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
checkNaN<float>("NaN");
|
||||
checkNaN<float>("nan");
|
||||
checkFloatNaN("NaN");
|
||||
checkFloatNaN("nan");
|
||||
}
|
||||
|
||||
SECTION("Infinity") {
|
||||
checkInf<float>("Infinity", false);
|
||||
checkInf<float>("+Infinity", false);
|
||||
checkInf<float>("-Infinity", true);
|
||||
checkInf<float>("inf", false);
|
||||
checkInf<float>("+inf", false);
|
||||
checkInf<float>("-inf", true);
|
||||
checkFloatInf("Infinity", false);
|
||||
checkFloatInf("+Infinity", false);
|
||||
checkFloatInf("-Infinity", true);
|
||||
checkFloatInf("inf", false);
|
||||
checkFloatInf("+inf", false);
|
||||
checkFloatInf("-inf", true);
|
||||
|
||||
checkInf<float>("1e300", false);
|
||||
checkInf<float>("-1e300", true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("parseFloat<double>()") {
|
||||
SECTION("Short_NoExponent") {
|
||||
checkFloat<double>("3.14", 3.14);
|
||||
checkFloat<double>("-3.14", -3.14);
|
||||
checkFloat<double>("+3.14", +3.14);
|
||||
}
|
||||
|
||||
SECTION("Short_NoDot") {
|
||||
checkFloat<double>("1E+308", 1E+308);
|
||||
checkFloat<double>("-1E+308", -1E+308);
|
||||
checkFloat<double>("+1E-308", +1E-308);
|
||||
checkFloat<double>("+1e+308", +1e+308);
|
||||
checkFloat<double>("-1e-308", -1e-308);
|
||||
}
|
||||
|
||||
SECTION("Max") {
|
||||
checkFloat<double>(".017976931348623147e+310", 1.7976931348623147e+308);
|
||||
checkFloat<double>(".17976931348623147e+309", 1.7976931348623147e+308);
|
||||
checkFloat<double>("1.7976931348623147e+308", 1.7976931348623147e+308);
|
||||
checkFloat<double>("17.976931348623147e+307", 1.7976931348623147e+308);
|
||||
checkFloat<double>("179.76931348623147e+306", 1.7976931348623147e+308);
|
||||
}
|
||||
|
||||
SECTION("Min") {
|
||||
checkFloat<double>(".022250738585072014e-306", 2.2250738585072014e-308);
|
||||
checkFloat<double>(".22250738585072014e-307", 2.2250738585072014e-308);
|
||||
checkFloat<double>("2.2250738585072014e-308", 2.2250738585072014e-308);
|
||||
checkFloat<double>("22.250738585072014e-309", 2.2250738585072014e-308);
|
||||
checkFloat<double>("222.50738585072014e-310", 2.2250738585072014e-308);
|
||||
}
|
||||
|
||||
SECTION("VeryLong") {
|
||||
checkFloat<double>("0.00000000000000000000000000000001", 1e-32);
|
||||
checkFloat<double>("100000000000000000000000000000000.0", 1e+32);
|
||||
checkFloat<double>(
|
||||
"100000000000000000000000000000000.00000000000000000000000000000",
|
||||
1e+32);
|
||||
}
|
||||
|
||||
SECTION("MantissaTooLongToFit") {
|
||||
checkFloat<double>("0.179769313486231571111111111111", 0.17976931348623157);
|
||||
checkFloat<double>("17976931348623157.11111111111111", 17976931348623157.0);
|
||||
checkFloat<double>("1797693.134862315711111111111111", 1797693.1348623157);
|
||||
|
||||
checkFloat<double>("-0.179769313486231571111111111111",
|
||||
-0.17976931348623157);
|
||||
checkFloat<double>("-17976931348623157.11111111111111",
|
||||
-17976931348623157.0);
|
||||
checkFloat<double>("-1797693.134862315711111111111111",
|
||||
-1797693.1348623157);
|
||||
}
|
||||
|
||||
SECTION("ExponentTooBig") {
|
||||
checkInf<double>("1e309", false);
|
||||
checkInf<double>("-1e309", true);
|
||||
checkInf<double>("1e65535", false);
|
||||
checkFloat<double>("1e-65535", 0.0);
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
checkNaN<double>("NaN");
|
||||
checkNaN<double>("nan");
|
||||
checkFloatInf("1e300", false);
|
||||
checkFloatInf("-1e300", true);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
// MIT License
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ArduinoJson/Numbers/parseInteger.hpp>
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
@ -11,11 +12,11 @@ using namespace ARDUINOJSON_NAMESPACE;
|
||||
template <typename T>
|
||||
void checkInteger(const char* input, T expected) {
|
||||
CAPTURE(input);
|
||||
T actual = parseInteger<T>(input);
|
||||
T actual = parseNumber<T>(input);
|
||||
REQUIRE(expected == actual);
|
||||
}
|
||||
|
||||
TEST_CASE("parseInteger<int8_t>()") {
|
||||
TEST_CASE("parseNumber<int8_t>()") {
|
||||
checkInteger<int8_t>("-128", -128);
|
||||
checkInteger<int8_t>("127", 127);
|
||||
checkInteger<int8_t>("+127", 127);
|
||||
@ -25,7 +26,7 @@ TEST_CASE("parseInteger<int8_t>()") {
|
||||
checkInteger<int8_t>("-129", 0); // overflow
|
||||
}
|
||||
|
||||
TEST_CASE("parseInteger<int16_t>()") {
|
||||
TEST_CASE("parseNumber<int16_t>()") {
|
||||
checkInteger<int16_t>("-32768", -32768);
|
||||
checkInteger<int16_t>("32767", 32767);
|
||||
checkInteger<int16_t>("+32767", 32767);
|
||||
@ -35,7 +36,7 @@ TEST_CASE("parseInteger<int16_t>()") {
|
||||
checkInteger<int16_t>("32768", 0); // overflow
|
||||
}
|
||||
|
||||
TEST_CASE("parseInteger<int32_t>()") {
|
||||
TEST_CASE("parseNumber<int32_t>()") {
|
||||
checkInteger<int32_t>("-2147483648", (-2147483647 - 1));
|
||||
checkInteger<int32_t>("2147483647", 2147483647);
|
||||
checkInteger<int32_t>("+2147483647", 2147483647);
|
||||
@ -45,7 +46,7 @@ TEST_CASE("parseInteger<int32_t>()") {
|
||||
checkInteger<int32_t>("2147483648", 0); // overflow
|
||||
}
|
||||
|
||||
TEST_CASE("parseInteger<uint8_t>()") {
|
||||
TEST_CASE("parseNumber<uint8_t>()") {
|
||||
checkInteger<uint8_t>("0", 0);
|
||||
checkInteger<uint8_t>("255", 255);
|
||||
checkInteger<uint8_t>("+255", 255);
|
||||
@ -55,7 +56,7 @@ TEST_CASE("parseInteger<uint8_t>()") {
|
||||
checkInteger<uint8_t>("256", 0);
|
||||
}
|
||||
|
||||
TEST_CASE("parseInteger<uint16_t>()") {
|
||||
TEST_CASE("parseNumber<uint16_t>()") {
|
||||
checkInteger<uint16_t>("0", 0);
|
||||
checkInteger<uint16_t>("65535", 65535);
|
||||
checkInteger<uint16_t>("+65535", 65535);
|
||||
|
@ -2,22 +2,37 @@
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Numbers/Integer.hpp>
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
#include <ArduinoJson/Variant/VariantImpl.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("Test uint32_t overflow") {
|
||||
ParsedNumber<float, uint32_t> first, second;
|
||||
parseNumber("4294967295", first);
|
||||
parseNumber("4294967296", second);
|
||||
TEST_CASE("Test unsigned integer overflow") {
|
||||
VariantData first, second;
|
||||
first.init();
|
||||
second.init();
|
||||
|
||||
// Avoids MSVC warning C4127 (conditional expression is constant)
|
||||
size_t integerSize = sizeof(Integer);
|
||||
|
||||
if (integerSize == 8) {
|
||||
parseNumber("18446744073709551615", first);
|
||||
parseNumber("18446744073709551616", second);
|
||||
} else {
|
||||
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;
|
||||
VariantData result;
|
||||
result.init();
|
||||
|
||||
parseNumber("6a3", result);
|
||||
|
||||
REQUIRE(result.type() == uint8_t(VALUE_IS_NULL));
|
||||
|
@ -28,6 +28,7 @@ DeserializationError KEYWORD1 DATA_TYPE
|
||||
DynamicJsonDocument KEYWORD1 DATA_TYPE
|
||||
JsonArray KEYWORD1 DATA_TYPE
|
||||
JsonArrayConst KEYWORD1 DATA_TYPE
|
||||
JsonDocument KEYWORD1 DATA_TYPE
|
||||
JsonFloat KEYWORD1 DATA_TYPE
|
||||
JsonInteger KEYWORD1 DATA_TYPE
|
||||
JsonObject KEYWORD1 DATA_TYPE
|
||||
|
@ -7,7 +7,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/bblanchon/ArduinoJson.git"
|
||||
},
|
||||
"version": "6.16.0",
|
||||
"version": "6.17.2",
|
||||
"authors": {
|
||||
"name": "Benoit Blanchon",
|
||||
"url": "https://blog.benoitblanchon.fr"
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=ArduinoJson
|
||||
version=6.16.0
|
||||
version=6.17.2
|
||||
author=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
sentence=A simple and efficient JSON library for embedded C++.
|
||||
|
@ -12,12 +12,13 @@ inline VariantData *arrayAdd(CollectionData *arr, MemoryPool *pool) {
|
||||
return arr ? arr->addElement(pool) : 0;
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
inline void arrayAccept(const CollectionData *arr, Visitor &visitor) {
|
||||
template <typename TVisitor>
|
||||
inline typename TVisitor::result_type arrayAccept(const CollectionData *arr,
|
||||
TVisitor &visitor) {
|
||||
if (arr)
|
||||
visitor.visitArray(*arr);
|
||||
return visitor.visitArray(*arr);
|
||||
else
|
||||
visitor.visitNull();
|
||||
return visitor.visitNull();
|
||||
}
|
||||
|
||||
inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) {
|
||||
|
@ -27,9 +27,9 @@ class ArrayRefBase {
|
||||
return VariantConstRef(reinterpret_cast<const VariantData*>(data));
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
FORCE_INLINE void accept(Visitor& visitor) const {
|
||||
arrayAccept(_data, visitor);
|
||||
template <typename TVisitor>
|
||||
FORCE_INLINE typename TVisitor::result_type accept(TVisitor& visitor) const {
|
||||
return arrayAccept(_data, visitor);
|
||||
}
|
||||
|
||||
FORCE_INLINE bool isNull() const {
|
||||
|
@ -19,10 +19,13 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename TArray>
|
||||
class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
public VariantShortcuts<ElementProxy<TArray> >,
|
||||
public Visitable {
|
||||
public Visitable,
|
||||
public VariantTag {
|
||||
typedef ElementProxy<TArray> this_type;
|
||||
|
||||
public:
|
||||
typedef VariantRef variant_type;
|
||||
|
||||
FORCE_INLINE ElementProxy(TArray array, size_t index)
|
||||
: _array(array), _index(index) {}
|
||||
|
||||
@ -98,8 +101,8 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
return getOrAddUpstreamElement().set(value);
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
void accept(Visitor& visitor) const {
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(TVisitor& visitor) const {
|
||||
return getUpstreamElement().accept(visitor);
|
||||
}
|
||||
|
||||
|
@ -65,40 +65,61 @@ inline bool copyArray(T (&src)[N1][N2], JsonDocument& dst) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class ArrayCopier1D {
|
||||
class ArrayCopier1D : public Visitor<size_t> {
|
||||
public:
|
||||
ArrayCopier1D(T* destination, size_t capacity)
|
||||
: _destination(destination), _capacity(capacity), _size(0) {}
|
||||
: _destination(destination), _capacity(capacity) {}
|
||||
|
||||
void visitArray(const CollectionData& array) {
|
||||
size_t visitArray(const CollectionData& array) {
|
||||
size_t size = 0;
|
||||
VariantSlot* slot = array.head();
|
||||
|
||||
while (slot != 0 && _size < _capacity) {
|
||||
_destination[_size++] = variantAs<T>(slot->data());
|
||||
while (slot != 0 && size < _capacity) {
|
||||
_destination[size++] = variantAs<T>(slot->data());
|
||||
slot = slot->next();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
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;
|
||||
size_t visitObject(const CollectionData&) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitFloat(Float) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitString(const char*) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitRawJson(const char*, size_t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitNegativeInteger(UInt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitPositiveInteger(UInt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitBoolean(bool) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t visitNull() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
T* _destination;
|
||||
size_t _capacity;
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
class ArrayCopier2D {
|
||||
class ArrayCopier2D : public Visitor<void> {
|
||||
public:
|
||||
ArrayCopier2D(T (*destination)[N1][N2]) : _destination(destination) {}
|
||||
|
||||
@ -136,8 +157,8 @@ inline typename enable_if<!is_array<T>::value, size_t>::type copyArray(
|
||||
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();
|
||||
|
||||
return src.accept(copier);
|
||||
}
|
||||
|
||||
// Copy a JsonArray to a 2D array
|
||||
|
@ -83,6 +83,18 @@
|
||||
#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10
|
||||
#endif
|
||||
|
||||
// Number of bits to store the pointer to next node
|
||||
// (saves RAM but limits the number of values in a document)
|
||||
#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE
|
||||
#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 2
|
||||
// Address space == 16-bit => max 127 values
|
||||
#define ARDUINOJSON_SLOT_OFFSET_SIZE 1
|
||||
#else
|
||||
// Address space > 16-bit => max 32767 values
|
||||
#define ARDUINOJSON_SLOT_OFFSET_SIZE 2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else // ARDUINOJSON_EMBEDDED_MODE
|
||||
|
||||
// On a computer we have plenty of memory so we can use doubles
|
||||
@ -114,6 +126,11 @@
|
||||
#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50
|
||||
#endif
|
||||
|
||||
// Number of bits to store the pointer to next node
|
||||
#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE
|
||||
#define ARDUINOJSON_SLOT_OFFSET_SIZE 4
|
||||
#endif
|
||||
|
||||
#endif // ARDUINOJSON_EMBEDDED_MODE
|
||||
|
||||
#ifdef ARDUINO
|
||||
@ -155,7 +172,8 @@
|
||||
#endif // ARDUINO
|
||||
|
||||
#ifndef ARDUINOJSON_ENABLE_PROGMEM
|
||||
#ifdef PROGMEM
|
||||
#if defined(PROGMEM) && defined(pgm_read_byte) && defined(pgm_read_dword) && \
|
||||
defined(pgm_read_ptr) && defined(pgm_read_float)
|
||||
#define ARDUINOJSON_ENABLE_PROGMEM 1
|
||||
#else
|
||||
#define ARDUINOJSON_ENABLE_PROGMEM 0
|
||||
@ -230,3 +248,8 @@
|
||||
#define ARDUINOJSON_DEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_HAS_NULLPTR && defined(nullptr)
|
||||
#error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr
|
||||
// See https://github.com/bblanchon/ArduinoJson/issues/1355
|
||||
#endif
|
||||
|
@ -5,6 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/preprocessor.hpp>
|
||||
#include <ArduinoJson/Polyfills/static_array.hpp>
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include <ostream>
|
||||
@ -20,6 +22,7 @@ class DeserializationError {
|
||||
public:
|
||||
enum Code {
|
||||
Ok,
|
||||
EmptyInput,
|
||||
IncompleteInput,
|
||||
InvalidInput,
|
||||
NoMemory,
|
||||
@ -77,24 +80,31 @@ class DeserializationError {
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
switch (_code) {
|
||||
case Ok:
|
||||
return "Ok";
|
||||
case TooDeep:
|
||||
return "TooDeep";
|
||||
case NoMemory:
|
||||
return "NoMemory";
|
||||
case InvalidInput:
|
||||
return "InvalidInput";
|
||||
case IncompleteInput:
|
||||
return "IncompleteInput";
|
||||
case NotSupported:
|
||||
return "NotSupported";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
static const char* messages[] = {
|
||||
"Ok", "EmptyInput", "IncompleteInput", "InvalidInput",
|
||||
"NoMemory", "NotSupported", "TooDeep"};
|
||||
ARDUINOJSON_ASSERT(static_cast<size_t>(_code) <
|
||||
sizeof(messages) / sizeof(messages[0]));
|
||||
return messages[_code];
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_ENABLE_PROGMEM
|
||||
const __FlashStringHelper* f_str() const {
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s0, "Ok");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s1, "EmptyInput");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s2, "IncompleteInput");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s3, "InvalidInput");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s4, "NoMemory");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s5, "NotSupported");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s6, "TooDeep");
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(
|
||||
const char*, messages,
|
||||
ARDUINOJSON_EXPAND7({s0, s1, s2, s3, s4, s5, s6}));
|
||||
return ARDUINOJSON_READ_STATIC_ARRAY(const __FlashStringHelper*, messages,
|
||||
_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
Code _code;
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ class Filter {
|
||||
if (_variant == true) // "true" means "allow recursively"
|
||||
return *this;
|
||||
else
|
||||
return Filter(_variant[key]);
|
||||
return Filter(_variant[key] | _variant["*"]);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
|
@ -32,8 +32,9 @@ deserialize(JsonDocument &doc, const TString &input, NestingLimit nestingLimit,
|
||||
TFilter filter) {
|
||||
Reader<TString> reader(input);
|
||||
doc.clear();
|
||||
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
|
||||
makeStringStorage(input))
|
||||
return makeDeserializer<TDeserializer>(
|
||||
doc.memoryPool(), reader,
|
||||
makeStringStorage(input, doc.memoryPool()))
|
||||
.parse(doc.data(), filter, nestingLimit);
|
||||
}
|
||||
//
|
||||
@ -47,8 +48,9 @@ DeserializationError deserialize(JsonDocument &doc, TChar *input,
|
||||
TFilter filter) {
|
||||
BoundedReader<TChar *> reader(input, inputSize);
|
||||
doc.clear();
|
||||
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
|
||||
makeStringStorage(input))
|
||||
return makeDeserializer<TDeserializer>(
|
||||
doc.memoryPool(), reader,
|
||||
makeStringStorage(input, doc.memoryPool()))
|
||||
.parse(doc.data(), filter, nestingLimit);
|
||||
}
|
||||
//
|
||||
@ -60,8 +62,9 @@ DeserializationError deserialize(JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit, TFilter filter) {
|
||||
Reader<TStream> reader(input);
|
||||
doc.clear();
|
||||
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
|
||||
makeStringStorage(input))
|
||||
return makeDeserializer<TDeserializer>(
|
||||
doc.memoryPool(), reader,
|
||||
makeStringStorage(input, doc.memoryPool()))
|
||||
.parse(doc.data(), filter, nestingLimit);
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class JsonDocument : public Visitable {
|
||||
public:
|
||||
template <typename Visitor>
|
||||
void accept(Visitor& visitor) const {
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(TVisitor& visitor) const {
|
||||
return getVariant().accept(visitor);
|
||||
}
|
||||
|
||||
@ -48,6 +48,10 @@ class JsonDocument : public Visitable {
|
||||
return _pool.size();
|
||||
}
|
||||
|
||||
bool overflowed() const {
|
||||
return _pool.overflowed();
|
||||
}
|
||||
|
||||
size_t nesting() const {
|
||||
return _data.nesting();
|
||||
}
|
||||
@ -81,6 +85,7 @@ class JsonDocument : public Visitable {
|
||||
return _pool;
|
||||
}
|
||||
|
||||
// for internal use only
|
||||
VariantData& data() {
|
||||
return _data;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ class JsonDeserializer {
|
||||
JsonDeserializer(MemoryPool &pool, TReader reader,
|
||||
TStringStorage stringStorage)
|
||||
: _stringStorage(stringStorage),
|
||||
_foundSomething(false),
|
||||
_latch(reader),
|
||||
_pool(&pool),
|
||||
_error(DeserializationError::Ok) {}
|
||||
@ -34,7 +35,7 @@ class JsonDeserializer {
|
||||
|
||||
if (!_error && _latch.last() != 0 && !variant.isEnclosed()) {
|
||||
// We don't detect trailing characters earlier, so we need to check now
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return DeserializationError::InvalidInput;
|
||||
}
|
||||
|
||||
return _error;
|
||||
@ -213,10 +214,8 @@ class JsonDeserializer {
|
||||
return false;
|
||||
|
||||
// Empty object?
|
||||
if (eat('}')) {
|
||||
_error = DeserializationError::Ok;
|
||||
return false;
|
||||
}
|
||||
if (eat('}'))
|
||||
return true;
|
||||
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
@ -243,7 +242,7 @@ class JsonDeserializer {
|
||||
if (!variant) {
|
||||
// Save key in memory pool.
|
||||
// This MUST be done before adding the slot.
|
||||
key = _stringStorage.save(_pool);
|
||||
key = _stringStorage.save();
|
||||
|
||||
// Allocate slot in object
|
||||
VariantSlot *slot = object.addSlot(_pool);
|
||||
@ -336,7 +335,7 @@ class JsonDeserializer {
|
||||
}
|
||||
|
||||
bool parseKey() {
|
||||
_stringStorage.startString(_pool);
|
||||
_stringStorage.startString();
|
||||
if (isQuote(current())) {
|
||||
return parseQuotedString();
|
||||
} else {
|
||||
@ -345,12 +344,11 @@ class JsonDeserializer {
|
||||
}
|
||||
|
||||
bool parseStringValue(VariantData &variant) {
|
||||
_stringStorage.startString(_pool);
|
||||
_stringStorage.startString();
|
||||
if (!parseQuotedString())
|
||||
return false;
|
||||
const char *value = _stringStorage.save(_pool);
|
||||
variant.setString(make_not_null(value),
|
||||
typename TStringStorage::storage_policy());
|
||||
const char *value = _stringStorage.save();
|
||||
variant.setStringPointer(value, typename TStringStorage::storage_policy());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -501,26 +499,12 @@ class JsonDeserializer {
|
||||
return true;
|
||||
}
|
||||
|
||||
ParsedNumber<Float, UInt> num;
|
||||
parseNumber<Float, UInt>(_buffer, num);
|
||||
|
||||
switch (num.type()) {
|
||||
case VALUE_IS_NEGATIVE_INTEGER:
|
||||
result.setNegativeInteger(num.uintValue);
|
||||
return true;
|
||||
|
||||
case VALUE_IS_POSITIVE_INTEGER:
|
||||
result.setPositiveInteger(num.uintValue);
|
||||
return true;
|
||||
|
||||
case VALUE_IS_FLOAT:
|
||||
result.setFloat(num.floatValue);
|
||||
return true;
|
||||
|
||||
default:
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
if (!parseNumber(_buffer, result)) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skipNumericValue() {
|
||||
@ -576,7 +560,8 @@ class JsonDeserializer {
|
||||
switch (current()) {
|
||||
// end of string
|
||||
case '\0':
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
_error = _foundSomething ? DeserializationError::IncompleteInput
|
||||
: DeserializationError::EmptyInput;
|
||||
return false;
|
||||
|
||||
// spaces
|
||||
@ -636,12 +621,14 @@ class JsonDeserializer {
|
||||
#endif
|
||||
|
||||
default:
|
||||
_foundSomething = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TStringStorage _stringStorage;
|
||||
bool _foundSomething;
|
||||
Latch<TReader> _latch;
|
||||
MemoryPool *_pool;
|
||||
char _buffer[64]; // using a member instead of a local variable because it
|
||||
@ -650,47 +637,60 @@ class JsonDeserializer {
|
||||
DeserializationError _error;
|
||||
};
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, const std::string&, ...)
|
||||
template <typename TInput>
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, const TInput &input,
|
||||
JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
template <typename TInput>
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, const TInput &input, Filter filter,
|
||||
JsonDocument &doc, const TString &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
template <typename TInput>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, const TInput &input,
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TString>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
// deserializeJson(JsonDocument&, const std::istream&, ...)
|
||||
template <typename TInput>
|
||||
//
|
||||
// deserializeJson(JsonDocument&, std::istream&, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TInput &input,
|
||||
JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
template <typename TInput>
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TInput &input, Filter filter,
|
||||
JsonDocument &doc, TStream &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
template <typename TInput>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TInput &input,
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, char*, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input,
|
||||
@ -698,19 +698,24 @@ DeserializationError deserializeJson(
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, char*, size_t, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize,
|
||||
@ -718,6 +723,7 @@ DeserializationError deserializeJson(
|
||||
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize, Filter filter,
|
||||
@ -725,6 +731,7 @@ DeserializationError deserializeJson(
|
||||
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TChar *input,
|
||||
size_t inputSize,
|
||||
|
@ -12,11 +12,11 @@
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class JsonSerializer {
|
||||
class JsonSerializer : public Visitor<size_t> {
|
||||
public:
|
||||
JsonSerializer(TWriter writer) : _formatter(writer) {}
|
||||
|
||||
FORCE_INLINE void visitArray(const CollectionData &array) {
|
||||
FORCE_INLINE size_t visitArray(const CollectionData &array) {
|
||||
write('[');
|
||||
|
||||
VariantSlot *slot = array.head();
|
||||
@ -32,9 +32,10 @@ class JsonSerializer {
|
||||
}
|
||||
|
||||
write(']');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitObject(const CollectionData &object) {
|
||||
size_t visitObject(const CollectionData &object) {
|
||||
write('{');
|
||||
|
||||
VariantSlot *slot = object.head();
|
||||
@ -52,41 +53,49 @@ class JsonSerializer {
|
||||
}
|
||||
|
||||
write('}');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitFloat(Float value) {
|
||||
size_t visitFloat(Float value) {
|
||||
_formatter.writeFloat(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitString(const char *value) {
|
||||
size_t visitString(const char *value) {
|
||||
_formatter.writeString(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitRawJson(const char *data, size_t n) {
|
||||
size_t visitRawJson(const char *data, size_t n) {
|
||||
_formatter.writeRaw(data, n);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitNegativeInteger(UInt value) {
|
||||
size_t visitNegativeInteger(UInt value) {
|
||||
_formatter.writeNegativeInteger(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitPositiveInteger(UInt value) {
|
||||
size_t visitPositiveInteger(UInt value) {
|
||||
_formatter.writePositiveInteger(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitBoolean(bool value) {
|
||||
size_t visitBoolean(bool value) {
|
||||
_formatter.writeBoolean(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitNull() {
|
||||
size_t visitNull() {
|
||||
_formatter.writeRaw("null");
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t bytesWritten() const {
|
||||
return _formatter.bytesWritten();
|
||||
}
|
||||
|
||||
protected:
|
||||
void write(char c) {
|
||||
_formatter.writeRaw(c);
|
||||
}
|
||||
|
@ -18,44 +18,48 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
public:
|
||||
PrettyJsonSerializer(TWriter &writer) : base(writer), _nesting(0) {}
|
||||
|
||||
void visitArray(const CollectionData &array) {
|
||||
size_t visitArray(const CollectionData &array) {
|
||||
VariantSlot *slot = array.head();
|
||||
if (!slot)
|
||||
return base::write("[]");
|
||||
if (slot) {
|
||||
base::write("[\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
indent();
|
||||
slot->data()->accept(*this);
|
||||
|
||||
base::write("[\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
base::write("]");
|
||||
} else {
|
||||
base::write("[]");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::write("]");
|
||||
return this->bytesWritten();
|
||||
}
|
||||
|
||||
void visitObject(const CollectionData &object) {
|
||||
size_t visitObject(const CollectionData &object) {
|
||||
VariantSlot *slot = object.head();
|
||||
if (!slot)
|
||||
return base::write("{}");
|
||||
if (slot) {
|
||||
base::write("{\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
indent();
|
||||
base::visitString(slot->key());
|
||||
base::write(": ");
|
||||
slot->data()->accept(*this);
|
||||
|
||||
base::write("{\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::visitString(slot->key());
|
||||
base::write(": ");
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
base::write("}");
|
||||
} else {
|
||||
base::write("{}");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::write("}");
|
||||
return this->bytesWritten();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -12,17 +12,18 @@
|
||||
#include <ArduinoJson/Numbers/Integer.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/attributes.hpp>
|
||||
#include <ArduinoJson/Serialization/CountingDecorator.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class TextFormatter {
|
||||
public:
|
||||
explicit TextFormatter(TWriter writer) : _writer(writer), _length(0) {}
|
||||
explicit TextFormatter(TWriter writer) : _writer(writer) {}
|
||||
|
||||
// Returns the number of bytes sent to the TWriter implementation.
|
||||
size_t bytesWritten() const {
|
||||
return _length;
|
||||
return _writer.count();
|
||||
}
|
||||
|
||||
void writeBoolean(bool value) {
|
||||
@ -128,28 +129,28 @@ class TextFormatter {
|
||||
}
|
||||
|
||||
void writeRaw(const char *s) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
|
||||
}
|
||||
|
||||
void writeRaw(const char *s, size_t n) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), n);
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(s), n);
|
||||
}
|
||||
|
||||
void writeRaw(const char *begin, const char *end) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(begin),
|
||||
static_cast<size_t>(end - begin));
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(begin),
|
||||
static_cast<size_t>(end - begin));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void writeRaw(const char (&s)[N]) {
|
||||
_length += _writer.write(reinterpret_cast<const uint8_t *>(s), N - 1);
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(s), N - 1);
|
||||
}
|
||||
void writeRaw(char c) {
|
||||
_length += _writer.write(static_cast<uint8_t>(c));
|
||||
_writer.write(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
protected:
|
||||
TWriter _writer;
|
||||
CountingDecorator<TWriter> _writer;
|
||||
size_t _length;
|
||||
|
||||
private:
|
||||
|
@ -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;
|
||||
|
@ -5,13 +5,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/Alignment.hpp>
|
||||
#include <ArduinoJson/Memory/StringSlot.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/mpl/max.hpp>
|
||||
#include <ArduinoJson/Variant/VariantSlot.hpp>
|
||||
|
||||
#include <string.h> // memmove
|
||||
|
||||
#define JSON_STRING_SIZE(SIZE) (SIZE + 1)
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
// _begin _end
|
||||
@ -28,7 +29,8 @@ class MemoryPool {
|
||||
: _begin(buf),
|
||||
_left(buf),
|
||||
_right(buf ? buf + capa : 0),
|
||||
_end(buf ? buf + capa : 0) {
|
||||
_end(buf ? buf + capa : 0),
|
||||
_overflowed(false) {
|
||||
ARDUINOJSON_ASSERT(isAligned(_begin));
|
||||
ARDUINOJSON_ASSERT(isAligned(_right));
|
||||
ARDUINOJSON_ASSERT(isAligned(_end));
|
||||
@ -47,6 +49,10 @@ class MemoryPool {
|
||||
return size_t(_left - _begin + _end - _right);
|
||||
}
|
||||
|
||||
bool overflowed() const {
|
||||
return _overflowed;
|
||||
}
|
||||
|
||||
VariantSlot* allocVariant() {
|
||||
return allocRight<VariantSlot>();
|
||||
}
|
||||
@ -90,9 +96,14 @@ class MemoryPool {
|
||||
return str;
|
||||
}
|
||||
|
||||
void markAsOverflowed() {
|
||||
_overflowed = true;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_left = _begin;
|
||||
_right = _end;
|
||||
_overflowed = false;
|
||||
}
|
||||
|
||||
bool canAlloc(size_t bytes) const {
|
||||
@ -143,10 +154,6 @@ class MemoryPool {
|
||||
}
|
||||
|
||||
private:
|
||||
StringSlot* allocStringSlot() {
|
||||
return allocRight<StringSlot>();
|
||||
}
|
||||
|
||||
void checkInvariants() {
|
||||
ARDUINOJSON_ASSERT(_begin <= _left);
|
||||
ARDUINOJSON_ASSERT(_left <= _right);
|
||||
@ -174,8 +181,10 @@ class MemoryPool {
|
||||
#endif
|
||||
|
||||
char* allocString(size_t n) {
|
||||
if (!canAlloc(n))
|
||||
if (!canAlloc(n)) {
|
||||
_overflowed = true;
|
||||
return 0;
|
||||
}
|
||||
char* s = _left;
|
||||
_left += n;
|
||||
checkInvariants();
|
||||
@ -188,13 +197,16 @@ class MemoryPool {
|
||||
}
|
||||
|
||||
void* allocRight(size_t bytes) {
|
||||
if (!canAlloc(bytes))
|
||||
if (!canAlloc(bytes)) {
|
||||
_overflowed = true;
|
||||
return 0;
|
||||
}
|
||||
_right -= bytes;
|
||||
return _right;
|
||||
}
|
||||
|
||||
char *_begin, *_left, *_right, *_end;
|
||||
bool _overflowed;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -8,6 +8,11 @@
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TResult>
|
||||
struct Visitor {
|
||||
typedef TResult result_type;
|
||||
};
|
||||
|
||||
struct Visitable {
|
||||
// template<Visitor>
|
||||
// void accept(Visitor&) const;
|
||||
|
@ -21,122 +21,244 @@ class MsgPackDeserializer {
|
||||
: _pool(&pool),
|
||||
_reader(reader),
|
||||
_stringStorage(stringStorage),
|
||||
_error(DeserializationError::Ok) {}
|
||||
_error(DeserializationError::Ok),
|
||||
_foundSomething(false) {}
|
||||
|
||||
// TODO: add support for filter
|
||||
DeserializationError parse(VariantData &variant, AllowAllFilter,
|
||||
template <typename TFilter>
|
||||
DeserializationError parse(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
parseVariant(variant, nestingLimit);
|
||||
return _error;
|
||||
parseVariant(variant, filter, nestingLimit);
|
||||
return _foundSomething ? _error : DeserializationError::EmptyInput;
|
||||
}
|
||||
|
||||
private:
|
||||
bool parseVariant(VariantData &variant, NestingLimit nestingLimit) {
|
||||
uint8_t code;
|
||||
if (!readByte(code)) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
// Prevent VS warning "assignment operator could not be generated"
|
||||
MsgPackDeserializer &operator=(const MsgPackDeserializer &);
|
||||
|
||||
bool invalidInput() {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool notSupported() {
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool parseVariant(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
uint8_t code = 0; // TODO: why do we need to initialize this variable?
|
||||
if (!readByte(code))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((code & 0x80) == 0) {
|
||||
variant.setUnsignedInteger(code);
|
||||
return true;
|
||||
}
|
||||
_foundSomething = true;
|
||||
|
||||
if ((code & 0xe0) == 0xe0) {
|
||||
variant.setSignedInteger(static_cast<int8_t>(code));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((code & 0xe0) == 0xa0) {
|
||||
return readString(variant, code & 0x1f);
|
||||
}
|
||||
|
||||
if ((code & 0xf0) == 0x90) {
|
||||
return readArray(variant.toArray(), code & 0x0F, nestingLimit);
|
||||
}
|
||||
|
||||
if ((code & 0xf0) == 0x80) {
|
||||
return readObject(variant.toObject(), code & 0x0F, nestingLimit);
|
||||
}
|
||||
bool allowValue = filter.allowValue();
|
||||
|
||||
switch (code) {
|
||||
case 0xc0:
|
||||
// already null
|
||||
return true;
|
||||
|
||||
case 0xc1:
|
||||
return invalidInput();
|
||||
|
||||
case 0xc2:
|
||||
variant.setBoolean(false);
|
||||
if (allowValue)
|
||||
variant.setBoolean(false);
|
||||
return true;
|
||||
|
||||
case 0xc3:
|
||||
variant.setBoolean(true);
|
||||
if (allowValue)
|
||||
variant.setBoolean(true);
|
||||
return true;
|
||||
|
||||
case 0xcc:
|
||||
return readInteger<uint8_t>(variant);
|
||||
case 0xc4: // bin 8
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipString<uint8_t>();
|
||||
|
||||
case 0xcd:
|
||||
return readInteger<uint16_t>(variant);
|
||||
case 0xc5: // bin 16
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipString<uint16_t>();
|
||||
|
||||
case 0xce:
|
||||
return readInteger<uint32_t>(variant);
|
||||
case 0xc6: // bin 32
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipString<uint32_t>();
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case 0xcf:
|
||||
return readInteger<uint64_t>(variant);
|
||||
#endif
|
||||
case 0xc7: // ext 8
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipExt<uint8_t>();
|
||||
|
||||
case 0xd0:
|
||||
return readInteger<int8_t>(variant);
|
||||
case 0xc8: // ext 16
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipExt<uint16_t>();
|
||||
|
||||
case 0xd1:
|
||||
return readInteger<int16_t>(variant);
|
||||
|
||||
case 0xd2:
|
||||
return readInteger<int32_t>(variant);
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case 0xd3:
|
||||
return readInteger<int64_t>(variant);
|
||||
#endif
|
||||
case 0xc9: // ext 32
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipExt<uint32_t>();
|
||||
|
||||
case 0xca:
|
||||
return readFloat<float>(variant);
|
||||
if (allowValue)
|
||||
return readFloat<float>(variant);
|
||||
else
|
||||
return skipBytes(4);
|
||||
|
||||
case 0xcb:
|
||||
return readDouble<double>(variant);
|
||||
if (allowValue)
|
||||
return readDouble<double>(variant);
|
||||
else
|
||||
return skipBytes(8);
|
||||
|
||||
case 0xcc:
|
||||
if (allowValue)
|
||||
return readInteger<uint8_t>(variant);
|
||||
else
|
||||
return skipBytes(1);
|
||||
|
||||
case 0xcd:
|
||||
if (allowValue)
|
||||
return readInteger<uint16_t>(variant);
|
||||
else
|
||||
return skipBytes(2);
|
||||
|
||||
case 0xce:
|
||||
if (allowValue)
|
||||
return readInteger<uint32_t>(variant);
|
||||
else
|
||||
return skipBytes(4);
|
||||
|
||||
case 0xcf:
|
||||
if (allowValue)
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
return readInteger<uint64_t>(variant);
|
||||
#else
|
||||
return notSupported();
|
||||
#endif
|
||||
else
|
||||
return skipBytes(8);
|
||||
|
||||
case 0xd0:
|
||||
if (allowValue)
|
||||
return readInteger<int8_t>(variant);
|
||||
else
|
||||
return skipBytes(1);
|
||||
|
||||
case 0xd1:
|
||||
if (allowValue)
|
||||
return readInteger<int16_t>(variant);
|
||||
else
|
||||
return skipBytes(2);
|
||||
|
||||
case 0xd2:
|
||||
if (allowValue)
|
||||
return readInteger<int32_t>(variant);
|
||||
else
|
||||
return skipBytes(4);
|
||||
|
||||
case 0xd3:
|
||||
if (allowValue)
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
return readInteger<int64_t>(variant);
|
||||
#else
|
||||
return notSupported();
|
||||
#endif
|
||||
else
|
||||
return skipBytes(8);
|
||||
|
||||
case 0xd4: // fixext 1
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipBytes(2);
|
||||
|
||||
case 0xd5: // fixext 2
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipBytes(3);
|
||||
|
||||
case 0xd6: // fixext 4
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipBytes(5);
|
||||
|
||||
case 0xd7: // fixext 8
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipBytes(9);
|
||||
|
||||
case 0xd8: // fixext 16
|
||||
if (allowValue)
|
||||
return notSupported();
|
||||
else
|
||||
return skipBytes(17);
|
||||
|
||||
case 0xd9:
|
||||
return readString<uint8_t>(variant);
|
||||
if (allowValue)
|
||||
return readString<uint8_t>(variant);
|
||||
else
|
||||
return skipString<uint8_t>();
|
||||
|
||||
case 0xda:
|
||||
return readString<uint16_t>(variant);
|
||||
if (allowValue)
|
||||
return readString<uint16_t>(variant);
|
||||
else
|
||||
return skipString<uint16_t>();
|
||||
|
||||
case 0xdb:
|
||||
return readString<uint32_t>(variant);
|
||||
if (allowValue)
|
||||
return readString<uint32_t>(variant);
|
||||
else
|
||||
return skipString<uint32_t>();
|
||||
|
||||
case 0xdc:
|
||||
return readArray<uint16_t>(variant.toArray(), nestingLimit);
|
||||
return readArray<uint16_t>(variant, filter, nestingLimit);
|
||||
|
||||
case 0xdd:
|
||||
return readArray<uint32_t>(variant.toArray(), nestingLimit);
|
||||
return readArray<uint32_t>(variant, filter, nestingLimit);
|
||||
|
||||
case 0xde:
|
||||
return readObject<uint16_t>(variant.toObject(), nestingLimit);
|
||||
return readObject<uint16_t>(variant, filter, nestingLimit);
|
||||
|
||||
case 0xdf:
|
||||
return readObject<uint32_t>(variant.toObject(), nestingLimit);
|
||||
|
||||
default:
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
return readObject<uint32_t>(variant, filter, nestingLimit);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Prevent VS warning "assignment operator could not be generated"
|
||||
MsgPackDeserializer &operator=(const MsgPackDeserializer &);
|
||||
switch (code & 0xf0) {
|
||||
case 0x80:
|
||||
return readObject(variant, code & 0x0F, filter, nestingLimit);
|
||||
|
||||
case 0x90:
|
||||
return readArray(variant, code & 0x0F, filter, nestingLimit);
|
||||
}
|
||||
|
||||
if ((code & 0xe0) == 0xa0) {
|
||||
if (allowValue)
|
||||
return readString(variant, code & 0x1f);
|
||||
else
|
||||
return skipBytes(code & 0x1f);
|
||||
}
|
||||
|
||||
if (allowValue)
|
||||
variant.setInteger(static_cast<int8_t>(code));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readByte(uint8_t &value) {
|
||||
int c = _reader.read();
|
||||
@ -160,6 +282,16 @@ class MsgPackDeserializer {
|
||||
return readBytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
|
||||
}
|
||||
|
||||
bool skipBytes(size_t n) {
|
||||
for (; n; --n) {
|
||||
if (_reader.read() < 0) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool readInteger(T &value) {
|
||||
if (!readBytes(value))
|
||||
@ -222,24 +354,31 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool readString(const char *&str) {
|
||||
bool readString() {
|
||||
T size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
return readString(str, size);
|
||||
return readString(size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool skipString() {
|
||||
T size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
return skipBytes(size);
|
||||
}
|
||||
|
||||
bool readString(VariantData &variant, size_t n) {
|
||||
const char *s = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
|
||||
if (!readString(s, n))
|
||||
if (!readString(n))
|
||||
return false;
|
||||
variant.setString(make_not_null(s),
|
||||
typename TStringStorage::storage_policy());
|
||||
variant.setStringPointer(_stringStorage.save(),
|
||||
typename TStringStorage::storage_policy());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readString(const char *&result, size_t n) {
|
||||
_stringStorage.startString(_pool);
|
||||
bool readString(size_t n) {
|
||||
_stringStorage.startString();
|
||||
for (; n; --n) {
|
||||
uint8_t c;
|
||||
if (!readBytes(c))
|
||||
@ -252,131 +391,247 @@ class MsgPackDeserializer {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = _stringStorage.save(_pool);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TSize>
|
||||
bool readArray(CollectionData &array, NestingLimit nestingLimit) {
|
||||
template <typename TSize, typename TFilter>
|
||||
bool readArray(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
TSize size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
return readArray(array, size, nestingLimit);
|
||||
return readArray(variant, size, filter, nestingLimit);
|
||||
}
|
||||
|
||||
bool readArray(CollectionData &array, size_t n, NestingLimit nestingLimit) {
|
||||
template <typename TFilter>
|
||||
bool readArray(VariantData &variant, size_t n, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool allowArray = filter.allowArray();
|
||||
|
||||
CollectionData *array = allowArray ? &variant.toArray() : 0;
|
||||
|
||||
TFilter memberFilter = filter[0U];
|
||||
|
||||
for (; n; --n) {
|
||||
VariantData *value = array.addElement(_pool);
|
||||
if (!value) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
VariantData *value;
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
value = array->addElement(_pool);
|
||||
if (!value) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
if (!parseVariant(*value, nestingLimit.decrement()))
|
||||
if (!parseVariant(*value, memberFilter, nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TSize>
|
||||
bool readObject(CollectionData &object, NestingLimit nestingLimit) {
|
||||
template <typename TSize, typename TFilter>
|
||||
bool readObject(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
TSize size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
return readObject(object, size, nestingLimit);
|
||||
return readObject(variant, size, filter, nestingLimit);
|
||||
}
|
||||
|
||||
bool readObject(CollectionData &object, size_t n, NestingLimit nestingLimit) {
|
||||
template <typename TFilter>
|
||||
bool readObject(VariantData &variant, size_t n, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
CollectionData *object = filter.allowObject() ? &variant.toObject() : 0;
|
||||
|
||||
for (; n; --n) {
|
||||
VariantSlot *slot = object.addSlot(_pool);
|
||||
if (!slot) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
if (!readKey())
|
||||
return false;
|
||||
|
||||
const char *key = _stringStorage.c_str();
|
||||
TFilter memberFilter = filter[key];
|
||||
VariantData *member;
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
// Save key in memory pool.
|
||||
// This MUST be done before adding the slot.
|
||||
key = _stringStorage.save();
|
||||
|
||||
VariantSlot *slot = object->addSlot(_pool);
|
||||
if (!slot) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
slot->setKey(key, typename TStringStorage::storage_policy());
|
||||
|
||||
member = slot->data();
|
||||
} else {
|
||||
member = 0;
|
||||
}
|
||||
|
||||
const char *key = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
|
||||
if (!parseKey(key))
|
||||
return false;
|
||||
|
||||
slot->setKey(key, typename TStringStorage::storage_policy());
|
||||
|
||||
if (!parseVariant(*slot->data(), nestingLimit.decrement()))
|
||||
if (!parseVariant(*member, memberFilter, nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseKey(const char *&key) {
|
||||
bool readKey() {
|
||||
uint8_t code;
|
||||
if (!readByte(code))
|
||||
return false;
|
||||
|
||||
if ((code & 0xe0) == 0xa0)
|
||||
return readString(key, code & 0x1f);
|
||||
return readString(code & 0x1f);
|
||||
|
||||
switch (code) {
|
||||
case 0xd9:
|
||||
return readString<uint8_t>(key);
|
||||
return readString<uint8_t>();
|
||||
|
||||
case 0xda:
|
||||
return readString<uint16_t>(key);
|
||||
return readString<uint16_t>();
|
||||
|
||||
case 0xdb:
|
||||
return readString<uint32_t>(key);
|
||||
return readString<uint32_t>();
|
||||
|
||||
default:
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
return notSupported();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool skipExt() {
|
||||
T size;
|
||||
if (!readInteger(size))
|
||||
return false;
|
||||
return skipBytes(size + 1);
|
||||
}
|
||||
|
||||
MemoryPool *_pool;
|
||||
TReader _reader;
|
||||
TStringStorage _stringStorage;
|
||||
DeserializationError _error;
|
||||
bool _foundSomething;
|
||||
};
|
||||
|
||||
template <typename TInput>
|
||||
//
|
||||
// deserializeMsgPack(JsonDocument&, const std::string&, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, const TInput &input,
|
||||
JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
|
||||
template <typename TInput>
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TInput *input,
|
||||
JsonDocument &doc, const TString &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TString>
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeMsgPack(JsonDocument&, std::istream&, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
|
||||
template <typename TInput>
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TInput *input, size_t inputSize,
|
||||
JsonDocument &doc, TStream &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeMsgPack(JsonDocument&, char*, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeMsgPack(JsonDocument&, char*, size_t, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
|
||||
template <typename TInput>
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(
|
||||
JsonDocument &doc, TInput &input,
|
||||
JsonDocument &doc, TChar *input, size_t inputSize, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeMsgPack(JsonDocument &doc, TChar *input,
|
||||
size_t inputSize,
|
||||
NestingLimit nestingLimit,
|
||||
Filter filter) {
|
||||
return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <ArduinoJson/MsgPack/endianess.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Serialization/CountingDecorator.hpp>
|
||||
#include <ArduinoJson/Serialization/measure.hpp>
|
||||
#include <ArduinoJson/Serialization/serialize.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
@ -14,19 +15,20 @@
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class MsgPackSerializer {
|
||||
class MsgPackSerializer : public Visitor<size_t> {
|
||||
public:
|
||||
MsgPackSerializer(TWriter writer) : _writer(writer), _bytesWritten(0) {}
|
||||
MsgPackSerializer(TWriter writer) : _writer(writer) {}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 4>::type visitFloat(T value32) {
|
||||
typename enable_if<sizeof(T) == 4, size_t>::type visitFloat(T value32) {
|
||||
writeByte(0xCA);
|
||||
writeInteger(value32);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ARDUINOJSON_NO_SANITIZE("float-cast-overflow")
|
||||
typename enable_if<sizeof(T) == 8>::type visitFloat(T value64) {
|
||||
typename enable_if<sizeof(T) == 8, size_t>::type visitFloat(T value64) {
|
||||
float value32 = float(value64);
|
||||
if (value32 == value64) {
|
||||
writeByte(0xCA);
|
||||
@ -35,9 +37,10 @@ class MsgPackSerializer {
|
||||
writeByte(0xCB);
|
||||
writeInteger(value64);
|
||||
}
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitArray(const CollectionData& array) {
|
||||
size_t visitArray(const CollectionData& array) {
|
||||
size_t n = array.size();
|
||||
if (n < 0x10) {
|
||||
writeByte(uint8_t(0x90 + array.size()));
|
||||
@ -51,9 +54,10 @@ class MsgPackSerializer {
|
||||
for (VariantSlot* slot = array.head(); slot; slot = slot->next()) {
|
||||
slot->data()->accept(*this);
|
||||
}
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitObject(const CollectionData& object) {
|
||||
size_t visitObject(const CollectionData& object) {
|
||||
size_t n = object.size();
|
||||
if (n < 0x10) {
|
||||
writeByte(uint8_t(0x80 + n));
|
||||
@ -68,9 +72,10 @@ class MsgPackSerializer {
|
||||
visitString(slot->key());
|
||||
slot->data()->accept(*this);
|
||||
}
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitString(const char* value) {
|
||||
size_t visitString(const char* value) {
|
||||
ARDUINOJSON_ASSERT(value != NULL);
|
||||
|
||||
size_t n = strlen(value);
|
||||
@ -88,13 +93,15 @@ class MsgPackSerializer {
|
||||
writeInteger(uint32_t(n));
|
||||
}
|
||||
writeBytes(reinterpret_cast<const uint8_t*>(value), n);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitRawJson(const char* data, size_t size) {
|
||||
size_t visitRawJson(const char* data, size_t size) {
|
||||
writeBytes(reinterpret_cast<const uint8_t*>(data), size);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitNegativeInteger(UInt value) {
|
||||
size_t visitNegativeInteger(UInt value) {
|
||||
UInt negated = UInt(~value + 1);
|
||||
if (value <= 0x20) {
|
||||
writeInteger(int8_t(negated));
|
||||
@ -114,9 +121,10 @@ class MsgPackSerializer {
|
||||
writeInteger(int64_t(negated));
|
||||
}
|
||||
#endif
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitPositiveInteger(UInt value) {
|
||||
size_t visitPositiveInteger(UInt value) {
|
||||
if (value <= 0x7F) {
|
||||
writeInteger(uint8_t(value));
|
||||
} else if (value <= 0xFF) {
|
||||
@ -141,27 +149,30 @@ class MsgPackSerializer {
|
||||
writeInteger(uint64_t(value));
|
||||
}
|
||||
#endif
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitBoolean(bool value) {
|
||||
size_t visitBoolean(bool value) {
|
||||
writeByte(value ? 0xC3 : 0xC2);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
void visitNull() {
|
||||
size_t visitNull() {
|
||||
writeByte(0xC0);
|
||||
}
|
||||
|
||||
size_t bytesWritten() const {
|
||||
return _bytesWritten;
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t bytesWritten() const {
|
||||
return _writer.count();
|
||||
}
|
||||
|
||||
void writeByte(uint8_t c) {
|
||||
_bytesWritten += _writer.write(c);
|
||||
_writer.write(c);
|
||||
}
|
||||
|
||||
void writeBytes(const uint8_t* p, size_t n) {
|
||||
_bytesWritten += _writer.write(p, n);
|
||||
_writer.write(p, n);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -170,8 +181,7 @@ class MsgPackSerializer {
|
||||
writeBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
|
||||
}
|
||||
|
||||
TWriter _writer;
|
||||
size_t _bytesWritten;
|
||||
CountingDecorator<TWriter> _writer;
|
||||
};
|
||||
|
||||
template <typename TSource, typename TDestination>
|
||||
|
@ -5,45 +5,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Polyfills/preprocessor.hpp>
|
||||
#include <ArduinoJson/version.hpp>
|
||||
|
||||
#ifndef ARDUINOJSON_NAMESPACE
|
||||
|
||||
#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_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, \
|
||||
#define ARDUINOJSON_NAMESPACE \
|
||||
ARDUINOJSON_CONCAT4( \
|
||||
ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \
|
||||
ARDUINOJSON_VERSION_MINOR, \
|
||||
ARDUINOJSON_VERSION_REVISION), \
|
||||
_, \
|
||||
ARDUINOJSON_HEX_DIGIT(ARDUINOJSON_ENABLE_PROGMEM, \
|
||||
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
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Polyfills/alias_cast.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
#include <ArduinoJson/Polyfills/preprocessor.hpp>
|
||||
#include <ArduinoJson/Polyfills/static_array.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
@ -46,48 +48,60 @@ struct FloatTraits<T, 8 /*64bits*/> {
|
||||
}
|
||||
|
||||
static T positiveBinaryPowerOfTen(int index) {
|
||||
static T factors[] = {
|
||||
1e1,
|
||||
1e2,
|
||||
1e4,
|
||||
1e8,
|
||||
1e16,
|
||||
forge(0x4693B8B5, 0xB5056E17), // 1e32
|
||||
forge(0x4D384F03, 0xE93FF9F5), // 1e64
|
||||
forge(0x5A827748, 0xF9301D32), // 1e128
|
||||
forge(0x75154FDD, 0x7F73BF3C) // 1e256
|
||||
};
|
||||
return factors[index];
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY( //
|
||||
uint32_t, factors,
|
||||
ARDUINOJSON_EXPAND18({
|
||||
0x40240000, 0x00000000, // 1e1
|
||||
0x40590000, 0x00000000, // 1e2
|
||||
0x40C38800, 0x00000000, // 1e4
|
||||
0x4197D784, 0x00000000, // 1e8
|
||||
0x4341C379, 0x37E08000, // 1e16
|
||||
0x4693B8B5, 0xB5056E17, // 1e32
|
||||
0x4D384F03, 0xE93FF9F5, // 1e64
|
||||
0x5A827748, 0xF9301D32, // 1e128
|
||||
0x75154FDD, 0x7F73BF3C // 1e256
|
||||
}));
|
||||
return forge(
|
||||
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
|
||||
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
|
||||
}
|
||||
|
||||
static T negativeBinaryPowerOfTen(int index) {
|
||||
static T factors[] = {
|
||||
forge(0x3FB99999, 0x9999999A), // 1e-1
|
||||
forge(0x3F847AE1, 0x47AE147B), // 1e-2
|
||||
forge(0x3F1A36E2, 0xEB1C432D), // 1e-4
|
||||
forge(0x3E45798E, 0xE2308C3A), // 1e-8
|
||||
forge(0x3C9CD2B2, 0x97D889BC), // 1e-16
|
||||
forge(0x3949F623, 0xD5A8A733), // 1e-32
|
||||
forge(0x32A50FFD, 0x44F4A73D), // 1e-64
|
||||
forge(0x255BBA08, 0xCF8C979D), // 1e-128
|
||||
forge(0x0AC80628, 0x64AC6F43) // 1e-256
|
||||
};
|
||||
return factors[index];
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY( //
|
||||
uint32_t, factors,
|
||||
ARDUINOJSON_EXPAND18({
|
||||
0x3FB99999, 0x9999999A, // 1e-1
|
||||
0x3F847AE1, 0x47AE147B, // 1e-2
|
||||
0x3F1A36E2, 0xEB1C432D, // 1e-4
|
||||
0x3E45798E, 0xE2308C3A, // 1e-8
|
||||
0x3C9CD2B2, 0x97D889BC, // 1e-16
|
||||
0x3949F623, 0xD5A8A733, // 1e-32
|
||||
0x32A50FFD, 0x44F4A73D, // 1e-64
|
||||
0x255BBA08, 0xCF8C979D, // 1e-128
|
||||
0x0AC80628, 0x64AC6F43 // 1e-256
|
||||
}));
|
||||
return forge(
|
||||
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
|
||||
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
|
||||
}
|
||||
|
||||
static T negativeBinaryPowerOfTenPlusOne(int index) {
|
||||
static T factors[] = {
|
||||
1e0,
|
||||
forge(0x3FB99999, 0x9999999A), // 1e-1
|
||||
forge(0x3F50624D, 0xD2F1A9FC), // 1e-3
|
||||
forge(0x3E7AD7F2, 0x9ABCAF48), // 1e-7
|
||||
forge(0x3CD203AF, 0x9EE75616), // 1e-15
|
||||
forge(0x398039D6, 0x65896880), // 1e-31
|
||||
forge(0x32DA53FC, 0x9631D10D), // 1e-63
|
||||
forge(0x25915445, 0x81B7DEC2), // 1e-127
|
||||
forge(0x0AFE07B2, 0x7DD78B14) // 1e-255
|
||||
};
|
||||
return factors[index];
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY( //
|
||||
uint32_t, factors,
|
||||
ARDUINOJSON_EXPAND18({
|
||||
0x3FF00000, 0x00000000, // 1e0
|
||||
0x3FB99999, 0x9999999A, // 1e-1
|
||||
0x3F50624D, 0xD2F1A9FC, // 1e-3
|
||||
0x3E7AD7F2, 0x9ABCAF48, // 1e-7
|
||||
0x3CD203AF, 0x9EE75616, // 1e-15
|
||||
0x398039D6, 0x65896880, // 1e-31
|
||||
0x32DA53FC, 0x9631D10D, // 1e-63
|
||||
0x25915445, 0x81B7DEC2, // 1e-127
|
||||
0x0AFE07B2, 0x7DD78B14 // 1e-255
|
||||
}));
|
||||
return forge(
|
||||
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
|
||||
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
|
||||
}
|
||||
|
||||
static T nan() {
|
||||
@ -144,18 +158,24 @@ struct FloatTraits<T, 4 /*32bits*/> {
|
||||
}
|
||||
|
||||
static T positiveBinaryPowerOfTen(int index) {
|
||||
static T factors[] = {1e1f, 1e2f, 1e4f, 1e8f, 1e16f, 1e32f};
|
||||
return factors[index];
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(
|
||||
T, factors,
|
||||
ARDUINOJSON_EXPAND6({1e1f, 1e2f, 1e4f, 1e8f, 1e16f, 1e32f}));
|
||||
return ARDUINOJSON_READ_STATIC_ARRAY(T, factors, index);
|
||||
}
|
||||
|
||||
static T negativeBinaryPowerOfTen(int index) {
|
||||
static T factors[] = {1e-1f, 1e-2f, 1e-4f, 1e-8f, 1e-16f, 1e-32f};
|
||||
return factors[index];
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(
|
||||
T, factors,
|
||||
ARDUINOJSON_EXPAND6({1e-1f, 1e-2f, 1e-4f, 1e-8f, 1e-16f, 1e-32f}));
|
||||
return ARDUINOJSON_READ_STATIC_ARRAY(T, factors, index);
|
||||
}
|
||||
|
||||
static T negativeBinaryPowerOfTenPlusOne(int index) {
|
||||
static T factors[] = {1e0f, 1e-1f, 1e-3f, 1e-7f, 1e-15f, 1e-31f};
|
||||
return factors[index];
|
||||
ARDUINOJSON_DEFINE_STATIC_ARRAY(
|
||||
T, factors,
|
||||
ARDUINOJSON_EXPAND6({1e0f, 1e-1f, 1e-3f, 1e-7f, 1e-15f, 1e-31f}));
|
||||
return ARDUINOJSON_READ_STATIC_ARRAY(T, factors, index);
|
||||
}
|
||||
|
||||
static T forge(uint32_t bits) {
|
||||
|
@ -1,20 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
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;
|
||||
ParsedNumber<TFloat, UInt> value;
|
||||
parseNumber(s, value);
|
||||
return value.template as<T>();
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -1,21 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Numbers/convertNumber.hpp>
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename T>
|
||||
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;
|
||||
ParsedNumber<Float, TUInt> value;
|
||||
parseNumber(s, value);
|
||||
return value.template as<T>();
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -10,59 +10,18 @@
|
||||
#include <ArduinoJson/Polyfills/ctype.hpp>
|
||||
#include <ArduinoJson/Polyfills/math.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Variant/VariantContent.hpp>
|
||||
#include <ArduinoJson/Variant/VariantAs.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TFloat, typename TUInt>
|
||||
struct ParsedNumber {
|
||||
ParsedNumber() : _type(VALUE_IS_NULL) {}
|
||||
|
||||
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 {
|
||||
switch (_type) {
|
||||
case VALUE_IS_NEGATIVE_INTEGER:
|
||||
return convertNegativeInteger<T>(uintValue);
|
||||
case VALUE_IS_POSITIVE_INTEGER:
|
||||
return convertPositiveInteger<T>(uintValue);
|
||||
case VALUE_IS_FLOAT:
|
||||
return convertFloat<T>(floatValue);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t type() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
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 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;
|
||||
inline bool parseNumber(const char* s, VariantData& result) {
|
||||
typedef FloatTraits<Float> traits;
|
||||
typedef choose_largest<traits::mantissa_type, UInt>::type mantissa_t;
|
||||
typedef traits::exponent_type exponent_t;
|
||||
|
||||
ARDUINOJSON_ASSERT(s != 0);
|
||||
|
||||
@ -80,24 +39,23 @@ inline void parseNumber(const char* s, ParsedNumber<TFloat, TUInt>& result) {
|
||||
#if ARDUINOJSON_ENABLE_NAN
|
||||
if (*s == 'n' || *s == 'N') {
|
||||
result.setFloat(traits::nan());
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_INFINITY
|
||||
if (*s == 'i' || *s == 'I') {
|
||||
result.setFloat(is_negative ? -traits::inf() : traits::inf());
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!isdigit(*s) && *s != '.')
|
||||
return;
|
||||
return false;
|
||||
|
||||
mantissa_t mantissa = 0;
|
||||
exponent_t exponent_offset = 0;
|
||||
const mantissa_t maxUint = TUInt(-1);
|
||||
const mantissa_t maxUint = UInt(-1);
|
||||
|
||||
while (isdigit(*s)) {
|
||||
uint8_t digit = uint8_t(*s - '0');
|
||||
@ -111,8 +69,11 @@ inline void parseNumber(const char* s, ParsedNumber<TFloat, TUInt>& result) {
|
||||
}
|
||||
|
||||
if (*s == '\0') {
|
||||
result.setInteger(TUInt(mantissa), is_negative);
|
||||
return;
|
||||
if (is_negative)
|
||||
result.setNegativeInteger(UInt(mantissa));
|
||||
else
|
||||
result.setPositiveInteger(UInt(mantissa));
|
||||
return true;
|
||||
}
|
||||
|
||||
// avoid mantissa overflow
|
||||
@ -156,7 +117,7 @@ inline void parseNumber(const char* s, ParsedNumber<TFloat, TUInt>& result) {
|
||||
result.setFloat(is_negative ? -0.0f : 0.0f);
|
||||
else
|
||||
result.setFloat(is_negative ? -traits::inf() : traits::inf());
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@ -167,11 +128,20 @@ inline void parseNumber(const char* s, ParsedNumber<TFloat, TUInt>& result) {
|
||||
|
||||
// we should be at the end of the string, otherwise it's an error
|
||||
if (*s != '\0')
|
||||
return;
|
||||
return false;
|
||||
|
||||
TFloat final_result =
|
||||
traits::make_float(static_cast<TFloat>(mantissa), exponent);
|
||||
Float final_result =
|
||||
traits::make_float(static_cast<Float>(mantissa), exponent);
|
||||
|
||||
result.setFloat(is_negative ? -final_result : final_result);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T parseNumber(const char* s) {
|
||||
VariantData value;
|
||||
value.init(); // VariantData is a POD, so it has no constructor
|
||||
parseNumber(s, value);
|
||||
return variantAs<T>(&value);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -21,10 +21,13 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename TObject, typename TStringRef>
|
||||
class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
|
||||
public VariantShortcuts<MemberProxy<TObject, TStringRef> >,
|
||||
public Visitable {
|
||||
public Visitable,
|
||||
public VariantTag {
|
||||
typedef MemberProxy<TObject, TStringRef> this_type;
|
||||
|
||||
public:
|
||||
typedef VariantRef variant_type;
|
||||
|
||||
FORCE_INLINE MemberProxy(TObject variant, TStringRef key)
|
||||
: _object(variant), _key(key) {}
|
||||
|
||||
@ -120,8 +123,8 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
|
||||
return getOrAddUpstreamMember().set(value);
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
void accept(Visitor &visitor) const {
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(TVisitor &visitor) const {
|
||||
return getUpstreamMember().accept(visitor);
|
||||
}
|
||||
|
||||
|
@ -8,12 +8,13 @@
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename Visitor>
|
||||
void objectAccept(const CollectionData *obj, Visitor &visitor) {
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type objectAccept(const CollectionData *obj,
|
||||
TVisitor &visitor) {
|
||||
if (obj)
|
||||
visitor.visitObject(*obj);
|
||||
return visitor.visitObject(*obj);
|
||||
else
|
||||
visitor.visitNull();
|
||||
return visitor.visitNull();
|
||||
}
|
||||
|
||||
inline bool objectEquals(const CollectionData *lhs, const CollectionData *rhs) {
|
||||
|
@ -22,9 +22,9 @@ class ObjectRefBase {
|
||||
return VariantConstRef(reinterpret_cast<const VariantData*>(data));
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
FORCE_INLINE void accept(Visitor& visitor) const {
|
||||
objectAccept(_data, visitor);
|
||||
template <typename TVisitor>
|
||||
typename TVisitor::result_type accept(TVisitor& visitor) const {
|
||||
return objectAccept(_data, visitor);
|
||||
}
|
||||
|
||||
FORCE_INLINE bool isNull() const {
|
||||
|
@ -1,34 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T>
|
||||
class not_null {
|
||||
public:
|
||||
explicit not_null(T ptr) : _ptr(ptr) {
|
||||
ARDUINOJSON_ASSERT(ptr != NULL);
|
||||
}
|
||||
|
||||
T get() const {
|
||||
ARDUINOJSON_ASSERT(_ptr != NULL);
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T _ptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
not_null<T> make_not_null(T ptr) {
|
||||
ARDUINOJSON_ASSERT(ptr != NULL);
|
||||
return not_null<T>(ptr);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
30
src/ArduinoJson/Polyfills/integer.hpp
Normal file
30
src/ArduinoJson/Polyfills/integer.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // int8_t, int16_t
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <int Bits>
|
||||
struct int_t;
|
||||
|
||||
template <>
|
||||
struct int_t<8> {
|
||||
typedef int8_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct int_t<16> {
|
||||
typedef int16_t type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct int_t<32> {
|
||||
typedef int32_t type;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
|
||||
|
32
src/ArduinoJson/Polyfills/pgmspace_generic.hpp
Normal file
32
src/ArduinoJson/Polyfills/pgmspace_generic.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_pointer<T>::value, T>::type pgm_read(const void* p) {
|
||||
return reinterpret_cast<T>(pgm_read_ptr(p));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_floating_point<T>::value &&
|
||||
sizeof(T) == sizeof(float), // on AVR sizeof(double) ==
|
||||
// sizeof(float)
|
||||
T>::type
|
||||
pgm_read(const void* p) {
|
||||
return pgm_read_float(p);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_same<T, uint32_t>::value, T>::type pgm_read(
|
||||
const void* p) {
|
||||
return pgm_read_dword(p);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
36
src/ArduinoJson/Polyfills/preprocessor.hpp
Normal file
36
src/ArduinoJson/Polyfills/preprocessor.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ARDUINOJSON_EXPAND6(a, b, c, d, e, f) a, b, c, d, e, f
|
||||
#define ARDUINOJSON_EXPAND7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
|
||||
#define ARDUINOJSON_EXPAND9(a, b, c, d, e, f, g, h, i) a, b, c, d, e, f, g, h, i
|
||||
#define ARDUINOJSON_EXPAND18(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, \
|
||||
q, r) \
|
||||
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r
|
||||
|
||||
#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_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)
|
34
src/ArduinoJson/Polyfills/static_array.hpp
Normal file
34
src/ArduinoJson/Polyfills/static_array.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
|
||||
#if ARDUINOJSON_ENABLE_PROGMEM
|
||||
|
||||
#include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
|
||||
|
||||
#ifndef ARDUINOJSON_DEFINE_STATIC_ARRAY
|
||||
#define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \
|
||||
static type const name[] PROGMEM = value;
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_READ_STATIC_ARRAY
|
||||
#define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) \
|
||||
pgm_read<type>(name + index)
|
||||
#endif
|
||||
|
||||
#else // i.e. ARDUINOJSON_ENABLE_PROGMEM == 0
|
||||
|
||||
#ifndef ARDUINOJSON_DEFINE_STATIC_ARRAY
|
||||
#define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \
|
||||
static type const name[] = value;
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_READ_STATIC_ARRAY
|
||||
#define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) name[index]
|
||||
#endif
|
||||
|
||||
#endif
|
@ -15,6 +15,7 @@
|
||||
#include "type_traits/is_enum.hpp"
|
||||
#include "type_traits/is_floating_point.hpp"
|
||||
#include "type_traits/is_integral.hpp"
|
||||
#include "type_traits/is_pointer.hpp"
|
||||
#include "type_traits/is_same.hpp"
|
||||
#include "type_traits/is_signed.hpp"
|
||||
#include "type_traits/is_unsigned.hpp"
|
||||
|
16
src/ArduinoJson/Polyfills/type_traits/is_pointer.hpp
Normal file
16
src/ArduinoJson/Polyfills/type_traits/is_pointer.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "integral_constant.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T>
|
||||
struct is_pointer : false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_pointer<T*> : true_type {};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user