Compare commits

..

38 Commits

Author SHA1 Message Date
1360b6a396 Set version to 6.17.2 2020-11-14 10:38:21 +01:00
16e51b83ab Changed the default value of ARDUINOJSON_ENABLE_PROGMEM (fixes #1433)
It now checks that the pgm_read_XXX macros are defined before enabling PROGMEM.
2020-11-10 14:58:31 +01:00
aa7cc5351c Travis: added smoke test for Particle Argon 2020-11-10 14:46:45 +01:00
30da920135 Fixed invalid conversion in operator|(Variant, char*) (fixes #1432) 2020-11-09 09:21:19 +01:00
bcdf5b7e52 Set version to 6.17.1 2020-11-07 10:11:41 +01:00
c711fe592d Allowed more than 32767 values in non-embedded mode (fixes #1414) 2020-10-28 09:59:40 +01:00
9d5c1b3742 Added an assert to detect too large variants (issue #1414) 2020-10-24 09:39:03 +02:00
6b26cd0977 Add JsonDocument to keywords.txt
see #1413
2020-10-24 09:37:16 +02:00
10ec0f21b0 Fixed operator|(MemberProxy, JsonObject) (fixes #1415) 2020-10-23 10:48:33 +02:00
ff66182dc6 Gathered the tests of MemberProxy in one file 2020-10-22 09:37:05 +02:00
d02ce2f1bb Gathered the tests of ElementProxy in one file 2020-10-22 09:37:05 +02:00
2664a2d0da Fixed error "ambiguous overload for operator|" (fixes #1411) 2020-10-22 09:30:31 +02:00
1f7350658e Set version to 6.17.0 2020-10-19 11:35:09 +02:00
41132b701b Fixed error "no matching function for pgm_read<double>()" on AVR 2020-10-17 14:02:26 +02:00
712005219c Added filtering for MessagePack (closes #1298, closes #1394) 2020-10-13 09:40:39 +02:00
0bd17aff8a Coverage: use -O0 instead of -Og 2020-10-11 15:10:52 +02:00
f3f44d7812 Renamed StdStringAdapter to StlStringAdapter (internal use only) 2020-10-01 10:01:33 +02:00
8385d5fa3a Added wildcard key (*) for filters (closes #1309) 2020-09-28 21:11:38 +02:00
726f8be341 Added operator|(JsonVariantConst, JsonVariantConst) 2020-09-26 14:48:17 +02:00
fee029b86e Moved float tables to PROGMEM 2020-09-19 16:12:49 +02:00
c3504ddf0a Added tests for DeserializationError::f_str() 2020-09-18 19:06:21 +02:00
6a878ee444 Fixed error "No such file or directory WString.h" (fixes #1381) 2020-09-16 10:29:20 +02:00
c4ec2ba88f Added DeserializationError::f_str() (issue #846) 2020-09-14 18:40:00 +02:00
c907ca6e5d Added DeserializationError::EmptyInput 2020-09-13 10:27:29 +02:00
8993a093e9 Travis: run sanitizer on recent GCC version to avoid false positives 2020-09-13 10:25:34 +02:00
d04669d0cc Fixed result of JsonVariant::set((char*)0) (fixes #1368) 2020-09-05 17:33:47 +02:00
05fc136915 Disabled memory sanitizer with clang 6 2020-09-05 13:44:40 +02:00
8d37939086 Added JsonDocument::overflowed() (closes #1358) 2020-09-05 13:44:40 +02:00
6d2ad4539f Simplified the implementation of parseNumber() 2020-09-04 09:23:40 +02:00
5ab53f42b2 Added a return value to visitors 2020-08-29 18:40:27 +02:00
f448e805e9 Removed dead code 2020-08-25 14:36:14 +02:00
67aa8efd5a Added a build failure when nullptr is defined as a macro (closes #1355) 2020-08-23 09:31:40 +02:00
61a5273aea Set version to 6.16.1 2020-08-04 10:33:38 +02:00
35a39b8d8f Fixed deserializeJson() that stopped reading after {} (fixes #1335) 2020-08-04 09:52:42 +02:00
96b6571352 Fixed use-of-uninitialized-value in encodeCodepoint() 2020-08-03 13:44:45 +02:00
74e7dd053f Added memory sanitizer to fuzzers 2020-08-03 13:44:45 +02:00
1f8636d762 Fixed build-arduino-package.sh to ignore .vs/ 2020-08-01 15:35:04 +02:00
e4cfa701d8 Fixed publish.sh to commit CMakeLists.txt 2020-08-01 15:34:27 +02:00
120 changed files with 3407 additions and 1526 deletions

4
.gitignore vendored
View File

@ -10,3 +10,7 @@
/extras/fuzzing/*_fuzzer.options
/extras/fuzzing/*_fuzzer_seed_corpus.zip
.vs/
# Used by CI for Particle
/src/*.ino
/project.properties

View File

@ -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"

View File

@ -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)

View File

@ -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)

View File

@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.16.0)](https://www.ardu-badge.com/ArduinoJson/6.16.0)
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.17.2)](https://www.ardu-badge.com/ArduinoJson/6.17.2)
[![Build Status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=6.x)](https://travis-ci.org/bblanchon/ArduinoJson)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)

View File

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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
View 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"

View File

@ -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 .

View File

@ -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()

View File

@ -0,0 +1 @@
name=ArduinoJsonCI

View File

@ -0,0 +1,5 @@
#include "ArduinoJson.h"
void setup() {}
void loop() {}

View File

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

View File

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

View File

@ -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)

View File

@ -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)

View File

@ -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\"]]");
}
}

View File

@ -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);
}
}

View File

@ -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]);
}
}

View File

@ -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
}

View File

@ -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\"]");
}
}

View File

@ -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);
}
}

View File

@ -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]]");
}
}

View 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"

View File

@ -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)));

View File

@ -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()) {

View File

@ -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]",

View File

@ -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") {

View File

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

View File

@ -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

View 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]]");
}
}

View 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]}");
}
}

View 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);
}
}

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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)

View File

@ -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\"]}");
}
}

View File

@ -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);
}
}

View File

@ -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"]);
}
}

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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\"}");
}
}

View File

@ -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);
}
}

View File

@ -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]}");
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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") {

View File

@ -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);
}

View File

@ -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
)

File diff suppressed because it is too large Load Diff

View 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);
}
}

View File

@ -4,6 +4,7 @@
add_executable(NumbersTests
parseFloat.cpp
parseDouble.cpp
parseInteger.cpp
parseNumber.cpp
)

View 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");
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));

View File

@ -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

View File

@ -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"

View File

@ -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++.

View File

@ -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) {

View File

@ -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 {

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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:

View File

@ -4,7 +4,7 @@
#pragma once
#include <Stream.h>
#include <Arduino.h>
namespace ARDUINOJSON_NAMESPACE {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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:

View File

@ -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:

View File

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

View File

@ -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

View File

@ -8,6 +8,11 @@
namespace ARDUINOJSON_NAMESPACE {
template <typename TResult>
struct Visitor {
typedef TResult result_type;
};
struct Visitable {
// template<Visitor>
// void accept(Visitor&) const;

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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

View 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

View File

@ -4,6 +4,7 @@
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>

View 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

View 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)

View 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

View File

@ -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"

View 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