Compare commits

..

42 Commits

Author SHA1 Message Date
f5c25823bc Set version to 6.11.5 2019-08-23 08:40:10 +02:00
f00dfd7bfe Added fallbacks strlen_P, strncmp_P, strcmp_P and memcpy_P (fixes #1073) 2019-08-21 15:25:54 +02:00
dcca4214f5 Set version to 6.11.4 2019-08-12 15:10:56 +02:00
1e9cc285bb Added support for basic_string<char, traits, allocator> (closes #1045) 2019-08-12 14:21:45 +02:00
b9c4a0c5f6 Include Arduino.h if ARDUINO is defined (closes #1071) 2019-08-11 13:04:12 +02:00
0d339300f9 Added tests for Flash strings (closes #1070) 2019-08-11 11:39:21 +02:00
63d7d87080 Fixed example JsonConfigFile.ino for ESP8266 2019-08-10 14:17:23 +02:00
2ee655f9ba Fix clang installation on Travis 2019-08-10 12:22:17 +02:00
61a4195ed4 Added measureJson() to the ArduinoJson namespace (fixes #1069) 2019-08-10 11:15:11 +02:00
a6f029ded0 Set version to 6.11.3 2019-07-22 08:59:54 +02:00
b54de58e6b Fixed comparison of JsonVariant with mixed strings (closes #1051) 2019-07-19 12:15:16 +02:00
795e37278f Added operators == and != for MemberProxy 2019-07-19 10:27:03 +02:00
7ce1039d7c Added operators == and != for MemberProxy 2019-07-19 10:23:06 +02:00
aba42faf69 Added operators == and != for JsonDocument 2019-07-19 10:23:06 +02:00
815326d42e Set version to 6.11.2 2019-07-08 08:44:27 +02:00
7d40a541c9 Fix invalid conversion error on Particle Argon (closes #1035) 2019-07-01 09:07:16 +02:00
2507ee2e56 Fixed assignment of JsonDocument to JsonVariant (issue #1023) 2019-06-25 08:56:14 +02:00
a0a451195b Set version to 6.11.1 2019-06-21 08:49:02 +02:00
ce247a5637 Fixed serialized() not working with Flash strings (fixes #1030) 2019-06-21 08:46:32 +02:00
59f9c9747f Add sponsor button on GitHub 2019-06-04 09:14:37 +02:00
fec088e3ed Set version to 6.11.0 2019-05-26 21:11:40 +02:00
4980ee8fb9 Fixed unexpected HTTP/1.0 (closes #944) 2019-05-26 21:07:01 +02:00
2ed77d2cc3 Added support for nullptr (closes #998) 2019-05-26 11:31:51 +02:00
630107ae8a Removed implicit conversion in comparison operators (issue #998) 2019-05-23 21:54:42 +02:00
4eb8074358 Set ARDUINOJSON_ENABLE_NAN and ARDUINOJSON_ENABLE_INFINITY to 0 2019-05-19 17:46:20 +02:00
80a02cd90d Added ARDUINOJSON_ENABLE_INFINITY to enable Infinity in JSON 2019-05-19 15:48:27 +02:00
7427888e05 Added ARDUINOJSON_ENABLE_NAN to enable NaN in JSON (closes #973) 2019-05-18 12:15:36 +02:00
90c1d549a8 Made deserializeJson() more picky about trailing characters (closes #980) 2019-05-16 20:41:07 +02:00
2af003e4e2 Fixed deserializeJson() silently accepting a Stream* (issue #978) 2019-05-09 08:33:09 +02:00
eaf55e174b Fixed invalid result from operator| (closes #981) 2019-05-07 08:12:18 +02:00
0588e578d5 Set version to 6.10.1 2019-04-23 08:47:54 +02:00
12f9aac4ea Fixed "no matching function for call to write(uint8_t)" (closes #972) 2019-04-23 08:46:18 +02:00
81bb3fce97 Fixed build on platformio 2019-04-21 14:35:23 +02:00
6011a2f51a Fixed deserializeJson() not being picky enough (fixes #969) 2019-04-21 14:35:13 +02:00
6071bd07ec Added a link to the FAQ 2019-04-20 12:10:29 +02:00
1c814d3bb6 Fixed warning on Clang 8 2019-03-24 19:39:23 +01:00
9862048a58 Fixed error "attributes are not allowed on a function-definition" 2019-03-24 19:00:23 +01:00
ebc52a5a65 Travis: Added Clang 8 2019-03-24 19:00:23 +01:00
eacad922df Travis: Added Clang 7 2019-03-24 19:00:23 +01:00
d910996613 Travis: Added GCC 8 2019-03-24 19:00:23 +01:00
2fc220fa33 Fixed code samples in the README 2019-03-24 18:21:58 +01:00
8cabda551d Fixed publish.sh not committing appveyor.yml 2019-03-22 22:04:03 +01:00
73 changed files with 1464 additions and 318 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
custom: https://arduinojson.org/book/

View File

@ -31,7 +31,7 @@ matrix:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-5']
env: SCRIPT=test _CC=gcc-5 _CXX=g++-5 # SANITIZE=undefined
env: SCRIPT=test _CC=gcc-5 _CXX=g++-5 # SANITIZE=undefined
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
@ -42,6 +42,11 @@ matrix:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-7']
env: SCRIPT=test _CC=gcc-7 _CXX=g++-7
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-8']
env: SCRIPT=test _CC=gcc-8 _CXX=g++-8
- addons:
apt:
packages: ['g++-arm-linux-gnueabihf']
@ -79,14 +84,24 @@ matrix:
env: SCRIPT=test _CC=clang-4.0 _CXX=clang++-4.0
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-5.0']
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-5.0']
env: SCRIPT=test _CC=clang-5.0 _CXX=clang++-5.0
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-6.0']
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-6.0']
env: SCRIPT=test _CC=clang-6.0 _CXX=clang++-6.0
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-7']
packages: ['clang-7']
env: SCRIPT=test _CC=clang-7 _CXX=clang++-7
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-8']
packages: ['clang-8']
env: SCRIPT=test _CC=clang-8 _CXX=clang++-8
- env: SCRIPT=coverage
- os: osx
osx_image: xcode7.3
@ -106,7 +121,7 @@ matrix:
- env: SCRIPT=platformio BOARD=esp01
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-6.0']
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-6.0','llvm-6.0']
env: SCRIPT=fuzz CLANG=6.0
cache:

View File

@ -1,6 +1,97 @@
ArduinoJson: change log
=======================
v6.11.5 (2019-08-23)
-------
* Added fallback implementations of `strlen_P()`, `strncmp_P()`, `strcmp_P()`, and `memcpy_P()` (issue #1073)
v6.11.4 (2019-08-12)
-------
* Added `measureJson()` to the `ArduinoJson` namespace (PR #1069 by @nomis)
* Added support for `basic_string<char, traits, allocator>` (issue #1045)
* Fixed example `JsonConfigFile.ino` for ESP8266
* Include `Arduino.h` if `ARDUINO` is defined (PR #1071 by @nomis)
v6.11.3 (2019-07-22)
-------
* Added operators `==` and `!=` for `JsonDocument`, `ElementProxy`, and `MemberProxy`
* Fixed comparison of `JsonVariant` when one contains a linked string and the other contains an owned string (issue #1051)
v6.11.2 (2019-07-08)
-------
* Fixed assignment of `JsonDocument` to `JsonVariant` (issue #1023)
* Fix invalid conversion error on Particle Argon (issue #1035)
v6.11.1 (2019-06-21)
-------
* Fixed `serialized()` not working with Flash strings (issue #1030)
v6.11.0 (2019-05-26)
-------
* Fixed `deserializeJson()` silently accepting a `Stream*` (issue #978)
* Fixed invalid result from `operator|` (issue #981)
* Made `deserializeJson()` more picky about trailing characters (issue #980)
* Added `ARDUINOJSON_ENABLE_NAN` (default=0) to enable NaN in JSON (issue #973)
* Added `ARDUINOJSON_ENABLE_INFINITY` (default=0) to enable Infinity in JSON
* Removed implicit conversion in comparison operators (issue #998)
* Added lexicographical comparison for `JsonVariant`
* Added support for `nullptr` (issue #998)
> ### BREAKING CHANGES
>
> #### NaN and Infinity
>
> The JSON specification allows neither NaN not Infinity, but previous
> versions of ArduinoJson supported it. Now, ArduinoJson behaves like most
> other libraries: a NaN or and Infinity in the `JsonDocument`, becomes
> a `null` in the output JSON. Also, `deserializeJson()` returns
> `InvalidInput` if the JSON document contains NaN or Infinity.
>
> This version still supports NaN and Infinity in JSON documents, but
> it's disabled by default to be compatible with other JSON parsers.
> If you need the old behavior back, define `ARDUINOJSON_ENABLE_NAN` and
> `ARDUINOJSON_ENABLE_INFINITY` to `1`;:
>
> ```c++
> #define ARDUINOJSON_ENABLE_NAN 1
> #define ARDUINOJSON_ENABLE_INFINITY 1
> #include <ArduinoJson.h>
> ```
>
> #### The "or" operator
>
> This version slightly changes the behavior of the | operator when the
> variant contains a float and the user requests an integer.
>
> Older versions returned the floating point value truncated.
> Now, it returns the default value.
>
> ```c++
> // suppose variant contains 1.2
> int value = variant | 3;
>
> // old behavior:
> value == 1
>
> // new behavior
> value == 3
> ```
>
> If you need the old behavior, you must add `if (variant.is<float>())`.
v6.10.1 (2019-04-23)
-------
* Fixed error "attributes are not allowed on a function-definition"
* Fixed `deserializeJson()` not being picky enough (issue #969)
* Fixed error "no matching function for call to write(uint8_t)" (issue #972)
v6.10.0 (2019-03-22)
-------

View File

@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.10.0)](https://www.ardu-badge.com/ArduinoJson/6.10.0)
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.11.5)](https://www.ardu-badge.com/ArduinoJson/6.11.5)
[![Build Status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=6.x)](https://travis-ci.org/bblanchon/ArduinoJson)
[![Coverage Status](https://coveralls.io/repos/github/bblanchon/ArduinoJson/badge.svg?branch=6.x)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
@ -67,11 +67,10 @@ char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObjectRef root = doc.as<JsonObject>();
const char* sensor = root["sensor"];
long time = root["time"];
double latitude = root["data"][0];
double longitude = root["data"][1];
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
```
See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme)
@ -83,11 +82,10 @@ Here is a program that generates a JSON document with ArduinoJson:
```c++
DynamicJsonDocument doc(1024);
JsonObject root = doc.to<JsonObject>();
root["sensor"] = "gps";
root["time"] = 1351824120;
doc["sensor"] = "gps";
doc["time"] = 1351824120;
JsonArray data = root.createNestedArray("data");
JsonArray data = doc.createNestedArray("data");
data.add(48.756080);
data.add(2.302038);

View File

@ -1,4 +1,4 @@
version: 6.9.1.{build}
version: 6.11.5.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
@ -17,4 +17,4 @@ before_build:
build_script:
- cmake --build . --config %CONFIGURATION%
test_script:
- ctest --output-on-failure .
- ctest -C %CONFIGURATION% --output-on-failure .

View File

@ -11,6 +11,12 @@
// "port": 2731
// }
//
// To run this program, you need an SD card connected to the SPI bus as follows:
// * MOSI <-> pin 11
// * MISO <-> pin 12
// * CLK <-> pin 13
// * CS <-> pin 4
//
// https://arduinojson.org/v6/example/config/
#include <ArduinoJson.h>
@ -21,7 +27,8 @@
//
// Never use a JsonDocument to store the configuration!
// A JsonDocument is *not* a permanent storage; it's only a temporary storage
// used during the serialization phase.
// used during the serialization phase. See:
// https://arduinojson.org/v6/faq/why-must-i-create-a-separate-config-object/
struct Config {
char hostname[64];
int port;
@ -110,7 +117,8 @@ void setup() {
while (!Serial) continue;
// Initialize SD library
while (!SD.begin()) {
const int chipSelect = 4;
while (!SD.begin(chipSelect)) {
Serial.println(F("Failed to initialize SD library"));
delay(1000);
}

View File

@ -59,7 +59,8 @@ void setup() {
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
if (strcmp(status, "HTTP/1.1 200 OK") != 0) {
// It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
if (strcmp(status + 9, "200 OK") != 0) {
Serial.print(F("Unexpected response: "));
Serial.println(status);
return;

View File

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

View File

@ -1,5 +1,5 @@
name=ArduinoJson
version=6.10.0
version=6.11.5
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=An efficient and elegant JSON library for Arduino.

View File

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

View File

@ -4,6 +4,18 @@ pip install --user platformio
rm -r test
case $BOARD in
uno)
platformio lib install 868 # SD library
platformio lib install 872 # Ethernet library
;;
esp01)
platformio lib uninstall 161 || true
platformio lib uninstall 868 || true
platformio lib uninstall 872 || true
;;
esac
for EXAMPLE in $PWD/examples/*/*.ino;
do
platformio ci $EXAMPLE -l '.' -b $BOARD

View File

@ -57,6 +57,7 @@ using ARDUINOJSON_NAMESPACE::deserializeJson;
using ARDUINOJSON_NAMESPACE::deserializeMsgPack;
using ARDUINOJSON_NAMESPACE::DynamicJsonDocument;
using ARDUINOJSON_NAMESPACE::JsonDocument;
using ARDUINOJSON_NAMESPACE::measureJson;
using ARDUINOJSON_NAMESPACE::serialized;
using ARDUINOJSON_NAMESPACE::serializeJson;
using ARDUINOJSON_NAMESPACE::serializeJsonPretty;

View File

@ -47,6 +47,14 @@ 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();
}

View File

@ -12,8 +12,10 @@
#if __cplusplus >= 201103L
#define ARDUINOJSON_HAS_LONG_LONG 1
#define ARDUINOJSON_HAS_NULLPTR 1
#else
#define ARDUINOJSON_HAS_LONG_LONG 0
#define ARDUINOJSON_HAS_NULLPTR 0
#endif
// Small or big machine?
@ -88,28 +90,40 @@
#ifdef ARDUINO
// Enable support for Arduino String
#include <Arduino.h>
// Enable support for Arduino's String class
#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#endif
// Enable support for Arduino Stream
// Enable support for Arduino's Stream class
#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
#endif
// Enable support for Arduino's Print class
#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT
#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1
#endif
#else // ARDUINO
// Disable support for Arduino String
// Disable support for Arduino's String class
#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
#endif
// Disable support for Arduino Stream
// Disable support for Arduino's Stream class
#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0
#endif
// Disable support for Arduino's Print class
#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT
#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0
#endif
#endif // ARDUINO
#ifndef ARDUINOJSON_ENABLE_PROGMEM
@ -125,6 +139,16 @@
#define ARDUINOJSON_DECODE_UNICODE 0
#endif
// Support NaN in JSON
#ifndef ARDUINOJSON_ENABLE_NAN
#define ARDUINOJSON_ENABLE_NAN 0
#endif
// Support Infinity in JSON
#ifndef ARDUINOJSON_ENABLE_INFINITY
#define ARDUINOJSON_ENABLE_INFINITY 0
#endif
// Control the exponentiation threshold for big numbers
// CAUTION: cannot be more that 1e9 !!!!
#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD

View File

@ -12,22 +12,14 @@ namespace ARDUINOJSON_NAMESPACE {
struct ArduinoStreamReader {
Stream& _stream;
char _current;
bool _ended;
public:
explicit ArduinoStreamReader(Stream& stream)
: _stream(stream), _current(0), _ended(false) {}
explicit ArduinoStreamReader(Stream& stream) : _stream(stream) {}
char read() {
int read() {
// don't use _stream.read() as it ignores the timeout
char c = 0;
_ended = _stream.readBytes(&c, 1) == 0;
return c;
}
bool ended() const {
return _ended;
char c;
return _stream.readBytes(&c, 1) ? c : -1;
}
};

View File

@ -6,6 +6,16 @@
namespace ARDUINOJSON_NAMESPACE {
template <typename T>
struct IsCharOrVoid {
static const bool value =
is_same<T, void>::value || is_same<T, char>::value ||
is_same<T, unsigned char>::value || is_same<T, signed char>::value;
};
template <typename T>
struct IsCharOrVoid<const T> : IsCharOrVoid<T> {};
class UnsafeCharPointerReader {
const char* _ptr;
@ -13,13 +23,8 @@ class UnsafeCharPointerReader {
explicit UnsafeCharPointerReader(const char* ptr)
: _ptr(ptr ? ptr : reinterpret_cast<const char*>("")) {}
char read() {
return static_cast<char>(*_ptr++);
}
bool ended() const {
// we cannot know, that's why it's unsafe
return false;
int read() {
return static_cast<unsigned char>(*_ptr++);
}
};
@ -31,22 +36,25 @@ class SafeCharPointerReader {
explicit SafeCharPointerReader(const char* ptr, size_t len)
: _ptr(ptr ? ptr : reinterpret_cast<const char*>("")), _end(_ptr + len) {}
char read() {
return static_cast<char>(*_ptr++);
}
bool ended() const {
return _ptr == _end;
int read() {
if (_ptr < _end)
return static_cast<unsigned char>(*_ptr++);
else
return -1;
}
};
template <typename TChar>
inline UnsafeCharPointerReader makeReader(TChar* input) {
inline typename enable_if<IsCharOrVoid<TChar>::value,
UnsafeCharPointerReader>::type
makeReader(TChar* input) {
return UnsafeCharPointerReader(reinterpret_cast<const char*>(input));
}
template <typename TChar>
inline SafeCharPointerReader makeReader(TChar* input, size_t n) {
inline
typename enable_if<IsCharOrVoid<TChar>::value, SafeCharPointerReader>::type
makeReader(TChar* input, size_t n) {
return SafeCharPointerReader(reinterpret_cast<const char*>(input), n);
}

View File

@ -14,13 +14,8 @@ class UnsafeFlashStringReader {
explicit UnsafeFlashStringReader(const __FlashStringHelper* ptr)
: _ptr(reinterpret_cast<const char*>(ptr)) {}
char read() {
return pgm_read_byte_near(_ptr++);
}
bool ended() const {
// this reader cannot detect the end
return false;
int read() {
return pgm_read_byte(_ptr++);
}
};
@ -32,12 +27,11 @@ class SafeFlashStringReader {
explicit SafeFlashStringReader(const __FlashStringHelper* ptr, size_t size)
: _ptr(reinterpret_cast<const char*>(ptr)), _end(_ptr + size) {}
char read() {
return pgm_read_byte_near(_ptr++);
}
bool ended() const {
return _ptr == _end;
int read() {
if (_ptr < _end)
return pgm_read_byte(_ptr++);
else
return -1;
}
};

View File

@ -14,12 +14,11 @@ class IteratorReader {
explicit IteratorReader(TIterator begin, TIterator end)
: _ptr(begin), _end(end) {}
bool ended() const {
return _ptr == _end;
}
char read() {
return char(*_ptr++);
int read() {
if (_ptr < _end)
return static_cast<unsigned char>(*_ptr++);
else
return -1;
}
};

View File

@ -18,12 +18,8 @@ class StdStreamReader {
explicit StdStreamReader(std::istream& stream)
: _stream(stream), _current(0) {}
bool ended() const {
return _stream.eof();
}
char read() {
return static_cast<char>(_stream.get());
int read() {
return _stream.get();
}
private:

View File

@ -278,6 +278,18 @@ class JsonDocument : public Visitable {
_data.remove(adaptString(key));
}
FORCE_INLINE operator VariantConstRef() const {
return VariantConstRef(&_data);
}
bool operator==(VariantConstRef rhs) const {
return getVariant() == rhs;
}
bool operator!=(VariantConstRef rhs) const {
return getVariant() != rhs;
}
protected:
JsonDocument(MemoryPool pool) : _pool(pool) {
_data.setNull();

View File

@ -28,19 +28,14 @@ class JsonDeserializer {
_nestingLimit(nestingLimit),
_loaded(false) {}
DeserializationError parse(VariantData &variant) {
DeserializationError err = skipSpacesAndComments();
if (err) return err;
DeserializationError err = parseVariant(variant);
switch (current()) {
case '[':
return parseArray(variant.toArray());
case '{':
return parseObject(variant.toObject());
default:
return parseValue(variant);
if (!err && _current != 0 && !variant.isEnclosed()) {
// We don't detect trailing characters earlier, so we need to check now
err = DeserializationError::InvalidInput;
}
return err;
}
private:
@ -48,10 +43,8 @@ class JsonDeserializer {
char current() {
if (!_loaded) {
if (_reader.ended())
_current = 0;
else
_current = _reader.read();
int c = _reader.read();
_current = static_cast<char>(c > 0 ? c : 0);
_loaded = true;
}
return _current;
@ -67,6 +60,26 @@ class JsonDeserializer {
return true;
}
DeserializationError parseVariant(VariantData &variant) {
DeserializationError err = skipSpacesAndComments();
if (err) return err;
switch (current()) {
case '[':
return parseArray(variant.toArray());
case '{':
return parseObject(variant.toObject());
case '\"':
case '\'':
return parseStringValue(variant);
default:
return parseNumericValue(variant);
}
}
DeserializationError parseArray(CollectionData &array) {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
@ -88,7 +101,7 @@ class JsonDeserializer {
// 1 - Parse value
_nestingLimit--;
err = parse(*value);
err = parseVariant(*value);
_nestingLimit++;
if (err) return err;
@ -134,7 +147,7 @@ class JsonDeserializer {
// Parse value
_nestingLimit--;
err = parse(*slot->data());
err = parseVariant(*slot->data());
_nestingLimit++;
if (err) return err;
@ -152,14 +165,6 @@ class JsonDeserializer {
}
}
DeserializationError parseValue(VariantData &variant) {
if (isQuote(current())) {
return parseStringValue(variant);
} else {
return parseNumericValue(variant);
}
}
DeserializationError parseKey(const char *&key) {
if (isQuote(current())) {
return parseQuotedString(key);

View File

@ -52,14 +52,23 @@ class TextFormatter {
template <typename T>
void writeFloat(T value) {
if (isnan(value)) return writeRaw("NaN");
if (isnan(value)) return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
#if ARDUINOJSON_ENABLE_INFINITY
if (value < 0.0) {
writeRaw('-');
value = -value;
}
if (isinf(value)) return writeRaw("Infinity");
#else
if (isinf(value)) return writeRaw("null");
if (value < 0.0) {
writeRaw('-');
value = -value;
}
#endif
FloatParts<T> parts(value);

View File

@ -134,17 +134,10 @@ class MsgPackDeserializer {
// Prevent VS warning "assignment operator could not be generated"
MsgPackDeserializer &operator=(const MsgPackDeserializer &);
bool skip(uint8_t n) {
while (n--) {
if (_reader.ended()) return false;
_reader.read();
}
return true;
}
bool readByte(uint8_t &value) {
if (_reader.ended()) return false;
value = static_cast<uint8_t>(_reader.read());
int c = _reader.read();
if (c < 0) return false;
value = static_cast<uint8_t>(c);
return true;
}

View File

@ -24,8 +24,8 @@ class MsgPackSerializer {
}
template <typename T>
typename enable_if<sizeof(T) == 8>::type visitFloat(T value64)
ARDUINOJSON_NO_SANITIZE("float-cast-overflow") {
ARDUINOJSON_NO_SANITIZE("float-cast-overflow")
typename enable_if<sizeof(T) == 8>::type visitFloat(T value64) {
float value32 = float(value64);
if (value32 == value64) {
writeByte(0xCA);

View File

@ -15,9 +15,13 @@
#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_CONCAT11(A, B, C, D, E, F, G, H, I, J, K) \
ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, ARDUINOJSON_CONCAT4(H, I, J, K))
#define ARDUINOJSON_NAMESPACE \
ARDUINOJSON_CONCAT8(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \
ARDUINOJSON_VERSION_MINOR, ARDUINOJSON_VERSION_REVISION, \
_, ARDUINOJSON_USE_LONG_LONG, ARDUINOJSON_USE_DOUBLE, \
ARDUINOJSON_DECODE_UNICODE)
#define ARDUINOJSON_NAMESPACE \
ARDUINOJSON_CONCAT11( \
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)

View File

@ -72,9 +72,15 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
break;
}
#if ARDUINOJSON_ENABLE_NAN
if (*s == 'n' || *s == 'N') return traits::nan();
#endif
#if ARDUINOJSON_ENABLE_INFINITY
if (*s == 'i' || *s == 'I')
return is_negative ? -traits::inf() : traits::inf();
#endif
if (!isdigit(*s) && *s != '.') return return_type();
mantissa_t mantissa = 0;
@ -140,6 +146,9 @@ inline ParsedNumber<TFloat, TUInt> parseNumber(const char *s) {
}
exponent += exponent_offset;
// we should be at the end of the string, otherwise it's an error
if (*s != '\0') return return_type();
TFloat result = traits::make_float(static_cast<TFloat>(mantissa), exponent);
return is_negative ? -result : result;

View File

@ -49,6 +49,14 @@ class MemberProxy : public VariantOperators<MemberProxy<TObject, TStringRef> >,
return *this;
}
FORCE_INLINE bool operator==(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamMember()) == rhs;
}
FORCE_INLINE bool operator!=(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamMember()) != rhs;
}
FORCE_INLINE void clear() const {
getUpstreamMember().clear();
}

View File

@ -7,154 +7,241 @@
#include "../Variant/VariantRef.hpp"
namespace ARDUINOJSON_NAMESPACE {
template <typename T, typename Enable = void>
struct Comparer;
template <typename T>
struct is_simple_value {
static const bool value = is_integral<T>::value ||
is_floating_point<T>::value ||
is_same<T, bool>::value;
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:
// const char* == TVariant
// value == TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator==(
T *lhs, TVariant rhs) {
return adaptString(lhs).equals(rhs.template as<const char *>());
friend bool operator==(T *lhs, TVariant rhs) {
return compare(rhs, lhs) == 0;
}
// std::string == TVariant
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator==(
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
const T &lhs, TVariant rhs) {
return adaptString(lhs).equals(rhs.template as<const char *>());
return compare(rhs, lhs) == 0;
}
// TVariant == const char*
// TVariant == value
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator==(
TVariant lhs, T *rhs) {
return adaptString(rhs).equals(lhs.template as<const char *>());
friend bool operator==(TVariant lhs, T *rhs) {
return compare(lhs, rhs) == 0;
}
// TVariant == std::string
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator==(
friend typename enable_if<!IsVisitable<T>::value, bool>::type operator==(
TVariant lhs, const T &rhs) {
return adaptString(rhs).equals(lhs.template as<const char *>());
return compare(lhs, rhs) == 0;
}
// bool/int/float == TVariant
// value != TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator==(
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 lhs == rhs.template as<T>();
return compare(rhs, lhs) != 0;
}
// TVariant == bool/int/float
// TVariant != value
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator==(
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 lhs.template as<T>() == rhs;
return compare(lhs, rhs) != 0;
}
// const char* != TVariant
// value < TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator!=(
T *lhs, TVariant rhs) {
return !adaptString(lhs).equals(rhs.template as<const char *>());
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;
}
// std::string != TVariant
// TVariant < value
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator!=(
const T &lhs, TVariant rhs) {
return !adaptString(lhs).equals(rhs.template as<const char *>());
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;
}
// TVariant != const char*
// value <= TVariant
template <typename T>
friend typename enable_if<IsString<T *>::value, bool>::type operator!=(
TVariant lhs, T *rhs) {
return !adaptString(rhs).equals(lhs.template as<const char *>());
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 != std::string
// TVariant <= value
template <typename T>
friend typename enable_if<IsString<T>::value, bool>::type operator!=(
TVariant lhs, const T &rhs) {
return !adaptString(rhs).equals(lhs.template as<const char *>());
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;
}
// bool/int/float != TVariant
// value > TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator!=(
const T &lhs, TVariant rhs) {
return lhs != rhs.template as<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 != bool/int/float
// TVariant > value
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator!=(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() != rhs;
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;
}
// bool/int/float < TVariant
// value >= TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<(
const T &lhs, TVariant rhs) {
return lhs < rhs.template as<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 < bool/int/float
// TVariant >= value
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() < rhs;
friend bool operator>=(TVariant lhs, T *rhs) {
return compare(lhs, rhs) >= 0;
}
// bool/int/float <= TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<=(
const T &lhs, TVariant rhs) {
return lhs <= rhs.template as<T>();
}
// TVariant <= bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator<=(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() <= rhs;
}
// bool/int/float > TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>(
const T &lhs, TVariant rhs) {
return lhs > rhs.template as<T>();
}
// TVariant > bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() > rhs;
}
// bool/int/float >= TVariant
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>=(
const T &lhs, TVariant rhs) {
return lhs >= rhs.template as<T>();
}
// TVariant >= bool/int/float
template <typename T>
friend typename enable_if<is_simple_value<T>::value, bool>::type operator>=(
TVariant lhs, const T &rhs) {
return lhs.template as<T>() >= rhs;
friend bool operator>=(TVariant lhs, const T &rhs) {
return compare(lhs, rhs) >= 0;
}
};

View File

@ -15,8 +15,7 @@ class VariantOr {
public:
// Returns the default value if the VariantRef is undefined of incompatible
template <typename T>
typename enable_if<!is_integral<T>::value, T>::type operator|(
const T &defaultValue) const {
T operator|(const T &defaultValue) const {
if (impl()->template is<T>())
return impl()->template as<T>();
else
@ -30,17 +29,6 @@ class VariantOr {
return value ? value : defaultValue;
}
// Returns the default value if the VariantRef is undefined of incompatible
// Special case for integers: we also accept double
template <typename Integer>
typename enable_if<is_integral<Integer>::value, Integer>::type operator|(
const Integer &defaultValue) const {
if (impl()->template is<double>())
return impl()->template as<Integer>();
else
return defaultValue;
}
private:
const TImpl *impl() const {
return static_cast<const TImpl *>(this);

View File

@ -0,0 +1,62 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#pragma once
namespace ARDUINOJSON_NAMESPACE {
// Wraps a const char* so that the our functions are picked only if the
// originals are missing
struct pgm_p {
pgm_p(const char* p) : address(p) {}
const char* address;
};
} // namespace ARDUINOJSON_NAMESPACE
#ifndef strlen_P
inline size_t strlen_P(ARDUINOJSON_NAMESPACE::pgm_p s) {
const char* p = s.address;
while (pgm_read_byte(p)) p++;
return size_t(p - s.address);
}
#endif
#ifndef strncmp_P
inline int strncmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) {
const char* s1 = a;
const char* s2 = b.address;
while (n-- > 0) {
char c1 = *s1++;
char c2 = static_cast<char>(pgm_read_byte(s2++));
if (c1 < c2) return -1;
if (c1 > c2) return 1;
if (c1 == 0 /* and c2 as well */) return 0;
}
return 0;
}
#endif
#ifndef strcmp_P
inline int strcmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b) {
const char* s1 = a;
const char* s2 = b.address;
for (;;) {
char c1 = *s1++;
char c2 = static_cast<char>(pgm_read_byte(s2++));
if (c1 < c2) return -1;
if (c1 > c2) return 1;
if (c1 == 0 /* and c2 as well */) return 0;
}
}
#endif
#ifndef memcpy_P
inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) {
uint8_t* d = reinterpret_cast<uint8_t*>(dst);
const char* s = src.address;
while (n-- > 0) {
*d++ = pgm_read_byte(s++);
}
return dst;
}
#endif

View File

@ -0,0 +1,22 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#pragma once
namespace ARDUINOJSON_NAMESPACE {
inline int8_t safe_strcmp(const char* a, const char* b) {
if (a == b) return 0;
if (!a) return -1;
if (!b) return 1;
return static_cast<int8_t>(strcmp(a, b));
}
inline int8_t safe_strncmp(const char* a, const char* b, size_t n) {
if (a == b) return 0;
if (!a) return -1;
if (!b) return 1;
return static_cast<int8_t>(strncmp(a, b, n));
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -54,13 +54,16 @@ class DynamicStringWriter<String> {
#endif
#if ARDUINOJSON_ENABLE_STD_STRING
template <>
struct IsWriteableString<std::string> : true_type {};
template <typename TCharTraits, typename TAllocator>
struct IsWriteableString<std::basic_string<char, TCharTraits, TAllocator> >
: true_type {};
template <typename TCharTraits, typename TAllocator>
class DynamicStringWriter<std::basic_string<char, TCharTraits, TAllocator> > {
typedef std::basic_string<char, TCharTraits, TAllocator> string_type;
template <>
class DynamicStringWriter<std::string> {
public:
DynamicStringWriter(std::string &str) : _str(&str) {}
DynamicStringWriter(string_type &str) : _str(&str) {}
size_t write(uint8_t c) {
_str->operator+=(static_cast<char>(c));
@ -73,7 +76,7 @@ class DynamicStringWriter<std::string> {
}
private:
std::string *_str;
string_type *_str;
};
#endif
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -14,32 +14,38 @@
namespace ARDUINOJSON_NAMESPACE {
template <template <typename> class TSerializer, typename TSource,
typename TPrint>
typename enable_if<!IsWriteableString<TPrint>::value, size_t>::type serialize(
const TSource &source, TPrint &destination) {
TSerializer<TPrint> serializer(destination);
typename TDestination>
size_t doSerialize(const TSource &source, TDestination &destination) {
TSerializer<TDestination> serializer(destination);
source.accept(serializer);
return serializer.bytesWritten();
}
#if ARDUINOJSON_ENABLE_STD_STREAM
template <template <typename> class TSerializer, typename TSource>
size_t serialize(const TSource &source, std::ostream &os) {
StreamWriter writer(os);
return serialize<TSerializer>(source, writer);
size_t serialize(const TSource &source, std::ostream &destination) {
StreamWriter writer(destination);
return doSerialize<TSerializer>(source, writer);
}
#endif
#if ARDUINOJSON_ENABLE_ARDUINO_PRINT
template <template <typename> class TSerializer, typename TSource>
size_t serialize(const TSource &source, Print &destination) {
return doSerialize<TSerializer>(source, destination);
}
#endif
template <template <typename> class TSerializer, typename TSource>
size_t serialize(const TSource &source, char *buffer, size_t bufferSize) {
StaticStringWriter writer(buffer, bufferSize);
return serialize<TSerializer>(source, writer);
return doSerialize<TSerializer>(source, writer);
}
template <template <typename> class TSerializer, typename TSource, size_t N>
size_t serialize(const TSource &source, char (&buffer)[N]) {
StaticStringWriter writer(buffer, N);
return serialize<TSerializer>(source, writer);
return doSerialize<TSerializer>(source, writer);
}
template <template <typename> class TSerializer, typename TSource,
@ -47,7 +53,7 @@ template <template <typename> class TSerializer, typename TSource,
typename enable_if<IsWriteableString<TString>::value, size_t>::type serialize(
const TSource &source, TString &str) {
DynamicStringWriter<TString> writer(str);
return serialize<TSerializer>(source, writer);
return doSerialize<TSerializer>(source, writer);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -5,6 +5,7 @@
#pragma once
#include <WString.h>
#include "../Polyfills/safe_strcmp.hpp"
namespace ARDUINOJSON_NAMESPACE {
@ -25,11 +26,14 @@ class ArduinoStringAdapter {
return !_str->c_str();
}
bool equals(const char* expected) const {
int8_t compare(const char* other) const {
// Arduino's String::c_str() can return NULL
const char* actual = _str->c_str();
if (!actual || !expected) return actual == expected;
return 0 == strcmp(actual, expected);
const char* me = _str->c_str();
return safe_strcmp(me, other);
}
bool equals(const char* expected) const {
return compare(expected) == 0;
}
const char* data() const {

View File

@ -6,6 +6,7 @@
#include <stddef.h> // size_t
#include <string.h> // strcmp
#include "../Polyfills/safe_strcmp.hpp"
namespace ARDUINOJSON_NAMESPACE {
@ -13,10 +14,12 @@ class ConstRamStringAdapter {
public:
ConstRamStringAdapter(const char* str = 0) : _str(str) {}
int8_t compare(const char* other) const {
return safe_strcmp(_str, other);
}
bool equals(const char* expected) const {
const char* actual = _str;
if (!actual || !expected) return actual == expected;
return strcmp(actual, expected) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -4,16 +4,23 @@
#pragma once
#include "../Polyfills/pgmspace.hpp"
namespace ARDUINOJSON_NAMESPACE {
class FlashStringAdapter {
public:
FlashStringAdapter(const __FlashStringHelper* str) : _str(str) {}
int8_t compare(const char* other) const {
if (!other && !_str) return 0;
if (!_str) return -1;
if (!other) return 1;
return int8_t(-strcmp_P(other, reinterpret_cast<const char*>(_str)));
}
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strcmp_P(expected, actual) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -11,10 +11,16 @@ class SizedFlashStringAdapter {
SizedFlashStringAdapter(const __FlashStringHelper* str, size_t sz)
: _str(str), _size(sz) {}
int8_t compare(const char* other) const {
if (!other && !_str) return 0;
if (!_str) return -1;
if (!other) return 1;
return int8_t(
-strncmp_P(other, reinterpret_cast<const char*>(_str), _size));
}
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strncmp_P(expected, actual, _size) == 0;
return compare(expected) == 0;
}
bool isNull() const {
@ -24,7 +30,7 @@ class SizedFlashStringAdapter {
char* save(MemoryPool* pool) const {
if (!_str) return NULL;
char* dup = pool->allocFrozenString(_size);
if (!dup) memcpy_P(dup, (const char*)_str, _size);
if (dup) memcpy_P(dup, reinterpret_cast<const char*>(_str), _size);
return dup;
}

View File

@ -12,10 +12,12 @@ class SizedRamStringAdapter {
public:
SizedRamStringAdapter(const char* str, size_t n) : _str(str), _size(n) {}
int8_t compare(const char* other) const {
return safe_strncmp(_str, other, _size) == 0;
}
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strcmp(actual, expected) == 0;
return compare(expected) == 0;
}
bool isNull() const {

View File

@ -8,9 +8,10 @@
namespace ARDUINOJSON_NAMESPACE {
template <typename TString>
class StlStringAdapter {
public:
StlStringAdapter(const std::string& str) : _str(&str) {}
StlStringAdapter(const TString& str) : _str(&str) {}
char* save(MemoryPool* pool) const {
size_t n = _str->length() + 1;
@ -23,6 +24,11 @@ class StlStringAdapter {
return false;
}
int8_t compare(const char* other) const {
if (!other) return 1;
return static_cast<int8_t>(_str->compare(other));
}
bool equals(const char* expected) const {
if (!expected) return false;
return *_str == expected;
@ -41,14 +47,18 @@ class StlStringAdapter {
}
private:
const std::string* _str;
const TString* _str;
};
template <>
struct IsString<std::string> : true_type {};
template <typename TCharTraits, typename TAllocator>
struct IsString<std::basic_string<char, TCharTraits, TAllocator> > : true_type {
};
inline StlStringAdapter adaptString(const std::string& str) {
return StlStringAdapter(str);
template <typename TCharTraits, typename TAllocator>
inline StlStringAdapter<std::basic_string<char, TCharTraits, TAllocator> >
adaptString(const std::basic_string<char, TCharTraits, TAllocator>& str) {
return StlStringAdapter<std::basic_string<char, TCharTraits, TAllocator> >(
str);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -16,15 +16,18 @@ namespace ARDUINOJSON_NAMESPACE {
enum {
VALUE_MASK = 0x7F,
OWNERSHIP_BIT = 0x01,
VALUE_IS_NULL = 0,
VALUE_IS_LINKED_RAW = 0x01,
VALUE_IS_OWNED_RAW = 0x02,
VALUE_IS_LINKED_STRING = 0x03,
VALUE_IS_OWNED_STRING = 0x04,
VALUE_IS_BOOLEAN = 0x05,
VALUE_IS_POSITIVE_INTEGER = 0x06,
VALUE_IS_NEGATIVE_INTEGER = 0x07,
VALUE_IS_FLOAT = 0x08,
VALUE_IS_LINKED_RAW = 0x02,
VALUE_IS_OWNED_RAW = 0x03,
VALUE_IS_LINKED_STRING = 0x04,
VALUE_IS_OWNED_STRING = 0x05,
// CAUTION: no OWNERSHIP_BIT below
VALUE_IS_BOOLEAN = 0x06,
VALUE_IS_POSITIVE_INTEGER = 0x08,
VALUE_IS_NEGATIVE_INTEGER = 0x0A,
VALUE_IS_FLOAT = 0x0C,
COLLECTION_MASK = 0x60,
VALUE_IS_OBJECT = 0x20,

View File

@ -101,7 +101,9 @@ class VariantData {
}
bool equals(const VariantData &other) const {
if (type() != other.type()) return false;
// Check that variant have the same type, but ignore string ownership
if ((type() | OWNERSHIP_BIT) != (other.type() | OWNERSHIP_BIT))
return false;
switch (type()) {
case VALUE_IS_LINKED_STRING:
@ -177,6 +179,10 @@ class VariantData {
return type() == VALUE_IS_NULL;
}
bool isEnclosed() const {
return isCollection() || isString();
}
void remove(size_t index) {
if (isArray()) _content.asCollection.remove(index);
}

View File

@ -222,6 +222,7 @@ class VariantRef : public VariantRefBase<VariantData>,
// set(ArrayConstRef)
// set(ObjectRef)
// set(ObjecConstRef)
// set(const JsonDocument&)
template <typename TVariant>
typename enable_if<IsVisitable<TVariant>::value, bool>::type set(
const TVariant &value) const;
@ -379,5 +380,13 @@ class VariantConstRef : public VariantRefBase<const VariantData>,
const CollectionData *obj = variantAsObject(_data);
return VariantConstRef(obj ? obj->get(adaptString(key)) : 0);
}
FORCE_INLINE bool operator==(VariantConstRef lhs) const {
return variantEquals(_data, lhs._data);
}
FORCE_INLINE bool operator!=(VariantConstRef lhs) const {
return !variantEquals(_data, lhs._data);
}
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -4,7 +4,7 @@
#pragma once
#define ARDUINOJSON_VERSION "6.10.0"
#define ARDUINOJSON_VERSION "6.11.5"
#define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 10
#define ARDUINOJSON_VERSION_REVISION 0
#define ARDUINOJSON_VERSION_MINOR 11
#define ARDUINOJSON_VERSION_REVISION 5

View File

@ -5,6 +5,7 @@
add_executable(ElementProxyTests
add.cpp
clear.cpp
compare.cpp
remove.cpp
set.cpp
size.cpp

View File

@ -0,0 +1,28 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::operator==()") {
DynamicJsonDocument doc(4096);
SECTION("same value") {
doc.add(1);
doc.add(1);
REQUIRE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] != doc[1]);
}
SECTION("different values") {
doc.add(1);
doc.add(2);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE(doc[0] != doc[1]);
}
}

View File

@ -7,8 +7,9 @@
#include <catch.hpp>
TEST_CASE("Invalid JSON input") {
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
"'\\u000G'", "'\\u000/'", "\\x1234"};
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'", "'\\u000G'",
"'\\u000/'", "\\x1234", "6a9", "1,",
"2]", "3}"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);

View File

@ -3,6 +3,8 @@
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
#define ARDUINOJSON_ENABLE_NAN 1
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson.h>
#include <limits.h>

View File

@ -5,6 +5,7 @@
add_executable(JsonDocumentTests
add.cpp
BasicJsonDocument.cpp
compare.cpp
containsKey.cpp
createNested.cpp
DynamicJsonDocument.cpp

View File

@ -0,0 +1,77 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("DynamicJsonDocument::operator==(const DynamicJsonDocument&)") {
DynamicJsonDocument doc1(4096);
DynamicJsonDocument doc2(4096);
SECTION("Empty") {
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With same object") {
doc1["hello"] = "world";
doc2["hello"] = "world";
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With different object") {
doc1["hello"] = "world";
doc2["world"] = "hello";
REQUIRE_FALSE(doc1 == doc2);
REQUIRE(doc1 != doc2);
}
}
TEST_CASE("DynamicJsonDocument::operator==(const StaticJsonDocument&)") {
DynamicJsonDocument doc1(4096);
StaticJsonDocument<256> doc2;
SECTION("Empty") {
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With same object") {
doc1["hello"] = "world";
doc2["hello"] = "world";
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With different object") {
doc1["hello"] = "world";
doc2["world"] = "hello";
REQUIRE_FALSE(doc1 == doc2);
REQUIRE(doc1 != doc2);
}
}
TEST_CASE("StaticJsonDocument::operator==(const DynamicJsonDocument&)") {
StaticJsonDocument<256> doc1;
DynamicJsonDocument doc2(4096);
SECTION("Empty") {
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With same object") {
doc1["hello"] = "world";
doc2["hello"] = "world";
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With different object") {
doc1["hello"] = "world";
doc2["world"] = "hello";
REQUIRE_FALSE(doc1 == doc2);
REQUIRE(doc1 != doc2);
}
}

View File

@ -161,7 +161,7 @@ TEST_CASE("JsonObject::operator[]") {
obj[null] = 666;
REQUIRE(obj.size() == 1);
REQUIRE(obj[null] == 0);
REQUIRE(obj[null] == null);
}
SECTION("obj[key].to<JsonArray>()") {

View File

@ -229,8 +229,13 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("Variants containing linked strings") {
variant1.set("0hello" + 1); // make sure they have
variant2.set("1hello" + 1); // different addresses
// 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);
@ -253,8 +258,13 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("Variants containing linked raws") {
variant1.set(serialized("0hello" + 1)); // make sure they have
variant2.set(serialized("1hello" + 1)); // different addresses
// 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);
@ -276,6 +286,17 @@ TEST_CASE("JsonVariant comparisons") {
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);
@ -327,23 +348,117 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
// SECTION("VariantsOfDifferentTypes") {
// DynamicJsonDocument doc1(4096);
// JsonObject obj = doc1.to<JsonObject>();
// DynamicJsonDocument doc2(4096);
// JsonArray arr = doc2.to<JsonArray>();
// JsonVariant variants[] = {
// true, 42, 666.667, "hello", arr, obj,
// };
// size_t n = sizeof(variants) / sizeof(variants[0]);
// for (size_t i = 0; i < n; i++) {
// for (size_t j = i + 1; j < n; j++) {
// REQUIRE(variants[i] != variants[j]);
// REQUIRE_FALSE(variants[i] == variants[j]);
// }
// }
// }
}
class VariantComparisionFixture {
private:
StaticJsonDocument<256> doc;
JsonVariant variant;
public:
VariantComparisionFixture() : variant(doc.to<JsonVariant>()) {}
protected:
template <typename T>
void setValue(const T& value) {
variant.set(value);
}
template <typename T>
void assertEqualsTo(const T& value) {
REQUIRE(variant == value);
REQUIRE(value == variant);
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);
}
}

View File

@ -14,7 +14,7 @@ TEST_CASE("JsonVariant::createNestedObject()") {
SECTION("promotes to array") {
JsonObject obj = variant.createNestedObject();
obj["value"] = "42";
obj["value"] = 42;
REQUIRE(variant.is<JsonArray>() == true);
REQUIRE(variant[0]["value"] == 42);
@ -23,7 +23,7 @@ TEST_CASE("JsonVariant::createNestedObject()") {
SECTION("works on MemberProxy") {
JsonObject obj = variant["items"].createNestedObject();
obj["value"] = "42";
obj["value"] = 42;
REQUIRE(variant["items"][0]["value"] == 42);
}
@ -42,7 +42,7 @@ TEST_CASE("JsonVariant::createNestedArray()") {
SECTION("works on MemberProxy") {
JsonArray arr = variant["items"].createNestedArray();
arr.add("42");
arr.add(42);
REQUIRE(variant["items"][0][0] == 42);
}
@ -54,7 +54,7 @@ TEST_CASE("JsonVariant::createNestedObject(key)") {
SECTION("promotes to object") {
JsonObject obj = variant.createNestedObject("weather");
obj["temp"] = "42";
obj["temp"] = 42;
REQUIRE(variant.is<JsonObject>() == true);
REQUIRE(variant["weather"]["temp"] == 42);
@ -62,7 +62,7 @@ TEST_CASE("JsonVariant::createNestedObject(key)") {
SECTION("works on MemberProxy") {
JsonObject obj = variant["status"].createNestedObject("weather");
obj["temp"] = "42";
obj["temp"] = 42;
REQUIRE(variant["status"]["weather"]["temp"] == 42);
}
@ -81,7 +81,7 @@ TEST_CASE("JsonVariant::createNestedArray(key)") {
SECTION("works on MemberProxy") {
JsonArray arr = variant["weather"].createNestedArray("temp");
arr.add("42");
arr.add(42);
REQUIRE(variant["weather"]["temp"][0] == 42);
}

View File

@ -51,6 +51,12 @@ TEST_CASE("JsonVariant::operator|()") {
REQUIRE(result == "default");
}
SECTION("int | uint8_t (out of range)") {
variant.set(666);
uint8_t result = variant | static_cast<uint8_t>(42);
REQUIRE(result == 42);
}
SECTION("int | int") {
variant.set(0);
int result = variant | 666;
@ -58,8 +64,9 @@ TEST_CASE("JsonVariant::operator|()") {
}
SECTION("double | int") {
variant.set(42.0);
int result = variant | 666;
// NOTE: changed the behavior to fix #981
variant.set(666.0);
int result = variant | 42;
REQUIRE(result == 42);
}

View File

@ -108,3 +108,19 @@ TEST_CASE("JsonVariant with not enough memory") {
REQUIRE(v.isNull());
}
}
TEST_CASE("JsonVariant::set(DynamicJsonDocument)") {
DynamicJsonDocument doc1(1024);
doc1["hello"] = "world";
DynamicJsonDocument doc2(1024);
JsonVariant v = doc2.to<JsonVariant>();
// Should copy the doc
v.set(doc1);
doc1.clear();
std::string json;
serializeJson(doc2, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}

View File

@ -5,6 +5,7 @@
add_executable(MemberProxyTests
add.cpp
clear.cpp
compare.cpp
containsKey.cpp
remove.cpp
set.cpp

View File

@ -0,0 +1,26 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
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("different values") {
doc["key1"] = "value1";
doc["key2"] = "value2";
REQUIRE_FALSE(doc["key1"] == doc["key2"]);
REQUIRE(doc["key1"] != doc["key2"]);
}
}

View File

@ -5,6 +5,8 @@
add_executable(MiscTests
conflicts.cpp
FloatParts.cpp
StreamReader.cpp
StringAdapters.cpp
StringWriter.cpp
TypeTraits.cpp
unsigned_char.cpp
@ -13,3 +15,24 @@ add_executable(MiscTests
target_link_libraries(MiscTests catch)
add_test(Misc MiscTests)
add_executable(Issue978
Issue978.cpp
)
set_target_properties(Issue978
PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE
)
add_test(
NAME
Issue978
COMMAND
${CMAKE_COMMAND} --build . --target Issue978 --config $<CONFIGURATION>
WORKING_DIRECTORY
${CMAKE_BINARY_DIR}
)
set_tests_properties(Issue978
PROPERTIES
WILL_FAIL TRUE)

13
test/Misc/Issue978.cpp Normal file
View File

@ -0,0 +1,13 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
struct Stream {};
int main() {
Stream* stream = 0;
DynamicJsonDocument doc(1024);
deserializeJson(doc, stream);
}

View File

@ -0,0 +1,33 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StdStreamReader") {
std::istringstream src("\x01\xFF");
StdStreamReader reader(src);
REQUIRE(reader.read() == 0x01);
REQUIRE(reader.read() == 0xFF);
REQUIRE(reader.read() == -1);
}
TEST_CASE("SafeCharPointerReader") {
SafeCharPointerReader reader("\x01\xFF", 2);
REQUIRE(reader.read() == 0x01);
REQUIRE(reader.read() == 0xFF);
REQUIRE(reader.read() == -1);
}
TEST_CASE("UnsafeCharPointerReader") {
UnsafeCharPointerReader reader("\x01\xFF");
REQUIRE(reader.read() == 0x01);
REQUIRE(reader.read() == 0xFF);
REQUIRE(reader.read() == 0);
}

View File

@ -0,0 +1,73 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "custom_string.hpp"
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ConstRamStringAdapter") {
SECTION("null") {
ConstRamStringAdapter adapter(NULL);
REQUIRE(adapter.compare("bravo") < 0);
REQUIRE(adapter.compare(NULL) == 0);
REQUIRE(adapter.equals(NULL));
REQUIRE_FALSE(adapter.equals("charlie"));
}
SECTION("non-null") {
ConstRamStringAdapter adapter("bravo");
REQUIRE(adapter.compare(NULL) > 0);
REQUIRE(adapter.compare("alpha") > 0);
REQUIRE(adapter.compare("bravo") == 0);
REQUIRE(adapter.compare("charlie") < 0);
REQUIRE(adapter.equals("bravo"));
REQUIRE_FALSE(adapter.equals("charlie"));
}
}
TEST_CASE("std::string") {
std::string str("bravo");
StlStringAdapter<std::string> adapter = adaptString(str);
REQUIRE(adapter.compare(NULL) > 0);
REQUIRE(adapter.compare("alpha") > 0);
REQUIRE(adapter.compare("bravo") == 0);
REQUIRE(adapter.compare("charlie") < 0);
REQUIRE(adapter.equals("bravo"));
REQUIRE_FALSE(adapter.equals("charlie"));
}
TEST_CASE("custom_string") {
custom_string str("bravo");
StlStringAdapter<custom_string> adapter = adaptString(str);
REQUIRE(adapter.compare(NULL) > 0);
REQUIRE(adapter.compare("alpha") > 0);
REQUIRE(adapter.compare("bravo") == 0);
REQUIRE(adapter.compare("charlie") < 0);
REQUIRE(adapter.equals("bravo"));
REQUIRE_FALSE(adapter.equals("charlie"));
}
TEST_CASE("IsString<T>") {
SECTION("std::string") {
REQUIRE(IsString<std::string>::value == true);
}
SECTION("basic_string<wchar_t>") {
REQUIRE(IsString<std::basic_string<wchar_t> >::value == false);
}
SECTION("custom_string") {
REQUIRE(IsString<custom_string>::value == true);
}
}

View File

@ -4,6 +4,7 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "custom_string.hpp"
using namespace ARDUINOJSON_NAMESPACE;
@ -48,8 +49,30 @@ TEST_CASE("StaticStringWriter") {
}
}
TEST_CASE("DynamicStringWriter") {
TEST_CASE("DynamicStringWriter<std::string>") {
std::string output;
DynamicStringWriter<std::string> sb(output);
common_tests(sb, output);
}
TEST_CASE("DynamicStringWriter<custom_string>") {
custom_string output;
DynamicStringWriter<custom_string> sb(output);
REQUIRE(4 == print(sb, "ABCD"));
REQUIRE("ABCD" == output);
}
TEST_CASE("IsWriteableString") {
SECTION("std::string") {
REQUIRE(IsWriteableString<std::string>::value == true);
}
SECTION("custom_string") {
REQUIRE(IsWriteableString<custom_string>::value == true);
}
SECTION("basic_string<wchar_t>") {
REQUIRE(IsWriteableString<std::basic_string<wchar_t> >::value == false);
}
}

View File

@ -0,0 +1,14 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#pragma once
#include <string>
using namespace ARDUINOJSON_NAMESPACE;
struct custom_char_traits : std::char_traits<char> {};
struct custom_allocator : std::allocator<char> {};
typedef std::basic_string<char, custom_char_traits, custom_allocator>
custom_string;

View File

@ -6,12 +6,18 @@
set(CMAKE_CXX_STANDARD 11)
add_executable(MixedConfigurationTests
cpp11.cpp
decode_unicode_0.cpp
decode_unicode_1.cpp
enable_infinity_0.cpp
enable_infinity_1.cpp
enable_nan_0.cpp
enable_nan_1.cpp
use_double_0.cpp
use_double_1.cpp
use_long_long_0.cpp
use_long_long_1.cpp
enable_progmem_1.cpp
)
target_link_libraries(MixedConfigurationTests catch)

View File

@ -0,0 +1,31 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#if __cplusplus >= 201103L
TEST_CASE("nullptr") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("JsonVariant == nullptr") {
REQUIRE((variant == nullptr));
REQUIRE_FALSE((variant != nullptr));
}
SECTION("JsonVariant != nullptr") {
variant.set(42);
REQUIRE_FALSE((variant == nullptr));
REQUIRE((variant != nullptr));
}
SECTION("JsonVariant.set(nullptr)") {
variant.set(42);
variant.set(nullptr);
REQUIRE(variant.isNull());
}
}
#endif

View File

@ -0,0 +1,35 @@
#define ARDUINOJSON_ENABLE_INFINITY 0
#include <ArduinoJson.h>
#include <catch.hpp>
#include <limits>
static void assertParseFails(const char* json) {
DynamicJsonDocument doc(4096);
auto err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::InvalidInput);
}
static void assertJsonEquals(const JsonDocument& doc,
std::string expectedJson) {
std::string actualJson;
serializeJson(doc, actualJson);
REQUIRE(actualJson == expectedJson);
}
TEST_CASE("ARDUINOJSON_ENABLE_INFINITY == 0") {
SECTION("serializeJson()") {
DynamicJsonDocument doc(4096);
doc.add(std::numeric_limits<double>::infinity());
doc.add(-std::numeric_limits<double>::infinity());
assertJsonEquals(doc, "[null,null]");
}
SECTION("deserializeJson()") {
assertParseFails("{\"X\":Infinity}");
assertParseFails("{\"X\":-Infinity}");
assertParseFails("{\"X\":+Infinity}");
}
}

View File

@ -0,0 +1,38 @@
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include <limits>
namespace my {
using ARDUINOJSON_NAMESPACE::isinf;
} // namespace my
TEST_CASE("ARDUINOJSON_ENABLE_INFINITY == 1") {
DynamicJsonDocument doc(4096);
SECTION("serializeJson()") {
doc.add(std::numeric_limits<double>::infinity());
doc.add(-std::numeric_limits<double>::infinity());
std::string json;
serializeJson(doc, json);
REQUIRE(json == "[Infinity,-Infinity]");
}
SECTION("deserializeJson()") {
auto err = deserializeJson(doc, "[Infinity,-Infinity,+Infinity]");
float a = doc[0];
float b = doc[1];
float c = doc[2];
REQUIRE(err == DeserializationError::Ok);
REQUIRE(my::isinf(a));
REQUIRE(a > 0);
REQUIRE(my::isinf(b));
REQUIRE(b < 0);
REQUIRE(my::isinf(c));
REQUIRE(c > 0);
}
}

View File

@ -0,0 +1,25 @@
#define ARDUINOJSON_ENABLE_NAN 0
#include <ArduinoJson.h>
#include <catch.hpp>
#include <limits>
TEST_CASE("ARDUINOJSON_ENABLE_NAN == 0") {
DynamicJsonDocument doc(4096);
JsonObject root = doc.to<JsonObject>();
SECTION("serializeJson()") {
root["X"] = std::numeric_limits<double>::signaling_NaN();
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"X\":null}");
}
SECTION("deserializeJson()") {
auto err = deserializeJson(doc, "{\"X\":NaN}");
REQUIRE(err == DeserializationError::InvalidInput);
}
}

View File

@ -0,0 +1,31 @@
#define ARDUINOJSON_ENABLE_NAN 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include <limits>
namespace my {
using ARDUINOJSON_NAMESPACE::isnan;
} // namespace my
TEST_CASE("ARDUINOJSON_ENABLE_NAN == 1") {
DynamicJsonDocument doc(4096);
JsonObject root = doc.to<JsonObject>();
SECTION("serializeJson()") {
root["X"] = std::numeric_limits<double>::signaling_NaN();
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"X\":NaN}");
}
SECTION("deserializeJson()") {
auto err = deserializeJson(doc, "{\"X\":NaN}");
float x = doc["X"];
REQUIRE(err == DeserializationError::Ok);
REQUIRE(my::isnan(x));
}
}

View File

@ -0,0 +1,87 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include "progmem_emulation.hpp"
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Flash strings") {
DynamicJsonDocument doc(2048);
SECTION("deserializeJson()") {
DeserializationError err = deserializeJson(doc, F("{'hello':'world'}"));
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc["hello"] == "world");
}
SECTION("JsonDocument::operator[]") {
doc[F("hello")] = F("world");
REQUIRE(doc["hello"] == "world");
}
SECTION("JsonDocument::add()") {
doc.add(F("world"));
REQUIRE(doc[0] == "world");
}
SECTION("JsonVariant::set()") {
JsonVariant var = doc.to<JsonVariant>();
var.set(F("world"));
REQUIRE(var == "world");
}
SECTION("MemberProxy::operator==") {
doc["hello"] = "world";
REQUIRE(doc["hello"] == F("world"));
}
SECTION("ElementProxy::operator==") {
doc.add("world");
REQUIRE(doc[0] == F("world"));
}
}
TEST_CASE("strlen_P") {
CHECK(strlen_P(FC("")) == 0);
CHECK(strlen_P(FC("a")) == 1);
CHECK(strlen_P(FC("ac")) == 2);
}
TEST_CASE("strncmp_P") {
CHECK(strncmp_P("a", FC("b"), 0) == 0);
CHECK(strncmp_P("a", FC("b"), 1) == -1);
CHECK(strncmp_P("b", FC("a"), 1) == 1);
CHECK(strncmp_P("a", FC("a"), 0) == 0);
CHECK(strncmp_P("a", FC("b"), 2) == -1);
CHECK(strncmp_P("b", FC("a"), 2) == 1);
CHECK(strncmp_P("a", FC("a"), 2) == 0);
}
TEST_CASE("strcmp_P") {
CHECK(strcmp_P("a", FC("b")) == -1);
CHECK(strcmp_P("b", FC("a")) == 1);
CHECK(strcmp_P("a", FC("a")) == 0);
CHECK(strcmp_P("aa", FC("ab")) == -1);
CHECK(strcmp_P("ab", FC("aa")) == 1);
CHECK(strcmp_P("aa", FC("aa")) == 0);
}
TEST_CASE("memcpy_P") {
char dst[4];
CHECK(memcpy_P(dst, FC("ABC"), 4) == dst);
CHECK(dst[0] == 'A');
CHECK(dst[1] == 'B');
CHECK(dst[2] == 'C');
CHECK(dst[3] == 0);
}

View File

@ -0,0 +1,23 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <stdint.h> // uint8_t
#include <string.h> // strcmp, strlen...
class __FlashStringHelper;
inline const void* convertPtrToFlash(const void* s) {
return reinterpret_cast<const char*>(s) + 42;
}
inline const void* convertFlashToPtr(const void* s) {
return reinterpret_cast<const char*>(s) - 42;
}
#define F(X) reinterpret_cast<const __FlashStringHelper*>(convertPtrToFlash(X))
#define FC(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
inline uint8_t pgm_read_byte(const void* p) {
return *reinterpret_cast<const uint8_t*>(convertFlashToPtr(p));
}

View File

@ -3,6 +3,8 @@
// MIT License
#define ARDUINOJSON_USE_DOUBLE 0
#define ARDUINOJSON_ENABLE_NAN 1
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson/Numbers/parseFloat.hpp>
#include <catch.hpp>

View File

@ -16,3 +16,9 @@ TEST_CASE("Test uint32_t overflow") {
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");
REQUIRE(result.type() == uint8_t(VALUE_IS_NULL));
}

View File

@ -6,6 +6,8 @@
#include <limits>
#include <string>
#define ARDUINOJSON_ENABLE_NAN 1
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson/Json/TextFormatter.hpp>
#include <ArduinoJson/Serialization/DynamicStringWriter.hpp>