mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-04 14:16:36 +02:00
Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
61a5273aea | |||
35a39b8d8f | |||
96b6571352 | |||
74e7dd053f | |||
1f8636d762 | |||
e4cfa701d8 | |||
b85181a6db | |||
6841b80466 | |||
298864bafe | |||
4d7f03836c | |||
c63eb80b95 | |||
0c0bf80074 | |||
144ff3b06e | |||
d6c50c3596 | |||
51b177ce47 | |||
824b7a25ca | |||
2223d40640 | |||
4df29fbac1 | |||
6dc36125c2 | |||
40085609e2 | |||
ebb6d80092 | |||
764ff2cd53 | |||
8ef226bcb8 | |||
63606c0985 | |||
1600d39693 | |||
04c59985a1 | |||
7e58347fbe | |||
0e794a28a1 | |||
6fb52c3638 | |||
b72ef09451 | |||
f7de027617 | |||
bc4c2dde33 | |||
95f05dad66 | |||
3bb0a7aa8e | |||
dcf7eeef28 | |||
3b923b6e4e | |||
8050f7404b | |||
959b1d9e4c | |||
daa279d57b | |||
ae9b7926a2 | |||
1f7a5e6943 | |||
9e354803de |
15
.travis.yml
15
.travis.yml
@ -107,10 +107,14 @@ matrix:
|
||||
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-8']
|
||||
packages: ['clang-8']
|
||||
env: SCRIPT=test _CC=clang-8 _CXX=clang++-8
|
||||
- addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
packages: ['clang-9']
|
||||
env: SCRIPT=test _CC=clang-9 _CXX=clang++-9
|
||||
- env: SCRIPT=coverage
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
env: SCRIPT=test
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
env: SCRIPT=test
|
||||
@ -124,11 +128,6 @@ matrix:
|
||||
- env: SCRIPT=arduino VERSION=1.8.2 BOARD=arduino:samd:mkr1000
|
||||
- env: SCRIPT=platformio BOARD=uno
|
||||
- env: SCRIPT=platformio BOARD=esp01
|
||||
- addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: ['clang-6.0','llvm-6.0']
|
||||
env: SCRIPT=fuzz CLANG=6.0
|
||||
cache:
|
||||
directories:
|
||||
- "~/.platformio"
|
||||
|
29
CHANGELOG.md
29
CHANGELOG.md
@ -1,6 +1,35 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v6.16.1 (2020-08-04)
|
||||
-------
|
||||
|
||||
* Fixed `deserializeJson()` that stopped reading after `{}` (issue #1335)
|
||||
|
||||
v6.16.0 (2020-08-01)
|
||||
-------
|
||||
|
||||
* Added comparisons (`>`, `>=`, `==`, `!=`, `<`, and `<=`) between `JsonVariant`s
|
||||
* Added string deduplication (issue #1303)
|
||||
* Added `JsonString::operator!=`
|
||||
* Set `ARDUINOJSON_DECODE_UNICODE` to `1` by default
|
||||
* Fixed `copyArray()` not working with `String`, `ElementProxy`, and `MemberProxy`
|
||||
* Fixed error `getOrAddElement is not a member of ElementProxy` (issue #1311)
|
||||
* Fixed excessive stack usage when compiled with `-Og` (issues #1210 and #1314)
|
||||
* Fixed `Warning[Pa093]: implicit conversion from floating point to integer` on IAR compiler (PR #1328 by @stawiski)
|
||||
|
||||
v6.15.2 (2020-05-15)
|
||||
-------
|
||||
|
||||
* CMake: don't build tests when imported in another project
|
||||
* CMake: made project arch-independent
|
||||
* Visual Studio: fixed error C2766 with flag `/Zc:__cplusplus` (issue #1250)
|
||||
* Added support for `JsonDocument` to `copyArray()` (issue #1255)
|
||||
* Added support for `enum`s in `as<T>()` and `is<T>()` (issue #1256)
|
||||
* Added `JsonVariant` as an input type for `deserializeXxx()`
|
||||
For example, you can do: `deserializeJson(doc2, doc1["payload"])`
|
||||
* Break the build if using 64-bit integers with ARDUINOJSON_USE_LONG_LONG==0
|
||||
|
||||
v6.15.1 (2020-04-08)
|
||||
-------
|
||||
|
||||
|
@ -3,42 +3,17 @@
|
||||
# MIT License
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(ArduinoJson)
|
||||
|
||||
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
|
||||
project(ArduinoJson VERSION 6.16.1)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_definitions(-DARDUINOJSON_DEBUG=1)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8)
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0)
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0)
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${COVERAGE})
|
||||
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
include(CTest)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(extras/tests)
|
||||
add_subdirectory(extras/fuzzing)
|
||||
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
|
||||
include(extras/CompileOptions.cmake)
|
||||
add_subdirectory(extras/tests)
|
||||
add_subdirectory(extras/fuzzing)
|
||||
endif()
|
||||
|
12
README.md
12
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.15.1)
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.16.1)
|
||||
[](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
|
||||
[](https://travis-ci.org/bblanchon/ArduinoJson)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
|
||||
@ -17,7 +17,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/?utm_source=github&utm_medium=readme)
|
||||
* [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme)
|
||||
* [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/?utm_source=github&utm_medium=readme)
|
||||
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v6/api/json/deserializejson/#filtering?utm_source=github&utm_medium=readme)
|
||||
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#filtering)
|
||||
* Supports single quotes as a string delimiter
|
||||
* Compatible with NDJSON and JSON Lines
|
||||
* [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme)
|
||||
@ -31,12 +31,13 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
|
||||
* [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/?utm_source=github&utm_medium=readme)
|
||||
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme)
|
||||
* Deduplicates strings
|
||||
* Versatile
|
||||
* [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
|
||||
* Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme)
|
||||
* Supports Arduino's `Stream` and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme)
|
||||
* [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme)
|
||||
* Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/#custom-writer?utm_source=github&utm_medium=readme)
|
||||
* Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer)
|
||||
* Portable
|
||||
* Usable on any C++ project (not limited to Arduino)
|
||||
* Compatible with C++98
|
||||
@ -62,14 +63,15 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
||||
* [Visual Micro](http://www.visualmicro.com/)
|
||||
* [Visual Studio](https://www.visualstudio.com/)
|
||||
* [Even works with online compilers like wandbox.org](https://wandbox.org/permlink/t7KP7I6dVuLhqzDl)
|
||||
* [CMake friendly](https://arduinojson.org/v6/how-to/use-arduinojson-with-cmake/?utm_source=github&utm_medium=readme)
|
||||
* Well designed
|
||||
* [Elegant API](http://127.0.0.1:4000/v6/example/)
|
||||
* [Elegant API](http://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme)
|
||||
* [Thread-safe](https://en.wikipedia.org/wiki/Thread_safety)
|
||||
* Self-contained (no external dependency)
|
||||
* `const` friendly
|
||||
* [`for` friendly](https://arduinojson.org/v6/api/jsonobject/begin_end/?utm_source=github&utm_medium=readme)
|
||||
* [TMP friendly](https://en.wikipedia.org/wiki/Template_metaprogramming)
|
||||
* Handles [integer overflows](https://arduinojson.org/v6/api/jsonvariant/as/#integer-overflows?utm_source=github&utm_medium=readme)
|
||||
* Handles [integer overflows](https://arduinojson.org/v6/api/jsonvariant/as/?utm_source=github&utm_medium=readme#integer-overflows)
|
||||
* Well tested
|
||||
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
|
||||
* Continuously tested on
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: 6.15.1.{build}
|
||||
version: 6.16.1.{build}
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
|
100
extras/CompileOptions.cmake
Normal file
100
extras/CompileOptions.cmake
Normal file
@ -0,0 +1,100 @@
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
add_compile_options(
|
||||
-pedantic
|
||||
-Wall
|
||||
-Wcast-align
|
||||
-Wcast-qual
|
||||
-Wconversion
|
||||
-Wctor-dtor-privacy
|
||||
-Wdisabled-optimization
|
||||
-Werror
|
||||
-Wextra
|
||||
-Wformat=2
|
||||
-Winit-self
|
||||
-Wmissing-include-dirs
|
||||
-Wnon-virtual-dtor
|
||||
-Wold-style-cast
|
||||
-Woverloaded-virtual
|
||||
-Wparentheses
|
||||
-Wredundant-decls
|
||||
-Wshadow
|
||||
-Wsign-promo
|
||||
-Wstrict-aliasing
|
||||
-Wundef
|
||||
)
|
||||
|
||||
if(NOT MINGW)
|
||||
add_compile_options(
|
||||
-std=c++98
|
||||
)
|
||||
endif()
|
||||
|
||||
if(${COVERAGE})
|
||||
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8)
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
-Wstrict-null-sentinel
|
||||
-Wno-vla # Allow VLA in tests
|
||||
)
|
||||
add_definitions(-DHAS_VARIABLE_LENGTH_ARRAY)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.5)
|
||||
add_compile_options(-Wlogical-op) # the flag exists in 4.4 but is buggy
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6)
|
||||
add_compile_options(-Wnoexcept)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(
|
||||
-Wc++11-compat
|
||||
-Wdeprecated-register
|
||||
-Wno-vla-extension # Allow VLA in tests
|
||||
)
|
||||
add_definitions(
|
||||
-DHAS_VARIABLE_LENGTH_ARRAY
|
||||
-DSUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0)
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0)
|
||||
add_compile_options(-g -Og)
|
||||
else()
|
||||
add_compile_options(-g -O0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
add_compile_options(
|
||||
/W4 # Set warning level
|
||||
/WX # Treats all compiler warnings as errors.
|
||||
)
|
||||
|
||||
if (NOT MSVC_VERSION LESS 1910) # >= Visual Studio 2017
|
||||
add_compile_options(
|
||||
/Zc:__cplusplus # Enable updated __cplusplus macro
|
||||
)
|
||||
endif()
|
||||
endif()
|
@ -3,12 +3,5 @@
|
||||
export CC="$_CC"
|
||||
export CXX="$_CXX"
|
||||
|
||||
if [ -n "$SANITIZE" ]; then
|
||||
export CXXFLAGS="-fsanitize=$SANITIZE"
|
||||
BUILD_TYPE="Debug"
|
||||
else
|
||||
BUILD_TYPE="Release"
|
||||
fi
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE .
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
cmake --build .
|
||||
|
@ -1,26 +1,22 @@
|
||||
#!/bin/bash -eux
|
||||
|
||||
ROOT_DIR=$(dirname $0)/../../
|
||||
INCLUDE_DIR=${ROOT_DIR}/src/
|
||||
FUZZING_DIR=${ROOT_DIR}/extras/fuzzing/
|
||||
CXXFLAGS="-g -fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all"
|
||||
|
||||
fuzz() {
|
||||
NAME="$1"
|
||||
FUZZER="${NAME}_fuzzer"
|
||||
FUZZER_CPP="${FUZZING_DIR}/${NAME}_fuzzer.cpp"
|
||||
CORPUS_DIR="${FUZZING_DIR}/${NAME}_corpus"
|
||||
SEED_CORPUS_DIR="${FUZZING_DIR}/${NAME}_seed_corpus"
|
||||
export CC="clang-${CLANG}"
|
||||
export CXX="clang++-${CLANG}"
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug .
|
||||
|
||||
clang++-${CLANG} ${CXXFLAGS} -o ${FUZZER} -I$INCLUDE_DIR ${FUZZER_CPP}
|
||||
FUZZER_TARGET="${FUZZER}_fuzzer"
|
||||
FUZZER_PATH="extras/fuzzing/${FUZZER_TARGET}"
|
||||
CORPUS_DIR="${FUZZING_DIR}/${FUZZER}_corpus"
|
||||
SEED_CORPUS_DIR="${FUZZING_DIR}/${FUZZER}_seed_corpus"
|
||||
|
||||
export ASAN_OPTIONS="detect_leaks=0"
|
||||
export LLVM_PROFILE_FILE="${FUZZER}.profraw"
|
||||
./${FUZZER} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=30 -timeout=1
|
||||
cmake --build . --target $FUZZER_TARGET
|
||||
|
||||
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER}.profdata
|
||||
llvm-cov-${CLANG} report ./${FUZZER} -instr-profile=${FUZZER}.profdata
|
||||
}
|
||||
export ASAN_OPTIONS="detect_leaks=0"
|
||||
export LLVM_PROFILE_FILE="${FUZZER_TARGET}.profraw"
|
||||
${FUZZER_PATH} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=60 -timeout=1
|
||||
|
||||
fuzz json
|
||||
fuzz msgpack
|
||||
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER_TARGET}.profdata
|
||||
llvm-cov-${CLANG} report ./${FUZZER_PATH} -instr-profile=${FUZZER_TARGET}.profdata
|
||||
|
@ -1,4 +1,10 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
"$(dirname "$0")/build.sh"
|
||||
export CC="$_CC"
|
||||
export CXX="$_CXX"
|
||||
|
||||
[ -n "$SANITIZE" ] && export CXXFLAGS="-fsanitize=$SANITIZE"
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug .
|
||||
cmake --build .
|
||||
ctest --output-on-failure .
|
||||
|
@ -6,18 +6,68 @@ if(MSVC)
|
||||
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
add_executable(msgpack_fuzzer
|
||||
add_executable(msgpack_reproducer
|
||||
msgpack_fuzzer.cpp
|
||||
fuzzer_main.cpp
|
||||
reproducer.cpp
|
||||
)
|
||||
target_link_libraries(msgpack_fuzzer
|
||||
target_link_libraries(msgpack_reproducer
|
||||
ArduinoJson
|
||||
)
|
||||
|
||||
add_executable(json_fuzzer
|
||||
add_executable(json_reproducer
|
||||
json_fuzzer.cpp
|
||||
fuzzer_main.cpp
|
||||
reproducer.cpp
|
||||
)
|
||||
target_link_libraries(json_fuzzer
|
||||
target_link_libraries(json_reproducer
|
||||
ArduinoJson
|
||||
)
|
||||
|
||||
# Infer path of llvm-symbolizer from the path of clang
|
||||
string(REPLACE "clang++" "llvm-symbolizer" LLVM_SYMBOLIZER ${CMAKE_CXX_COMPILER})
|
||||
|
||||
macro(add_fuzzer name mode)
|
||||
set(FUZZER "${name}_${mode}_fuzzer")
|
||||
set(CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_corpus")
|
||||
set(SEED_CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_seed_corpus")
|
||||
add_executable("${FUZZER}"
|
||||
"${name}_fuzzer.cpp"
|
||||
)
|
||||
target_link_libraries("${FUZZER}"
|
||||
ArduinoJson
|
||||
)
|
||||
set_target_properties("${FUZZER}"
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS
|
||||
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
|
||||
LINK_FLAGS
|
||||
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
"${FUZZER}"
|
||||
COMMAND
|
||||
"${FUZZER}" "${CORPUS_DIR}" "${SEED_CORPUS_DIR}" -max_total_time=5 -timeout=1
|
||||
)
|
||||
|
||||
set_tests_properties("${FUZZER}"
|
||||
PROPERTIES
|
||||
ENVIRONMENT
|
||||
ASAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
|
||||
ENVIRONMENT
|
||||
LLVM_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
|
||||
ENVIRONMENT
|
||||
MSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
|
||||
ENVIRONMENT
|
||||
UBSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
|
||||
)
|
||||
endmacro()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
|
||||
add_fuzzer(json address)
|
||||
add_fuzzer(json memory)
|
||||
add_fuzzer(json undefined)
|
||||
add_fuzzer(msgpack address)
|
||||
add_fuzzer(msgpack memory)
|
||||
add_fuzzer(msgpack undefined)
|
||||
endif()
|
||||
|
@ -21,7 +21,7 @@ std::vector<uint8_t> read(const char* path) {
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
size_t size = static_cast<size_t>(ftell(f));
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
std::vector<uint8_t> buffer(size);
|
@ -10,6 +10,7 @@ rm -f $OUTPUT
|
||||
|
||||
# create zip
|
||||
7z a $OUTPUT \
|
||||
-xr!.vs \
|
||||
ArduinoJson/CHANGELOG.md \
|
||||
ArduinoJson/examples \
|
||||
ArduinoJson/src \
|
||||
|
@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
export PATH="$PATH:/Applications/CMake.app/Contents/bin/"
|
||||
|
||||
cd $(dirname $0)/../..
|
||||
ROOT=$(pwd)
|
||||
|
||||
mkdir "build"
|
||||
cd build
|
||||
BUILD=$(pwd)
|
||||
|
||||
build-env()
|
||||
{
|
||||
cd $BUILD
|
||||
mkdir "$1"
|
||||
cd "$1"
|
||||
cmake "$ROOT" -G "$2"
|
||||
}
|
||||
|
||||
if [[ $(uname) == MINGW* ]]
|
||||
then
|
||||
build-env "Make" "MinGW Makefiles"
|
||||
build-env "SublimeText" "Sublime Text 2 - Ninja"
|
||||
build-env "VisualStudio" "Visual Studio 14 2015"
|
||||
else
|
||||
build-env "SublimeText" "Sublime Text 2 - Ninja"
|
||||
build-env "Make" "Unix Makefiles"
|
||||
build-env "Xcode" "Xcode"
|
||||
fi
|
@ -14,19 +14,22 @@ update_version_in_source () {
|
||||
UNDERLINE=$(printf -- '-%.0s' $(seq 1 ${#TAG}))
|
||||
|
||||
sed -i~ -bE "s/version=$VERSION_REGEX/version=$VERSION/; s|ardu-badge.com/ArduinoJson/$VERSION_REGEX|ardu-badge.com/ArduinoJson/$VERSION|; " README.md
|
||||
rm README.md*~
|
||||
rm README.md~
|
||||
|
||||
sed -i~ -bE "4s/HEAD/$TAG ($DATE)/; 5s/-+/$UNDERLINE/" CHANGELOG.md
|
||||
rm CHANGELOG.md*~
|
||||
rm CHANGELOG.md~
|
||||
|
||||
sed -i~ -bE "s/(project\\s*\\(ArduinoJson\\s+VERSION\\s+).*?\\)/\\1$MAJOR.$MINOR.$REVISION)/" CMakeLists.txt
|
||||
rm CMakeLists.txt~
|
||||
|
||||
sed -i~ -bE "s/\"version\":.*$/\"version\": \"$VERSION\",/" library.json
|
||||
rm library.json*~
|
||||
rm library.json~
|
||||
|
||||
sed -i~ -bE "s/version=.*$/version=$VERSION/" library.properties
|
||||
rm library.properties*~
|
||||
rm library.properties~
|
||||
|
||||
sed -i~ -bE "s/version: .*$/version: $VERSION.{build}/" appveyor.yml
|
||||
rm appveyor.yml*~
|
||||
rm appveyor.yml~
|
||||
|
||||
sed -i~ -bE \
|
||||
-e "s/ARDUINOJSON_VERSION .*$/ARDUINOJSON_VERSION \"$VERSION\"/" \
|
||||
@ -38,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"
|
||||
}
|
||||
|
||||
|
@ -4,76 +4,11 @@
|
||||
|
||||
add_subdirectory(catch)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
add_compile_options(
|
||||
-pedantic
|
||||
-Wall
|
||||
-Wcast-align
|
||||
-Wcast-qual
|
||||
-Wconversion
|
||||
-Wctor-dtor-privacy
|
||||
-Wdisabled-optimization
|
||||
-Werror
|
||||
-Wextra
|
||||
-Wformat=2
|
||||
-Winit-self
|
||||
-Wmissing-include-dirs
|
||||
-Wnon-virtual-dtor
|
||||
-Wold-style-cast
|
||||
-Woverloaded-virtual
|
||||
-Wparentheses
|
||||
-Wredundant-decls
|
||||
-Wshadow
|
||||
-Wsign-promo
|
||||
-Wstrict-aliasing
|
||||
-Wundef
|
||||
)
|
||||
|
||||
if(NOT MINGW)
|
||||
add_compile_options(
|
||||
-std=c++98
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
add_compile_options(
|
||||
-Wstrict-null-sentinel
|
||||
-Wno-vla # Allow VLA in tests
|
||||
)
|
||||
add_definitions(-DHAS_VARIABLE_LENGTH_ARRAY)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.5)
|
||||
add_compile_options(-Wlogical-op) # the flag exists in 4.4 but is buggy
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6)
|
||||
add_compile_options(-Wnoexcept)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(
|
||||
-Wc++11-compat
|
||||
-Wdeprecated-register
|
||||
-Wno-vla-extension # Allow VLA in tests
|
||||
)
|
||||
add_definitions(
|
||||
-DHAS_VARIABLE_LENGTH_ARRAY
|
||||
-DSUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
add_compile_options(
|
||||
/W4 # Set warning level
|
||||
/WX # Treats all compiler warnings as errors.
|
||||
)
|
||||
endif()
|
||||
link_libraries(ArduinoJson catch)
|
||||
|
||||
include_directories(Helpers)
|
||||
add_subdirectory(ElementProxy)
|
||||
add_subdirectory(FailingBuilds)
|
||||
add_subdirectory(IntegrationTests)
|
||||
add_subdirectory(JsonArray)
|
||||
add_subdirectory(JsonDeserializer)
|
||||
|
@ -9,11 +9,7 @@ add_executable(ElementProxyTests
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(ElementProxyTests
|
||||
ArduinoJson
|
||||
catch
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
add_test(ElementProxy ElementProxyTests)
|
||||
|
@ -10,19 +10,39 @@ using namespace ARDUINOJSON_NAMESPACE;
|
||||
TEST_CASE("ElementProxy::operator==()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
SECTION("same value") {
|
||||
SECTION("1 vs 1") {
|
||||
doc.add(1);
|
||||
doc.add(1);
|
||||
|
||||
REQUIRE(doc[0] <= doc[1]);
|
||||
REQUIRE(doc[0] == doc[1]);
|
||||
REQUIRE(doc[0] >= doc[1]);
|
||||
REQUIRE_FALSE(doc[0] != doc[1]);
|
||||
REQUIRE_FALSE(doc[0] < doc[1]);
|
||||
REQUIRE_FALSE(doc[0] > doc[1]);
|
||||
}
|
||||
|
||||
SECTION("different values") {
|
||||
SECTION("1 vs 2") {
|
||||
doc.add(1);
|
||||
doc.add(2);
|
||||
|
||||
REQUIRE_FALSE(doc[0] == doc[1]);
|
||||
REQUIRE(doc[0] != doc[1]);
|
||||
REQUIRE(doc[0] < doc[1]);
|
||||
REQUIRE(doc[0] <= doc[1]);
|
||||
REQUIRE_FALSE(doc[0] == doc[1]);
|
||||
REQUIRE_FALSE(doc[0] > doc[1]);
|
||||
REQUIRE_FALSE(doc[0] >= doc[1]);
|
||||
}
|
||||
|
||||
SECTION("'abc' vs 'bcd'") {
|
||||
doc.add("abc");
|
||||
doc.add("bcd");
|
||||
|
||||
REQUIRE(doc[0] != doc[1]);
|
||||
REQUIRE(doc[0] < doc[1]);
|
||||
REQUIRE(doc[0] <= doc[1]);
|
||||
REQUIRE_FALSE(doc[0] == doc[1]);
|
||||
REQUIRE_FALSE(doc[0] > doc[1]);
|
||||
REQUIRE_FALSE(doc[0] >= doc[1]);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemberProxy::operator[]") {
|
||||
TEST_CASE("ElementProxy::operator[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
ElementProxy<JsonDocument&> ep = doc[1];
|
||||
|
||||
|
38
extras/tests/FailingBuilds/CMakeLists.txt
Normal file
38
extras/tests/FailingBuilds/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
# ArduinoJson - arduinojson.org
|
||||
# Copyright Benoit Blanchon 2014-2020
|
||||
# MIT License
|
||||
|
||||
macro(build_should_fail target)
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||
)
|
||||
add_test(
|
||||
NAME
|
||||
${target}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_BINARY_DIR}
|
||||
)
|
||||
set_tests_properties(${target}
|
||||
PROPERTIES
|
||||
WILL_FAIL TRUE
|
||||
)
|
||||
endmacro()
|
||||
|
||||
|
||||
add_executable(Issue978 Issue978.cpp)
|
||||
build_should_fail(Issue978)
|
||||
|
||||
add_executable(Issue1189 Issue1189.cpp)
|
||||
build_should_fail(Issue1189)
|
||||
|
||||
add_executable(read_long_long read_long_long.cpp)
|
||||
set_property(TARGET read_long_long PROPERTY CXX_STANDARD 11)
|
||||
build_should_fail(read_long_long)
|
||||
|
||||
add_executable(write_long_long write_long_long.cpp)
|
||||
set_property(TARGET write_long_long PROPERTY CXX_STANDARD 11)
|
||||
build_should_fail(write_long_long)
|
20
extras/tests/FailingBuilds/read_long_long.cpp
Normal file
20
extras/tests/FailingBuilds/read_long_long.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
|
||||
#error This test requires sizeof(long) < 8
|
||||
#endif
|
||||
|
||||
#if !ARDUINOJSON_HAS_LONG_LONG
|
||||
#error This test requires C++11
|
||||
#endif
|
||||
|
||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(long long)
|
||||
int main() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["dummy"].as<long long>();
|
||||
}
|
19
extras/tests/FailingBuilds/write_long_long.cpp
Normal file
19
extras/tests/FailingBuilds/write_long_long.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
|
||||
#error This test requires sizeof(long) < 8
|
||||
#endif
|
||||
|
||||
#if !ARDUINOJSON_HAS_LONG_LONG
|
||||
#error This test requires C++11
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["dummy"] = static_cast<long long>(42);
|
||||
}
|
@ -9,6 +9,9 @@
|
||||
// Reproduces Arduino's String class
|
||||
class String {
|
||||
public:
|
||||
String() {}
|
||||
explicit String(const char* s) : _str(s) {}
|
||||
|
||||
String& operator+=(const char* rhs) {
|
||||
_str += rhs;
|
||||
return *this;
|
||||
|
@ -16,9 +16,4 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(IntegrationTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(IntegrationTests IntegrationTests)
|
||||
|
@ -19,9 +19,4 @@ add_executable(JsonArrayTests
|
||||
undefined.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonArrayTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(JsonArray JsonArrayTests)
|
||||
|
@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") {
|
||||
|
||||
SECTION("should duplicate char*") {
|
||||
array.add(const_cast<char*>("world"));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string") {
|
||||
array.add(std::string("world"));
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
|
@ -6,20 +6,57 @@
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("copyArray()") {
|
||||
SECTION("1D -> JsonArray") {
|
||||
SECTION("int[] -> JsonArray") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
REQUIRE(ok);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json, sizeof(json));
|
||||
REQUIRE(std::string("[1,2,3]") == json);
|
||||
serializeJson(array, json);
|
||||
CHECK(std::string("[1,2,3]") == json);
|
||||
}
|
||||
|
||||
SECTION("1D -> JsonArray, but not enough memory") {
|
||||
SECTION("std::string[] -> JsonArray") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
std::string source[] = {"a", "b", "c"};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json);
|
||||
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json);
|
||||
}
|
||||
|
||||
SECTION("int[] -> JsonDocument") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[32];
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, doc);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK(std::string("[1,2,3]") == json);
|
||||
}
|
||||
|
||||
SECTION("int[] -> MemberProxy") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[32];
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, doc["data"]);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK(std::string("{\"data\":[1,2,3]}") == json);
|
||||
}
|
||||
|
||||
SECTION("int[] -> JsonArray, but not enough memory") {
|
||||
const size_t SIZE = JSON_ARRAY_SIZE(2);
|
||||
StaticJsonDocument<SIZE> doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
@ -29,24 +66,48 @@ TEST_CASE("copyArray()") {
|
||||
bool ok = copyArray(source, array);
|
||||
REQUIRE_FALSE(ok);
|
||||
|
||||
serializeJson(array, json, sizeof(json));
|
||||
REQUIRE(std::string("[1,2]") == json);
|
||||
serializeJson(array, json);
|
||||
CHECK(std::string("[1,2]") == json);
|
||||
}
|
||||
|
||||
SECTION("2D -> JsonArray") {
|
||||
SECTION("int[][] -> JsonArray") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
REQUIRE(ok);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json, sizeof(json));
|
||||
REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json);
|
||||
serializeJson(array, json);
|
||||
CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
|
||||
}
|
||||
|
||||
SECTION("2D -> JsonArray, but not enough memory") {
|
||||
SECTION("int[][] -> MemberProxy") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[32];
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, doc["data"]);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK(std::string("{\"data\":[[1,2,3],[4,5,6]]}") == json);
|
||||
}
|
||||
|
||||
SECTION("int[][] -> JsonDocument") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[32];
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, doc);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
|
||||
}
|
||||
|
||||
SECTION("int[][] -> JsonArray, but not enough memory") {
|
||||
const size_t SIZE =
|
||||
JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
|
||||
StaticJsonDocument<SIZE> doc;
|
||||
@ -60,58 +121,159 @@ TEST_CASE("copyArray()") {
|
||||
CAPTURE(doc.memoryUsage());
|
||||
CHECK_FALSE(ok);
|
||||
|
||||
serializeJson(array, json, sizeof(json));
|
||||
REQUIRE(std::string("[[1,2,3],[4,5]]") == json);
|
||||
serializeJson(array, json);
|
||||
CHECK(std::string("[[1,2,3],[4,5]]") == json);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> 1D, with more space than needed") {
|
||||
SECTION("JsonArray -> int[], with more space than needed") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[1,2,3]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
REQUIRE(3 == result);
|
||||
REQUIRE(1 == destination[0]);
|
||||
REQUIRE(2 == destination[1]);
|
||||
REQUIRE(3 == destination[2]);
|
||||
REQUIRE(0 == destination[3]);
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> 1D, without enough space") {
|
||||
SECTION("JsonArray -> int[], without enough space") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[1,2,3]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
int destination[2] = {0};
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
REQUIRE(2 == result);
|
||||
REQUIRE(1 == destination[0]);
|
||||
REQUIRE(2 == destination[1]);
|
||||
CHECK(2 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> 2D") {
|
||||
SECTION("JsonArray -> std::string[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[\"a\",\"b\",\"c\"]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
std::string destination[4];
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK("a" == destination[0]);
|
||||
CHECK("b" == destination[1]);
|
||||
CHECK("c" == destination[2]);
|
||||
CHECK("" == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonDocument -> int[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[1,2,3]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(doc, destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("MemberProxy -> int[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "{\"data\":[1,2,3]}";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(doc["data"], destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("ElementProxy -> int[]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[[1,2,3]]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(doc[0], destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> int[][]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[[1,2],[3],[4]]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
int destination[3][2] = {{0}};
|
||||
copyArray(array, destination);
|
||||
|
||||
REQUIRE(1 == destination[0][0]);
|
||||
REQUIRE(2 == destination[0][1]);
|
||||
REQUIRE(3 == destination[1][0]);
|
||||
REQUIRE(0 == destination[1][1]);
|
||||
REQUIRE(4 == destination[2][0]);
|
||||
REQUIRE(0 == destination[2][1]);
|
||||
CHECK(1 == destination[0][0]);
|
||||
CHECK(2 == destination[0][1]);
|
||||
CHECK(3 == destination[1][0]);
|
||||
CHECK(0 == destination[1][1]);
|
||||
CHECK(4 == destination[2][0]);
|
||||
CHECK(0 == destination[2][1]);
|
||||
}
|
||||
|
||||
SECTION("JsonDocument -> int[][]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "[[1,2],[3],[4]]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[3][2] = {{0}};
|
||||
copyArray(doc, destination);
|
||||
|
||||
CHECK(1 == destination[0][0]);
|
||||
CHECK(2 == destination[0][1]);
|
||||
CHECK(3 == destination[1][0]);
|
||||
CHECK(0 == destination[1][1]);
|
||||
CHECK(4 == destination[2][0]);
|
||||
CHECK(0 == destination[2][1]);
|
||||
}
|
||||
|
||||
SECTION("MemberProxy -> int[][]") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
char json[] = "{\"data\":[[1,2],[3],[4]]}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[3][2] = {{0}};
|
||||
copyArray(doc["data"], destination);
|
||||
|
||||
CHECK(1 == destination[0][0]);
|
||||
CHECK(2 == destination[0][1]);
|
||||
CHECK(3 == destination[1][0]);
|
||||
CHECK(0 == destination[1][1]);
|
||||
CHECK(4 == destination[2][0]);
|
||||
CHECK(0 == destination[2][1]);
|
||||
}
|
||||
}
|
||||
|
@ -65,4 +65,15 @@ TEST_CASE("JsonArray::remove()") {
|
||||
REQUIRE(_array[0] == 1);
|
||||
REQUIRE(_array[1] == 2);
|
||||
}
|
||||
|
||||
SECTION("In a loop") {
|
||||
for (JsonArray::iterator it = _array.begin(); it != _array.end(); ++it) {
|
||||
if (*it == 2)
|
||||
_array.remove(it);
|
||||
}
|
||||
|
||||
REQUIRE(2 == _array.size());
|
||||
REQUIRE(_array[0] == 1);
|
||||
REQUIRE(_array[1] == 3);
|
||||
}
|
||||
}
|
||||
|
@ -119,13 +119,13 @@ TEST_CASE("JsonArray::operator[]") {
|
||||
|
||||
SECTION("should duplicate char*") {
|
||||
array[0] = const_cast<char*>("world");
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string") {
|
||||
array[0] = std::string("world");
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,6 @@ add_executable(JsonDeserializerTests
|
||||
string.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonDeserializerTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
set_target_properties(JsonDeserializerTests PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
add_test(JsonDeserializer JsonDeserializerTests)
|
||||
|
@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
|
||||
|
||||
deserializeJson(doc, " [ \"1234567\" ] ");
|
||||
|
||||
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(8) == doc.memoryUsage());
|
||||
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(7) == doc.memoryUsage());
|
||||
// note: we use a string of 8 bytes to be sure that the StaticMemoryPool
|
||||
// will not insert bytes to enforce alignement
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ TEST_CASE("Filtering") {
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[{\"example\":1},{\"example\":3}]",
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8
|
||||
},
|
||||
{
|
||||
"[',2,3]",
|
||||
@ -525,7 +525,7 @@ TEST_CASE("Filtering") {
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
JSON_OBJECT_SIZE(0) + 8
|
||||
JSON_OBJECT_SIZE(0)
|
||||
},
|
||||
{
|
||||
// incomplete comment after key
|
||||
@ -534,7 +534,7 @@ TEST_CASE("Filtering") {
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
JSON_OBJECT_SIZE(0) + 8
|
||||
JSON_OBJECT_SIZE(0)
|
||||
},
|
||||
{
|
||||
// invalid comment after colon
|
||||
@ -730,20 +730,3 @@ TEST_CASE("Overloads") {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("StringMover::reclaim()") {
|
||||
StaticJsonDocument<200> filter;
|
||||
filter["a"] = true;
|
||||
filter["c"] = true;
|
||||
char input[] = "{\"a\":1,\"b\":2,\"c\":1}";
|
||||
|
||||
StaticJsonDocument<200> doc;
|
||||
deserializeJson(doc, input, DeserializationOption::Filter(filter));
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"c\":1}");
|
||||
|
||||
CHECK(input[0] == 'a');
|
||||
CHECK(input[1] == 0);
|
||||
CHECK(input[2] == 'c');
|
||||
CHECK(input[3] == 0);
|
||||
}
|
||||
|
@ -3,11 +3,27 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#include "CustomReader.hpp"
|
||||
|
||||
TEST_CASE("deserializeJson(char*)") {
|
||||
StaticJsonDocument<1024> doc;
|
||||
|
||||
SECTION("should not duplicate strings") {
|
||||
char input[] = "{\"hello\":\"world\"}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
|
||||
CHECK(doc.as<JsonVariant>().memoryUsage() ==
|
||||
JSON_OBJECT_SIZE(1)); // issue #1318
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(const std::string&)") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
@ -126,3 +142,57 @@ TEST_CASE("deserializeJson(CustomReader)") {
|
||||
REQUIRE(doc[0] == 4);
|
||||
REQUIRE(doc[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
|
||||
DynamicJsonDocument doc1(4096);
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
DynamicJsonDocument doc2(4096);
|
||||
DeserializationError err = deserializeJson(doc2, doc1["payload"]);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
|
||||
DynamicJsonDocument doc1(4096);
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
DynamicJsonDocument doc2(4096);
|
||||
DeserializationError err =
|
||||
deserializeJson(doc2, doc1["payload"].as<JsonVariant>());
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
|
||||
DynamicJsonDocument doc1(4096);
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
DynamicJsonDocument doc2(4096);
|
||||
DeserializationError err =
|
||||
deserializeJson(doc2, doc1["payload"].as<JsonVariantConst>());
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, ElementProxy)") {
|
||||
DynamicJsonDocument doc1(4096);
|
||||
doc1[0] = "[4,2]";
|
||||
|
||||
DynamicJsonDocument doc2(4096);
|
||||
DeserializationError err = deserializeJson(doc2, doc1[0]);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -74,16 +74,16 @@ TEST_CASE("Invalid JSON string") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Not enough room to duplicate the string") {
|
||||
DynamicJsonDocument doc(JSON_OBJECT_SIZE(0));
|
||||
TEST_CASE("Not enough room to save the key") {
|
||||
DynamicJsonDocument doc(JSON_OBJECT_SIZE(1) + 8);
|
||||
|
||||
SECTION("Quoted string") {
|
||||
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
||||
REQUIRE(deserializeJson(doc, "{\"accuracy\":1}") ==
|
||||
DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("Non-quoted string") {
|
||||
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
||||
REQUIRE(deserializeJson(doc, "{accuracy:1}") ==
|
||||
DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,4 @@ add_executable(JsonDocumentTests
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonDocumentTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(JsonDocument JsonDocumentTests)
|
||||
|
@ -212,7 +212,7 @@ TEST_CASE("StaticJsonDocument") {
|
||||
|
||||
SECTION("garbageCollect()") {
|
||||
StaticJsonDocument<256> doc;
|
||||
doc[std::string("example")] = std::string("example");
|
||||
doc[std::string("example")] = std::string("jukebox");
|
||||
doc.remove("example");
|
||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);
|
||||
|
||||
|
@ -94,8 +94,8 @@ TEST_CASE("BasicJsonDocument::shrinkToFit()") {
|
||||
}
|
||||
|
||||
SECTION("owned raw") {
|
||||
doc.set(serialized(std::string("[{},123]")));
|
||||
testShrinkToFit(doc, "[{},123]", 8);
|
||||
doc.set(serialized(std::string("[{},12]")));
|
||||
testShrinkToFit(doc, "[{},12]", 8);
|
||||
}
|
||||
|
||||
SECTION("linked key") {
|
||||
|
@ -19,9 +19,4 @@ add_executable(JsonObjectTests
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonObjectTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(JsonObject JsonObjectTests)
|
||||
|
@ -107,43 +107,43 @@ TEST_CASE("JsonObject::operator[]") {
|
||||
|
||||
SECTION("should duplicate char* value") {
|
||||
obj["hello"] = const_cast<char*>("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key") {
|
||||
obj[const_cast<char*>("hello")] = "world";
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key&value") {
|
||||
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize <= doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string value") {
|
||||
obj["hello"] = std::string("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key") {
|
||||
obj[std::string("hello")] = "world";
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key&value") {
|
||||
obj[std::string("hello")] = std::string("world");
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize <= doc.memoryUsage());
|
||||
}
|
||||
|
||||
SECTION("should duplicate a non-static JsonString key") {
|
||||
obj[JsonString("hello", false)] = "world";
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
|
||||
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
|
||||
REQUIRE(expectedSize == doc.memoryUsage());
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,4 @@ add_executable(JsonSerializerTests
|
||||
std_string.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonSerializerTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(JsonSerializer JsonSerializerTests)
|
||||
|
@ -24,9 +24,4 @@ add_executable(JsonVariantTests
|
||||
undefined.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonVariantTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(JsonVariant JsonVariantTests)
|
||||
|
@ -10,6 +10,8 @@ namespace my {
|
||||
using ARDUINOJSON_NAMESPACE::isinf;
|
||||
} // namespace my
|
||||
|
||||
enum MY_ENUM { ONE = 1, TWO = 2 };
|
||||
|
||||
TEST_CASE("JsonVariant::as()") {
|
||||
static const char* null = 0;
|
||||
|
||||
@ -212,4 +214,10 @@ TEST_CASE("JsonVariant::as()") {
|
||||
REQUIRE(cvar.as<char*>() == std::string("hello"));
|
||||
// REQUIRE(cvar.as<std::string>() == std::string("hello"));
|
||||
}
|
||||
|
||||
SECTION("as<enum>()") {
|
||||
variant.set(1);
|
||||
|
||||
REQUIRE(variant.as<MY_ENUM>() == ONE);
|
||||
}
|
||||
}
|
||||
|
@ -5,460 +5,276 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
template <typename T>
|
||||
void checkEquals(T a, T b) {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set(a);
|
||||
// Most code is already covered by arithmeticCompare.cpp.
|
||||
// Here, we're just filling the holes
|
||||
|
||||
REQUIRE(b == variant);
|
||||
REQUIRE(variant == b);
|
||||
REQUIRE(b <= variant);
|
||||
REQUIRE(variant <= b);
|
||||
REQUIRE(b >= variant);
|
||||
REQUIRE(variant >= b);
|
||||
|
||||
REQUIRE_FALSE(b != variant);
|
||||
REQUIRE_FALSE(variant != b);
|
||||
REQUIRE_FALSE(b > variant);
|
||||
REQUIRE_FALSE(variant > b);
|
||||
REQUIRE_FALSE(b < variant);
|
||||
REQUIRE_FALSE(variant < b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkGreater(T a, T b) {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set(a);
|
||||
|
||||
REQUIRE(variant > b);
|
||||
REQUIRE(b < variant);
|
||||
REQUIRE(variant != b);
|
||||
REQUIRE(b != variant);
|
||||
|
||||
REQUIRE_FALSE(variant < b);
|
||||
REQUIRE_FALSE(b > variant);
|
||||
REQUIRE_FALSE(variant == b);
|
||||
REQUIRE_FALSE(b == variant);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkLower(T a, T b) {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set(a);
|
||||
|
||||
REQUIRE(variant < b);
|
||||
REQUIRE(b > variant);
|
||||
REQUIRE(variant != b);
|
||||
REQUIRE(b != variant);
|
||||
|
||||
REQUIRE_FALSE(variant > b);
|
||||
REQUIRE_FALSE(b < variant);
|
||||
REQUIRE_FALSE(variant == b);
|
||||
REQUIRE_FALSE(b == variant);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void checkComparisons(T low, T mid, T high) {
|
||||
checkEquals(mid, mid);
|
||||
checkGreater(mid, low);
|
||||
checkLower(mid, high);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant comparisons") {
|
||||
static const char* null = 0;
|
||||
|
||||
SECTION("Double") {
|
||||
checkComparisons<double>(123.44, 123.45, 123.46);
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
checkComparisons<float>(123.44f, 123.45f, 123.46f);
|
||||
}
|
||||
|
||||
SECTION("SChar") {
|
||||
checkComparisons<signed char>(122, 123, 124);
|
||||
}
|
||||
|
||||
SECTION("SInt") {
|
||||
checkComparisons<signed int>(122, 123, 124);
|
||||
}
|
||||
|
||||
SECTION("SLong") {
|
||||
checkComparisons<signed long>(122L, 123L, 124L);
|
||||
}
|
||||
|
||||
SECTION("SShort") {
|
||||
checkComparisons<signed short>(122, 123, 124);
|
||||
}
|
||||
|
||||
SECTION("UChar") {
|
||||
checkComparisons<unsigned char>(122, 123, 124);
|
||||
}
|
||||
|
||||
SECTION("UInt") {
|
||||
checkComparisons<unsigned int>(122, 123, 124);
|
||||
}
|
||||
|
||||
SECTION("ULong") {
|
||||
checkComparisons<unsigned long>(122L, 123L, 124L);
|
||||
}
|
||||
|
||||
SECTION("UShort") {
|
||||
checkComparisons<unsigned short>(122, 123, 124);
|
||||
}
|
||||
|
||||
SECTION("null") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set(null);
|
||||
|
||||
REQUIRE(variant == variant);
|
||||
REQUIRE_FALSE(variant != variant);
|
||||
|
||||
REQUIRE(variant == null);
|
||||
REQUIRE_FALSE(variant != null);
|
||||
|
||||
REQUIRE(variant != "null");
|
||||
REQUIRE_FALSE(variant == "null");
|
||||
}
|
||||
|
||||
SECTION("StringLiteral") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
deserializeJson(doc, "\"hello\"");
|
||||
JsonVariant variant = doc.as<JsonVariant>();
|
||||
|
||||
REQUIRE(variant == variant);
|
||||
REQUIRE_FALSE(variant != variant);
|
||||
|
||||
REQUIRE(variant == "hello");
|
||||
REQUIRE_FALSE(variant != "hello");
|
||||
|
||||
REQUIRE(variant != "world");
|
||||
REQUIRE_FALSE(variant == "world");
|
||||
|
||||
REQUIRE(variant != null);
|
||||
REQUIRE_FALSE(variant == null);
|
||||
|
||||
REQUIRE("hello" == variant);
|
||||
REQUIRE_FALSE("hello" != variant);
|
||||
|
||||
REQUIRE("world" != variant);
|
||||
REQUIRE_FALSE("world" == variant);
|
||||
|
||||
REQUIRE(null != variant);
|
||||
REQUIRE_FALSE(null == variant);
|
||||
}
|
||||
|
||||
SECTION("String") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set("hello");
|
||||
|
||||
REQUIRE(variant == variant);
|
||||
REQUIRE_FALSE(variant != variant);
|
||||
|
||||
REQUIRE(variant == std::string("hello"));
|
||||
REQUIRE_FALSE(variant != std::string("hello"));
|
||||
|
||||
REQUIRE(variant != std::string("world"));
|
||||
REQUIRE_FALSE(variant == std::string("world"));
|
||||
|
||||
REQUIRE(variant != null);
|
||||
REQUIRE_FALSE(variant == null);
|
||||
|
||||
REQUIRE(std::string("hello") == variant);
|
||||
REQUIRE_FALSE(std::string("hello") != variant);
|
||||
|
||||
REQUIRE(std::string("world") != variant);
|
||||
REQUIRE_FALSE(std::string("world") == variant);
|
||||
|
||||
REQUIRE(null != variant);
|
||||
REQUIRE_FALSE(null == variant);
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("VLA equals") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set("hello");
|
||||
|
||||
REQUIRE((vla == variant));
|
||||
REQUIRE((variant == vla));
|
||||
REQUIRE_FALSE((vla != variant));
|
||||
REQUIRE_FALSE((variant != vla));
|
||||
}
|
||||
|
||||
SECTION("VLA differs") {
|
||||
int i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
variant.set("world");
|
||||
|
||||
REQUIRE((vla != variant));
|
||||
REQUIRE((variant != vla));
|
||||
REQUIRE_FALSE((vla == variant));
|
||||
REQUIRE_FALSE((variant == vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
DynamicJsonDocument doc1(4096), doc2(4096), doc3(4096);
|
||||
JsonVariant variant1 = doc1.to<JsonVariant>();
|
||||
JsonVariant variant2 = doc2.to<JsonVariant>();
|
||||
JsonVariant variant3 = doc3.to<JsonVariant>();
|
||||
|
||||
SECTION("Variants containing integers") {
|
||||
variant1.set(42);
|
||||
variant2.set(42);
|
||||
variant3.set(666);
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("Variants containing linked strings") {
|
||||
// create two identical strings at different addresses
|
||||
char hello1[] = "hello";
|
||||
char hello2[] = "hello";
|
||||
REQUIRE(hello1 != hello2);
|
||||
|
||||
variant1.set(hello1);
|
||||
variant2.set(hello2);
|
||||
variant3.set("world");
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("Variants containing owned strings") {
|
||||
variant1.set(std::string("hello"));
|
||||
variant2.set(std::string("hello"));
|
||||
variant3.set(std::string("world"));
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("Variants containing linked raws") {
|
||||
// create two identical strings at different addresses
|
||||
char hello1[] = "hello";
|
||||
char hello2[] = "hello";
|
||||
REQUIRE(hello1 != hello2);
|
||||
|
||||
variant1.set(serialized(hello1));
|
||||
variant2.set(serialized(hello2));
|
||||
variant3.set(serialized("world"));
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("Variants containing owned raws") {
|
||||
variant1.set(serialized(std::string("hello")));
|
||||
variant2.set(serialized(std::string("hello")));
|
||||
variant3.set(serialized(std::string("world")));
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("Variants containing mixed strings (issue #1051)") {
|
||||
variant1.set("hello");
|
||||
variant2.set(std::string("hello"));
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant2 == variant1);
|
||||
REQUIRE_FALSE(variant2 != variant1);
|
||||
}
|
||||
|
||||
SECTION("Variants containing double") {
|
||||
variant1.set(42.0);
|
||||
variant2.set(42.0);
|
||||
variant3.set(666.0);
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("BoolInVariant") {
|
||||
variant1.set(true);
|
||||
variant2.set(true);
|
||||
variant3.set(false);
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("ArrayInVariant") {
|
||||
JsonArray array1 = variant1.to<JsonArray>();
|
||||
JsonArray array2 = variant2.to<JsonArray>();
|
||||
|
||||
array1.add(42);
|
||||
array2.add(42);
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
|
||||
SECTION("ObjectInVariant") {
|
||||
JsonObject obj1 = variant1.to<JsonObject>();
|
||||
JsonObject obj2 = variant2.to<JsonObject>();
|
||||
|
||||
obj1["hello"] = "world";
|
||||
obj2["hello"] = "world";
|
||||
|
||||
REQUIRE(variant1 == variant2);
|
||||
REQUIRE_FALSE(variant1 != variant2);
|
||||
|
||||
REQUIRE(variant1 != variant3);
|
||||
REQUIRE_FALSE(variant1 == variant3);
|
||||
}
|
||||
}
|
||||
|
||||
class VariantComparisionFixture {
|
||||
private:
|
||||
TEST_CASE("Compare JsonVariant with value") {
|
||||
StaticJsonDocument<256> doc;
|
||||
JsonVariant variant;
|
||||
JsonVariant a = doc.addElement();
|
||||
|
||||
public:
|
||||
VariantComparisionFixture() : variant(doc.to<JsonVariant>()) {}
|
||||
SECTION("null vs (char*)0") {
|
||||
char* b = 0;
|
||||
|
||||
protected:
|
||||
template <typename T>
|
||||
void setValue(const T& value) {
|
||||
variant.set(value);
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assertEqualsTo(const T& value) {
|
||||
REQUIRE(variant == value);
|
||||
REQUIRE(value == variant);
|
||||
SECTION("42 vs 42") {
|
||||
a.set(42);
|
||||
int b = 42;
|
||||
|
||||
REQUIRE_FALSE(variant != value);
|
||||
REQUIRE_FALSE(value != variant);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assertDiffersFrom(const T& value) {
|
||||
REQUIRE(variant != value);
|
||||
REQUIRE(value != variant);
|
||||
|
||||
REQUIRE_FALSE(variant == value);
|
||||
REQUIRE_FALSE(value == variant);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assertGreaterThan(const T& value) {
|
||||
REQUIRE((variant > value));
|
||||
REQUIRE((variant >= value));
|
||||
REQUIRE(value < variant);
|
||||
REQUIRE(value <= variant);
|
||||
|
||||
REQUIRE_FALSE((variant < value));
|
||||
REQUIRE_FALSE((variant <= value));
|
||||
REQUIRE_FALSE(value > variant);
|
||||
REQUIRE_FALSE(value >= variant);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void assertLowerThan(const T& value) {
|
||||
REQUIRE(variant < value);
|
||||
REQUIRE(variant <= value);
|
||||
REQUIRE(value > variant);
|
||||
REQUIRE(value >= variant);
|
||||
|
||||
REQUIRE_FALSE(variant > value);
|
||||
REQUIRE_FALSE(variant >= value);
|
||||
REQUIRE_FALSE(value < variant);
|
||||
REQUIRE_FALSE(value <= variant);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(VariantComparisionFixture,
|
||||
"Compare variant with another type") {
|
||||
SECTION("null") {
|
||||
assertDiffersFrom(3);
|
||||
assertDiffersFrom("world");
|
||||
}
|
||||
|
||||
SECTION("string") {
|
||||
setValue("hello");
|
||||
assertEqualsTo("hello");
|
||||
assertDiffersFrom(3);
|
||||
assertDiffersFrom("world");
|
||||
assertGreaterThan("helln");
|
||||
assertLowerThan("hellp");
|
||||
}
|
||||
|
||||
SECTION("positive integer") {
|
||||
setValue(42);
|
||||
assertEqualsTo(42);
|
||||
assertDiffersFrom(43);
|
||||
assertGreaterThan(41);
|
||||
assertLowerThan(43);
|
||||
assertDiffersFrom("world");
|
||||
}
|
||||
|
||||
SECTION("negative integer") {
|
||||
setValue(-42);
|
||||
assertEqualsTo(-42);
|
||||
assertDiffersFrom(42);
|
||||
assertGreaterThan(-43);
|
||||
assertLowerThan(-41);
|
||||
assertDiffersFrom("world");
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
setValue(42.0);
|
||||
assertEqualsTo(42.0);
|
||||
assertDiffersFrom(42.1);
|
||||
assertGreaterThan(41.0);
|
||||
assertLowerThan(43.0);
|
||||
assertDiffersFrom("42.0");
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
setValue(true);
|
||||
assertEqualsTo(true);
|
||||
assertDiffersFrom(false);
|
||||
assertDiffersFrom(1);
|
||||
assertDiffersFrom("true");
|
||||
assertDiffersFrom(1.0);
|
||||
assertGreaterThan(false);
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonVariant with JsonVariant") {
|
||||
StaticJsonDocument<256> doc;
|
||||
JsonVariant a = doc.addElement();
|
||||
JsonVariant b = doc.addElement();
|
||||
|
||||
SECTION("'abc' vs 'abc'") {
|
||||
a.set("abc");
|
||||
b.set("abc");
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("'abc' vs 'bcd'") {
|
||||
a.set("abc");
|
||||
b.set("bcd");
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a < b);
|
||||
CHECK(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK_FALSE(a > b);
|
||||
CHECK_FALSE(a >= b);
|
||||
}
|
||||
|
||||
SECTION("'bcd' vs 'abc'") {
|
||||
a.set("bcd");
|
||||
b.set("abc");
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a > b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
}
|
||||
|
||||
SECTION("serialized('abc') vs serialized('abc')") {
|
||||
a.set(serialized("abc"));
|
||||
b.set(serialized("abc"));
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("serialized('abc') vs serialized('bcd')") {
|
||||
a.set(serialized("abc"));
|
||||
b.set(serialized("bcd"));
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a < b);
|
||||
CHECK(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK_FALSE(a > b);
|
||||
CHECK_FALSE(a >= b);
|
||||
}
|
||||
|
||||
SECTION("serialized('bcd') vs serialized('abc')") {
|
||||
a.set(serialized("bcd"));
|
||||
b.set(serialized("abc"));
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a > b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
}
|
||||
|
||||
SECTION("false vs true") {
|
||||
a.set(false);
|
||||
b.set(true);
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a < b);
|
||||
CHECK(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK_FALSE(a > b);
|
||||
CHECK_FALSE(a >= b);
|
||||
}
|
||||
|
||||
SECTION("false vs -1") {
|
||||
a.set(false);
|
||||
b.set(-1);
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a > b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
}
|
||||
|
||||
SECTION("null vs null") {
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("42 vs 42") {
|
||||
a.set(42);
|
||||
b.set(42);
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("42 vs 42.0") {
|
||||
a.set(42);
|
||||
b.set(42.0);
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("42.0 vs 42") {
|
||||
a.set(42.0);
|
||||
b.set(42);
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("-42 vs -42") {
|
||||
a.set(-42);
|
||||
b.set(-42);
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK(a <= b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("-42 vs 42") {
|
||||
a.set(-42);
|
||||
b.set(42);
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a < b);
|
||||
CHECK(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK_FALSE(a > b);
|
||||
CHECK_FALSE(a >= b);
|
||||
}
|
||||
|
||||
SECTION("42 vs -42") {
|
||||
a.set(42);
|
||||
b.set(-42);
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a > b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
}
|
||||
|
||||
SECTION("42.0 vs -42") {
|
||||
a.set(42.0);
|
||||
b.set(-42);
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK(a > b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
}
|
||||
|
||||
SECTION("[1] vs [1]") {
|
||||
a.add(1);
|
||||
b.add(1);
|
||||
|
||||
CHECK(a <= b);
|
||||
CHECK(a == b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("[1] vs [2]") {
|
||||
a.add(1);
|
||||
b.add(2);
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK_FALSE(a > b);
|
||||
CHECK_FALSE(a >= b);
|
||||
}
|
||||
|
||||
SECTION("{x:1} vs {x:1}") {
|
||||
a["x"] = 1;
|
||||
b["x"] = 1;
|
||||
|
||||
CHECK(a <= b);
|
||||
CHECK(a == b);
|
||||
CHECK(a >= b);
|
||||
CHECK_FALSE(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a > b);
|
||||
}
|
||||
|
||||
SECTION("{x:1} vs {x:2}") {
|
||||
a["x"] = 1;
|
||||
b["x"] = 2;
|
||||
|
||||
CHECK(a != b);
|
||||
CHECK_FALSE(a < b);
|
||||
CHECK_FALSE(a <= b);
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK_FALSE(a > b);
|
||||
CHECK_FALSE(a >= b);
|
||||
}
|
||||
}
|
||||
|
@ -47,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
var1.set(str);
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
}
|
||||
|
||||
SECTION("stores std::string by copy") {
|
||||
var1.set(std::string("hello!!"));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
}
|
||||
|
||||
SECTION("stores Serialized<const char*> by reference") {
|
||||
var1.set(serialized("hello!!", JSON_STRING_SIZE(8)));
|
||||
var1.set(serialized("hello!!", 8));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == 0);
|
||||
@ -69,18 +69,18 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
|
||||
SECTION("stores Serialized<char*> by copy") {
|
||||
char str[] = "hello!!";
|
||||
var1.set(serialized(str, 8));
|
||||
var1.set(serialized(str, 7));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
}
|
||||
|
||||
SECTION("stores Serialized<std::string> by copy") {
|
||||
var1.set(serialized(std::string("hello!!!")));
|
||||
var1.set(serialized(std::string("hello!!")));
|
||||
var2.set(var1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
|
||||
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
enum MYENUM2 { ONE = 1, TWO = 2 };
|
||||
|
||||
template <typename TVariant>
|
||||
void checkIsArray(TVariant var) {
|
||||
REQUIRE(var.template is<JsonArray>());
|
||||
@ -80,6 +82,7 @@ void checkIsInteger(TVariant var) {
|
||||
REQUIRE(var.template is<int>());
|
||||
REQUIRE(var.template is<float>());
|
||||
REQUIRE(var.template is<double>());
|
||||
REQUIRE(var.template is<MYENUM2>());
|
||||
|
||||
REQUIRE_FALSE(var.template is<bool>());
|
||||
REQUIRE_FALSE(var.template is<const char *>());
|
||||
@ -107,6 +110,7 @@ void checkIsString(TVariant var) {
|
||||
REQUIRE_FALSE(var.template is<double>());
|
||||
REQUIRE_FALSE(var.template is<float>());
|
||||
REQUIRE_FALSE(var.template is<long>());
|
||||
REQUIRE_FALSE(var.template is<MYENUM2>());
|
||||
REQUIRE_FALSE(var.template is<JsonArray>());
|
||||
REQUIRE_FALSE(var.template is<JsonObject>());
|
||||
}
|
||||
|
@ -13,9 +13,4 @@ add_executable(MemberProxyTests
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MemberProxyTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(MemberProxy MemberProxyTests)
|
||||
|
@ -10,17 +10,39 @@ using namespace ARDUINOJSON_NAMESPACE;
|
||||
TEST_CASE("MemberProxy::operator==()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
|
||||
SECTION("same values") {
|
||||
doc["key1"] = "value";
|
||||
doc["key2"] = "value";
|
||||
REQUIRE(doc["key1"] == doc["key2"]);
|
||||
REQUIRE_FALSE(doc["key1"] != doc["key2"]);
|
||||
SECTION("1 vs 1") {
|
||||
doc["a"] = 1;
|
||||
doc["b"] = 1;
|
||||
|
||||
REQUIRE(doc["a"] <= doc["b"]);
|
||||
REQUIRE(doc["a"] == doc["b"]);
|
||||
REQUIRE(doc["a"] >= doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] != doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] < doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] > doc["b"]);
|
||||
}
|
||||
|
||||
SECTION("different values") {
|
||||
doc["key1"] = "value1";
|
||||
doc["key2"] = "value2";
|
||||
REQUIRE_FALSE(doc["key1"] == doc["key2"]);
|
||||
REQUIRE(doc["key1"] != doc["key2"]);
|
||||
SECTION("1 vs 2") {
|
||||
doc["a"] = 1;
|
||||
doc["b"] = 2;
|
||||
|
||||
REQUIRE(doc["a"] != doc["b"]);
|
||||
REQUIRE(doc["a"] < doc["b"]);
|
||||
REQUIRE(doc["a"] <= doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] == doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] > doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] >= doc["b"]);
|
||||
}
|
||||
|
||||
SECTION("'abc' vs 'bcd'") {
|
||||
doc["a"] = "abc";
|
||||
doc["b"] = "bcd";
|
||||
|
||||
REQUIRE(doc["a"] != doc["b"]);
|
||||
REQUIRE(doc["a"] < doc["b"]);
|
||||
REQUIRE(doc["a"] <= doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] == doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] > doc["b"]);
|
||||
REQUIRE_FALSE(doc["a"] >= doc["b"]);
|
||||
}
|
||||
}
|
||||
|
@ -4,15 +4,10 @@
|
||||
|
||||
add_executable(MemoryPoolTests
|
||||
allocVariant.cpp
|
||||
allocString.cpp
|
||||
clear.cpp
|
||||
saveString.cpp
|
||||
size.cpp
|
||||
StringBuilder.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MemoryPoolTests
|
||||
ArduinoJson
|
||||
catch
|
||||
StringCopier.cpp
|
||||
)
|
||||
|
||||
add_test(MemoryPool MemoryPoolTests)
|
||||
|
@ -1,41 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("StringBuilder") {
|
||||
char buffer[4096];
|
||||
|
||||
SECTION("Works when buffer is big enough") {
|
||||
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
|
||||
|
||||
StringBuilder str(&pool);
|
||||
str.append("hello");
|
||||
|
||||
REQUIRE(str.complete() == std::string("hello"));
|
||||
}
|
||||
|
||||
SECTION("Returns null when too small") {
|
||||
MemoryPool pool(buffer, sizeof(void*));
|
||||
|
||||
StringBuilder str(&pool);
|
||||
str.append("hello world!");
|
||||
|
||||
REQUIRE(str.complete() == 0);
|
||||
}
|
||||
|
||||
SECTION("Increases size of memory pool") {
|
||||
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
|
||||
|
||||
StringBuilder str(&pool);
|
||||
str.append('h');
|
||||
str.complete();
|
||||
|
||||
REQUIRE(JSON_STRING_SIZE(2) == pool.size());
|
||||
}
|
||||
}
|
84
extras/tests/MemoryPool/StringCopier.cpp
Normal file
84
extras/tests/MemoryPool/StringCopier.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/StringStorage/StringCopier.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("StringCopier") {
|
||||
char buffer[4096];
|
||||
|
||||
SECTION("Works when buffer is big enough") {
|
||||
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
|
||||
StringCopier str;
|
||||
|
||||
str.startString(&pool);
|
||||
str.append("hello");
|
||||
str.append('\0');
|
||||
|
||||
REQUIRE(str.isValid() == true);
|
||||
REQUIRE(str.c_str() == std::string("hello"));
|
||||
}
|
||||
|
||||
SECTION("Returns null when too small") {
|
||||
MemoryPool pool(buffer, sizeof(void*));
|
||||
StringCopier str;
|
||||
|
||||
str.startString(&pool);
|
||||
str.append("hello world!");
|
||||
|
||||
REQUIRE(str.isValid() == false);
|
||||
}
|
||||
|
||||
SECTION("Increases size of memory pool") {
|
||||
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
|
||||
StringCopier str;
|
||||
|
||||
str.startString(&pool);
|
||||
str.append('h');
|
||||
str.save(&pool);
|
||||
|
||||
REQUIRE(1 == pool.size());
|
||||
}
|
||||
}
|
||||
|
||||
static const char* addStringToPool(MemoryPool* pool, const char* s) {
|
||||
StringCopier str;
|
||||
str.startString(pool);
|
||||
str.append(s);
|
||||
str.append('\0');
|
||||
return str.save(pool);
|
||||
}
|
||||
|
||||
TEST_CASE("StringCopier::save() deduplicates strings") {
|
||||
char buffer[4096];
|
||||
MemoryPool pool(buffer, 4096);
|
||||
|
||||
SECTION("Basic") {
|
||||
const char* s1 = addStringToPool(&pool, "hello");
|
||||
const char* s2 = addStringToPool(&pool, "world");
|
||||
const char* s3 = addStringToPool(&pool, "hello");
|
||||
|
||||
REQUIRE(s1 == s3);
|
||||
REQUIRE(s2 != s3);
|
||||
REQUIRE(pool.size() == 12);
|
||||
}
|
||||
|
||||
SECTION("Requires terminator") {
|
||||
const char* s1 = addStringToPool(&pool, "hello world");
|
||||
const char* s2 = addStringToPool(&pool, "hello");
|
||||
|
||||
REQUIRE(s2 != s1);
|
||||
REQUIRE(pool.size() == 12 + 6);
|
||||
}
|
||||
|
||||
SECTION("Don't overrun") {
|
||||
const char* s1 = addStringToPool(&pool, "hello world");
|
||||
const char* s2 = addStringToPool(&pool, "wor");
|
||||
|
||||
REQUIRE(s2 != s1);
|
||||
REQUIRE(pool.size() == 12 + 4);
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("MemoryPool::allocFrozenString()") {
|
||||
const size_t poolCapacity = 64;
|
||||
const size_t longestString = poolCapacity;
|
||||
char buffer[poolCapacity];
|
||||
MemoryPool pool(buffer, poolCapacity);
|
||||
|
||||
SECTION("Returns different addresses") {
|
||||
char *a = pool.allocFrozenString(1);
|
||||
char *b = pool.allocFrozenString(1);
|
||||
REQUIRE(a != b);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when full") {
|
||||
void *p1 = pool.allocFrozenString(longestString);
|
||||
REQUIRE(p1 != 0);
|
||||
|
||||
void *p2 = pool.allocFrozenString(1);
|
||||
REQUIRE(p2 == 0);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when pool is too small") {
|
||||
void *p = pool.allocFrozenString(longestString + 1);
|
||||
REQUIRE(0 == p);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when buffer is NULL") {
|
||||
MemoryPool pool2(0, poolCapacity);
|
||||
REQUIRE(0 == pool2.allocFrozenString(2));
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when capacity is 0") {
|
||||
MemoryPool pool2(buffer, 0);
|
||||
REQUIRE(0 == pool2.allocFrozenString(2));
|
||||
}
|
||||
|
||||
SECTION("Returns same address after clear()") {
|
||||
void *a = pool.allocFrozenString(1);
|
||||
pool.clear();
|
||||
void *b = pool.allocFrozenString(1);
|
||||
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
|
||||
SECTION("Can use full capacity when fresh") {
|
||||
void *a = pool.allocFrozenString(longestString);
|
||||
|
||||
REQUIRE(a != 0);
|
||||
}
|
||||
|
||||
SECTION("Can use full capacity after clear") {
|
||||
pool.allocFrozenString(longestString);
|
||||
pool.clear();
|
||||
|
||||
void *a = pool.allocFrozenString(longestString);
|
||||
|
||||
REQUIRE(a != 0);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
@ -21,8 +22,8 @@ TEST_CASE("MemoryPool::clear()") {
|
||||
}
|
||||
|
||||
SECTION("Discards allocated strings") {
|
||||
pool.allocFrozenString(10);
|
||||
REQUIRE(pool.size() > 0);
|
||||
pool.saveString(adaptString(const_cast<char *>("123456789")));
|
||||
REQUIRE(pool.size() == 10);
|
||||
|
||||
pool.clear();
|
||||
|
||||
|
81
extras/tests/MemoryPool/saveString.cpp
Normal file
81
extras/tests/MemoryPool/saveString.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
static const char *saveString(MemoryPool &pool, const char *s) {
|
||||
return pool.saveString(adaptString(const_cast<char *>(s)));
|
||||
}
|
||||
|
||||
TEST_CASE("MemoryPool::saveString()") {
|
||||
char buffer[32];
|
||||
MemoryPool pool(buffer, 32);
|
||||
|
||||
SECTION("Duplicates different strings") {
|
||||
const char *a = saveString(pool, "hello");
|
||||
const char *b = saveString(pool, "world");
|
||||
REQUIRE(a != b);
|
||||
REQUIRE(pool.size() == 6 + 6);
|
||||
}
|
||||
|
||||
SECTION("Deduplicates identical strings") {
|
||||
const char *a = saveString(pool, "hello");
|
||||
const char *b = saveString(pool, "hello");
|
||||
REQUIRE(a == b);
|
||||
REQUIRE(pool.size() == 6);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when full") {
|
||||
REQUIRE(pool.capacity() == 32);
|
||||
|
||||
const void *p1 = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
REQUIRE(p1 != 0);
|
||||
REQUIRE(pool.size() == 32);
|
||||
|
||||
const void *p2 = saveString(pool, "b");
|
||||
REQUIRE(p2 == 0);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when pool is too small") {
|
||||
const void *p = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
REQUIRE(0 == p);
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when buffer is NULL") {
|
||||
MemoryPool pool2(0, 32);
|
||||
REQUIRE(0 == saveString(pool2, "a"));
|
||||
}
|
||||
|
||||
SECTION("Returns NULL when capacity is 0") {
|
||||
MemoryPool pool2(buffer, 0);
|
||||
REQUIRE(0 == saveString(pool2, "a"));
|
||||
}
|
||||
|
||||
SECTION("Returns same address after clear()") {
|
||||
const void *a = saveString(pool, "hello");
|
||||
pool.clear();
|
||||
const void *b = saveString(pool, "world");
|
||||
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
|
||||
SECTION("Can use full capacity when fresh") {
|
||||
const void *a = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
|
||||
REQUIRE(a != 0);
|
||||
}
|
||||
|
||||
SECTION("Can use full capacity after clear") {
|
||||
saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
pool.clear();
|
||||
|
||||
const void *a = saveString(pool, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||||
|
||||
REQUIRE(a != 0);
|
||||
}
|
||||
}
|
@ -22,29 +22,6 @@ TEST_CASE("MemoryPool::size()") {
|
||||
REQUIRE(0 == pool.size());
|
||||
}
|
||||
|
||||
SECTION("size() == capacity() after allocExpandableString()") {
|
||||
pool.allocExpandableString();
|
||||
REQUIRE(pool.size() == pool.capacity());
|
||||
}
|
||||
|
||||
SECTION("Decreases after freezeString()") {
|
||||
StringSlot a = pool.allocExpandableString();
|
||||
pool.freezeString(a, 1);
|
||||
REQUIRE(pool.size() == JSON_STRING_SIZE(1));
|
||||
|
||||
StringSlot b = pool.allocExpandableString();
|
||||
pool.freezeString(b, 1);
|
||||
REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(1));
|
||||
}
|
||||
|
||||
SECTION("Increases after allocFrozenString()") {
|
||||
pool.allocFrozenString(0);
|
||||
REQUIRE(pool.size() == JSON_STRING_SIZE(0));
|
||||
|
||||
pool.allocFrozenString(0);
|
||||
REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(0));
|
||||
}
|
||||
|
||||
SECTION("Doesn't grow when memory pool is full") {
|
||||
const size_t variantCount = sizeof(buffer) / sizeof(VariantSlot);
|
||||
|
||||
|
@ -3,56 +3,20 @@
|
||||
# MIT License
|
||||
|
||||
add_executable(MiscTests
|
||||
arithmeticCompare.cpp
|
||||
conflicts.cpp
|
||||
FloatParts.cpp
|
||||
JsonString.cpp
|
||||
Readers.cpp
|
||||
StringAdapters.cpp
|
||||
StringWriter.cpp
|
||||
TypeTraits.cpp
|
||||
unsigned_char.cpp
|
||||
Utf8.cpp
|
||||
Utf16.cpp
|
||||
Utf8.cpp
|
||||
version.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MiscTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
set_target_properties(MiscTests PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
add_test(Misc MiscTests)
|
||||
|
||||
macro(build_should_fail target)
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||
)
|
||||
add_test(
|
||||
NAME
|
||||
${target}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_BINARY_DIR}
|
||||
)
|
||||
set_tests_properties(${target}
|
||||
|
||||
|
||||
|
||||
PROPERTIES
|
||||
WILL_FAIL TRUE)
|
||||
endmacro()
|
||||
|
||||
|
||||
add_executable(Issue978
|
||||
Issue978.cpp
|
||||
)
|
||||
build_should_fail(Issue978)
|
||||
|
||||
add_executable(Issue1189
|
||||
Issue1189.cpp
|
||||
)
|
||||
build_should_fail(Issue1189)
|
||||
|
60
extras/tests/Misc/JsonString.cpp
Normal file
60
extras/tests/Misc/JsonString.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonString") {
|
||||
SECTION("Default constructor creates a null JsonString") {
|
||||
JsonString s;
|
||||
|
||||
CHECK(s.isNull() == true);
|
||||
CHECK(s.c_str() == 0);
|
||||
CHECK(s.isStatic() == true);
|
||||
}
|
||||
|
||||
SECTION("Compare null with null") {
|
||||
JsonString a, b;
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK_FALSE(a != b);
|
||||
}
|
||||
|
||||
SECTION("Compare null with non-null") {
|
||||
JsonString a(0), b("hello");
|
||||
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK(a != b);
|
||||
}
|
||||
|
||||
SECTION("Compare non-null with null") {
|
||||
JsonString a("hello"), b(0);
|
||||
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK(a != b);
|
||||
}
|
||||
|
||||
SECTION("Compare different strings") {
|
||||
JsonString a("hello"), b("world");
|
||||
|
||||
CHECK_FALSE(a == b);
|
||||
CHECK(a != b);
|
||||
}
|
||||
|
||||
SECTION("Compare identical by pointer") {
|
||||
JsonString a("hello"), b("hello");
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK_FALSE(a != b);
|
||||
}
|
||||
|
||||
SECTION("Compare identical by value") {
|
||||
char s1[] = "hello";
|
||||
char s2[] = "hello";
|
||||
JsonString a(s1), b(s2);
|
||||
|
||||
CHECK(a == b);
|
||||
CHECK_FALSE(a != b);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
#include "progmem_emulation.hpp"
|
||||
#include "weird_strcmp.hpp"
|
||||
|
||||
#include <ArduinoJson/Strings/ArduinoStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
|
||||
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
|
||||
@ -114,6 +115,21 @@ TEST_CASE("std::string") {
|
||||
CHECK(adapter.size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("Arduino String") {
|
||||
::String str("bravo");
|
||||
ArduinoStringAdapter adapter = adaptString(str);
|
||||
|
||||
CHECK(adapter.compare(NULL) > 0);
|
||||
CHECK(adapter.compare("alpha") > 0);
|
||||
CHECK(adapter.compare("bravo") == 0);
|
||||
CHECK(adapter.compare("charlie") < 0);
|
||||
|
||||
CHECK(adapter.equals("bravo"));
|
||||
CHECK_FALSE(adapter.equals("charlie"));
|
||||
|
||||
CHECK(adapter.size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("custom_string") {
|
||||
custom_string str("bravo");
|
||||
StlStringAdapter<custom_string> adapter = adaptString(str);
|
||||
@ -145,4 +161,12 @@ TEST_CASE("IsString<T>") {
|
||||
SECTION("const __FlashStringHelper*") {
|
||||
CHECK(IsString<const __FlashStringHelper*>::value == true);
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
CHECK(IsString<const char*>::value == true);
|
||||
}
|
||||
|
||||
SECTION("const char[]") {
|
||||
CHECK(IsString<const char[8]>::value == true);
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,24 @@ TEST_CASE("Polyfills/type_traits") {
|
||||
CHECK(is_const<const char>::value == true);
|
||||
}
|
||||
|
||||
SECTION("is_integral") {
|
||||
CHECK(is_integral<double>::value == false);
|
||||
CHECK(is_integral<float>::value == false);
|
||||
|
||||
CHECK(is_integral<bool>::value == true);
|
||||
CHECK(is_integral<char>::value == true);
|
||||
CHECK(is_integral<signed char>::value == true);
|
||||
CHECK(is_integral<signed int>::value == true);
|
||||
CHECK(is_integral<signed long>::value == true);
|
||||
CHECK(is_integral<signed short>::value == true);
|
||||
CHECK(is_integral<unsigned char>::value == true);
|
||||
CHECK(is_integral<unsigned int>::value == true);
|
||||
CHECK(is_integral<unsigned long>::value == true);
|
||||
CHECK(is_integral<unsigned short>::value == true);
|
||||
|
||||
CHECK(is_integral<UInt>::value == true);
|
||||
}
|
||||
|
||||
SECTION("is_signed") {
|
||||
CHECK(is_signed<char>::value == true);
|
||||
CHECK(is_signed<signed char>::value == true);
|
||||
|
@ -12,12 +12,14 @@ using namespace ARDUINOJSON_NAMESPACE;
|
||||
static void testCodepoint(uint32_t codepoint, std::string expected) {
|
||||
char buffer[4096];
|
||||
MemoryPool pool(buffer, 4096);
|
||||
StringBuilder str(&pool);
|
||||
StringCopier str;
|
||||
str.startString(&pool);
|
||||
|
||||
CAPTURE(codepoint);
|
||||
Utf8::encodeCodepoint(codepoint, str);
|
||||
|
||||
REQUIRE(str.complete() == expected);
|
||||
str.append('\0');
|
||||
REQUIRE(str.c_str() == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("Utf8::encodeCodepoint()") {
|
||||
|
103
extras/tests/Misc/arithmeticCompare.cpp
Normal file
103
extras/tests/Misc/arithmeticCompare.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Numbers/arithmeticCompare.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("arithmeticCompare()") {
|
||||
SECTION("int vs uint8_t") {
|
||||
CHECK((arithmeticCompare<int, uint8_t>(256, 1) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<int, uint8_t>(41, 42) == COMPARE_RESULT_LESS));
|
||||
CHECK((arithmeticCompare<int, uint8_t>(42, 42) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<int, uint8_t>(43, 42) == COMPARE_RESULT_GREATER));
|
||||
}
|
||||
|
||||
SECTION("unsigned vs int") {
|
||||
CHECK((arithmeticCompare<unsigned, int>(0, -1) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<unsigned, int>(42, 41) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<unsigned, int>(42, 42) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<unsigned, int>(42, 43) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
|
||||
SECTION("float vs int") {
|
||||
CHECK((arithmeticCompare<float, int>(42, 41) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<float, int>(42, 42) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<float, int>(42, 43) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
|
||||
SECTION("int vs unsigned") {
|
||||
CHECK((arithmeticCompare<int, unsigned>(-1, 0) == COMPARE_RESULT_LESS));
|
||||
CHECK((arithmeticCompare<int, unsigned>(0, 0) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<int, unsigned>(1, 0) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<int, unsigned>(42, 41) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<int, unsigned>(42, 42) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<int, unsigned>(42, 43) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
|
||||
SECTION("unsigned vs unsigned") {
|
||||
CHECK((arithmeticCompare<unsigned, unsigned>(42, 41) ==
|
||||
COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<unsigned, unsigned>(42, 42) ==
|
||||
COMPARE_RESULT_EQUAL));
|
||||
CHECK(
|
||||
(arithmeticCompare<unsigned, unsigned>(42, 43) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
|
||||
SECTION("bool vs bool") {
|
||||
CHECK(
|
||||
(arithmeticCompare<bool, bool>(false, false) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<bool, bool>(true, true) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<bool, bool>(false, true) == COMPARE_RESULT_LESS));
|
||||
CHECK(
|
||||
(arithmeticCompare<bool, bool>(true, false) == COMPARE_RESULT_GREATER));
|
||||
}
|
||||
|
||||
SECTION("bool vs int") {
|
||||
CHECK((arithmeticCompare<bool, int>(false, -1) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<bool, int>(false, 0) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<bool, int>(false, 1) == COMPARE_RESULT_LESS));
|
||||
CHECK((arithmeticCompare<bool, int>(true, 0) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<bool, int>(true, 1) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<bool, int>(true, 2) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
|
||||
SECTION("bool vs int") {
|
||||
CHECK((arithmeticCompare<int, bool>(0, false) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<int, bool>(1, true) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompare<int, bool>(1, false) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompare<int, bool>(0, true) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("arithmeticCompareNegateLeft()") {
|
||||
SECTION("unsigned vs int") {
|
||||
CHECK((arithmeticCompareNegateLeft<int>(0, 1) == COMPARE_RESULT_LESS));
|
||||
CHECK((arithmeticCompareNegateLeft<int>(42, -41) == COMPARE_RESULT_LESS));
|
||||
CHECK((arithmeticCompareNegateLeft<int>(42, -42) == COMPARE_RESULT_EQUAL));
|
||||
CHECK(
|
||||
(arithmeticCompareNegateLeft<int>(42, -43) == COMPARE_RESULT_GREATER));
|
||||
}
|
||||
|
||||
SECTION("unsigned vs unsigned") {
|
||||
CHECK(
|
||||
(arithmeticCompareNegateLeft<unsigned>(42, 42) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("arithmeticCompareNegateRight()") {
|
||||
SECTION("int vs unsigned") {
|
||||
CHECK((arithmeticCompareNegateRight<int>(1, 0) == COMPARE_RESULT_GREATER));
|
||||
CHECK(
|
||||
(arithmeticCompareNegateRight<int>(-41, 42) == COMPARE_RESULT_GREATER));
|
||||
CHECK((arithmeticCompareNegateRight<int>(-42, 42) == COMPARE_RESULT_EQUAL));
|
||||
CHECK((arithmeticCompareNegateRight<int>(-43, 42) == COMPARE_RESULT_LESS));
|
||||
}
|
||||
|
||||
SECTION("unsigned vs unsigned") {
|
||||
CHECK((arithmeticCompareNegateRight<unsigned>(42, 42) ==
|
||||
COMPARE_RESULT_GREATER));
|
||||
}
|
||||
}
|
@ -18,17 +18,14 @@ add_executable(MixedConfigurationTests
|
||||
enable_nan_0.cpp
|
||||
enable_nan_1.cpp
|
||||
enable_progmem_1.cpp
|
||||
enable_string_deduplication_0.cpp
|
||||
enable_string_deduplication_1.cpp
|
||||
use_double_0.cpp
|
||||
use_double_1.cpp
|
||||
use_long_long_0.cpp
|
||||
use_long_long_1.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MixedConfigurationTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
set_target_properties(MixedConfigurationTests PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
add_test(MixedConfiguration MixedConfigurationTests)
|
||||
|
@ -0,0 +1,125 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include "progmem_emulation.hpp"
|
||||
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
|
||||
#define ARDUINOJSON_ENABLE_PROGMEM 1
|
||||
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 0
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||
StaticJsonDocument<1024> doc;
|
||||
|
||||
SECTION("deserializeJson()") {
|
||||
SECTION("Deduplicate values") {
|
||||
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("Deduplicate keys") {
|
||||
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 16);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
|
||||
CHECK(key1 != key2);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("JsonDocument") {
|
||||
SECTION("values") {
|
||||
SECTION("std::string") {
|
||||
doc.add(std::string("example"));
|
||||
doc.add(std::string("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
char value[] = "example";
|
||||
doc.add(value);
|
||||
doc.add(value);
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
doc.add(String("example"));
|
||||
doc.add(String("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("Flash string") {
|
||||
doc.add(F("example"));
|
||||
doc.add(F("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("keys") {
|
||||
SECTION("std::string") {
|
||||
doc[0][std::string("example")] = 1;
|
||||
doc[1][std::string("example")] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 != key2);
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
char key[] = "example";
|
||||
doc[0][key] = 1;
|
||||
doc[1][key] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 != key2);
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
doc[0][String("example")] = 1;
|
||||
doc[1][String("example")] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 != key2);
|
||||
}
|
||||
|
||||
SECTION("Flash string") {
|
||||
doc[0][F("example")] = 1;
|
||||
doc[1][F("example")] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 != key2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include "progmem_emulation.hpp"
|
||||
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
|
||||
#define ARDUINOJSON_ENABLE_PROGMEM 1
|
||||
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||
StaticJsonDocument<1024> doc;
|
||||
|
||||
SECTION("deserializeJson()") {
|
||||
SECTION("Deduplicate values") {
|
||||
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("Deduplicate keys") {
|
||||
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 8);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 == key2);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("JsonDocument") {
|
||||
SECTION("values") {
|
||||
SECTION("std::string") {
|
||||
doc.add(std::string("example"));
|
||||
doc.add(std::string("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
char value[] = "example";
|
||||
doc.add(value);
|
||||
doc.add(value);
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
doc.add(String("example"));
|
||||
doc.add(String("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
}
|
||||
|
||||
SECTION("Flash string") {
|
||||
doc.add(F("example"));
|
||||
doc.add(F("example"));
|
||||
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("keys") {
|
||||
SECTION("std::string") {
|
||||
doc[0][std::string("example")] = 1;
|
||||
doc[1][std::string("example")] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 == key2);
|
||||
}
|
||||
|
||||
SECTION("char*") {
|
||||
char key[] = "example";
|
||||
doc[0][key] = 1;
|
||||
doc[1][key] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 == key2);
|
||||
}
|
||||
|
||||
SECTION("Arduino String") {
|
||||
doc[0][String("example")] = 1;
|
||||
doc[1][String("example")] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 == key2);
|
||||
}
|
||||
|
||||
SECTION("Flash string") {
|
||||
doc[0][F("example")] = 1;
|
||||
doc[1][F("example")] = 2;
|
||||
|
||||
CHECK(doc.memoryUsage() ==
|
||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 == key2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,28 +3,14 @@
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
template <size_t size_of_long>
|
||||
std::string get_expected_json();
|
||||
|
||||
template <>
|
||||
std::string get_expected_json<4>() {
|
||||
return "{\"A\":2899336981,\"B\":2129924785}";
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string get_expected_json<8>() {
|
||||
return "{\"A\":123456789123456789,\"B\":987654321987654321}";
|
||||
}
|
||||
|
||||
TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
|
||||
root["A"] = 123456789123456789;
|
||||
root["B"] = 987654321987654321;
|
||||
doc["A"] = 42;
|
||||
doc["B"] = 84;
|
||||
|
||||
std::string json;
|
||||
serializeJson(doc, json);
|
||||
|
||||
REQUIRE(json == get_expected_json<sizeof(long)>());
|
||||
REQUIRE(json == "{\"A\":42,\"B\":84}");
|
||||
}
|
||||
|
@ -14,9 +14,4 @@ add_executable(MsgPackDeserializerTests
|
||||
notSupported.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MsgPackDeserializerTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(MsgPackDeserializer MsgPackDeserializerTests)
|
||||
|
@ -7,9 +7,9 @@
|
||||
|
||||
template <size_t Capacity>
|
||||
static void check(const char* input, DeserializationError expected) {
|
||||
StaticJsonDocument<Capacity> variant;
|
||||
StaticJsonDocument<Capacity> doc;
|
||||
|
||||
DeserializationError error = deserializeMsgPack(variant, input);
|
||||
DeserializationError error = deserializeMsgPack(doc, input);
|
||||
|
||||
CAPTURE(input);
|
||||
REQUIRE(error == expected);
|
||||
@ -17,7 +17,7 @@ static void check(const char* input, DeserializationError expected) {
|
||||
|
||||
template <size_t Size>
|
||||
static void checkString(const char* input, DeserializationError expected) {
|
||||
check<JSON_STRING_SIZE(Size)>(input, expected);
|
||||
check<Size>(input, expected);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {
|
||||
|
@ -11,9 +11,4 @@ add_executable(MsgPackSerializerTests
|
||||
serializeVariant.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MsgPackSerializerTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
add_test(MsgPackSerializer MsgPackSerializerTests)
|
||||
|
@ -8,10 +8,5 @@ add_executable(NumbersTests
|
||||
parseNumber.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(NumbersTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
|
||||
add_test(Numbers NumbersTests)
|
||||
|
@ -8,17 +8,17 @@
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("Test uint32_t overflow") {
|
||||
ParsedNumber<float, uint32_t> first =
|
||||
parseNumber<float, uint32_t>("4294967295");
|
||||
ParsedNumber<float, uint32_t> second =
|
||||
parseNumber<float, uint32_t>("4294967296");
|
||||
ParsedNumber<float, uint32_t> first, second;
|
||||
parseNumber("4294967295", first);
|
||||
parseNumber("4294967296", second);
|
||||
|
||||
REQUIRE(first.type() == uint8_t(VALUE_IS_POSITIVE_INTEGER));
|
||||
REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT));
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid value") {
|
||||
ParsedNumber<float, uint32_t> result = parseNumber<float, uint32_t>("6a3");
|
||||
ParsedNumber<float, uint32_t> result;
|
||||
parseNumber("6a3", result);
|
||||
|
||||
REQUIRE(result.type() == uint8_t(VALUE_IS_NULL));
|
||||
}
|
||||
|
@ -7,11 +7,6 @@ add_executable(TextFormatterTests
|
||||
writeString.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(TextFormatterTests
|
||||
ArduinoJson
|
||||
catch
|
||||
)
|
||||
|
||||
set_target_properties(TextFormatterTests PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
add_test(TextFormatter TextFormatterTests)
|
||||
|
@ -7,7 +7,7 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/bblanchon/ArduinoJson.git"
|
||||
},
|
||||
"version": "6.15.1",
|
||||
"version": "6.16.1",
|
||||
"authors": {
|
||||
"name": "Benoit Blanchon",
|
||||
"url": "https://blog.benoitblanchon.fr"
|
||||
|
@ -1,5 +1,5 @@
|
||||
name=ArduinoJson
|
||||
version=6.15.1
|
||||
version=6.16.1
|
||||
author=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
|
||||
sentence=A simple and efficient JSON library for embedded C++.
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "ArduinoJson/Object/MemberProxy.hpp"
|
||||
#include "ArduinoJson/Object/ObjectImpl.hpp"
|
||||
#include "ArduinoJson/Variant/VariantAsImpl.hpp"
|
||||
#include "ArduinoJson/Variant/VariantCompare.hpp"
|
||||
#include "ArduinoJson/Variant/VariantImpl.hpp"
|
||||
|
||||
#include "ArduinoJson/Json/JsonDeserializer.hpp"
|
||||
|
@ -5,7 +5,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Operators/VariantOperators.hpp>
|
||||
#include <ArduinoJson/Variant/VariantOperators.hpp>
|
||||
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
|
||||
#include <ArduinoJson/Variant/VariantTo.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -17,6 +18,7 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TArray>
|
||||
class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
public VariantShortcuts<ElementProxy<TArray> >,
|
||||
public Visitable {
|
||||
typedef ElementProxy<TArray> this_type;
|
||||
|
||||
@ -51,14 +53,6 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(VariantConstRef rhs) const {
|
||||
return static_cast<VariantConstRef>(getUpstreamElement()) == rhs;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator!=(VariantConstRef rhs) const {
|
||||
return static_cast<VariantConstRef>(getUpstreamElement()) != rhs;
|
||||
}
|
||||
|
||||
FORCE_INLINE void clear() const {
|
||||
getUpstreamElement().clear();
|
||||
}
|
||||
@ -141,6 +135,10 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
|
||||
return getOrAddUpstreamElement().getElement(index);
|
||||
}
|
||||
|
||||
VariantRef getOrAddElement(size_t index) const {
|
||||
return getOrAddUpstreamElement().getOrAddElement(index);
|
||||
}
|
||||
|
||||
FORCE_INLINE void remove(size_t index) const {
|
||||
getUpstreamElement().remove(index);
|
||||
}
|
||||
|
@ -5,18 +5,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Array/ArrayRef.hpp>
|
||||
#include <ArduinoJson/Document/JsonDocument.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
// Copy a 1D array to a JsonArray
|
||||
template <typename T, size_t N>
|
||||
inline bool copyArray(T (&src)[N], ArrayRef dst) {
|
||||
template <typename T, size_t N, typename TDestination>
|
||||
inline typename enable_if<!is_array<T>::value &&
|
||||
!is_base_of<JsonDocument, TDestination>::value,
|
||||
bool>::type
|
||||
copyArray(T (&src)[N], const TDestination& dst) {
|
||||
return copyArray(src, N, dst);
|
||||
}
|
||||
|
||||
// Copy a 1D array to a JsonDocument
|
||||
template <typename T, size_t N>
|
||||
inline bool copyArray(T (&src)[N], JsonDocument& dst) {
|
||||
return copyArray(src, dst.to<ArrayRef>());
|
||||
}
|
||||
|
||||
// Copy a 1D array to a JsonArray
|
||||
template <typename T>
|
||||
inline bool copyArray(T* src, size_t len, ArrayRef dst) {
|
||||
template <typename T, typename TDestination>
|
||||
inline typename enable_if<!is_array<T>::value &&
|
||||
!is_base_of<JsonDocument, TDestination>::value,
|
||||
bool>::type
|
||||
copyArray(T* src, size_t len, const TDestination& dst) {
|
||||
bool ok = true;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ok &= dst.add(src[i]);
|
||||
@ -24,9 +37,17 @@ inline bool copyArray(T* src, size_t len, ArrayRef dst) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Copy a 1D array to a JsonDocument
|
||||
template <typename T>
|
||||
inline bool copyArray(T* src, size_t len, JsonDocument& dst) {
|
||||
return copyArray(src, len, dst.to<ArrayRef>());
|
||||
}
|
||||
|
||||
// Copy a 2D array to a JsonArray
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) {
|
||||
template <typename T, size_t N1, size_t N2, typename TDestination>
|
||||
inline typename enable_if<!is_base_of<JsonDocument, TDestination>::value,
|
||||
bool>::type
|
||||
copyArray(T (&src)[N1][N2], const TDestination& dst) {
|
||||
bool ok = true;
|
||||
for (size_t i = 0; i < N1; i++) {
|
||||
ArrayRef nestedArray = dst.createNestedArray();
|
||||
@ -37,30 +58,93 @@ inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Copy a 2D array to a JsonDocument
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
inline bool copyArray(T (&src)[N1][N2], JsonDocument& dst) {
|
||||
return copyArray(src, dst.to<ArrayRef>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class ArrayCopier1D {
|
||||
public:
|
||||
ArrayCopier1D(T* destination, size_t capacity)
|
||||
: _destination(destination), _capacity(capacity), _size(0) {}
|
||||
|
||||
void visitArray(const CollectionData& array) {
|
||||
VariantSlot* slot = array.head();
|
||||
|
||||
while (slot != 0 && _size < _capacity) {
|
||||
_destination[_size++] = variantAs<T>(slot->data());
|
||||
slot = slot->next();
|
||||
}
|
||||
}
|
||||
void visitObject(const CollectionData&) {}
|
||||
void visitFloat(Float) {}
|
||||
void visitString(const char*) {}
|
||||
void visitRawJson(const char*, size_t) {}
|
||||
void visitNegativeInteger(UInt) {}
|
||||
void visitPositiveInteger(UInt) {}
|
||||
void visitBoolean(bool) {}
|
||||
void visitNull() {}
|
||||
|
||||
size_t result() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
T* _destination;
|
||||
size_t _capacity;
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
class ArrayCopier2D {
|
||||
public:
|
||||
ArrayCopier2D(T (*destination)[N1][N2]) : _destination(destination) {}
|
||||
|
||||
void visitArray(const CollectionData& array) {
|
||||
VariantSlot* slot = array.head();
|
||||
size_t n = 0;
|
||||
while (slot != 0 && n < N1) {
|
||||
ArrayCopier1D<T> copier((*_destination)[n++], N2);
|
||||
variantAccept(slot->data(), copier);
|
||||
slot = slot->next();
|
||||
}
|
||||
}
|
||||
void visitObject(const CollectionData&) {}
|
||||
void visitFloat(Float) {}
|
||||
void visitString(const char*) {}
|
||||
void visitRawJson(const char*, size_t) {}
|
||||
void visitNegativeInteger(UInt) {}
|
||||
void visitPositiveInteger(UInt) {}
|
||||
void visitBoolean(bool) {}
|
||||
void visitNull() {}
|
||||
|
||||
private:
|
||||
T (*_destination)[N1][N2];
|
||||
size_t _capacity1, _capacity2;
|
||||
};
|
||||
|
||||
// Copy a JsonArray to a 1D array
|
||||
template <typename T, size_t N>
|
||||
inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) {
|
||||
template <typename TSource, typename T, size_t N>
|
||||
inline typename enable_if<!is_array<T>::value, size_t>::type copyArray(
|
||||
const TSource& src, T (&dst)[N]) {
|
||||
return copyArray(src, dst, N);
|
||||
}
|
||||
|
||||
// Copy a JsonArray to a 1D array
|
||||
template <typename T>
|
||||
inline size_t copyArray(ArrayConstRef src, T* dst, size_t len) {
|
||||
size_t i = 0;
|
||||
for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < len;
|
||||
++it)
|
||||
dst[i++] = *it;
|
||||
return i;
|
||||
template <typename TSource, typename T>
|
||||
inline size_t copyArray(const TSource& src, T* dst, size_t len) {
|
||||
ArrayCopier1D<T> copier(dst, len);
|
||||
src.accept(copier);
|
||||
return copier.result();
|
||||
}
|
||||
|
||||
// Copy a JsonArray to a 2D array
|
||||
template <typename T, size_t N1, size_t N2>
|
||||
inline void copyArray(ArrayConstRef src, T (&dst)[N1][N2]) {
|
||||
size_t i = 0;
|
||||
for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < N1;
|
||||
++it) {
|
||||
copyArray(it->as<ArrayConstRef>(), dst[i++]);
|
||||
}
|
||||
template <typename TSource, typename T, size_t N1, size_t N2>
|
||||
inline void copyArray(const TSource& src, T (&dst)[N1][N2]) {
|
||||
ArrayCopier2D<T, N1, N2> copier(&dst);
|
||||
src.accept(copier);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -9,6 +9,10 @@
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
inline bool variantEquals(const VariantData* a, const VariantData* b) {
|
||||
return variantCompare(a, b) == COMPARE_RESULT_EQUAL;
|
||||
}
|
||||
|
||||
inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) {
|
||||
VariantSlot* slot = pool->allocVariant();
|
||||
if (!slot)
|
||||
|
@ -4,12 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define ARDUINOJSON_HAS_INT64 1
|
||||
#else
|
||||
#define ARDUINOJSON_HAS_INT64 0
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#define ARDUINOJSON_HAS_LONG_LONG 1
|
||||
#define ARDUINOJSON_HAS_NULLPTR 1
|
||||
@ -20,6 +14,12 @@
|
||||
#define ARDUINOJSON_HAS_RVALUE_REFERENCES 0
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG
|
||||
#define ARDUINOJSON_HAS_INT64 1
|
||||
#else
|
||||
#define ARDUINOJSON_HAS_INT64 0
|
||||
#endif
|
||||
|
||||
// Small or big machine?
|
||||
#ifndef ARDUINOJSON_EMBEDDED_MODE
|
||||
#if defined(ARDUINO) /* Arduino*/ \
|
||||
@ -164,7 +164,7 @@
|
||||
|
||||
// Convert unicode escape sequence (\u0123) to UTF-8
|
||||
#ifndef ARDUINOJSON_DECODE_UNICODE
|
||||
#define ARDUINOJSON_DECODE_UNICODE 0
|
||||
#define ARDUINOJSON_DECODE_UNICODE 1
|
||||
#endif
|
||||
|
||||
// Ignore comments in input
|
||||
@ -215,6 +215,10 @@
|
||||
#define ARDUINOJSON_TAB " "
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINOJSON_STRING_BUFFER_SIZE
|
||||
#define ARDUINOJSON_STRING_BUFFER_SIZE 32
|
||||
#endif
|
||||
|
@ -37,6 +37,7 @@ struct BoundedReader {
|
||||
|
||||
#include <ArduinoJson/Deserialization/Readers/IteratorReader.hpp>
|
||||
#include <ArduinoJson/Deserialization/Readers/RamReader.hpp>
|
||||
#include <ArduinoJson/Deserialization/Readers/VariantReader.hpp>
|
||||
|
||||
#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
|
||||
#include <ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp>
|
||||
|
34
src/ArduinoJson/Deserialization/Readers/VariantReader.hpp
Normal file
34
src/ArduinoJson/Deserialization/Readers/VariantReader.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Object/MemberProxy.hpp>
|
||||
#include <ArduinoJson/Variant/VariantRef.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TArray>
|
||||
struct Reader<ElementProxy<TArray>, void> : Reader<char*, void> {
|
||||
explicit Reader(const ElementProxy<TArray>& x)
|
||||
: Reader<char*, void>(x.template as<const char*>()) {}
|
||||
};
|
||||
|
||||
template <typename TObject, typename TStringRef>
|
||||
struct Reader<MemberProxy<TObject, TStringRef>, void> : Reader<char*, void> {
|
||||
explicit Reader(const MemberProxy<TObject, TStringRef>& x)
|
||||
: Reader<char*, void>(x.template as<const char*>()) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Reader<VariantRef, void> : Reader<char*, void> {
|
||||
explicit Reader(VariantRef x) : Reader<char*, void>(x.as<const char*>()) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Reader<VariantConstRef, void> : Reader<char*, void> {
|
||||
explicit Reader(VariantConstRef x)
|
||||
: Reader<char*, void>(x.as<const char*>()) {}
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -32,9 +32,8 @@ deserialize(JsonDocument &doc, const TString &input, NestingLimit nestingLimit,
|
||||
TFilter filter) {
|
||||
Reader<TString> reader(input);
|
||||
doc.clear();
|
||||
return makeDeserializer<TDeserializer>(
|
||||
doc.memoryPool(), reader,
|
||||
makeStringStorage(doc.memoryPool(), input))
|
||||
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
|
||||
makeStringStorage(input))
|
||||
.parse(doc.data(), filter, nestingLimit);
|
||||
}
|
||||
//
|
||||
@ -48,9 +47,8 @@ DeserializationError deserialize(JsonDocument &doc, TChar *input,
|
||||
TFilter filter) {
|
||||
BoundedReader<TChar *> reader(input, inputSize);
|
||||
doc.clear();
|
||||
return makeDeserializer<TDeserializer>(
|
||||
doc.memoryPool(), reader,
|
||||
makeStringStorage(doc.memoryPool(), input))
|
||||
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
|
||||
makeStringStorage(input))
|
||||
.parse(doc.data(), filter, nestingLimit);
|
||||
}
|
||||
//
|
||||
@ -62,9 +60,8 @@ DeserializationError deserialize(JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit, TFilter filter) {
|
||||
Reader<TStream> reader(input);
|
||||
doc.clear();
|
||||
return makeDeserializer<TDeserializer>(
|
||||
doc.memoryPool(), reader,
|
||||
makeStringStorage(doc.memoryPool(), input))
|
||||
return makeDeserializer<TDeserializer>(doc.memoryPool(), reader,
|
||||
makeStringStorage(input))
|
||||
.parse(doc.data(), filter, nestingLimit);
|
||||
}
|
||||
|
||||
|
@ -19,34 +19,25 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TReader, typename TStringStorage>
|
||||
class JsonDeserializer {
|
||||
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
||||
StringBuilder;
|
||||
|
||||
struct StringOrError {
|
||||
DeserializationError err;
|
||||
const char *value;
|
||||
|
||||
StringOrError(DeserializationError e) : err(e) {}
|
||||
StringOrError(DeserializationError::Code c) : err(c) {}
|
||||
StringOrError(const char *s) : err(DeserializationError::Ok), value(s) {}
|
||||
};
|
||||
|
||||
public:
|
||||
JsonDeserializer(MemoryPool &pool, TReader reader,
|
||||
TStringStorage stringStorage)
|
||||
: _pool(&pool), _stringStorage(stringStorage), _latch(reader) {}
|
||||
: _stringStorage(stringStorage),
|
||||
_latch(reader),
|
||||
_pool(&pool),
|
||||
_error(DeserializationError::Ok) {}
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError parse(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
DeserializationError err = parseVariant(variant, filter, nestingLimit);
|
||||
parseVariant(variant, filter, nestingLimit);
|
||||
|
||||
if (!err && _latch.last() != 0 && !variant.isEnclosed()) {
|
||||
if (!_error && _latch.last() != 0 && !variant.isEnclosed()) {
|
||||
// We don't detect trailing characters earlier, so we need to check now
|
||||
err = DeserializationError::InvalidInput;
|
||||
_error = DeserializationError::InvalidInput;
|
||||
}
|
||||
|
||||
return err;
|
||||
return _error;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -68,11 +59,10 @@ class JsonDeserializer {
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError parseVariant(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
DeserializationError err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
bool parseVariant(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
switch (current()) {
|
||||
case '[':
|
||||
@ -102,10 +92,9 @@ class JsonDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError skipVariant(NestingLimit nestingLimit) {
|
||||
DeserializationError err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
bool skipVariant(NestingLimit nestingLimit) {
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
switch (current()) {
|
||||
case '[':
|
||||
@ -124,23 +113,24 @@ class JsonDeserializer {
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError parseArray(CollectionData &array, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
bool parseArray(CollectionData &array, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening braket
|
||||
ARDUINOJSON_ASSERT(current() == '[');
|
||||
move();
|
||||
|
||||
// Skip spaces
|
||||
DeserializationError err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Empty array?
|
||||
if (eat(']'))
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
TFilter memberFilter = filter[0UL];
|
||||
|
||||
@ -149,35 +139,38 @@ class JsonDeserializer {
|
||||
if (memberFilter.allow()) {
|
||||
// Allocate slot in array
|
||||
VariantData *value = array.addElement(_pool);
|
||||
if (!value)
|
||||
return DeserializationError::NoMemory;
|
||||
if (!value) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1 - Parse value
|
||||
err = parseVariant(*value, memberFilter, nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!parseVariant(*value, memberFilter, nestingLimit.decrement()))
|
||||
return false;
|
||||
} else {
|
||||
err = skipVariant(nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2 - Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// 3 - More values?
|
||||
if (eat(']'))
|
||||
return DeserializationError::Ok;
|
||||
if (!eat(','))
|
||||
return DeserializationError::InvalidInput;
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError skipArray(NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
bool skipArray(NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening braket
|
||||
ARDUINOJSON_ASSERT(current() == '[');
|
||||
@ -186,153 +179,162 @@ class JsonDeserializer {
|
||||
// Read each value
|
||||
for (;;) {
|
||||
// 1 - Skip value
|
||||
DeserializationError err = skipVariant(nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
|
||||
// 2 - Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// 3 - More values?
|
||||
if (eat(']'))
|
||||
return DeserializationError::Ok;
|
||||
if (!eat(','))
|
||||
return DeserializationError::InvalidInput;
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError parseObject(CollectionData &object, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
bool parseObject(CollectionData &object, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening brace
|
||||
ARDUINOJSON_ASSERT(current() == '{');
|
||||
move();
|
||||
|
||||
// Skip spaces
|
||||
DeserializationError err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Empty object?
|
||||
if (eat('}'))
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
// Parse key
|
||||
StringOrError key = parseKey();
|
||||
err = key.err; // <- this trick saves 62 bytes on AVR
|
||||
if (err)
|
||||
return err;
|
||||
if (!parseKey())
|
||||
return false;
|
||||
|
||||
// Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err; // Colon
|
||||
if (!eat(':'))
|
||||
return DeserializationError::InvalidInput;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
TFilter memberFilter = filter[key.value];
|
||||
// Colon
|
||||
if (!eat(':')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *key = _stringStorage.c_str();
|
||||
|
||||
TFilter memberFilter = filter[key];
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
VariantData *variant = object.getMember(adaptString(key.value));
|
||||
VariantData *variant = object.getMember(adaptString(key));
|
||||
if (!variant) {
|
||||
// Save key in memory pool.
|
||||
// This MUST be done before adding the slot.
|
||||
key = _stringStorage.save(_pool);
|
||||
|
||||
// Allocate slot in object
|
||||
VariantSlot *slot = object.addSlot(_pool);
|
||||
if (!slot)
|
||||
return DeserializationError::NoMemory;
|
||||
if (!slot) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
slot->setOwnedKey(make_not_null(key.value));
|
||||
slot->setKey(key, typename TStringStorage::storage_policy());
|
||||
|
||||
variant = slot->data();
|
||||
}
|
||||
|
||||
// Parse value
|
||||
err = parseVariant(*variant, memberFilter, nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!parseVariant(*variant, memberFilter, nestingLimit.decrement()))
|
||||
return false;
|
||||
} else {
|
||||
_stringStorage.reclaim(key.value);
|
||||
err = skipVariant(nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// More keys/values?
|
||||
if (eat('}'))
|
||||
return DeserializationError::Ok;
|
||||
if (!eat(','))
|
||||
return DeserializationError::InvalidInput;
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError skipObject(NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
bool skipObject(NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening brace
|
||||
ARDUINOJSON_ASSERT(current() == '{');
|
||||
move();
|
||||
|
||||
// Skip spaces
|
||||
DeserializationError err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Empty object?
|
||||
if (eat('}'))
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
// Skip key
|
||||
err = skipVariant(nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
|
||||
// Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Colon
|
||||
if (!eat(':'))
|
||||
return DeserializationError::InvalidInput;
|
||||
if (!eat(':')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip value
|
||||
err = skipVariant(nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
|
||||
// Skip spaces
|
||||
err = skipSpacesAndComments();
|
||||
if (err)
|
||||
return err;
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// More keys/values?
|
||||
if (eat('}'))
|
||||
return DeserializationError::Ok;
|
||||
if (!eat(','))
|
||||
return DeserializationError::InvalidInput;
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringOrError parseKey() {
|
||||
bool parseKey() {
|
||||
_stringStorage.startString(_pool);
|
||||
if (isQuote(current())) {
|
||||
return parseQuotedString();
|
||||
} else {
|
||||
@ -340,16 +342,17 @@ class JsonDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError parseStringValue(VariantData &variant) {
|
||||
StringOrError result = parseQuotedString();
|
||||
if (result.err)
|
||||
return result.err;
|
||||
variant.setOwnedString(make_not_null(result.value));
|
||||
return DeserializationError::Ok;
|
||||
bool parseStringValue(VariantData &variant) {
|
||||
_stringStorage.startString(_pool);
|
||||
if (!parseQuotedString())
|
||||
return false;
|
||||
const char *value = _stringStorage.save(_pool);
|
||||
variant.setString(make_not_null(value),
|
||||
typename TStringStorage::storage_policy());
|
||||
return true;
|
||||
}
|
||||
|
||||
StringOrError parseQuotedString() {
|
||||
StringBuilder builder = _stringStorage.startString();
|
||||
bool parseQuotedString() {
|
||||
#if ARDUINOJSON_DECODE_UNICODE
|
||||
Utf16::Codepoint codepoint;
|
||||
#endif
|
||||
@ -362,66 +365,82 @@ class JsonDeserializer {
|
||||
if (c == stopChar)
|
||||
break;
|
||||
|
||||
if (c == '\0')
|
||||
return DeserializationError::IncompleteInput;
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
c = current();
|
||||
if (c == '\0')
|
||||
return DeserializationError::IncompleteInput;
|
||||
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == 'u') {
|
||||
#if ARDUINOJSON_DECODE_UNICODE
|
||||
move();
|
||||
uint16_t codeunit;
|
||||
DeserializationError err = parseHex4(codeunit);
|
||||
if (err)
|
||||
return err;
|
||||
if (!parseHex4(codeunit))
|
||||
return false;
|
||||
if (codepoint.append(codeunit))
|
||||
Utf8::encodeCodepoint(codepoint.value(), builder);
|
||||
Utf8::encodeCodepoint(codepoint.value(), _stringStorage);
|
||||
continue;
|
||||
#else
|
||||
return DeserializationError::NotSupported;
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// replace char
|
||||
c = EscapeSequence::unescapeChar(c);
|
||||
if (c == '\0')
|
||||
return DeserializationError::InvalidInput;
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
move();
|
||||
}
|
||||
|
||||
builder.append(c);
|
||||
_stringStorage.append(c);
|
||||
}
|
||||
|
||||
const char *result = builder.complete();
|
||||
if (!result)
|
||||
return DeserializationError::NoMemory;
|
||||
return result;
|
||||
_stringStorage.append('\0');
|
||||
|
||||
if (!_stringStorage.isValid()) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StringOrError parseNonQuotedString() {
|
||||
StringBuilder builder = _stringStorage.startString();
|
||||
|
||||
bool parseNonQuotedString() {
|
||||
char c = current();
|
||||
ARDUINOJSON_ASSERT(c);
|
||||
|
||||
if (canBeInNonQuotedString(c)) { // no quotes
|
||||
do {
|
||||
move();
|
||||
builder.append(c);
|
||||
_stringStorage.append(c);
|
||||
c = current();
|
||||
} while (canBeInNonQuotedString(c));
|
||||
} else {
|
||||
return DeserializationError::InvalidInput;
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *result = builder.complete();
|
||||
if (!result)
|
||||
return DeserializationError::NoMemory;
|
||||
return result;
|
||||
_stringStorage.append('\0');
|
||||
|
||||
if (!_stringStorage.isValid()) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DeserializationError skipString() {
|
||||
bool skipString() {
|
||||
const char stopChar = current();
|
||||
|
||||
move();
|
||||
@ -430,87 +449,104 @@ class JsonDeserializer {
|
||||
move();
|
||||
if (c == stopChar)
|
||||
break;
|
||||
if (c == '\0')
|
||||
return DeserializationError::IncompleteInput;
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
if (c == '\\') {
|
||||
if (current() != '\0')
|
||||
move();
|
||||
}
|
||||
}
|
||||
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
DeserializationError parseNumericValue(VariantData &result) {
|
||||
char buffer[64];
|
||||
bool parseNumericValue(VariantData &result) {
|
||||
uint8_t n = 0;
|
||||
|
||||
char c = current();
|
||||
while (canBeInNonQuotedString(c) && n < 63) {
|
||||
move();
|
||||
buffer[n++] = c;
|
||||
_buffer[n++] = c;
|
||||
c = current();
|
||||
}
|
||||
buffer[n] = 0;
|
||||
_buffer[n] = 0;
|
||||
|
||||
c = buffer[0];
|
||||
c = _buffer[0];
|
||||
if (c == 't') { // true
|
||||
result.setBoolean(true);
|
||||
return n == 4 ? DeserializationError::Ok
|
||||
: DeserializationError::IncompleteInput;
|
||||
if (n != 4) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == 'f') { // false
|
||||
result.setBoolean(false);
|
||||
return n == 5 ? DeserializationError::Ok
|
||||
: DeserializationError::IncompleteInput;
|
||||
if (n != 5) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == 'n') { // null
|
||||
// the variant is already null
|
||||
return n == 4 ? DeserializationError::Ok
|
||||
: DeserializationError::IncompleteInput;
|
||||
if (n != 4) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ParsedNumber<Float, UInt> num = parseNumber<Float, UInt>(buffer);
|
||||
ParsedNumber<Float, UInt> num;
|
||||
parseNumber<Float, UInt>(_buffer, num);
|
||||
|
||||
switch (num.type()) {
|
||||
case VALUE_IS_NEGATIVE_INTEGER:
|
||||
result.setNegativeInteger(num.uintValue);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
case VALUE_IS_POSITIVE_INTEGER:
|
||||
result.setPositiveInteger(num.uintValue);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
case VALUE_IS_FLOAT:
|
||||
result.setFloat(num.floatValue);
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
return true;
|
||||
|
||||
return DeserializationError::InvalidInput;
|
||||
default:
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError skipNumericValue() {
|
||||
bool skipNumericValue() {
|
||||
char c = current();
|
||||
while (canBeInNonQuotedString(c)) {
|
||||
move();
|
||||
c = current();
|
||||
}
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
DeserializationError parseHex4(uint16_t &result) {
|
||||
bool parseHex4(uint16_t &result) {
|
||||
result = 0;
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
char digit = current();
|
||||
if (!digit)
|
||||
return DeserializationError::IncompleteInput;
|
||||
if (!digit) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
uint8_t value = decodeHex(digit);
|
||||
if (value > 0x0F)
|
||||
return DeserializationError::InvalidInput;
|
||||
if (value > 0x0F) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
result = uint16_t((result << 4) | value);
|
||||
move();
|
||||
}
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool isBetween(char c, char min, char max) {
|
||||
@ -533,12 +569,13 @@ class JsonDeserializer {
|
||||
return uint8_t(c - 'A' + 10);
|
||||
}
|
||||
|
||||
DeserializationError skipSpacesAndComments() {
|
||||
bool skipSpacesAndComments() {
|
||||
for (;;) {
|
||||
switch (current()) {
|
||||
// end of string
|
||||
case '\0':
|
||||
return DeserializationError::IncompleteInput;
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
|
||||
// spaces
|
||||
case ' ':
|
||||
@ -559,8 +596,10 @@ class JsonDeserializer {
|
||||
bool wasStar = false;
|
||||
for (;;) {
|
||||
char c = current();
|
||||
if (c == '\0')
|
||||
return DeserializationError::IncompleteInput;
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
if (c == '/' && wasStar) {
|
||||
move();
|
||||
break;
|
||||
@ -577,8 +616,10 @@ class JsonDeserializer {
|
||||
for (;;) {
|
||||
move();
|
||||
char c = current();
|
||||
if (c == '\0')
|
||||
return DeserializationError::IncompleteInput;
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
@ -586,20 +627,25 @@ class JsonDeserializer {
|
||||
|
||||
// not a comment, just a '/'
|
||||
default:
|
||||
return DeserializationError::InvalidInput;
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemoryPool *_pool;
|
||||
TStringStorage _stringStorage;
|
||||
Latch<TReader> _latch;
|
||||
MemoryPool *_pool;
|
||||
char _buffer[64]; // using a member instead of a local variable because it
|
||||
// ended in the recursive path after compiler inlined the
|
||||
// code
|
||||
DeserializationError _error;
|
||||
};
|
||||
|
||||
// deserializeJson(JsonDocument&, const std::string&, ...)
|
||||
|
@ -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;
|
||||
|
@ -51,32 +51,43 @@ class MemoryPool {
|
||||
return allocRight<VariantSlot>();
|
||||
}
|
||||
|
||||
char* allocFrozenString(size_t n) {
|
||||
if (!canAlloc(n))
|
||||
template <typename TAdaptedString>
|
||||
const char* saveString(const TAdaptedString& str) {
|
||||
if (str.isNull())
|
||||
return 0;
|
||||
char* s = _left;
|
||||
_left += n;
|
||||
checkInvariants();
|
||||
return s;
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
const char* existingCopy = findString(str.begin());
|
||||
if (existingCopy)
|
||||
return existingCopy;
|
||||
#endif
|
||||
|
||||
size_t n = str.size();
|
||||
|
||||
char* newCopy = allocString(n + 1);
|
||||
if (newCopy) {
|
||||
str.copyTo(newCopy, n);
|
||||
newCopy[n] = 0; // force null-terminator
|
||||
}
|
||||
return newCopy;
|
||||
}
|
||||
|
||||
StringSlot allocExpandableString() {
|
||||
StringSlot s;
|
||||
s.value = _left;
|
||||
s.size = size_t(_right - _left);
|
||||
_left = _right;
|
||||
checkInvariants();
|
||||
return s;
|
||||
void getFreeZone(char** zoneStart, size_t* zoneSize) const {
|
||||
*zoneStart = _left;
|
||||
*zoneSize = size_t(_right - _left);
|
||||
}
|
||||
|
||||
void freezeString(StringSlot& s, size_t newSize) {
|
||||
_left -= (s.size - newSize);
|
||||
s.size = newSize;
|
||||
checkInvariants();
|
||||
}
|
||||
const char* saveStringFromFreeZone(size_t len) {
|
||||
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
const char* dup = findString(_left);
|
||||
if (dup)
|
||||
return dup;
|
||||
#endif
|
||||
|
||||
void reclaimLastString(const char* s) {
|
||||
_left = const_cast<char*>(s);
|
||||
const char* str = _left;
|
||||
_left += len;
|
||||
checkInvariants();
|
||||
return str;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@ -92,18 +103,6 @@ class MemoryPool {
|
||||
return _begin <= p && p < _end;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocRight() {
|
||||
return reinterpret_cast<T*>(allocRight(sizeof(T)));
|
||||
}
|
||||
|
||||
void* allocRight(size_t bytes) {
|
||||
if (!canAlloc(bytes))
|
||||
return 0;
|
||||
_right -= bytes;
|
||||
return _right;
|
||||
}
|
||||
|
||||
// Workaround for missing placement new
|
||||
void* operator new(size_t, void* p) {
|
||||
return p;
|
||||
@ -155,6 +154,46 @@ class MemoryPool {
|
||||
ARDUINOJSON_ASSERT(isAligned(_right));
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||
template <typename TIterator>
|
||||
const char* findString(TIterator str) {
|
||||
for (char* next = _begin; next < _left; ++next) {
|
||||
char* begin = next;
|
||||
|
||||
// try to match
|
||||
for (TIterator it = str; *it == *next; ++it) {
|
||||
if (*next++ == 0)
|
||||
return begin;
|
||||
}
|
||||
|
||||
// jump to next terminator
|
||||
while (*next) ++next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
char* allocString(size_t n) {
|
||||
if (!canAlloc(n))
|
||||
return 0;
|
||||
char* s = _left;
|
||||
_left += n;
|
||||
checkInvariants();
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocRight() {
|
||||
return reinterpret_cast<T*>(allocRight(sizeof(T)));
|
||||
}
|
||||
|
||||
void* allocRight(size_t bytes) {
|
||||
if (!canAlloc(bytes))
|
||||
return 0;
|
||||
_right -= bytes;
|
||||
return _right;
|
||||
}
|
||||
|
||||
char *_begin, *_left, *_right, *_end;
|
||||
};
|
||||
|
||||
|
@ -1,51 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class StringBuilder {
|
||||
public:
|
||||
explicit StringBuilder(MemoryPool* parent) : _parent(parent), _size(0) {
|
||||
_slot = _parent->allocExpandableString();
|
||||
}
|
||||
|
||||
void append(const char* s) {
|
||||
while (*s) append(*s++);
|
||||
}
|
||||
|
||||
void append(const char* s, size_t n) {
|
||||
while (n-- > 0) append(*s++);
|
||||
}
|
||||
|
||||
void append(char c) {
|
||||
if (!_slot.value)
|
||||
return;
|
||||
|
||||
if (_size >= _slot.size) {
|
||||
_slot.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_slot.value[_size++] = c;
|
||||
}
|
||||
|
||||
char* complete() {
|
||||
append('\0');
|
||||
if (_slot.value) {
|
||||
_parent->freezeString(_slot, _size);
|
||||
}
|
||||
return _slot.value;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* _parent;
|
||||
size_t _size;
|
||||
StringSlot _slot;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
#define JSON_STRING_SIZE(SIZE) (SIZE)
|
||||
#define JSON_STRING_SIZE(SIZE) (SIZE + 1)
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
|
@ -15,34 +15,37 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TReader, typename TStringStorage>
|
||||
class MsgPackDeserializer {
|
||||
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
||||
StringBuilder;
|
||||
|
||||
public:
|
||||
MsgPackDeserializer(MemoryPool &pool, TReader reader,
|
||||
TStringStorage stringStorage)
|
||||
: _pool(&pool), _reader(reader), _stringStorage(stringStorage) {}
|
||||
: _pool(&pool),
|
||||
_reader(reader),
|
||||
_stringStorage(stringStorage),
|
||||
_error(DeserializationError::Ok) {}
|
||||
|
||||
// TODO: add support for filter
|
||||
DeserializationError parse(VariantData &variant, AllowAllFilter,
|
||||
NestingLimit nestingLimit) {
|
||||
return parse(variant, nestingLimit);
|
||||
parseVariant(variant, nestingLimit);
|
||||
return _error;
|
||||
}
|
||||
|
||||
DeserializationError parse(VariantData &variant, NestingLimit nestingLimit) {
|
||||
private:
|
||||
bool parseVariant(VariantData &variant, NestingLimit nestingLimit) {
|
||||
uint8_t code;
|
||||
if (!readByte(code))
|
||||
return DeserializationError::IncompleteInput;
|
||||
if (!readByte(code)) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((code & 0x80) == 0) {
|
||||
variant.setUnsignedInteger(code);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((code & 0xe0) == 0xe0) {
|
||||
// TODO: add setNegativeInteger()
|
||||
variant.setSignedInteger(static_cast<int8_t>(code));
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((code & 0xe0) == 0xa0) {
|
||||
@ -60,15 +63,15 @@ class MsgPackDeserializer {
|
||||
switch (code) {
|
||||
case 0xc0:
|
||||
// already null
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
case 0xc2:
|
||||
variant.setBoolean(false);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
case 0xc3:
|
||||
variant.setBoolean(true);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
|
||||
case 0xcc:
|
||||
return readInteger<uint8_t>(variant);
|
||||
@ -79,11 +82,9 @@ class MsgPackDeserializer {
|
||||
case 0xce:
|
||||
return readInteger<uint32_t>(variant);
|
||||
|
||||
case 0xcf:
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case 0xcf:
|
||||
return readInteger<uint64_t>(variant);
|
||||
#else
|
||||
return DeserializationError::NotSupported;
|
||||
#endif
|
||||
|
||||
case 0xd0:
|
||||
@ -95,11 +96,9 @@ class MsgPackDeserializer {
|
||||
case 0xd2:
|
||||
return readInteger<int32_t>(variant);
|
||||
|
||||
case 0xd3:
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
case 0xd3:
|
||||
return readInteger<int64_t>(variant);
|
||||
#else
|
||||
return DeserializationError::NotSupported;
|
||||
#endif
|
||||
|
||||
case 0xca:
|
||||
@ -130,7 +129,8 @@ class MsgPackDeserializer {
|
||||
return readObject<uint32_t>(variant.toObject(), nestingLimit);
|
||||
|
||||
default:
|
||||
return DeserializationError::NotSupported;
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,14 +140,19 @@ class MsgPackDeserializer {
|
||||
|
||||
bool readByte(uint8_t &value) {
|
||||
int c = _reader.read();
|
||||
if (c < 0)
|
||||
if (c < 0) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
value = static_cast<uint8_t>(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readBytes(uint8_t *p, size_t n) {
|
||||
return _reader.readBytes(reinterpret_cast<char *>(p), n) == n;
|
||||
if (_reader.readBytes(reinterpret_cast<char *>(p), n) == n)
|
||||
return true;
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -155,14 +160,6 @@ class MsgPackDeserializer {
|
||||
return readBytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T readInteger() {
|
||||
T value;
|
||||
readBytes(value);
|
||||
fixEndianess(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool readInteger(T &value) {
|
||||
if (!readBytes(value))
|
||||
@ -172,152 +169,159 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DeserializationError readInteger(VariantData &variant) {
|
||||
bool readInteger(VariantData &variant) {
|
||||
T value;
|
||||
if (!readInteger(value))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
variant.setInteger(value);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat(
|
||||
typename enable_if<sizeof(T) == 4, bool>::type readFloat(
|
||||
VariantData &variant) {
|
||||
T value;
|
||||
if (!readBytes(value))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
fixEndianess(value);
|
||||
variant.setFloat(value);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble(
|
||||
typename enable_if<sizeof(T) == 8, bool>::type readDouble(
|
||||
VariantData &variant) {
|
||||
T value;
|
||||
if (!readBytes(value))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
fixEndianess(value);
|
||||
variant.setFloat(value);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble(
|
||||
typename enable_if<sizeof(T) == 4, bool>::type readDouble(
|
||||
VariantData &variant) {
|
||||
uint8_t i[8]; // input is 8 bytes
|
||||
T value; // output is 4 bytes
|
||||
uint8_t *o = reinterpret_cast<uint8_t *>(&value);
|
||||
if (!readBytes(i, 8))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
doubleToFloat(i, o);
|
||||
fixEndianess(value);
|
||||
variant.setFloat(value);
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DeserializationError readString(VariantData &variant) {
|
||||
bool readString(VariantData &variant) {
|
||||
T size;
|
||||
if (!readInteger(size))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
return readString(variant, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DeserializationError readString(const char *&str) {
|
||||
bool readString(const char *&str) {
|
||||
T size;
|
||||
if (!readInteger(size))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
return readString(str, size);
|
||||
}
|
||||
|
||||
DeserializationError readString(VariantData &variant, size_t n) {
|
||||
bool readString(VariantData &variant, size_t n) {
|
||||
const char *s = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
|
||||
DeserializationError err = readString(s, n);
|
||||
if (!err)
|
||||
variant.setOwnedString(make_not_null(s));
|
||||
return err;
|
||||
if (!readString(s, n))
|
||||
return false;
|
||||
variant.setString(make_not_null(s),
|
||||
typename TStringStorage::storage_policy());
|
||||
return true;
|
||||
}
|
||||
|
||||
DeserializationError readString(const char *&result, size_t n) {
|
||||
StringBuilder builder = _stringStorage.startString();
|
||||
bool readString(const char *&result, size_t n) {
|
||||
_stringStorage.startString(_pool);
|
||||
for (; n; --n) {
|
||||
uint8_t c;
|
||||
if (!readBytes(c))
|
||||
return DeserializationError::IncompleteInput;
|
||||
builder.append(static_cast<char>(c));
|
||||
return false;
|
||||
_stringStorage.append(static_cast<char>(c));
|
||||
}
|
||||
result = builder.complete();
|
||||
if (!result)
|
||||
return DeserializationError::NoMemory;
|
||||
return DeserializationError::Ok;
|
||||
_stringStorage.append('\0');
|
||||
if (!_stringStorage.isValid()) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = _stringStorage.save(_pool);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TSize>
|
||||
DeserializationError readArray(CollectionData &array,
|
||||
NestingLimit nestingLimit) {
|
||||
bool readArray(CollectionData &array, NestingLimit nestingLimit) {
|
||||
TSize size;
|
||||
if (!readInteger(size))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
return readArray(array, size, nestingLimit);
|
||||
}
|
||||
|
||||
DeserializationError readArray(CollectionData &array, size_t n,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
bool readArray(CollectionData &array, size_t n, NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; n; --n) {
|
||||
VariantData *value = array.addElement(_pool);
|
||||
if (!value)
|
||||
return DeserializationError::NoMemory;
|
||||
if (!value) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
DeserializationError err = parse(*value, nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
if (!parseVariant(*value, nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TSize>
|
||||
DeserializationError readObject(CollectionData &object,
|
||||
NestingLimit nestingLimit) {
|
||||
bool readObject(CollectionData &object, NestingLimit nestingLimit) {
|
||||
TSize size;
|
||||
if (!readInteger(size))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
return readObject(object, size, nestingLimit);
|
||||
}
|
||||
|
||||
DeserializationError readObject(CollectionData &object, size_t n,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached())
|
||||
return DeserializationError::TooDeep;
|
||||
bool readObject(CollectionData &object, size_t n, NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; n; --n) {
|
||||
VariantSlot *slot = object.addSlot(_pool);
|
||||
if (!slot)
|
||||
return DeserializationError::NoMemory;
|
||||
if (!slot) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *key = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
|
||||
DeserializationError err = parseKey(key);
|
||||
if (err)
|
||||
return err;
|
||||
slot->setOwnedKey(make_not_null(key));
|
||||
if (!parseKey(key))
|
||||
return false;
|
||||
|
||||
err = parse(*slot->data(), nestingLimit.decrement());
|
||||
if (err)
|
||||
return err;
|
||||
slot->setKey(key, typename TStringStorage::storage_policy());
|
||||
|
||||
if (!parseVariant(*slot->data(), nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return DeserializationError::Ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
DeserializationError parseKey(const char *&key) {
|
||||
bool parseKey(const char *&key) {
|
||||
uint8_t code;
|
||||
if (!readByte(code))
|
||||
return DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
|
||||
if ((code & 0xe0) == 0xa0)
|
||||
return readString(key, code & 0x1f);
|
||||
@ -333,13 +337,15 @@ class MsgPackDeserializer {
|
||||
return readString<uint32_t>(key);
|
||||
|
||||
default:
|
||||
return DeserializationError::NotSupported;
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryPool *_pool;
|
||||
TReader _reader;
|
||||
TStringStorage _stringStorage;
|
||||
DeserializationError _error;
|
||||
};
|
||||
|
||||
template <typename TInput>
|
||||
|
@ -9,23 +9,41 @@
|
||||
|
||||
#ifndef ARDUINOJSON_NAMESPACE
|
||||
|
||||
#define ARDUINOJSON_DO_CONCAT(A, B) A##B
|
||||
#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_DO_CONCAT(A, B)
|
||||
#define ARDUINOJSON_HEX_DIGIT_0000() 0
|
||||
#define ARDUINOJSON_HEX_DIGIT_0001() 1
|
||||
#define ARDUINOJSON_HEX_DIGIT_0010() 2
|
||||
#define ARDUINOJSON_HEX_DIGIT_0011() 3
|
||||
#define ARDUINOJSON_HEX_DIGIT_0100() 4
|
||||
#define ARDUINOJSON_HEX_DIGIT_0101() 5
|
||||
#define ARDUINOJSON_HEX_DIGIT_0110() 6
|
||||
#define ARDUINOJSON_HEX_DIGIT_0111() 7
|
||||
#define ARDUINOJSON_HEX_DIGIT_1000() 8
|
||||
#define ARDUINOJSON_HEX_DIGIT_1001() 9
|
||||
#define ARDUINOJSON_HEX_DIGIT_1010() A
|
||||
#define ARDUINOJSON_HEX_DIGIT_1011() B
|
||||
#define ARDUINOJSON_HEX_DIGIT_1100() C
|
||||
#define ARDUINOJSON_HEX_DIGIT_1101() D
|
||||
#define ARDUINOJSON_HEX_DIGIT_1110() E
|
||||
#define ARDUINOJSON_HEX_DIGIT_1111() F
|
||||
#define ARDUINOJSON_HEX_DIGIT_(A, B, C, D) ARDUINOJSON_HEX_DIGIT_##A##B##C##D()
|
||||
#define ARDUINOJSON_HEX_DIGIT(A, B, C, D) ARDUINOJSON_HEX_DIGIT_(A, B, C, D)
|
||||
|
||||
#define ARDUINOJSON_CONCAT_(A, B) A##B
|
||||
#define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B)
|
||||
#define ARDUINOJSON_CONCAT4(A, B, C, D) \
|
||||
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D))
|
||||
#define ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, H) \
|
||||
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \
|
||||
ARDUINOJSON_CONCAT4(E, F, G, H))
|
||||
#define ARDUINOJSON_CONCAT12(A, B, C, D, E, F, G, H, I, J, K, L) \
|
||||
ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, \
|
||||
ARDUINOJSON_CONCAT4(H, I, J, ARDUINOJSON_CONCAT2(K, L)))
|
||||
|
||||
#define ARDUINOJSON_NAMESPACE \
|
||||
ARDUINOJSON_CONCAT12( \
|
||||
ArduinoJson, ARDUINOJSON_VERSION_MAJOR, ARDUINOJSON_VERSION_MINOR, \
|
||||
ARDUINOJSON_VERSION_REVISION, _, ARDUINOJSON_USE_LONG_LONG, \
|
||||
ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_DECODE_UNICODE, \
|
||||
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
|
||||
ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_ENABLE_COMMENTS)
|
||||
#define ARDUINOJSON_NAMESPACE \
|
||||
ARDUINOJSON_CONCAT4( \
|
||||
ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \
|
||||
ARDUINOJSON_VERSION_MINOR, \
|
||||
ARDUINOJSON_VERSION_REVISION), \
|
||||
_, \
|
||||
ARDUINOJSON_HEX_DIGIT(0, ARDUINOJSON_USE_LONG_LONG, \
|
||||
ARDUINOJSON_USE_DOUBLE, \
|
||||
ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \
|
||||
ARDUINOJSON_HEX_DIGIT( \
|
||||
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
|
||||
ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE))
|
||||
|
||||
#endif
|
||||
|
@ -18,4 +18,15 @@ typedef uint64_t UInt;
|
||||
typedef long Integer;
|
||||
typedef unsigned long UInt;
|
||||
#endif
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
#if ARDUINOJSON_HAS_LONG_LONG && !ARDUINOJSON_USE_LONG_LONG
|
||||
#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \
|
||||
static_assert(sizeof(T) <= sizeof(ARDUINOJSON_NAMESPACE::Integer), \
|
||||
"To use 64-bit integers with ArduinoJson, you must set " \
|
||||
"ARDUINOJSON_USE_LONG_LONG to 1. See " \
|
||||
"https://arduinojson.org/v6/api/config/use_long_long/");
|
||||
#else
|
||||
#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T)
|
||||
#endif
|
||||
|
121
src/ArduinoJson/Numbers/arithmeticCompare.hpp
Normal file
121
src/ArduinoJson/Numbers/arithmeticCompare.hpp
Normal file
@ -0,0 +1,121 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Numbers/Integer.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
enum CompareResult {
|
||||
COMPARE_RESULT_DIFFER = 0,
|
||||
COMPARE_RESULT_EQUAL = 1,
|
||||
COMPARE_RESULT_GREATER = 2,
|
||||
COMPARE_RESULT_LESS = 4,
|
||||
|
||||
COMPARE_RESULT_GREATER_OR_EQUAL = 3,
|
||||
COMPARE_RESULT_LESS_OR_EQUAL = 5
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
CompareResult arithmeticCompare(const T &lhs, const T &rhs) {
|
||||
if (lhs < rhs)
|
||||
return COMPARE_RESULT_LESS;
|
||||
else if (lhs > rhs)
|
||||
return COMPARE_RESULT_GREATER;
|
||||
else
|
||||
return COMPARE_RESULT_EQUAL;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
CompareResult arithmeticCompare(
|
||||
const T1 &lhs, const T2 &rhs,
|
||||
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
|
||||
sizeof(T1) < sizeof(T2),
|
||||
int // Using int instead of void to avoid C2572 on
|
||||
// Visual Studio 2012, 2013, and 2015
|
||||
>::type * = 0) {
|
||||
return arithmeticCompare<T2>(static_cast<T2>(lhs), rhs);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
CompareResult arithmeticCompare(
|
||||
const T1 &lhs, const T2 &rhs,
|
||||
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
|
||||
sizeof(T2) < sizeof(T1)>::type * = 0) {
|
||||
return arithmeticCompare<T1>(lhs, static_cast<T1>(rhs));
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
CompareResult arithmeticCompare(
|
||||
const T1 &lhs, const T2 &rhs,
|
||||
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
|
||||
is_signed<T1>::value == is_signed<T2>::value &&
|
||||
sizeof(T2) == sizeof(T1)>::type * = 0) {
|
||||
return arithmeticCompare<T1>(lhs, static_cast<T1>(rhs));
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
CompareResult arithmeticCompare(
|
||||
const T1 &lhs, const T2 &rhs,
|
||||
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
|
||||
is_unsigned<T1>::value && is_signed<T2>::value &&
|
||||
sizeof(T2) == sizeof(T1)>::type * = 0) {
|
||||
if (rhs < 0)
|
||||
return COMPARE_RESULT_GREATER;
|
||||
return arithmeticCompare<T1>(lhs, static_cast<T1>(rhs));
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
CompareResult arithmeticCompare(
|
||||
const T1 &lhs, const T2 &rhs,
|
||||
typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
|
||||
is_signed<T1>::value && is_unsigned<T2>::value &&
|
||||
sizeof(T2) == sizeof(T1)>::type * = 0) {
|
||||
if (lhs < 0)
|
||||
return COMPARE_RESULT_LESS;
|
||||
return arithmeticCompare<T2>(static_cast<T2>(lhs), rhs);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
CompareResult arithmeticCompare(
|
||||
const T1 &lhs, const T2 &rhs,
|
||||
typename enable_if<is_floating_point<T1>::value ||
|
||||
is_floating_point<T2>::value>::type * = 0) {
|
||||
return arithmeticCompare<double>(static_cast<double>(lhs),
|
||||
static_cast<double>(rhs));
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
CompareResult arithmeticCompareNegateLeft(
|
||||
UInt, const T2 &, typename enable_if<is_unsigned<T2>::value>::type * = 0) {
|
||||
return COMPARE_RESULT_LESS;
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
CompareResult arithmeticCompareNegateLeft(
|
||||
UInt lhs, const T2 &rhs,
|
||||
typename enable_if<is_signed<T2>::value>::type * = 0) {
|
||||
if (rhs > 0)
|
||||
return COMPARE_RESULT_LESS;
|
||||
return arithmeticCompare(-rhs, static_cast<T2>(lhs));
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
CompareResult arithmeticCompareNegateRight(
|
||||
const T1 &, UInt, typename enable_if<is_unsigned<T1>::value>::type * = 0) {
|
||||
return COMPARE_RESULT_GREATER;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
CompareResult arithmeticCompareNegateRight(
|
||||
const T1 &lhs, UInt rhs,
|
||||
typename enable_if<is_signed<T1>::value>::type * = 0) {
|
||||
if (lhs > 0)
|
||||
return COMPARE_RESULT_GREATER;
|
||||
return arithmeticCompare(static_cast<T1>(rhs), -lhs);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -13,6 +13,8 @@ template <typename T>
|
||||
inline T parseFloat(const char* s) {
|
||||
// try to reuse the same parameters as JsonDeserializer
|
||||
typedef typename choose_largest<Float, T>::type TFloat;
|
||||
return parseNumber<TFloat, UInt>(s).template as<T>();
|
||||
ParsedNumber<TFloat, UInt> value;
|
||||
parseNumber(s, value);
|
||||
return value.template as<T>();
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -14,6 +14,8 @@ T parseInteger(const char *s) {
|
||||
// try to reuse the same parameters as JsonDeserializer
|
||||
typedef typename choose_largest<UInt, typename make_unsigned<T>::type>::type
|
||||
TUInt;
|
||||
return parseNumber<Float, TUInt>(s).template as<T>();
|
||||
ParsedNumber<Float, TUInt> value;
|
||||
parseNumber(s, value);
|
||||
return value.template as<T>();
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -16,14 +16,18 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TFloat, typename TUInt>
|
||||
struct ParsedNumber {
|
||||
ParsedNumber() : uintValue(0), floatValue(0), _type(VALUE_IS_NULL) {}
|
||||
ParsedNumber() : _type(VALUE_IS_NULL) {}
|
||||
|
||||
ParsedNumber(TUInt value, bool is_negative)
|
||||
: uintValue(value),
|
||||
floatValue(TFloat(value)),
|
||||
_type(uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER
|
||||
: VALUE_IS_POSITIVE_INTEGER)) {}
|
||||
ParsedNumber(TFloat value) : floatValue(value), _type(VALUE_IS_FLOAT) {}
|
||||
void setInteger(TUInt value, bool is_negative) {
|
||||
uintValue = value;
|
||||
_type = uint8_t(is_negative ? VALUE_IS_NEGATIVE_INTEGER
|
||||
: VALUE_IS_POSITIVE_INTEGER);
|
||||
}
|
||||
|
||||
void setFloat(TFloat value) {
|
||||
floatValue = value;
|
||||
_type = VALUE_IS_FLOAT;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T as() const {
|
||||
@ -43,21 +47,22 @@ struct ParsedNumber {
|
||||
return _type;
|
||||
}
|
||||
|
||||
TUInt uintValue;
|
||||
TFloat floatValue;
|
||||
union {
|
||||
TUInt uintValue;
|
||||
TFloat floatValue;
|
||||
};
|
||||
uint8_t _type;
|
||||
};
|
||||
}; // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
template <typename A, typename B>
|
||||
struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {};
|
||||
|
||||
template <typename TFloat, typename TUInt>
|
||||
inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
|
||||
inline void parseNumber(const char* s, ParsedNumber<TFloat, TUInt>& result) {
|
||||
typedef FloatTraits<TFloat> traits;
|
||||
typedef typename choose_largest<typename traits::mantissa_type, TUInt>::type
|
||||
mantissa_t;
|
||||
typedef typename traits::exponent_type exponent_t;
|
||||
typedef ParsedNumber<TFloat, TUInt> return_type;
|
||||
|
||||
ARDUINOJSON_ASSERT(s != 0);
|
||||
|
||||
@ -73,17 +78,22 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_ENABLE_NAN
|
||||
if (*s == 'n' || *s == 'N')
|
||||
return traits::nan();
|
||||
if (*s == 'n' || *s == 'N') {
|
||||
result.setFloat(traits::nan());
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_INFINITY
|
||||
if (*s == 'i' || *s == 'I')
|
||||
return is_negative ? -traits::inf() : traits::inf();
|
||||
if (*s == 'i' || *s == 'I') {
|
||||
result.setFloat(is_negative ? -traits::inf() : traits::inf());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!isdigit(*s) && *s != '.')
|
||||
return return_type();
|
||||
return;
|
||||
|
||||
mantissa_t mantissa = 0;
|
||||
exponent_t exponent_offset = 0;
|
||||
@ -100,8 +110,10 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
|
||||
s++;
|
||||
}
|
||||
|
||||
if (*s == '\0')
|
||||
return return_type(TUInt(mantissa), is_negative);
|
||||
if (*s == '\0') {
|
||||
result.setInteger(TUInt(mantissa), is_negative);
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid mantissa overflow
|
||||
while (mantissa > traits::mantissa_max) {
|
||||
@ -141,9 +153,10 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
|
||||
exponent = exponent * 10 + (*s - '0');
|
||||
if (exponent + exponent_offset > traits::exponent_max) {
|
||||
if (negative_exponent)
|
||||
return is_negative ? -0.0f : 0.0f;
|
||||
result.setFloat(is_negative ? -0.0f : 0.0f);
|
||||
else
|
||||
return is_negative ? -traits::inf() : traits::inf();
|
||||
result.setFloat(is_negative ? -traits::inf() : traits::inf());
|
||||
return;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
@ -154,10 +167,11 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
|
||||
|
||||
// we should be at the end of the string, otherwise it's an error
|
||||
if (*s != '\0')
|
||||
return return_type();
|
||||
return;
|
||||
|
||||
TFloat result = traits::make_float(static_cast<TFloat>(mantissa), exponent);
|
||||
TFloat final_result =
|
||||
traits::make_float(static_cast<TFloat>(mantissa), exponent);
|
||||
|
||||
return is_negative ? -result : result;
|
||||
result.setFloat(is_negative ? -final_result : final_result);
|
||||
}
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -5,8 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Operators/VariantOperators.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Variant/VariantOperators.hpp>
|
||||
#include <ArduinoJson/Variant/VariantRef.hpp>
|
||||
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
|
||||
#include <ArduinoJson/Variant/VariantTo.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
@ -17,6 +20,7 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TObject, typename TStringRef>
|
||||
class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
|
||||
public VariantShortcuts<MemberProxy<TObject, TStringRef> >,
|
||||
public Visitable {
|
||||
typedef MemberProxy<TObject, TStringRef> this_type;
|
||||
|
||||
@ -52,14 +56,6 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator==(VariantConstRef rhs) const {
|
||||
return static_cast<VariantConstRef>(getUpstreamMember()) == rhs;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool operator!=(VariantConstRef rhs) const {
|
||||
return static_cast<VariantConstRef>(getUpstreamMember()) != rhs;
|
||||
}
|
||||
|
||||
FORCE_INLINE void clear() const {
|
||||
getUpstreamMember().clear();
|
||||
}
|
||||
|
@ -1,248 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Variant/VariantRef.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct Comparer;
|
||||
|
||||
template <typename T>
|
||||
struct Comparer<T, typename enable_if<IsString<T>::value>::type> {
|
||||
T rhs;
|
||||
int result;
|
||||
|
||||
explicit Comparer(T value) : rhs(value), result(1) {}
|
||||
|
||||
void visitArray(const CollectionData &) {}
|
||||
void visitObject(const CollectionData &) {}
|
||||
void visitFloat(Float) {}
|
||||
void visitString(const char *lhs) {
|
||||
result = -adaptString(rhs).compare(lhs);
|
||||
}
|
||||
void visitRawJson(const char *, size_t) {}
|
||||
void visitNegativeInteger(UInt) {}
|
||||
void visitPositiveInteger(UInt) {}
|
||||
void visitBoolean(bool) {}
|
||||
void visitNull() {
|
||||
result = adaptString(rhs).compare(NULL);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_signed<T>::value, int>::type sign(const T &value) {
|
||||
return value < 0 ? -1 : value > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename enable_if<is_unsigned<T>::value, int>::type sign(const T &value) {
|
||||
return value > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Comparer<T, typename enable_if<is_integral<T>::value ||
|
||||
is_floating_point<T>::value>::type> {
|
||||
T rhs;
|
||||
int result;
|
||||
|
||||
explicit Comparer(T value) : rhs(value), result(1) {}
|
||||
|
||||
void visitArray(const CollectionData &) {}
|
||||
void visitObject(const CollectionData &) {}
|
||||
void visitFloat(Float lhs) {
|
||||
result = sign(lhs - static_cast<Float>(rhs));
|
||||
}
|
||||
void visitString(const char *) {}
|
||||
void visitRawJson(const char *, size_t) {}
|
||||
void visitNegativeInteger(UInt lhs) {
|
||||
result = -sign(static_cast<T>(lhs) + rhs);
|
||||
}
|
||||
void visitPositiveInteger(UInt lhs) {
|
||||
result = static_cast<T>(lhs) < rhs ? -1 : static_cast<T>(lhs) > rhs ? 1 : 0;
|
||||
}
|
||||
void visitBoolean(bool) {}
|
||||
void visitNull() {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Comparer<bool, void> {
|
||||
bool rhs;
|
||||
int result;
|
||||
|
||||
explicit Comparer(bool value) : rhs(value), result(1) {}
|
||||
|
||||
void visitArray(const CollectionData &) {}
|
||||
void visitObject(const CollectionData &) {}
|
||||
void visitFloat(Float) {}
|
||||
void visitString(const char *) {}
|
||||
void visitRawJson(const char *, size_t) {}
|
||||
void visitNegativeInteger(UInt) {}
|
||||
void visitPositiveInteger(UInt) {}
|
||||
void visitBoolean(bool lhs) {
|
||||
result = static_cast<int>(lhs - rhs);
|
||||
}
|
||||
void visitNull() {}
|
||||
};
|
||||
|
||||
#if ARDUINOJSON_HAS_NULLPTR
|
||||
template <>
|
||||
struct Comparer<decltype(nullptr), void> {
|
||||
int result;
|
||||
|
||||
explicit Comparer(decltype(nullptr)) : result(1) {}
|
||||
|
||||
void visitArray(const CollectionData &) {}
|
||||
void visitObject(const CollectionData &) {}
|
||||
void visitFloat(Float) {}
|
||||
void visitString(const char *) {}
|
||||
void visitRawJson(const char *, size_t) {}
|
||||
void visitNegativeInteger(UInt) {}
|
||||
void visitPositiveInteger(UInt) {}
|
||||
void visitBoolean(bool) {}
|
||||
void visitNull() {
|
||||
result = 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename TVariant>
|
||||
class VariantComparisons {
|
||||
private:
|
||||
template <typename T>
|
||||
static int compare(TVariant lhs, const T &rhs) {
|
||||
Comparer<T> comparer(rhs);
|
||||
lhs.accept(comparer);
|
||||
return comparer.result;
|
||||
}
|
||||
|
||||
public:
|
||||
// value == TVariant
|
||||
template <typename T>
|
||||
friend bool operator==(T *lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) == 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
|
||||
const T &lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) == 0;
|
||||
}
|
||||
|
||||
// TVariant == value
|
||||
template <typename T>
|
||||
friend bool operator==(TVariant lhs, T *rhs) {
|
||||
return compare(lhs, rhs) == 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
|
||||
TVariant lhs, const T &rhs) {
|
||||
return compare(lhs, rhs) == 0;
|
||||
}
|
||||
|
||||
// value != TVariant
|
||||
template <typename T>
|
||||
friend bool operator!=(T *lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) != 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator!=(
|
||||
const T &lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) != 0;
|
||||
}
|
||||
|
||||
// TVariant != value
|
||||
template <typename T>
|
||||
friend bool operator!=(TVariant lhs, T *rhs) {
|
||||
return compare(lhs, rhs) != 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator!=(
|
||||
TVariant lhs, const T &rhs) {
|
||||
return compare(lhs, rhs) != 0;
|
||||
}
|
||||
|
||||
// value < TVariant
|
||||
template <typename T>
|
||||
friend bool operator<(T *lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) > 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator<(const T &lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) > 0;
|
||||
}
|
||||
|
||||
// TVariant < value
|
||||
template <typename T>
|
||||
friend bool operator<(TVariant lhs, T *rhs) {
|
||||
return compare(lhs, rhs) < 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator<(TVariant lhs, const T &rhs) {
|
||||
return compare(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
// value <= TVariant
|
||||
template <typename T>
|
||||
friend bool operator<=(T *lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) >= 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator<=(const T &lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) >= 0;
|
||||
}
|
||||
|
||||
// TVariant <= value
|
||||
template <typename T>
|
||||
friend bool operator<=(TVariant lhs, T *rhs) {
|
||||
return compare(lhs, rhs) <= 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator<=(TVariant lhs, const T &rhs) {
|
||||
return compare(lhs, rhs) <= 0;
|
||||
}
|
||||
|
||||
// value > TVariant
|
||||
template <typename T>
|
||||
friend bool operator>(T *lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) < 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator>(const T &lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) < 0;
|
||||
}
|
||||
|
||||
// TVariant > value
|
||||
template <typename T>
|
||||
friend bool operator>(TVariant lhs, T *rhs) {
|
||||
return compare(lhs, rhs) > 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator>(TVariant lhs, const T &rhs) {
|
||||
return compare(lhs, rhs) > 0;
|
||||
}
|
||||
|
||||
// value >= TVariant
|
||||
template <typename T>
|
||||
friend bool operator>=(T *lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) <= 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator>=(const T &lhs, TVariant rhs) {
|
||||
return compare(rhs, lhs) <= 0;
|
||||
}
|
||||
|
||||
// TVariant >= value
|
||||
template <typename T>
|
||||
friend bool operator>=(TVariant lhs, T *rhs) {
|
||||
return compare(lhs, rhs) >= 0;
|
||||
}
|
||||
template <typename T>
|
||||
friend bool operator>=(TVariant lhs, const T &rhs) {
|
||||
return compare(lhs, rhs) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -1,17 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Operators/VariantComparisons.hpp>
|
||||
#include <ArduinoJson/Operators/VariantOr.hpp>
|
||||
#include <ArduinoJson/Operators/VariantShortcuts.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TImpl>
|
||||
class VariantOperators : public VariantComparisons<TImpl>,
|
||||
public VariantOr<TImpl>,
|
||||
public VariantShortcuts<TImpl> {};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -1,37 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Polyfills/attributes.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Variant/VariantAs.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TImpl>
|
||||
class VariantOr {
|
||||
public:
|
||||
// Returns the default value if the VariantRef is undefined of incompatible
|
||||
template <typename T>
|
||||
T operator|(const T &defaultValue) const {
|
||||
if (impl()->template is<T>())
|
||||
return impl()->template as<T>();
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Returns the default value if the VariantRef is undefined of incompatible
|
||||
// Special case for string: null is treated as undefined
|
||||
const char *operator|(const char *defaultValue) const {
|
||||
const char *value = impl()->template as<const char *>();
|
||||
return value ? value : defaultValue;
|
||||
}
|
||||
|
||||
private:
|
||||
const TImpl *impl() const {
|
||||
return static_cast<const TImpl *>(this);
|
||||
}
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -12,6 +12,11 @@
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
|
||||
#ifdef __ICCARM__
|
||||
// Suppress IAR Compiler Warning[Pa093]: implicit conversion from floating point to integer
|
||||
#pragma diag_suppress=Pa093
|
||||
#endif
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename From, typename To>
|
||||
@ -32,3 +37,7 @@ struct is_convertible {
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __ICCARM__
|
||||
#pragma diag_default=Pa093
|
||||
#endif
|
||||
|
@ -16,8 +16,7 @@ template <typename T>
|
||||
struct is_enum {
|
||||
static const bool value = is_convertible<T, int>::value &&
|
||||
!is_class<T>::value && !is_integral<T>::value &&
|
||||
!is_floating_point<T>::value &&
|
||||
!is_same<T, bool>::value;
|
||||
!is_floating_point<T>::value;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user