Compare commits

..

14 Commits

118 changed files with 2850 additions and 2245 deletions

View File

@ -1,18 +1,80 @@
ArduinoJson: change log
=======================
v6.2.2-beta
v6.4.0-beta (2018-09-11)
-----------
* Copy `JsonArray` and `JsonObject`, instead of storing pointers (issue #780)
* Added `JsonVariant::to<JsonArray>()` and `JsonVariant::to<JsonObject>()`
v6.3.0-beta (2018-08-31)
-----------
* Implemented reference semantics for `JsonVariant`
* Replaced `JsonPair`'s `key` and `value` with `key()` and `value()`
* Fixed `serializeJson(obj[key], dst)` (issue #794)
> ### BREAKING CHANGES
>
> #### JsonVariant
>
> `JsonVariant` now has a semantic similar to `JsonObject` and `JsonArray`.
> It's a reference to a value stored in the `JsonDocument`.
> As a consequence, a `JsonVariant` cannot be used as a standalone variable anymore.
>
> Old code:
>
> ```c++
> JsonVariant myValue = 42;
> ```
>
> New code:
>
> ```c++
> DynamicJsonDocument doc;
> JsonVariant myValue = doc.to<JsonVariant>();
> myValue.set(42);
> ```
>
> #### JsonPair
>
> Old code:
>
> ```c++
> for(JsonPair p : myObject) {
> Serial.println(p.key);
> Serial.println(p.value.as<int>());
> }
> ```
>
> New code:
>
> ```c++
> for(JsonPair p : myObject) {
> Serial.println(p.key());
> Serial.println(p.value().as<int>());
> }
> ```
>
> CAUTION: the key is now read only!
v6.2.3-beta (2018-07-19)
-----------
* Fixed exception when using Flash strings as object keys (issue #784)
v6.2.2-beta (2018-07-18)
-----------
* Fixed `invalid application of 'sizeof' to incomplete type '__FlashStringHelper'` (issue #783)
* Fixed `char[]` not duplicated when passed to `JsonVariant::operator[]`
v6.2.1-beta
v6.2.1-beta (2018-07-17)
-----------
* Fixed `JsonObject` not inserting keys of type `String` (issue #782)
v6.2.0-beta
v6.2.0-beta (2018-07-12)
-----------
* Disabled lazy number deserialization (issue #772)
@ -41,7 +103,7 @@ v6.2.0-beta
> object["values"] = serialized("[1,2,3,4]");
> ```
v6.1.0-beta
v6.1.0-beta (2018-07-02)
-----------
* Return `JsonArray` and `JsonObject` by value instead of reference (issue #309)
@ -71,12 +133,12 @@ v6.1.0-beta
> }
> ```
v6.0.1-beta
v6.0.1-beta (2018-06-11)
-----------
* Fixed conflicts with `isnan()` and `isinf()` macros (issue #752)
v6.0.0-beta
v6.0.0-beta (2018-06-07)
-----------
* Added `DynamicJsonDocument` and `StaticJsonDocument`

View File

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

View File

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

49
scripts/publish.sh Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -eu
cd "$(dirname "$0")/.."
VERSION="$1"
DATE=$(date +%F)
TAG="v$VERSION"
update_version_in_source () {
IFS=".-" read MAJOR MINOR REVISION EXTRA < <(echo "$VERSION")
UNDERLINE=$(printf -- '-%.0s' $(seq 1 ${#TAG}))
sed -i~ -bE "4s/HEAD/$TAG ($DATE)/; 5s/-+/$UNDERLINE/" CHANGELOG.md
rm CHANGELOG.md*~
sed -i~ -bE "s/\"version\":.*$/\"version\": \"$VERSION\",/" library.json
rm library.json*~
sed -i~ -bE "s/version=.*$/version=$VERSION/" library.properties
rm library.properties*~
sed -i~ -bE \
-e "s/ARDUINOJSON_VERSION .*$/ARDUINOJSON_VERSION \"$VERSION\"/" \
-e "s/ARDUINOJSON_VERSION_MAJOR .*$/ARDUINOJSON_VERSION_MAJOR $MAJOR/" \
-e "s/ARDUINOJSON_VERSION_MINOR .*$/ARDUINOJSON_VERSION_MINOR $MINOR/" \
-e "s/ARDUINOJSON_VERSION_REVISION .*$/ARDUINOJSON_VERSION_REVISION $REVISION/" \
src/ArduinoJson/version.hpp
rm src/ArduinoJson/version.hpp*~
}
commit_new_version () {
git add src/ArduinoJson/version.hpp CHANGELOG.md library.json library.properties
git commit -m "Set version to $VERSION"
}
add_tag () {
CHANGES=$(awk '/\* /{ FOUND=1; print; next } { if (FOUND) exit}' CHANGELOG.md)
git tag -m "ArduinoJson $VERSION"$'\n'"$CHANGES" "$TAG"
}
push () {
git push --follow-tags
}
update_version_in_source
commit_new_version
add_tag
push

View File

@ -4,27 +4,41 @@
#pragma once
#include <stdlib.h> // size_t
#include "JsonFloat.hpp"
#include "JsonInteger.hpp"
namespace ArduinoJson {
namespace Internals {
// Forward declarations
struct JsonArrayData;
struct JsonObjectData;
struct JsonObjectData {
struct Slot* head;
struct Slot* tail;
};
// A union that defines the actual content of a JsonVariant.
struct JsonArrayData {
struct Slot* head;
struct Slot* tail;
};
struct RawData {
const char* data;
size_t size;
};
// A union that defines the actual content of a JsonVariantData.
// The enum JsonVariantType determines which member is in use.
union JsonVariantContent {
JsonFloat asFloat; // used for double and float
JsonUInt asInteger; // used for bool, char, short, int and longs
JsonArrayData* asArray; // asArray cannot be null
JsonObjectData* asObject; // asObject cannot be null
const char* asString; // asString can be null
JsonFloat asFloat;
JsonUInt asInteger;
JsonArrayData asArray;
JsonObjectData asObject;
const char* asString;
struct {
const char* data;
size_t size;
} asRaw;
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -0,0 +1,161 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Numbers/parseFloat.hpp"
#include "../Numbers/parseInteger.hpp"
#include "JsonVariantContent.hpp"
#include "JsonVariantType.hpp"
namespace ArduinoJson {
namespace Internals {
struct JsonVariantData {
JsonVariantType type;
JsonVariantContent content;
JsonVariantData() {
type = JSON_NULL;
}
void setBoolean(bool value) {
type = JSON_BOOLEAN;
content.asInteger = static_cast<JsonUInt>(value);
}
void setFloat(JsonFloat value) {
type = JSON_FLOAT;
content.asFloat = value;
}
void setNegativeInteger(JsonUInt value) {
type = JSON_NEGATIVE_INTEGER;
content.asInteger = value;
}
void setPostiveInteger(JsonUInt value) {
type = JSON_POSITIVE_INTEGER;
content.asInteger = value;
}
void setOwnedString(const char *value) {
type = JSON_OWNED_STRING;
content.asString = value;
}
void setLinkedString(const char *value) {
type = JSON_LINKED_STRING;
content.asString = value;
}
void setOwnedRaw(const char *data, size_t size) {
type = JSON_OWNED_RAW;
content.asRaw.data = data;
content.asRaw.size = size;
}
void setLinkedRaw(const char *data, size_t size) {
type = JSON_LINKED_RAW;
content.asRaw.data = data;
content.asRaw.size = size;
}
void setNull() {
type = JSON_NULL;
}
JsonArrayData *toArray() {
type = JSON_ARRAY;
content.asArray.head = 0;
content.asArray.tail = 0;
return &content.asArray;
}
JsonObjectData *toObject() {
type = JSON_OBJECT;
content.asObject.head = 0;
content.asObject.tail = 0;
return &content.asObject;
}
JsonArrayData *asArray() {
return type == JSON_ARRAY ? &content.asArray : 0;
}
JsonObjectData *asObject() {
return type == JSON_OBJECT ? &content.asObject : 0;
}
template <typename T>
T asInteger() const {
switch (type) {
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return T(content.asInteger);
case JSON_NEGATIVE_INTEGER:
return T(~content.asInteger + 1);
case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return parseInteger<T>(content.asString);
case JSON_FLOAT:
return T(content.asFloat);
default:
return 0;
}
}
template <typename T>
T asFloat() const {
switch (type) {
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return static_cast<T>(content.asInteger);
case JSON_NEGATIVE_INTEGER:
return -static_cast<T>(content.asInteger);
case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return parseFloat<T>(content.asString);
case JSON_FLOAT:
return static_cast<T>(content.asFloat);
default:
return 0;
}
}
const char *asString() const {
return isString() ? content.asString : NULL;
}
bool isArray() const {
return type == JSON_ARRAY;
}
bool isBoolean() const {
return type == JSON_BOOLEAN;
}
bool isFloat() const {
return type == JSON_FLOAT || type == JSON_POSITIVE_INTEGER ||
type == JSON_NEGATIVE_INTEGER;
}
bool isInteger() const {
return type == JSON_POSITIVE_INTEGER || type == JSON_NEGATIVE_INTEGER;
}
bool isNull() const {
return type == JSON_NULL;
}
bool isObject() const {
return type == JSON_OBJECT;
}
bool isString() const {
return type == JSON_LINKED_STRING || type == JSON_OWNED_STRING;
}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -1,23 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
namespace ArduinoJson {
namespace Internals {
template <typename T>
struct JsonVariantDefault {
static T get() {
return T();
}
};
template <typename T>
struct JsonVariantDefault<const T> : JsonVariantDefault<T> {};
template <typename T>
struct JsonVariantDefault<T&> : JsonVariantDefault<T> {};
}
}

View File

@ -0,0 +1,33 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
namespace ArduinoJson {
class JsonArray;
class JsonObject;
class JsonVariant;
namespace Internals {
// A metafunction that returns the type of the value returned by
// JsonVariant::to<T>()
template <typename T>
struct JsonVariantTo {};
template <>
struct JsonVariantTo<JsonArray> {
typedef JsonArray type;
};
template <>
struct JsonVariantTo<JsonObject> {
typedef JsonObject type;
};
template <>
struct JsonVariantTo<JsonVariant> {
typedef JsonVariant type;
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -11,15 +11,17 @@ namespace Internals {
// Enumerated type to know the current type of a JsonVariant.
// The value determines which member of JsonVariantContent is used.
enum JsonVariantType {
JSON_UNDEFINED, // JsonVariant has not been initialized
JSON_UNPARSED, // JsonVariant contains an unparsed string
JSON_STRING, // JsonVariant stores a const char*
JSON_BOOLEAN, // JsonVariant stores a bool
JSON_POSITIVE_INTEGER, // JsonVariant stores an JsonUInt
JSON_NEGATIVE_INTEGER, // JsonVariant stores an JsonUInt that must be negated
JSON_ARRAY, // JsonVariant stores a pointer to a JsonArrayData
JSON_OBJECT, // JsonVariant stores a pointer to a JsonObjectData
JSON_FLOAT // JsonVariant stores a JsonFloat
JSON_NULL,
JSON_LINKED_RAW,
JSON_OWNED_RAW,
JSON_LINKED_STRING,
JSON_OWNED_STRING,
JSON_BOOLEAN,
JSON_POSITIVE_INTEGER,
JSON_NEGATIVE_INTEGER,
JSON_ARRAY,
JSON_OBJECT,
JSON_FLOAT
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -1,88 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Memory/JsonBuffer.hpp"
#include "ListConstIterator.hpp"
#include "ListIterator.hpp"
namespace ArduinoJson {
namespace Internals {
// A singly linked list of T.
// The linked list is composed of ListNode<T>.
// It is derived by JsonArrayData and JsonObjectData
template <typename T>
class List {
public:
typedef T value_type;
typedef ListNode<T> node_type;
typedef ListIterator<T> iterator;
typedef ListConstIterator<T> const_iterator;
explicit List(JsonBuffer *buf) : _buffer(buf), _firstNode(NULL) {}
// Returns the numbers of elements in the list.
// For a JsonObjectData, it would return the number of key-value pairs
size_t size() const {
size_t nodeCount = 0;
for (node_type *node = _firstNode; node; node = node->next) nodeCount++;
return nodeCount;
}
iterator add() {
node_type *newNode = new (_buffer) node_type();
if (_firstNode) {
node_type *lastNode = _firstNode;
while (lastNode->next) lastNode = lastNode->next;
lastNode->next = newNode;
} else {
_firstNode = newNode;
}
return iterator(newNode);
}
iterator begin() {
return iterator(_firstNode);
}
iterator end() {
return iterator(NULL);
}
const_iterator begin() const {
return const_iterator(_firstNode);
}
const_iterator end() const {
return const_iterator(NULL);
}
void remove(iterator it) {
node_type *nodeToRemove = it._node;
if (!nodeToRemove) return;
if (nodeToRemove == _firstNode) {
_firstNode = nodeToRemove->next;
} else {
for (node_type *node = _firstNode; node; node = node->next)
if (node->next == nodeToRemove) node->next = nodeToRemove->next;
}
}
JsonBuffer &buffer() const {
return *_buffer;
}
JsonBuffer *_buffer; // TODO!!
protected:
void clear() {
_firstNode = 0;
}
private:
node_type *_firstNode;
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -1,50 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "ListNode.hpp"
namespace ArduinoJson {
namespace Internals {
// A read-only forward itertor for List<T>
template <typename T>
class ListConstIterator {
public:
explicit ListConstIterator(const ListNode<T> *node = NULL) : _node(node) {}
const T &operator*() const {
return _node->content;
}
const T *operator->() {
return &_node->content;
}
bool operator==(const ListConstIterator<T> &other) const {
return _node == other._node;
}
bool operator!=(const ListConstIterator<T> &other) const {
return _node != other._node;
}
ListConstIterator<T> &operator++() {
if (_node) _node = _node->next;
return *this;
}
ListConstIterator<T> &operator+=(size_t distance) {
while (_node && distance) {
_node = _node->next;
--distance;
}
return *this;
}
private:
const ListNode<T> *_node;
};
}
}

View File

@ -1,60 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "ListConstIterator.hpp"
#include "ListNode.hpp"
namespace ArduinoJson {
namespace Internals {
template <typename T>
class List;
// A read-write forward iterator for List<T>
template <typename T>
class ListIterator {
friend class List<T>;
public:
explicit ListIterator(ListNode<T> *node = NULL) : _node(node) {}
T &operator*() const {
return _node->content;
}
T *operator->() {
return &_node->content;
}
bool operator==(const ListIterator<T> &other) const {
return _node == other._node;
}
bool operator!=(const ListIterator<T> &other) const {
return _node != other._node;
}
ListIterator<T> &operator++() {
if (_node) _node = _node->next;
return *this;
}
ListIterator<T> &operator+=(size_t distance) {
while (_node && distance) {
_node = _node->next;
--distance;
}
return *this;
}
operator ListConstIterator<T>() const {
return ListConstIterator<T>(_node);
}
private:
ListNode<T> *_node;
};
}
}

View File

@ -1,24 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include <stddef.h> // for NULL
#include "../Memory/JsonBufferAllocated.hpp"
namespace ArduinoJson {
namespace Internals {
// A node for a singly-linked list.
// Used by List<T> and its iterators.
template <typename T>
struct ListNode : public Internals::JsonBufferAllocated {
ListNode() throw() : next(NULL) {}
ListNode<T> *next;
T content;
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -0,0 +1,21 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Memory/AllocableInMemoryPool.hpp"
#include "JsonVariantData.hpp"
namespace ArduinoJson {
namespace Internals {
struct Slot : AllocableInMemoryPool {
JsonVariantData value;
struct Slot* next;
struct Slot* prev;
const char* key;
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -1,53 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../JsonVariant.hpp"
#include "../Memory/JsonBuffer.hpp"
#include "../Polyfills/type_traits.hpp"
#include "../Strings/StringTypes.hpp"
namespace ArduinoJson {
namespace Internals {
template <typename Source, typename Enable = void>
struct ValueSaver {
template <typename Destination>
static bool save(JsonBuffer*, Destination& destination, Source source) {
destination = source;
return true;
}
};
// We duplicate all strings except const char*
template <typename TString>
struct ValueSaver<
TString, typename enable_if<IsString<TString>::value &&
!is_same<const char*, TString>::value>::type> {
template <typename Destination>
static bool save(JsonBuffer* buffer, Destination& dest, TString source) {
const char* dup = makeString(source).save(buffer);
if (!dup) return false;
dest = dup;
return true;
}
};
// We duplicate all SerializedValue<T> except SerializedValue<const char*>
template <typename TString>
struct ValueSaver<
const SerializedValue<TString>&,
typename enable_if<!is_same<const char*, TString>::value>::type> {
template <typename Destination>
static bool save(JsonBuffer* buffer, Destination& dest,
const SerializedValue<TString>& source) {
const char* dup = makeString(source.data(), source.size()).save(buffer);
if (!dup) return false;
dest = SerializedValue<const char*>(dup, source.size());
return true;
}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -16,11 +16,12 @@ namespace ArduinoJson {
namespace Internals {
template <template <typename, typename> class TDeserializer,
typename TJsonBuffer, typename TReader, typename TWriter>
TDeserializer<TReader, TWriter> makeDeserializer(TJsonBuffer *buffer,
typename TMemoryPool, typename TReader, typename TWriter>
TDeserializer<TReader, TWriter> makeDeserializer(TMemoryPool &memoryPool,
TReader reader, TWriter writer,
uint8_t nestingLimit) {
return TDeserializer<TReader, TWriter>(buffer, reader, writer, nestingLimit);
return TDeserializer<TReader, TWriter>(memoryPool, reader, writer,
nestingLimit);
}
// DeserializationError deserialize(TDocument& doc, TString input);
@ -32,9 +33,9 @@ typename Internals::enable_if<!Internals::is_array<TString>::value,
DeserializationError>::type
deserialize(TDocument &doc, const TString &input) {
using namespace Internals;
return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input),
makeStringStorage(doc.buffer(), input),
doc.nestingLimit)
return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariant>());
}
//
@ -45,9 +46,9 @@ template <template <typename, typename> class TDeserializer, typename TDocument,
typename TChar>
DeserializationError deserialize(TDocument &doc, TChar *input) {
using namespace Internals;
return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input),
makeStringStorage(doc.buffer(), input),
doc.nestingLimit)
return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariant>());
}
//
@ -61,8 +62,8 @@ DeserializationError deserialize(TDocument &doc, TChar *input,
size_t inputSize) {
using namespace Internals;
return makeDeserializer<TDeserializer>(
&doc.buffer(), makeReader(input, inputSize),
makeStringStorage(doc.buffer(), input), doc.nestingLimit)
doc.memoryPool(), makeReader(input, inputSize),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariant>());
}
//
@ -73,9 +74,9 @@ template <template <typename, typename> class TDeserializer, typename TDocument,
typename TStream>
DeserializationError deserialize(TDocument &doc, TStream &input) {
using namespace Internals;
return makeDeserializer<TDeserializer>(&doc.buffer(), makeReader(input),
makeStringStorage(doc.buffer(), input),
doc.nestingLimit)
return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariant>());
}
} // namespace Internals

View File

@ -4,81 +4,61 @@
#pragma once
#include "JsonArray.hpp"
#include "JsonObject.hpp"
#include "Data/JsonVariantTo.hpp"
#include "JsonVariant.hpp"
#include "Memory/DynamicJsonBuffer.hpp"
#include "Memory/DynamicMemoryPool.hpp"
namespace ArduinoJson {
class DynamicJsonDocument {
Internals::DynamicJsonBuffer _buffer;
JsonVariant _root;
public:
uint8_t nestingLimit;
DynamicJsonDocument() : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
DynamicJsonDocument(size_t capacity)
: _buffer(capacity), nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
: nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT),
_memoryPool(capacity) {}
template <typename T>
bool is() const {
return _root.is<T>();
return getVariant().is<T>();
}
template <typename T>
typename Internals::JsonVariantAs<T>::type as() const {
return _root.as<T>();
return getVariant().as<T>();
}
// JsonObject to<JsonObject>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonObject>::value,
JsonObject>::type
to() {
clear();
JsonObject object(&_buffer);
_root = object;
return object;
}
// JsonArray to<JsonArray>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonArray>::value,
JsonArray>::type
to() {
clear();
JsonArray array(&_buffer);
_root = array;
return array;
}
// JsonVariant& to<JsonVariant>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
T&>::type
to() {
clear();
return _root;
}
Internals::DynamicJsonBuffer& buffer() {
return _buffer;
typename Internals::JsonVariantTo<T>::type to() {
_memoryPool.clear();
return getVariant().to<T>();
}
void clear() {
_buffer.clear();
_root = JsonVariant();
_memoryPool.clear();
_rootData.setNull();
}
size_t memoryUsage() const {
return _buffer.size();
return _memoryPool.size();
}
template <typename Visitor>
void visit(Visitor& visitor) const {
return _root.visit(visitor);
void accept(Visitor& visitor) const {
return getVariant().accept(visitor);
}
Internals::DynamicMemoryPool& memoryPool() {
return _memoryPool;
}
private:
JsonVariant getVariant() const {
return JsonVariant(&_memoryPool, &_rootData);
}
mutable Internals::DynamicMemoryPool _memoryPool;
mutable Internals::JsonVariantData _rootData;
};
} // namespace ArduinoJson

View File

@ -6,7 +6,9 @@
#include "../Deserialization/deserialize.hpp"
#include "../JsonVariant.hpp"
#include "../Memory/JsonBuffer.hpp"
#include "../Memory/MemoryPool.hpp"
#include "../Numbers/isFloat.hpp"
#include "../Numbers/isInteger.hpp"
#include "../Polyfills/type_traits.hpp"
#include "./EscapeSequence.hpp"
@ -16,14 +18,14 @@ namespace Internals {
template <typename TReader, typename TStringStorage>
class JsonDeserializer {
public:
JsonDeserializer(JsonBuffer *buffer, TReader reader,
JsonDeserializer(MemoryPool &memoryPool, TReader reader,
TStringStorage stringStorage, uint8_t nestingLimit)
: _buffer(buffer),
: _memoryPool(&memoryPool),
_reader(reader),
_stringStorage(stringStorage),
_nestingLimit(nestingLimit),
_loaded(false) {}
DeserializationError parse(JsonVariant &variant) {
DeserializationError parse(JsonVariant variant) {
DeserializationError err = skipSpacesAndComments();
if (err) return err;
@ -63,12 +65,11 @@ class JsonDeserializer {
return true;
}
DeserializationError parseArray(JsonVariant &variant) {
DeserializationError parseArray(JsonVariant variant) {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
JsonArray array(_buffer);
JsonArray array = variant.to<JsonArray>();
if (array.isNull()) return DeserializationError::NoMemory;
variant = array;
// Check opening braket
if (!eat('[')) return DeserializationError::InvalidInput;
@ -82,13 +83,15 @@ class JsonDeserializer {
// Read each value
for (;;) {
// Allocate slot in array
JsonVariant value = array.add();
if (value.isInvalid()) return DeserializationError::NoMemory;
// 1 - Parse value
JsonVariant value;
_nestingLimit--;
err = parse(value);
_nestingLimit++;
if (err) return err;
if (!array.add(value)) return DeserializationError::NoMemory;
// 2 - Skip spaces
err = skipSpacesAndComments();
@ -100,12 +103,11 @@ class JsonDeserializer {
}
}
DeserializationError parseObject(JsonVariant &variant) {
DeserializationError parseObject(JsonVariant variant) {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
JsonObject object(_buffer);
JsonObject object = variant.to<JsonObject>();
if (object.isNull()) return DeserializationError::NoMemory;
variant = object;
// Check opening brace
if (!eat('{')) return DeserializationError::InvalidInput;
@ -129,13 +131,15 @@ class JsonDeserializer {
if (err) return err; // Colon
if (!eat(':')) return DeserializationError::InvalidInput;
// Allocate slot in object
JsonVariant value = object.set(key);
if (value.isInvalid()) return DeserializationError::NoMemory;
// Parse value
JsonVariant value;
_nestingLimit--;
err = parse(value);
_nestingLimit++;
if (err) return err;
if (!object.set(key, value)) return DeserializationError::NoMemory;
// Skip spaces
err = skipSpacesAndComments();
@ -151,7 +155,7 @@ class JsonDeserializer {
}
}
DeserializationError parseValue(JsonVariant &variant) {
DeserializationError parseValue(JsonVariant variant) {
if (isQuote(current())) {
return parseStringValue(variant);
} else {
@ -167,11 +171,11 @@ class JsonDeserializer {
}
}
DeserializationError parseStringValue(JsonVariant &variant) {
DeserializationError parseStringValue(JsonVariant variant) {
const char *value;
DeserializationError err = parseQuotedString(&value);
if (err) return err;
variant = value;
variant.set(value);
return DeserializationError::Ok;
}
@ -229,7 +233,7 @@ class JsonDeserializer {
return DeserializationError::Ok;
}
DeserializationError parseNumericValue(JsonVariant &result) {
DeserializationError parseNumericValue(JsonVariant result) {
char buffer[64];
uint8_t n = 0;
@ -242,15 +246,15 @@ class JsonDeserializer {
buffer[n] = 0;
if (isInteger(buffer)) {
result = parseInteger<JsonInteger>(buffer);
result.set(parseInteger<JsonInteger>(buffer));
} else if (isFloat(buffer)) {
result = parseFloat<JsonFloat>(buffer);
result.set(parseFloat<JsonFloat>(buffer));
} else if (!strcmp(buffer, "true")) {
result = true;
result.set(true);
} else if (!strcmp(buffer, "false")) {
result = false;
result.set(false);
} else if (!strcmp(buffer, "null")) {
result = static_cast<const char *>(0);
// already null
} else {
return DeserializationError::InvalidInput;
}
@ -329,7 +333,7 @@ class JsonDeserializer {
}
}
JsonBuffer *_buffer;
MemoryPool *_memoryPool;
TReader _reader;
TStringStorage _stringStorage;
uint8_t _nestingLimit;

View File

@ -16,16 +16,16 @@ class JsonSerializer {
public:
JsonSerializer(TWriter &writer) : _writer(writer) {}
void acceptFloat(JsonFloat value) {
void visitFloat(JsonFloat value) {
_writer.writeFloat(value);
}
void acceptArray(const JsonArray &array) {
void visitArray(JsonArray array) {
_writer.beginArray();
JsonArray::const_iterator it = array.begin();
JsonArray::iterator it = array.begin();
while (it != array.end()) {
it->visit(*this);
it->accept(*this);
++it;
if (it == array.end()) break;
@ -36,14 +36,14 @@ class JsonSerializer {
_writer.endArray();
}
void acceptObject(const JsonObject &object) {
void visitObject(JsonObject object) {
_writer.beginObject();
JsonObject::const_iterator it = object.begin();
JsonObject::iterator it = object.begin();
while (it != object.end()) {
_writer.writeString(it->key);
_writer.writeString(it->key());
_writer.writeColon();
it->value.visit(*this);
it->value().accept(*this);
++it;
if (it == object.end()) break;
@ -54,29 +54,29 @@ class JsonSerializer {
_writer.endObject();
}
void acceptString(const char *value) {
void visitString(const char *value) {
_writer.writeString(value);
}
void acceptRawJson(const char *data, size_t n) {
void visitRawJson(const char *data, size_t n) {
// TODO
for (size_t i = 0; i < n; i++) _writer.writeRaw(data[i]);
}
void acceptNegativeInteger(JsonUInt value) {
void visitNegativeInteger(JsonUInt value) {
_writer.writeRaw('-');
_writer.writeInteger(value);
}
void acceptPositiveInteger(JsonUInt value) {
void visitPositiveInteger(JsonUInt value) {
_writer.writeInteger(value);
}
void acceptBoolean(bool value) {
void visitBoolean(bool value) {
_writer.writeBoolean(value);
}
void acceptNull() {
void visitNull() {
_writer.writeRaw("null");
}
@ -91,7 +91,7 @@ class JsonSerializer {
} // namespace Internals
template <typename TSource, typename TDestination>
size_t serializeJson(TSource &source, TDestination &destination) {
size_t serializeJson(const TSource &source, TDestination &destination) {
using namespace Internals;
return serialize<JsonSerializer>(source, destination);
}

View File

@ -4,12 +4,17 @@
#pragma once
#include "./JsonArrayData.hpp"
#include "Data/JsonVariantData.hpp"
#include "JsonArrayIterator.hpp"
// Returns the size (in bytes) of an array with n elements.
// Can be very handy to determine the size of a StaticMemoryPool.
#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \
((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::Internals::Slot))
namespace ArduinoJson {
class JsonObject;
namespace Internals {
class JsonArraySubscript;
}
@ -18,52 +23,67 @@ class JsonArray {
friend class JsonVariant;
public:
typedef Internals::JsonArrayData::iterator iterator;
typedef Internals::JsonArrayData::const_iterator const_iterator;
typedef JsonArrayIterator iterator;
JsonArray() : _data(0) {}
JsonArray(Internals::JsonArrayData* arr) : _data(arr) {}
JsonArray(Internals::JsonBuffer* buf)
: _data(new (buf) Internals::JsonArrayData(buf)) {}
FORCE_INLINE JsonArray() : _memoryPool(0), _data(0) {}
FORCE_INLINE JsonArray(Internals::MemoryPool* buf,
Internals::JsonArrayData* arr)
: _memoryPool(buf), _data(arr) {}
// Adds the specified value at the end of the array.
//
// bool add(TValue);
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
// std::string, String, JsonArrayData, JsonObject
// std::string, String, JsonObject
template <typename T>
bool add(const T& value) {
return add_impl<const T&>(value);
FORCE_INLINE bool add(const T& value) {
return add().set(value);
}
// Adds the specified value at the end of the array.
FORCE_INLINE bool add(JsonArray value) {
return add().set(value);
}
//
// bool add(TValue);
// TValue = char*, const char*, const FlashStringHelper*
template <typename T>
bool add(T* value) {
return add_impl<T*>(value);
FORCE_INLINE bool add(T* value) {
return add().set(value);
}
iterator begin() {
JsonVariant add() {
if (!_data) return JsonVariant();
Internals::Slot* slot = new (_memoryPool) Internals::Slot();
if (!slot) return JsonVariant();
slot->next = 0;
if (_data->tail) {
slot->prev = _data->tail;
_data->tail->next = slot;
_data->tail = slot;
} else {
slot->prev = 0;
_data->head = slot;
_data->tail = slot;
}
return JsonVariant(_memoryPool, &slot->value);
}
FORCE_INLINE iterator begin() const {
if (!_data) return iterator();
return _data->begin();
return iterator(_memoryPool, _data->head);
}
const_iterator begin() const {
if (!_data) return const_iterator();
return _data->begin();
}
iterator end() {
FORCE_INLINE iterator end() const {
return iterator();
}
const_iterator end() const {
return const_iterator();
}
// Imports a 1D array
template <typename T, size_t N>
bool copyFrom(T (&array)[N]) {
FORCE_INLINE bool copyFrom(T (&array)[N]) {
return copyFrom(array, N);
}
@ -90,9 +110,18 @@ class JsonArray {
return ok;
}
// Copy a JsonArray
bool copyFrom(JsonArray src) {
bool ok = _data != 0;
for (iterator it = src.begin(); it != src.end(); ++it) {
ok &= add(*it);
}
return ok;
}
// Exports a 1D array
template <typename T, size_t N>
size_t copyTo(T (&array)[N]) const {
FORCE_INLINE size_t copyTo(T (&array)[N]) const {
return copyTo(array, N);
}
@ -100,8 +129,7 @@ class JsonArray {
template <typename T>
size_t copyTo(T* array, size_t len) const {
size_t i = 0;
for (const_iterator it = begin(); it != end() && i < len; ++it)
array[i++] = *it;
for (iterator it = begin(); it != end() && i < len; ++it) array[i++] = *it;
return i;
}
@ -110,44 +138,66 @@ class JsonArray {
void copyTo(T (&array)[N1][N2]) const {
if (!_data) return;
size_t i = 0;
for (const_iterator it = begin(); it != end() && i < N1; ++it) {
for (iterator it = begin(); it != end() && i < N1; ++it) {
it->as<JsonArray>().copyTo(array[i++]);
}
}
JsonArray createNestedArray();
JsonObject createNestedObject();
FORCE_INLINE JsonArray createNestedArray();
FORCE_INLINE JsonObject createNestedObject();
Internals::JsonArraySubscript operator[](size_t index);
FORCE_INLINE Internals::JsonArraySubscript operator[](size_t index);
const Internals::JsonArraySubscript operator[](size_t index) const;
FORCE_INLINE const Internals::JsonArraySubscript operator[](
size_t index) const;
bool operator==(const JsonArray& rhs) const {
return _data == rhs._data;
FORCE_INLINE bool operator==(JsonArray rhs) const {
iterator it1 = begin();
iterator it2 = rhs.begin();
for (;;) {
if (it1 == end() && it2 == rhs.end()) return true;
if (it1 == end()) return false;
if (it2 == end()) return false;
if (*it1 != *it2) return false;
++it1;
++it2;
}
}
// Gets the value at the specified index.
template <typename T>
typename Internals::JsonVariantAs<T>::type get(size_t index) const {
const_iterator it = begin() += index;
return it != end() ? it->as<T>() : Internals::JsonVariantDefault<T>::get();
FORCE_INLINE typename Internals::JsonVariantAs<T>::type get(
size_t index) const {
iterator it = begin() += index;
return it != end() ? it->as<T>() : T();
}
// Check the type of the value at specified index.
template <typename T>
bool is(size_t index) const {
const_iterator it = begin() += index;
FORCE_INLINE bool is(size_t index) const {
iterator it = begin() += index;
return it != end() ? it->is<T>() : false;
}
// Removes element at specified position.
void remove(iterator it) {
FORCE_INLINE void remove(iterator it) {
if (!_data) return;
_data->remove(it);
Internals::Slot* slot = it.internal();
if (!slot) return;
if (slot->prev)
slot->prev->next = slot->next;
else
_data->head = slot->next;
if (slot->next)
slot->next->prev = slot->prev;
else
_data->tail = slot->prev;
}
// Removes element at specified index.
void remove(size_t index) {
FORCE_INLINE void remove(size_t index) {
remove(begin() += index);
}
@ -157,7 +207,7 @@ class JsonArray {
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
// std::string, String, JsonArrayData, JsonObject
template <typename T>
bool set(size_t index, const T& value) {
FORCE_INLINE bool set(size_t index, const T& value) {
if (!_data) return false;
return set_impl<const T&>(index, value);
}
@ -165,44 +215,56 @@ class JsonArray {
// bool add(size_t index, TValue);
// TValue = char*, const char*, const FlashStringHelper*
template <typename T>
bool set(size_t index, T* value) {
FORCE_INLINE bool set(size_t index, T* value) {
if (!_data) return false;
return set_impl<T*>(index, value);
}
size_t size() const {
if (!_data) return 0;
return _data->size();
// Sets the value at specified index.
//
// bool add(size_t index, JsonArray);
template <typename T>
FORCE_INLINE bool set(size_t index, JsonArray value) {
if (!_data) return false;
return get<JsonVariant>(index).set(value);
}
bool isNull() const {
FORCE_INLINE size_t size() const {
if (!_data) return 0;
Internals::Slot* slot = _data->head;
size_t n = 0;
while (slot) {
slot = slot->next;
n++;
}
return n;
}
FORCE_INLINE bool isNull() const {
return _data == 0;
}
template <typename Visitor>
void visit(Visitor& visitor) const {
FORCE_INLINE void accept(Visitor& visitor) const {
if (_data)
return visitor.acceptArray(*this);
visitor.visitArray(*this);
else
visitor.acceptNull();
visitor.visitNull();
}
private:
template <typename TValueRef>
bool set_impl(size_t index, TValueRef value) {
FORCE_INLINE bool set_impl(size_t index, TValueRef value) {
iterator it = begin() += index;
if (it == end()) return false;
return Internals::ValueSaver<TValueRef>::save(_data->_buffer, *it, value);
return it->set(value);
}
template <typename TValueRef>
bool add_impl(TValueRef value) {
if (!_data) return false;
iterator it = _data->add();
if (it == end()) return false;
return Internals::ValueSaver<TValueRef>::save(_data->_buffer, *it, value);
FORCE_INLINE bool add_impl(TValueRef value) {
return add().set(value);
}
Internals::MemoryPool* _memoryPool;
Internals::JsonArrayData* _data;
};
} // namespace ArduinoJson

View File

@ -1,26 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/List.hpp"
#include "Data/ValueSaver.hpp"
#include "JsonVariant.hpp"
#include "Memory/JsonBufferAllocated.hpp"
#include "Polyfills/type_traits.hpp"
// Returns the size (in bytes) of an array with n elements.
// Can be very handy to determine the size of a StaticJsonBuffer.
#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \
(sizeof(ArduinoJson::Internals::JsonArrayData) + \
(NUMBER_OF_ELEMENTS) * \
sizeof(ArduinoJson::Internals::JsonArrayData::node_type))
namespace ArduinoJson {
namespace Internals {
struct JsonArrayData : List<JsonVariant>, JsonBufferAllocated {
explicit JsonArrayData(JsonBuffer *buf) throw() : List<JsonVariant>(buf) {}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -10,16 +10,10 @@
namespace ArduinoJson {
inline JsonArray JsonArray::createNestedArray() {
if (!_data) return JsonArray();
JsonArray array(_data->_buffer);
if (!array.isNull()) add(array);
return array;
return add().to<JsonArray>();
}
inline JsonObject JsonArray::createNestedObject() {
if (!_data) return JsonObject();
JsonObject object(_data->_buffer);
if (!object.isNull()) add(object);
return object;
return add().to<JsonObject>();
}
} // namespace ArduinoJson

View File

@ -0,0 +1,73 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/Slot.hpp"
#include "JsonVariant.hpp"
namespace ArduinoJson {
class JsonVariantPtr {
public:
JsonVariantPtr(Internals::MemoryPool *memoryPool,
Internals::JsonVariantData *data)
: _variant(memoryPool, data) {}
JsonVariant *operator->() {
return &_variant;
}
JsonVariant &operator*() {
return _variant;
}
private:
JsonVariant _variant;
};
class JsonArrayIterator {
public:
JsonArrayIterator() : _slot(0) {}
explicit JsonArrayIterator(Internals::MemoryPool *memoryPool,
Internals::Slot *iterator)
: _memoryPool(memoryPool), _slot(iterator) {}
JsonVariant operator*() const {
return JsonVariant(_memoryPool, &_slot->value);
}
JsonVariantPtr operator->() {
return JsonVariantPtr(_memoryPool, &_slot->value);
}
bool operator==(const JsonArrayIterator &other) const {
return _slot == other._slot;
}
bool operator!=(const JsonArrayIterator &other) const {
return _slot != other._slot;
}
JsonArrayIterator &operator++() {
_slot = _slot->next;
return *this;
}
JsonArrayIterator &operator+=(size_t distance) {
while (distance && _slot) {
_slot = _slot->next;
distance--;
}
return *this;
}
Internals::Slot *internal() {
return _slot;
}
private:
Internals::MemoryPool *_memoryPool;
Internals::Slot *_slot;
};
} // namespace ArduinoJson

View File

@ -20,7 +20,7 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
: _array(array), _index(index) {}
FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) {
_array.set(_index, src);
_array.set(_index, src.as<JsonVariant>());
return *this;
}
@ -57,6 +57,11 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
return _array.is<T>(_index);
}
template <typename T>
FORCE_INLINE typename JsonVariantTo<T>::type to() {
return _array.get<JsonVariant>(_index).to<T>();
}
// Replaces the value
//
// bool set(const TValue&)
@ -75,8 +80,8 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
}
template <typename Visitor>
void visit(Visitor& visitor) const {
return _array.get<JsonVariant>(_index).visit(visitor);
void accept(Visitor& visitor) const {
return _array.get<JsonVariant>(_index).accept(visitor);
}
private:

View File

@ -4,7 +4,12 @@
#pragma once
#include "./JsonObjectData.hpp"
#include "./JsonObjectIterator.hpp"
// Returns the size (in bytes) of an object with n elements.
// Can be very handy to determine the size of a StaticMemoryPool.
#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \
((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::Internals::Slot))
namespace ArduinoJson {
@ -12,22 +17,21 @@ class JsonObject {
friend class JsonVariant;
public:
typedef Internals::JsonObjectData::iterator iterator;
typedef Internals::JsonObjectData::const_iterator const_iterator;
typedef JsonObjectIterator iterator;
JsonObject() : _data(0) {}
JsonObject(Internals::JsonObjectData* object) : _data(object) {}
JsonObject(Internals::JsonBuffer* buf)
: _data(new (buf) Internals::JsonObjectData(buf)) {}
FORCE_INLINE JsonObject() : _memoryPool(0), _data(0) {}
FORCE_INLINE JsonObject(Internals::MemoryPool* buf,
Internals::JsonObjectData* object)
: _memoryPool(buf), _data(object) {}
iterator begin() {
FORCE_INLINE iterator begin() const {
if (!_data) return iterator();
return _data->begin();
return iterator(_memoryPool, _data->head);
}
const_iterator begin() const {
if (!_data) return const_iterator();
return _data->begin();
void clear() {
_data->head = 0;
_data->tail = 0;
}
// Tells weither the specified key is present and associated with a value.
@ -35,23 +39,28 @@ class JsonObject {
// bool containsKey(TKey);
// TKey = const std::string&, const String&
template <typename TString>
bool containsKey(const TString& key) const {
FORCE_INLINE bool containsKey(const TString& key) const {
return containsKey_impl<const TString&>(key);
}
//
// bool containsKey(TKey);
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
bool containsKey(TString* key) const {
FORCE_INLINE bool containsKey(TString* key) const {
return containsKey_impl<TString*>(key);
}
iterator end() {
return iterator();
bool copyFrom(JsonObject src) {
bool ok = _data != 0;
clear();
for (iterator it = src.begin(); it != src.end(); ++it) {
ok &= set(it->key(), it->value());
}
return ok;
}
const_iterator end() const {
return const_iterator();
FORCE_INLINE iterator end() const {
return iterator();
}
// Creates and adds a JsonArray.
@ -59,18 +68,18 @@ class JsonObject {
// JsonArray createNestedArray(TKey);
// TKey = const std::string&, const String&
template <typename TString>
JsonArray createNestedArray(const TString& key);
FORCE_INLINE JsonArray createNestedArray(const TString& key);
// JsonArray createNestedArray(TKey);
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
JsonArray createNestedArray(TString* key);
FORCE_INLINE JsonArray createNestedArray(TString* key);
// Creates and adds a JsonObject.
//
// JsonObject createNestedObject(TKey);
// TKey = const std::string&, const String&
template <typename TString>
JsonObject createNestedObject(const TString& key) {
FORCE_INLINE JsonObject createNestedObject(const TString& key) {
if (!_data) return JsonObject();
return createNestedObject_impl<const TString&>(key);
}
@ -78,7 +87,7 @@ class JsonObject {
// JsonObject createNestedObject(TKey);
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
JsonObject createNestedObject(TString* key) {
FORCE_INLINE JsonObject createNestedObject(TString* key) {
return createNestedObject_impl<TString*>(key);
}
@ -89,7 +98,7 @@ class JsonObject {
// TValue = bool, char, long, int, short, float, double,
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename Internals::JsonVariantAs<TValue>::type get(
FORCE_INLINE typename Internals::JsonVariantAs<TValue>::type get(
const TString& key) const {
return get_impl<const TString&, TValue>(key);
}
@ -99,7 +108,8 @@ class JsonObject {
// TValue = bool, char, long, int, short, float, double,
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename Internals::JsonVariantAs<TValue>::type get(TString* key) const {
FORCE_INLINE typename Internals::JsonVariantAs<TValue>::type get(
TString* key) const {
return get_impl<TString*, TValue>(key);
}
@ -111,7 +121,7 @@ class JsonObject {
// TValue = bool, char, long, int, short, float, double,
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
bool is(const TString& key) const {
FORCE_INLINE bool is(const TString& key) const {
return is_impl<const TString&, TValue>(key);
}
//
@ -120,7 +130,7 @@ class JsonObject {
// TValue = bool, char, long, int, short, float, double,
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
bool is(TString* key) const {
FORCE_INLINE bool is(TString* key) const {
return is_impl<TString*, TValue>(key);
}
@ -129,7 +139,7 @@ class JsonObject {
// JsonObjectSubscript operator[](TKey)
// TKey = const std::string&, const String&
template <typename TString>
Internals::JsonObjectSubscript<const TString&> operator[](
FORCE_INLINE Internals::JsonObjectSubscript<const TString&> operator[](
const TString& key) {
return Internals::JsonObjectSubscript<const TString&>(*this, key);
}
@ -137,7 +147,8 @@ class JsonObject {
// JsonObjectSubscript operator[](TKey)
// TKey = char*, const char*, char[], const char[N], const FlashStringHelper*
template <typename TString>
Internals::JsonObjectSubscript<TString*> operator[](TString* key) {
FORCE_INLINE Internals::JsonObjectSubscript<TString*> operator[](
TString* key) {
return Internals::JsonObjectSubscript<TString*>(*this, key);
}
@ -146,7 +157,7 @@ class JsonObject {
// const JsonObjectSubscript operator[](TKey) const;
// TKey = const std::string&, const String&
template <typename TString>
const Internals::JsonObjectSubscript<const TString&> operator[](
FORCE_INLINE const Internals::JsonObjectSubscript<const TString&> operator[](
const TString& key) const {
return Internals::JsonObjectSubscript<const TString&>(*this, key);
}
@ -154,18 +165,31 @@ class JsonObject {
// const JsonObjectSubscript operator[](TKey) const;
// TKey = const char*, const char[N], const FlashStringHelper*
template <typename TString>
const Internals::JsonObjectSubscript<TString*> operator[](
FORCE_INLINE const Internals::JsonObjectSubscript<TString*> operator[](
TString* key) const {
return Internals::JsonObjectSubscript<TString*>(*this, key);
}
bool operator==(const JsonObject& rhs) const {
return _data == rhs._data;
FORCE_INLINE bool operator==(JsonObject rhs) const {
if (size() != rhs.size()) return false;
for (iterator it = begin(); it != end(); ++it) {
if (rhs.get<JsonVariant>(it->key()) != it->value()) return false;
}
return true;
}
void remove(iterator it) {
FORCE_INLINE void remove(iterator it) {
if (!_data) return;
_data->remove(it);
Internals::Slot* slot = it.internal();
if (!slot) return;
if (slot->prev)
slot->prev->next = slot->next;
else
_data->head = slot->next;
if (slot->next)
slot->next->prev = slot->prev;
else
_data->tail = slot->prev;
}
// Removes the specified key and the associated value.
@ -173,14 +197,14 @@ class JsonObject {
// void remove(TKey);
// TKey = const std::string&, const String&
template <typename TString>
void remove(const TString& key) {
FORCE_INLINE void remove(const TString& key) {
remove_impl<const TString&>(key);
}
//
// void remove(TKey);
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
void remove(TString* key) {
FORCE_INLINE void remove(TString* key) {
remove_impl<TString*>(key);
}
@ -191,16 +215,16 @@ class JsonObject {
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
bool set(const TString& key, const TValue& value) {
return set_impl<const TString&, const TValue&>(key, value);
FORCE_INLINE bool set(const TString& key, const TValue& value) {
return set(key).set(value);
}
//
// bool set(TKey, TValue);
// TKey = const std::string&, const String&
// TValue = char*, const char*, const FlashStringHelper*
template <typename TValue, typename TString>
bool set(const TString& key, TValue* value) {
return set_impl<const TString&, TValue*>(key, value);
FORCE_INLINE bool set(const TString& key, TValue* value) {
return set(key).set(value);
}
//
// bool set(TKey, const TValue&);
@ -208,104 +232,154 @@ class JsonObject {
// TValue = bool, long, int, short, float, double, serialized, JsonVariant,
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
bool set(TString* key, const TValue& value) {
return set_impl<TString*, const TValue&>(key, value);
FORCE_INLINE bool set(TString* key, const TValue& value) {
return set(key).set(value);
}
//
// bool set(TKey, TValue);
// TKey = char*, const char*, const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename TValue, typename TString>
bool set(TString* key, TValue* value) {
return set_impl<TString*, TValue*>(key, value);
FORCE_INLINE bool set(TString* key, TValue* value) {
return set(key).set(value);
}
size_t size() const {
template <typename TString>
FORCE_INLINE JsonVariant set(TString* key) {
return set_impl<TString*>(key);
}
template <typename TString>
FORCE_INLINE JsonVariant set(const TString& key) {
return set_impl<const TString&>(key);
}
FORCE_INLINE size_t size() const {
if (!_data) return 0;
return _data->size();
size_t n = 0;
Internals::Slot* slot = _data->head;
while (slot) {
n++;
slot = slot->next;
}
return n;
}
bool isNull() const {
FORCE_INLINE bool isNull() const {
return _data == 0;
}
template <typename Visitor>
void visit(Visitor& visitor) const {
FORCE_INLINE void accept(Visitor& visitor) const {
if (_data)
visitor.acceptObject(*this);
visitor.visitObject(*this);
else
return visitor.acceptNull();
visitor.visitNull();
}
private:
template <typename TStringRef>
bool containsKey_impl(TStringRef key) const {
return findKey<TStringRef>(key) != end();
FORCE_INLINE bool containsKey_impl(TStringRef key) const {
return findSlot<TStringRef>(key) != 0;
}
template <typename TStringRef>
JsonArray createNestedArray_impl(TStringRef key);
FORCE_INLINE JsonArray createNestedArray_impl(TStringRef key);
template <typename TStringRef>
JsonObject createNestedObject_impl(TStringRef key);
FORCE_INLINE JsonObject createNestedObject_impl(TStringRef key);
// Returns the list node that matches the specified key.
template <typename TStringRef>
iterator findKey(TStringRef key) {
iterator it;
for (it = begin(); it != end(); ++it) {
if (Internals::makeString(key).equals(it->key)) break;
Internals::Slot* findSlot(TStringRef key) {
if (!_data) return 0;
Internals::Slot* slot = _data->head;
while (slot) {
if (Internals::makeString(key).equals(slot->key)) break;
slot = slot->next;
}
return it;
return slot;
}
template <typename TStringRef>
const_iterator findKey(TStringRef key) const {
return const_cast<JsonObject*>(this)->findKey<TStringRef>(key);
FORCE_INLINE Internals::Slot* findSlot(TStringRef key) const {
return const_cast<JsonObject*>(this)->findSlot<TStringRef>(key);
}
template <typename TStringRef, typename TValue>
typename Internals::JsonVariantAs<TValue>::type get_impl(
FORCE_INLINE typename Internals::JsonVariantAs<TValue>::type get_impl(
TStringRef key) const {
const_iterator it = findKey<TStringRef>(key);
return it != end() ? it->value.as<TValue>()
: Internals::JsonVariantDefault<TValue>::get();
Internals::Slot* slot = findSlot<TStringRef>(key);
return slot ? JsonVariant(_memoryPool, &slot->value).as<TValue>()
: TValue();
}
template <typename TStringRef, typename TValue>
bool is_impl(TStringRef key) const {
const_iterator it = findKey<TStringRef>(key);
return it != end() ? it->value.is<TValue>() : false;
FORCE_INLINE bool is_impl(TStringRef key) const {
Internals::Slot* slot = findSlot<TStringRef>(key);
return slot ? JsonVariant(_memoryPool, &slot->value).is<TValue>() : false;
}
template <typename TStringRef>
void remove_impl(TStringRef key) {
FORCE_INLINE void remove_impl(TStringRef key) {
if (!_data) return;
_data->remove(findKey<TStringRef>(key));
Internals::Slot* slot = findSlot<TStringRef>(key);
if (!slot) return;
if (slot->prev)
slot->prev->next = slot->next;
else
_data->head = slot->next;
if (slot->next)
slot->next->prev = slot->prev;
else
_data->tail = slot->prev;
}
template <typename TStringRef, typename TValueRef>
bool set_impl(TStringRef key, TValueRef value) {
if (!_data) return false;
template <typename TStringRef>
FORCE_INLINE JsonVariant set_impl(TStringRef key) {
if (!_data) return JsonVariant();
// ignore null key
if (Internals::makeString(key).is_null()) return false;
if (Internals::makeString(key).is_null()) return JsonVariant();
// search a matching key
iterator it = findKey<TStringRef>(key);
if (it == end()) {
Internals::Slot* slot = findSlot<TStringRef>(key);
if (!slot) {
// add the key
it = _data->add();
if (it == end()) return false;
bool key_ok =
Internals::ValueSaver<TStringRef>::save(_data->_buffer, it->key, key);
if (!key_ok) return false;
slot = new (_memoryPool) Internals::Slot();
if (!slot) return JsonVariant();
slot->next = 0;
if (_data->tail) {
slot->prev = _data->tail;
_data->tail->next = slot;
_data->tail = slot;
} else {
slot->prev = 0;
_data->head = slot;
_data->tail = slot;
}
if (!set_key(slot, key)) return JsonVariant();
}
// save the value
return Internals::ValueSaver<TValueRef>::save(_data->_buffer, it->value,
value);
return JsonVariant(_memoryPool, &slot->value);
}
Internals::JsonObjectData* _data;
};
FORCE_INLINE bool set_key(Internals::Slot* slot, const char* key) {
slot->key = key;
return true;
}
template <typename T>
FORCE_INLINE bool set_key(Internals::Slot* slot, const T& key) {
const char* dup = Internals::makeString(key).save(_memoryPool);
if (!dup) return false;
slot->key = dup;
return true;
}
mutable Internals::MemoryPool* _memoryPool;
mutable Internals::JsonObjectData* _data;
}; // namespace ArduinoJson
} // namespace ArduinoJson

View File

@ -1,26 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/List.hpp"
#include "Data/ValueSaver.hpp"
#include "JsonPair.hpp"
#include "Memory/JsonBufferAllocated.hpp"
#include "Polyfills/type_traits.hpp"
// Returns the size (in bytes) of an object with n elements.
// Can be very handy to determine the size of a StaticJsonBuffer.
#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \
(sizeof(ArduinoJson::Internals::JsonObjectData) + \
(NUMBER_OF_ELEMENTS) * \
sizeof(ArduinoJson::Internals::JsonObjectData::node_type))
namespace ArduinoJson {
namespace Internals {
struct JsonObjectData : List<JsonPair>, JsonBufferAllocated {
explicit JsonObjectData(JsonBuffer* buf) throw() : List<JsonPair>(buf) {}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -22,16 +22,12 @@ inline JsonArray JsonObject::createNestedArray(TString* key) {
template <typename TStringRef>
inline JsonArray JsonObject::createNestedArray_impl(TStringRef key) {
if (!_data) return JsonArray();
JsonArray array(_data->_buffer);
if (!array.isNull()) set(key, array);
return array;
return set(key).template to<JsonArray>();
}
template <typename TStringRef>
inline JsonObject JsonObject::createNestedObject_impl(TStringRef key) {
if (!_data) return JsonObject();
JsonObject object(_data->_buffer);
if (!object.isNull()) set(key, object);
return object;
return set(key).template to<JsonObject>();
}
} // namespace ArduinoJson

View File

@ -0,0 +1,72 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "JsonPair.hpp"
namespace ArduinoJson {
class JsonPairPtr {
public:
JsonPairPtr(Internals::MemoryPool *memoryPool, Internals::Slot *slot)
: _pair(memoryPool, slot) {}
const JsonPair *operator->() const {
return &_pair;
}
const JsonPair &operator*() const {
return _pair;
}
private:
JsonPair _pair;
};
class JsonObjectIterator {
public:
JsonObjectIterator() : _slot(0) {}
explicit JsonObjectIterator(Internals::MemoryPool *memoryPool,
Internals::Slot *slot)
: _memoryPool(memoryPool), _slot(slot) {}
JsonPair operator*() const {
return JsonPair(_memoryPool, _slot);
}
JsonPairPtr operator->() {
return JsonPairPtr(_memoryPool, _slot);
}
bool operator==(const JsonObjectIterator &other) const {
return _slot == other._slot;
}
bool operator!=(const JsonObjectIterator &other) const {
return _slot != other._slot;
}
JsonObjectIterator &operator++() {
if (_slot) _slot = _slot->next;
return *this;
}
JsonObjectIterator &operator+=(size_t distance) {
while (_slot && distance > 0) {
_slot = _slot->next;
distance--;
}
return *this;
}
Internals::Slot *internal() {
return _slot;
}
private:
Internals::MemoryPool *_memoryPool;
Internals::Slot *_slot;
};
} // namespace ArduinoJson

View File

@ -64,6 +64,11 @@ class JsonObjectSubscript
return _object.is<TValue>(_key);
}
template <typename TValue>
FORCE_INLINE typename JsonVariantTo<TValue>::type to() {
return _object.set(_key).template to<TValue>();
}
// Sets the specified value.
//
// bool set(const TValue&);
@ -84,8 +89,8 @@ class JsonObjectSubscript
}
template <typename Visitor>
void visit(Visitor &visitor) const {
return _object.get<JsonVariant>(_key).visit(visitor);
void accept(Visitor &visitor) const {
return _object.get<JsonVariant>(_key).accept(visitor);
}
private:

View File

@ -9,8 +9,27 @@
namespace ArduinoJson {
// A key value pair for JsonObjectData.
struct JsonPair {
const char* key;
JsonVariant value;
class JsonPair {
public:
JsonPair(Internals::MemoryPool* memoryPool, Internals::Slot* slot) {
if (slot) {
_key = slot->key;
_value = JsonVariant(memoryPool, &slot->value);
} else {
_key = 0;
}
}
const char* key() const {
return _key;
}
JsonVariant value() const {
return _value;
}
private:
const char* _key;
JsonVariant _value;
};
} // namespace ArduinoJson

View File

@ -7,10 +7,10 @@
#include <stddef.h>
#include <stdint.h> // for uint8_t
#include "Data/JsonVariantContent.hpp"
#include "Data/JsonVariantDefault.hpp"
#include "Data/JsonVariantType.hpp"
#include "Data/JsonVariantData.hpp"
#include "JsonVariant.hpp"
#include "JsonVariantBase.hpp"
#include "Memory/MemoryPool.hpp"
#include "Polyfills/type_traits.hpp"
#include "Serialization/DynamicStringWriter.hpp"
#include "SerializedValue.hpp"
@ -30,84 +30,139 @@ class JsonObject;
// - a reference to a JsonArray or JsonObject
class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
public:
// Intenal use only
FORCE_INLINE JsonVariant(Internals::MemoryPool *memoryPool,
Internals::JsonVariantData *data)
: _memoryPool(memoryPool), _data(data) {}
// Creates an uninitialized JsonVariant
JsonVariant() : _type(Internals::JSON_UNDEFINED) {}
FORCE_INLINE JsonVariant() : _memoryPool(0), _data(0) {}
// Create a JsonVariant containing a boolean value.
// It will be serialized as "true" or "false" in JSON.
JsonVariant(bool value) {
using namespace Internals;
_type = JSON_BOOLEAN;
_content.asInteger = static_cast<JsonUInt>(value);
// set(bool value)
FORCE_INLINE bool set(bool value) {
if (!_data) return false;
_data->setBoolean(value);
return true;
}
// Create a JsonVariant containing a floating point value.
// JsonVariant(double value);
// JsonVariant(float value);
// set(double value);
// set(float value);
template <typename T>
JsonVariant(T value,
typename Internals::enable_if<
Internals::is_floating_point<T>::value>::type * = 0) {
using namespace Internals;
_type = JSON_FLOAT;
_content.asFloat = static_cast<JsonFloat>(value);
FORCE_INLINE bool set(
T value, typename Internals::enable_if<
Internals::is_floating_point<T>::value>::type * = 0) {
if (!_data) return false;
_data->setFloat(static_cast<Internals::JsonFloat>(value));
return true;
}
// Create a JsonVariant containing an integer value.
// JsonVariant(char)
// JsonVariant(signed short)
// JsonVariant(signed int)
// JsonVariant(signed long)
// JsonVariant(signed char)
// set(char)
// set(signed short)
// set(signed int)
// set(signed long)
// set(signed char)
template <typename T>
JsonVariant(
FORCE_INLINE bool set(
T value,
typename Internals::enable_if<Internals::is_integral<T>::value &&
Internals::is_signed<T>::value>::type * =
0) {
using namespace Internals;
if (value >= 0) {
_type = JSON_POSITIVE_INTEGER;
_content.asInteger = static_cast<JsonUInt>(value);
} else {
_type = JSON_NEGATIVE_INTEGER;
_content.asInteger = ~static_cast<JsonUInt>(value) + 1;
}
if (!_data) return false;
if (value >= 0)
_data->setPostiveInteger(static_cast<Internals::JsonUInt>(value));
else
_data->setNegativeInteger(~static_cast<Internals::JsonUInt>(value) + 1);
return true;
}
// JsonVariant(unsigned short)
// JsonVariant(unsigned int)
// JsonVariant(unsigned long)
// set(unsigned short)
// set(unsigned int)
// set(unsigned long)
template <typename T>
JsonVariant(
FORCE_INLINE bool set(
T value,
typename Internals::enable_if<Internals::is_integral<T>::value &&
Internals::is_unsigned<T>::value>::type * =
0) {
using namespace Internals;
_type = JSON_POSITIVE_INTEGER;
_content.asInteger = static_cast<JsonUInt>(value);
if (!_data) return false;
_data->setPostiveInteger(static_cast<Internals::JsonUInt>(value));
return true;
}
// Create a JsonVariant containing a string.
// JsonVariant(const char*);
// JsonVariant(const signed char*);
// JsonVariant(const unsigned char*);
template <typename TChar>
JsonVariant(const TChar *value,
typename Internals::enable_if<sizeof(TChar) == 1>::type * = 0) {
_type = Internals::JSON_STRING;
_content.asString = reinterpret_cast<const char *>(value);
// set(SerializedValue<const char *>)
FORCE_INLINE bool set(Internals::SerializedValue<const char *> value) {
if (!_data) return false;
_data->setLinkedRaw(value.data(), value.size());
return true;
}
// Create a JsonVariant containing an unparsed string
JsonVariant(Internals::SerializedValue<const char *> value) {
_type = Internals::JSON_UNPARSED;
_content.asRaw.data = value.data();
_content.asRaw.size = value.size();
// set(SerializedValue<std::string>)
// set(SerializedValue<String>)
// set(SerializedValue<const __FlashStringHelper*>)
template <typename T>
FORCE_INLINE bool set(
Internals::SerializedValue<T> value,
typename Internals::enable_if<
!Internals::is_same<const char *, T>::value>::type * = 0) {
if (!_data) return false;
const char *dup =
Internals::makeString(value.data(), value.size()).save(_memoryPool);
if (dup)
_data->setOwnedRaw(dup, value.size());
else
_data->setNull();
return true;
}
JsonVariant(JsonArray array);
JsonVariant(JsonObject object);
// set(const std::string&)
// set(const String&)
template <typename T>
FORCE_INLINE bool set(
const T &value,
typename Internals::enable_if<Internals::IsString<T>::value>::type * =
0) {
if (!_data) return false;
const char *dup = Internals::makeString(value).save(_memoryPool);
if (dup) {
_data->setOwnedString(dup);
return true;
} else {
_data->setNull();
return false;
}
}
// set(char*)
template <typename T>
FORCE_INLINE bool set(
T *value,
typename Internals::enable_if<Internals::IsString<T *>::value>::type * =
0) {
if (!_data) return false;
const char *dup = Internals::makeString(value).save(_memoryPool);
if (dup) {
_data->setOwnedString(dup);
return true;
} else {
_data->setNull();
return false;
}
}
// set(const char*);
FORCE_INLINE bool set(const char *value) {
if (!_data) return false;
_data->setLinkedString(value);
return true;
}
bool set(const JsonVariant &value);
FORCE_INLINE bool set(JsonArray array);
FORCE_INLINE bool set(const Internals::JsonArraySubscript &);
FORCE_INLINE bool set(JsonObject object);
template <typename TString>
FORCE_INLINE bool set(const Internals::JsonObjectSubscript<TString> &);
// Get the variant as the specified type.
//
@ -121,43 +176,47 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
// unsigned int as<unsigned int>() const;
// unsigned long as<unsigned long>() const;
template <typename T>
const typename Internals::enable_if<Internals::is_integral<T>::value, T>::type
FORCE_INLINE const typename Internals::enable_if<
Internals::is_integral<T>::value, T>::type
as() const {
return variantAsInteger<T>();
return _data ? _data->asInteger<T>() : T();
}
// bool as<bool>() const
template <typename T>
const typename Internals::enable_if<Internals::is_same<T, bool>::value,
T>::type
FORCE_INLINE const typename Internals::enable_if<
Internals::is_same<T, bool>::value, T>::type
as() const {
return variantAsInteger<int>() != 0;
return _data && _data->asInteger<int>() != 0;
}
//
// double as<double>() const;
// float as<float>() const;
template <typename T>
const typename Internals::enable_if<Internals::is_floating_point<T>::value,
T>::type
FORCE_INLINE const typename Internals::enable_if<
Internals::is_floating_point<T>::value, T>::type
as() const {
return variantAsFloat<T>();
return _data ? _data->asFloat<T>() : 0;
}
//
// const char* as<const char*>() const;
// const char* as<char*>() const;
template <typename T>
typename Internals::enable_if<Internals::is_same<T, const char *>::value ||
Internals::is_same<T, char *>::value,
const char *>::type
FORCE_INLINE typename Internals::enable_if<
Internals::is_same<T, const char *>::value ||
Internals::is_same<T, char *>::value,
const char *>::type
as() const {
return variantAsString();
return _data ? _data->asString() : 0;
}
//
// std::string as<std::string>() const;
// String as<String>() const;
template <typename T>
typename Internals::enable_if<Internals::IsWriteableString<T>::value, T>::type
as() const {
const char *cstr = variantAsString();
FORCE_INLINE
typename Internals::enable_if<Internals::IsWriteableString<T>::value,
T>::type
as() const {
const char *cstr = _data ? _data->asString() : 0;
if (cstr) return T(cstr);
T s;
serializeJson(*this, s);
@ -167,7 +226,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
// JsonArray as<JsonArray>() const;
// const JsonArray as<const JsonArray>() const;
template <typename T>
typename Internals::enable_if<
FORCE_INLINE typename Internals::enable_if<
Internals::is_same<typename Internals::remove_const<T>::type,
JsonArray>::value,
JsonArray>::type
@ -176,7 +235,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
// JsonObject as<JsonObject>() const;
// const JsonObject as<const JsonObject>() const;
template <typename T>
typename Internals::enable_if<
FORCE_INLINE typename Internals::enable_if<
Internals::is_same<typename Internals::remove_const<T>::type,
JsonObject>::value,
T>::type
@ -184,9 +243,10 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
//
// JsonVariant as<JsonVariant> const;
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
T>::type
as() const {
FORCE_INLINE
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
T>::type
as() const {
return *this;
}
@ -203,122 +263,96 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
// bool is<unsigned int>() const;
// bool is<unsigned long>() const;
template <typename T>
typename Internals::enable_if<Internals::is_integral<T>::value, bool>::type
FORCE_INLINE typename Internals::enable_if<Internals::is_integral<T>::value,
bool>::type
is() const {
return variantIsInteger();
return _data && _data->isInteger();
}
//
// bool is<double>() const;
// bool is<float>() const;
template <typename T>
typename Internals::enable_if<Internals::is_floating_point<T>::value,
bool>::type
is() const {
return variantIsFloat();
FORCE_INLINE
typename Internals::enable_if<Internals::is_floating_point<T>::value,
bool>::type
is() const {
return _data && _data->isFloat();
}
//
// bool is<bool>() const
template <typename T>
typename Internals::enable_if<Internals::is_same<T, bool>::value, bool>::type
FORCE_INLINE typename Internals::enable_if<Internals::is_same<T, bool>::value,
bool>::type
is() const {
return variantIsBoolean();
return _data && _data->isBoolean();
}
//
// bool is<const char*>() const;
// bool is<char*>() const;
template <typename T>
typename Internals::enable_if<Internals::is_same<T, const char *>::value ||
Internals::is_same<T, char *>::value,
bool>::type
FORCE_INLINE typename Internals::enable_if<
Internals::is_same<T, const char *>::value ||
Internals::is_same<T, char *>::value,
bool>::type
is() const {
return variantIsString();
return _data && _data->isString();
}
//
// bool is<JsonArray> const;
// bool is<const JsonArray> const;
template <typename T>
typename Internals::enable_if<
FORCE_INLINE typename Internals::enable_if<
Internals::is_same<typename Internals::remove_const<T>::type,
JsonArray>::value,
bool>::type
is() const {
return variantIsArray();
return _data && _data->isArray();
}
//
// bool is<JsonObject> const;
// bool is<const JsonObject> const;
template <typename T>
typename Internals::enable_if<
FORCE_INLINE typename Internals::enable_if<
Internals::is_same<typename Internals::remove_const<T>::type,
JsonObject>::value,
bool>::type
is() const {
return variantIsObject();
return _data && _data->isObject();
}
// Returns true if the variant has a value
bool isNull() const {
return _type == Internals::JSON_UNDEFINED;
FORCE_INLINE bool isNull() const {
return _data == 0 || _data->isNull();
}
FORCE_INLINE bool isInvalid() const {
return _data == 0;
}
template <typename Visitor>
void visit(Visitor &visitor) const {
using namespace Internals;
switch (_type) {
case JSON_FLOAT:
return visitor.acceptFloat(_content.asFloat);
void accept(Visitor &visitor) const;
case JSON_ARRAY:
return visitor.acceptArray(_content.asArray);
case JSON_OBJECT:
return visitor.acceptObject(_content.asObject);
case JSON_STRING:
return visitor.acceptString(_content.asString);
case JSON_UNPARSED:
return visitor.acceptRawJson(_content.asRaw.data, _content.asRaw.size);
case JSON_NEGATIVE_INTEGER:
return visitor.acceptNegativeInteger(_content.asInteger);
case JSON_POSITIVE_INTEGER:
return visitor.acceptPositiveInteger(_content.asInteger);
case JSON_BOOLEAN:
return visitor.acceptBoolean(_content.asInteger != 0);
default: // JSON_UNDEFINED
return visitor.acceptNull();
}
}
// Change the type of the variant
//
// JsonArray to<JsonArray>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonArray>::value,
JsonArray>::type
to();
//
// JsonObject to<JsonObject>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonObject>::value,
JsonObject>::type
to();
//
// JsonObject to<JsonVariant>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
JsonVariant>::type
to();
private:
JsonArray variantAsArray() const;
JsonObject variantAsObject() const;
const char *variantAsString() const;
template <typename T>
T variantAsFloat() const;
template <typename T>
T variantAsInteger() const;
bool variantIsBoolean() const;
bool variantIsFloat() const;
bool variantIsInteger() const;
bool variantIsArray() const {
return _type == Internals::JSON_ARRAY;
}
bool variantIsObject() const {
return _type == Internals::JSON_OBJECT;
}
bool variantIsString() const {
return _type == Internals::JSON_STRING;
}
// The current type of the variant
Internals::JsonVariantType _type;
// The various alternatives for the value of the variant.
Internals::JsonVariantContent _content;
};
Internals::MemoryPool *_memoryPool;
Internals::JsonVariantData *_data;
}; // namespace ArduinoJson
} // namespace ArduinoJson

View File

@ -5,6 +5,8 @@
#pragma once
#include "Data/IsVariant.hpp"
#include "Data/JsonFloat.hpp"
#include "Data/JsonInteger.hpp"
#include "Polyfills/type_traits.hpp"
#include "Strings/StringTypes.hpp"

View File

@ -5,11 +5,7 @@
#pragma once
#include "Configuration.hpp"
#include "JsonArrayData.hpp"
#include "JsonObjectData.hpp"
#include "JsonVariant.hpp"
#include "Numbers/isFloat.hpp"
#include "Numbers/isInteger.hpp"
#include "Numbers/parseFloat.hpp"
#include "Numbers/parseInteger.hpp"
@ -17,21 +13,43 @@
namespace ArduinoJson {
inline JsonVariant::JsonVariant(JsonArray array) {
if (!array.isNull()) {
_type = Internals::JSON_ARRAY;
_content.asArray = array._data;
} else {
_type = Internals::JSON_UNDEFINED;
}
inline bool JsonVariant::set(JsonArray array) {
return to<JsonArray>().copyFrom(array);
}
inline JsonVariant::JsonVariant(JsonObject object) {
if (!object.isNull()) {
_type = Internals::JSON_OBJECT;
_content.asObject = object._data;
} else {
_type = Internals::JSON_UNDEFINED;
inline bool JsonVariant::set(const Internals::JsonArraySubscript& value) {
return set(value.as<JsonVariant>());
}
inline bool JsonVariant::set(JsonObject object) {
return to<JsonObject>().copyFrom(object);
}
template <typename TString>
inline bool JsonVariant::set(
const Internals::JsonObjectSubscript<TString>& value) {
return set(value.template as<JsonVariant>());
}
inline bool JsonVariant::set(const JsonVariant& value) {
if (!_data) return false;
if (!value._data) {
_data->setNull();
return true;
}
switch (value._data->type) {
case Internals::JSON_ARRAY:
return set(value.as<JsonArray>());
case Internals::JSON_OBJECT:
return set(value.as<JsonObject>());
case Internals::JSON_OWNED_STRING:
return set(const_cast<char*>(value._data->content.asString));
case Internals::JSON_OWNED_RAW:
return set(serialized(const_cast<char*>(value._data->content.asRaw.data),
value._data->content.asRaw.size));
default:
*_data = *value._data;
return true;
}
}
@ -41,7 +59,7 @@ inline typename Internals::enable_if<
JsonArray>::value,
JsonArray>::type
JsonVariant::as() const {
return variantAsArray();
return _data ? JsonArray(_memoryPool, _data->asArray()) : JsonArray();
}
template <typename T>
@ -50,78 +68,71 @@ inline typename Internals::enable_if<
JsonObject>::value,
T>::type
JsonVariant::as() const {
return variantAsObject();
}
inline JsonArray JsonVariant::variantAsArray() const {
if (_type == Internals::JSON_ARRAY) return _content.asArray;
return JsonArray();
}
inline JsonObject JsonVariant::variantAsObject() const {
if (_type == Internals::JSON_OBJECT) return _content.asObject;
return JsonObject();
return _data ? JsonObject(_memoryPool, _data->asObject()) : JsonObject();
}
template <typename T>
inline T JsonVariant::variantAsInteger() const {
using namespace Internals;
switch (_type) {
case JSON_UNDEFINED:
case JSON_UNPARSED:
return 0;
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return T(_content.asInteger);
case JSON_NEGATIVE_INTEGER:
return T(~_content.asInteger + 1);
case JSON_STRING:
return parseInteger<T>(_content.asString);
default:
return T(_content.asFloat);
}
}
inline const char *JsonVariant::variantAsString() const {
using namespace Internals;
return _type == JSON_STRING ? _content.asString : NULL;
inline typename Internals::enable_if<Internals::is_same<T, JsonArray>::value,
JsonArray>::type
JsonVariant::to() {
if (!_data) return JsonArray();
return JsonArray(_memoryPool, _data->toArray());
}
template <typename T>
inline T JsonVariant::variantAsFloat() const {
typename Internals::enable_if<Internals::is_same<T, JsonObject>::value,
JsonObject>::type
JsonVariant::to() {
if (!_data) return JsonObject();
return JsonObject(_memoryPool, _data->toObject());
}
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
JsonVariant>::type
JsonVariant::to() {
if (!_data) return JsonVariant();
_data->setNull();
return *this;
}
template <typename Visitor>
inline void JsonVariant::accept(Visitor& visitor) const {
using namespace Internals;
switch (_type) {
case JSON_UNDEFINED:
case JSON_UNPARSED:
return 0;
case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN:
return static_cast<T>(_content.asInteger);
if (!_data) return visitor.visitNull();
switch (_data->type) {
case JSON_FLOAT:
return visitor.visitFloat(_data->content.asFloat);
case JSON_ARRAY:
return visitor.visitArray(
JsonArray(_memoryPool, &_data->content.asArray));
case JSON_OBJECT:
return visitor.visitObject(
JsonObject(_memoryPool, &_data->content.asObject));
case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return visitor.visitString(_data->content.asString);
case JSON_LINKED_RAW:
case JSON_OWNED_RAW:
return visitor.visitRawJson(_data->content.asRaw.data,
_data->content.asRaw.size);
case JSON_NEGATIVE_INTEGER:
return -static_cast<T>(_content.asInteger);
case JSON_STRING:
return parseFloat<T>(_content.asString);
return visitor.visitNegativeInteger(_data->content.asInteger);
case JSON_POSITIVE_INTEGER:
return visitor.visitPositiveInteger(_data->content.asInteger);
case JSON_BOOLEAN:
return visitor.visitBoolean(_data->content.asInteger != 0);
default:
return static_cast<T>(_content.asFloat);
return visitor.visitNull();
}
}
inline bool JsonVariant::variantIsBoolean() const {
using namespace Internals;
return _type == JSON_BOOLEAN;
}
inline bool JsonVariant::variantIsInteger() const {
using namespace Internals;
return _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER;
}
inline bool JsonVariant::variantIsFloat() const {
using namespace Internals;
return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER ||
_type == JSON_NEGATIVE_INTEGER;
}
} // namespace ArduinoJson

View File

@ -0,0 +1,21 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "MemoryPool.hpp"
namespace ArduinoJson {
namespace Internals {
class AllocableInMemoryPool {
public:
void *operator new(size_t n, MemoryPool *memoryPool) NOEXCEPT {
return memoryPool->alloc(n);
}
void operator delete(void *, MemoryPool *)NOEXCEPT {}
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -4,7 +4,7 @@
#pragma once
#include "JsonBuffer.hpp"
#include "MemoryPool.hpp"
#include <stdlib.h>
@ -31,7 +31,7 @@ class DefaultAllocator {
};
template <typename TAllocator>
class DynamicJsonBufferBase : public JsonBuffer {
class DynamicMemoryPoolBase : public MemoryPool {
struct Block;
struct EmptyBlock {
Block* next;
@ -45,27 +45,27 @@ class DynamicJsonBufferBase : public JsonBuffer {
public:
enum { EmptyBlockSize = sizeof(EmptyBlock) };
DynamicJsonBufferBase(size_t initialSize = 256)
DynamicMemoryPoolBase(size_t initialSize = 256)
: _head(NULL), _nextBlockCapacity(initialSize) {}
~DynamicJsonBufferBase() {
~DynamicMemoryPoolBase() {
clear();
}
// Gets the number of bytes occupied in the buffer
// Gets the number of bytes occupied in the memoryPool
size_t size() const {
size_t total = 0;
for (const Block* b = _head; b; b = b->next) total += b->size;
return total;
}
// Allocates the specified amount of bytes in the buffer
// Allocates the specified amount of bytes in the memoryPool
virtual void* alloc(size_t bytes) {
alignNextAlloc();
return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
}
// Resets the buffer.
// Resets the memoryPool.
// USE WITH CAUTION: this invalidates all previously allocated data
void clear() {
Block* currentBlock = _head;
@ -80,7 +80,7 @@ class DynamicJsonBufferBase : public JsonBuffer {
class String {
public:
String(DynamicJsonBufferBase* parent)
String(DynamicMemoryPoolBase* parent)
: _parent(parent), _start(NULL), _length(0) {}
void append(char c) {
@ -104,7 +104,7 @@ class DynamicJsonBufferBase : public JsonBuffer {
}
private:
DynamicJsonBufferBase* _parent;
DynamicMemoryPoolBase* _parent;
char* _start;
size_t _length;
};
@ -152,12 +152,12 @@ class DynamicJsonBufferBase : public JsonBuffer {
size_t _nextBlockCapacity;
};
// Implements a JsonBuffer with dynamic memory allocation.
// You are strongly encouraged to consider using StaticJsonBuffer which is much
// Implements a MemoryPool with dynamic memory allocation.
// You are strongly encouraged to consider using StaticMemoryPool which is much
// more suitable for embedded systems.
typedef Internals::DynamicJsonBufferBase<Internals::DefaultAllocator>
DynamicJsonBuffer;
typedef DynamicMemoryPoolBase<DefaultAllocator> DynamicMemoryPool;
} // namespace Internals
} // namespace ArduinoJson
#if defined(__clang__)
#pragma clang diagnostic pop
@ -166,4 +166,3 @@ typedef Internals::DynamicJsonBufferBase<Internals::DefaultAllocator>
#pragma GCC diagnostic pop
#endif
#endif
} // namespace ArduinoJson

View File

@ -1,22 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "JsonBuffer.hpp"
namespace ArduinoJson {
namespace Internals {
class JsonBufferAllocated {
public:
void *operator new(size_t n, JsonBuffer *jsonBuffer) throw() {
if (!jsonBuffer) return NULL;
return jsonBuffer->alloc(n);
}
void operator delete(void *, JsonBuffer *)throw();
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -14,11 +14,11 @@
namespace ArduinoJson {
namespace Internals {
// Handle the memory management (done in derived classes) and calls the parser.
// This abstract class is implemented by StaticJsonBuffer which implements a
// This abstract class is implemented by StaticMemoryPool which implements a
// fixed memory allocation.
class JsonBuffer {
class MemoryPool {
public:
// Allocates n bytes in the JsonBuffer.
// Allocates n bytes in the MemoryPool.
// Return a pointer to the allocated memory or NULL if allocation fails.
virtual void *alloc(size_t size) = 0;
@ -26,7 +26,7 @@ class JsonBuffer {
// CAUTION: NO VIRTUAL DESTRUCTOR!
// If we add a virtual constructor the Arduino compiler will add malloc()
// and free() to the binary, adding 706 useless bytes.
~JsonBuffer() {}
~MemoryPool() {}
// Preserve aligment if necessary
static FORCE_INLINE size_t round_size_up(size_t bytes) {

View File

@ -5,16 +5,16 @@
#pragma once
#include "../Polyfills/mpl/max.hpp"
#include "JsonBuffer.hpp"
#include "MemoryPool.hpp"
namespace ArduinoJson {
namespace Internals {
class StaticJsonBufferBase : public JsonBuffer {
class StaticMemoryPoolBase : public MemoryPool {
public:
class String {
public:
String(StaticJsonBufferBase* parent) : _parent(parent) {
String(StaticMemoryPoolBase* parent) : _parent(parent) {
_start = parent->_buffer + parent->_size;
}
@ -36,31 +36,31 @@ class StaticJsonBufferBase : public JsonBuffer {
}
private:
StaticJsonBufferBase* _parent;
StaticMemoryPoolBase* _parent;
char* _start;
};
StaticJsonBufferBase(char* buffer, size_t capa)
: _buffer(buffer), _capacity(capa), _size(0) {}
StaticMemoryPoolBase(char* memoryPool, size_t capa)
: _buffer(memoryPool), _capacity(capa), _size(0) {}
// Gets the capacity of the buffer in bytes
// Gets the capacity of the memoryPool in bytes
size_t capacity() const {
return _capacity;
}
// Gets the current usage of the buffer in bytes
// Gets the current usage of the memoryPool in bytes
size_t size() const {
return _size;
}
// Allocates the specified amount of bytes in the buffer
// Allocates the specified amount of bytes in the memoryPool
virtual void* alloc(size_t bytes) {
alignNextAlloc();
if (!canAlloc(bytes)) return NULL;
return doAlloc(bytes);
}
// Resets the buffer.
// Resets the memoryPool.
// USE WITH CAUTION: this invalidates all previously allocated data
void clear() {
_size = 0;
@ -71,7 +71,7 @@ class StaticJsonBufferBase : public JsonBuffer {
}
protected:
~StaticJsonBufferBase() {}
~StaticMemoryPoolBase() {}
private:
void alignNextAlloc() {
@ -103,16 +103,16 @@ class StaticJsonBufferBase : public JsonBuffer {
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#endif
// Implements a JsonBuffer with fixed memory allocation.
// The template paramenter CAPACITY specifies the capacity of the buffer in
// Implements a MemoryPool with fixed memory allocation.
// The template paramenter CAPACITY specifies the capacity of the memoryPool in
// bytes.
template <size_t CAPACITY>
class StaticJsonBuffer : public Internals::StaticJsonBufferBase {
static const size_t ACTUAL_CAPACITY = Internals::Max<1, CAPACITY>::value;
class StaticMemoryPool : public StaticMemoryPoolBase {
static const size_t ACTUAL_CAPACITY = Max<1, CAPACITY>::value;
public:
explicit StaticJsonBuffer()
: Internals::StaticJsonBufferBase(_buffer, ACTUAL_CAPACITY) {}
explicit StaticMemoryPool()
: StaticMemoryPoolBase(_buffer, ACTUAL_CAPACITY) {}
private:
char _buffer[ACTUAL_CAPACITY];

View File

@ -6,7 +6,7 @@
#include "../Deserialization/deserialize.hpp"
#include "../JsonVariant.hpp"
#include "../Memory/JsonBuffer.hpp"
#include "../Memory/MemoryPool.hpp"
#include "../Polyfills/type_traits.hpp"
#include "./endianess.hpp"
#include "./ieee754.hpp"
@ -17,24 +17,24 @@ namespace Internals {
template <typename TReader, typename TStringStorage>
class MsgPackDeserializer {
public:
MsgPackDeserializer(JsonBuffer *buffer, TReader reader,
MsgPackDeserializer(MemoryPool &memoryPool, TReader reader,
TStringStorage stringStorage, uint8_t nestingLimit)
: _buffer(buffer),
: _memoryPool(&memoryPool),
_reader(reader),
_stringStorage(stringStorage),
_nestingLimit(nestingLimit) {}
DeserializationError parse(JsonVariant &variant) {
DeserializationError parse(JsonVariant variant) {
uint8_t code;
if (!readByte(code)) return DeserializationError::IncompleteInput;
if ((code & 0x80) == 0) {
variant = code;
variant.set(code);
return DeserializationError::Ok;
}
if ((code & 0xe0) == 0xe0) {
variant = static_cast<int8_t>(code);
variant.set(static_cast<int8_t>(code));
return DeserializationError::Ok;
}
@ -48,15 +48,15 @@ class MsgPackDeserializer {
switch (code) {
case 0xc0:
variant = static_cast<char *>(0);
// already null
return DeserializationError::Ok;
case 0xc2:
variant = false;
variant.set(false);
return DeserializationError::Ok;
case 0xc3:
variant = true;
variant.set(true);
return DeserializationError::Ok;
case 0xcc:
@ -171,54 +171,54 @@ class MsgPackDeserializer {
}
template <typename T>
DeserializationError readInteger(JsonVariant &variant) {
DeserializationError readInteger(JsonVariant variant) {
T value;
if (!readInteger(value)) return DeserializationError::IncompleteInput;
variant = value;
variant.set(value);
return DeserializationError::Ok;
}
template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat(
JsonVariant &variant) {
JsonVariant variant) {
T value;
if (!readBytes(value)) return DeserializationError::IncompleteInput;
fixEndianess(value);
variant = value;
variant.set(value);
return DeserializationError::Ok;
}
template <typename T>
typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble(
JsonVariant &variant) {
JsonVariant variant) {
T value;
if (!readBytes(value)) return DeserializationError::IncompleteInput;
fixEndianess(value);
variant = value;
variant.set(value);
return DeserializationError::Ok;
}
template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble(
JsonVariant &variant) {
JsonVariant 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;
doubleToFloat(i, o);
fixEndianess(value);
variant = value;
variant.set(value);
return DeserializationError::Ok;
}
template <typename T>
DeserializationError readString(JsonVariant &variant) {
DeserializationError readString(JsonVariant variant) {
T size;
if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readString(variant, size);
}
DeserializationError readString(JsonVariant &variant, size_t n) {
DeserializationError readString(JsonVariant variant, size_t n) {
typename remove_reference<TStringStorage>::type::String str =
_stringStorage.startString();
for (; n; --n) {
@ -228,21 +228,20 @@ class MsgPackDeserializer {
}
const char *s = str.c_str();
if (s == NULL) return DeserializationError::NoMemory;
variant = s;
variant.set(s);
return DeserializationError::Ok;
}
template <typename TSize>
DeserializationError readArray(JsonVariant &variant) {
DeserializationError readArray(JsonVariant variant) {
TSize size;
if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readArray(variant, size);
}
DeserializationError readArray(JsonVariant &variant, size_t n) {
JsonArray array(_buffer);
DeserializationError readArray(JsonVariant variant, size_t n) {
JsonArray array = variant.to<JsonArray>();
if (array.isNull()) return DeserializationError::NoMemory;
variant = array;
return readArray(array, n);
}
@ -250,26 +249,27 @@ class MsgPackDeserializer {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
--_nestingLimit;
for (; n; --n) {
JsonVariant variant;
DeserializationError err = parse(variant);
JsonVariant value = array.add();
if (value.isInvalid()) return DeserializationError::NoMemory;
DeserializationError err = parse(value);
if (err) return err;
if (!array.add(variant)) return DeserializationError::NoMemory;
}
++_nestingLimit;
return DeserializationError::Ok;
}
template <typename TSize>
DeserializationError readObject(JsonVariant &variant) {
DeserializationError readObject(JsonVariant variant) {
TSize size;
if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readObject(variant, size);
}
DeserializationError readObject(JsonVariant &variant, size_t n) {
JsonObject object(_buffer);
DeserializationError readObject(JsonVariant variant, size_t n) {
JsonObject object = variant.to<JsonObject>();
if (object.isNull()) return DeserializationError::NoMemory;
variant = object;
return readObject(object, n);
}
@ -277,21 +277,23 @@ class MsgPackDeserializer {
if (_nestingLimit == 0) return DeserializationError::TooDeep;
--_nestingLimit;
for (; n; --n) {
DeserializationError err;
JsonVariant variant;
err = parse(variant);
JsonVariantData keyData;
JsonVariant key(_memoryPool, &keyData);
DeserializationError err = parse(key);
if (err) return err;
const char *key = variant.as<char *>();
if (!key) return DeserializationError::NotSupported;
err = parse(variant);
if (!keyData.isString()) return DeserializationError::NotSupported;
JsonVariant value = object.set(keyData.asString());
if (value.isInvalid()) return DeserializationError::NoMemory;
err = parse(value);
if (err) return err;
if (!object.set(key, variant)) return DeserializationError::NoMemory;
}
++_nestingLimit;
return DeserializationError::Ok;
}
JsonBuffer *_buffer;
MemoryPool *_memoryPool;
TReader _reader;
TStringStorage _stringStorage;
uint8_t _nestingLimit;

View File

@ -19,13 +19,13 @@ class MsgPackSerializer {
MsgPackSerializer(TWriter& writer) : _writer(&writer), _bytesWritten(0) {}
template <typename T>
typename enable_if<sizeof(T) == 4>::type acceptFloat(T value32) {
typename enable_if<sizeof(T) == 4>::type visitFloat(T value32) {
writeByte(0xCA);
writeInteger(value32);
}
template <typename T>
typename enable_if<sizeof(T) == 8>::type acceptFloat(T value64) {
typename enable_if<sizeof(T) == 8>::type visitFloat(T value64) {
float value32 = float(value64);
if (value32 == value64) {
writeByte(0xCA);
@ -36,7 +36,7 @@ class MsgPackSerializer {
}
}
void acceptArray(const JsonArray& array) {
void visitArray(JsonArray array) {
size_t n = array.size();
if (n < 0x10) {
writeByte(uint8_t(0x90 + array.size()));
@ -47,13 +47,12 @@ class MsgPackSerializer {
writeByte(0xDD);
writeInteger(uint32_t(n));
}
for (JsonArray::const_iterator it = array.begin(); it != array.end();
++it) {
it->visit(*this);
for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) {
it->accept(*this);
}
}
void acceptObject(const JsonObject& object) {
void visitObject(JsonObject object) {
size_t n = object.size();
if (n < 0x10) {
writeByte(uint8_t(0x80 + n));
@ -64,14 +63,13 @@ class MsgPackSerializer {
writeByte(0xDF);
writeInteger(uint32_t(n));
}
for (JsonObject::const_iterator it = object.begin(); it != object.end();
++it) {
acceptString(it->key);
it->value.visit(*this);
for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) {
visitString(it->key());
it->value().accept(*this);
}
}
void acceptString(const char* value) {
void visitString(const char* value) {
if (!value) return writeByte(0xC0); // nil
size_t n = strlen(value);
@ -91,11 +89,11 @@ class MsgPackSerializer {
writeBytes(reinterpret_cast<const uint8_t*>(value), n);
}
void acceptRawJson(const char* data, size_t size) {
void visitRawJson(const char* data, size_t size) {
writeBytes(reinterpret_cast<const uint8_t*>(data), size);
}
void acceptNegativeInteger(JsonUInt value) {
void visitNegativeInteger(JsonUInt value) {
JsonUInt negated = JsonUInt(~value + 1);
if (value <= 0x20) {
writeInteger(int8_t(negated));
@ -117,7 +115,7 @@ class MsgPackSerializer {
#endif
}
void acceptPositiveInteger(JsonUInt value) {
void visitPositiveInteger(JsonUInt value) {
if (value <= 0x7F) {
writeInteger(uint8_t(value));
} else if (value <= 0xFF) {
@ -138,11 +136,11 @@ class MsgPackSerializer {
#endif
}
void acceptBoolean(bool value) {
void visitBoolean(bool value) {
writeByte(value ? 0xC3 : 0xC2);
}
void acceptNull() {
void visitNull() {
writeByte(0xC0);
}

View File

@ -27,3 +27,9 @@
#define DEPRECATED(msg)
#endif
#if __cplusplus >= 201103L
#define NOEXCEPT noexcept
#else
#define NOEXCEPT throw()
#endif

View File

@ -4,6 +4,7 @@
#pragma once
#include "../../Configuration.hpp"
#include "is_same.hpp"
namespace ArduinoJson {

View File

@ -13,7 +13,7 @@ template <template <typename> class TSerializer, typename TSource>
size_t measure(const TSource &source) {
DummyWriter dp;
TSerializer<DummyWriter> serializer(dp);
source.visit(serializer);
source.accept(serializer);
return serializer.bytesWritten();
}

View File

@ -19,7 +19,7 @@ template <template <typename> class TSerializer, typename TSource,
typename enable_if<!IsWriteableString<TPrint>::value, size_t>::type serialize(
const TSource &source, TPrint &destination) {
TSerializer<TPrint> serializer(destination);
source.visit(serializer);
source.accept(serializer);
return serializer.bytesWritten();
}

View File

@ -4,79 +4,60 @@
#pragma once
#include "Data/JsonVariantTo.hpp"
#include "JsonVariant.hpp"
#include "Memory/StaticJsonBuffer.hpp"
#include "Memory/StaticMemoryPool.hpp"
namespace ArduinoJson {
template <size_t CAPACITY = sizeof(JsonVariant)>
template <size_t CAPACITY>
class StaticJsonDocument {
Internals::StaticJsonBuffer<CAPACITY> _buffer;
JsonVariant _root;
public:
uint8_t nestingLimit;
StaticJsonDocument() : nestingLimit(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
Internals::StaticJsonBufferBase& buffer() {
return _buffer;
Internals::StaticMemoryPoolBase& memoryPool() {
return _memoryPool;
}
template <typename T>
bool is() const {
return _root.is<T>();
return getVariant().template is<T>();
}
template <typename T>
typename Internals::JsonVariantAs<T>::type as() const {
return _root.as<T>();
return getVariant().template as<T>();
}
// JsonObject to<JsonObject>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonObject>::value,
JsonObject>::type
to() {
clear();
JsonObject object(&_buffer);
_root = object;
return object;
}
// JsonArray to<JsonArray>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonArray>::value,
JsonArray>::type
to() {
clear();
JsonArray array(&_buffer);
_root = array;
return array;
}
// JsonVariant to<JsonVariant>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
T&>::type
to() {
clear();
return _root;
typename Internals::JsonVariantTo<T>::type to() {
_memoryPool.clear();
return getVariant().template to<T>();
}
void clear() {
_buffer.clear();
_root = JsonVariant();
_memoryPool.clear();
_rootData.setNull();
}
size_t memoryUsage() const {
return _buffer.size();
return _memoryPool.size();
}
template <typename Visitor>
void visit(Visitor& visitor) const {
return _root.visit(visitor);
void accept(Visitor& visitor) const {
return getVariant().accept(visitor);
}
private:
JsonVariant getVariant() const {
return JsonVariant(&_memoryPool, &_rootData);
}
mutable Internals::StaticMemoryPool<CAPACITY> _memoryPool;
mutable Internals::JsonVariantData _rootData;
};
} // namespace ArduinoJson

View File

@ -7,19 +7,19 @@
namespace ArduinoJson {
namespace Internals {
template <typename TJsonBuffer>
template <typename TMemoryPool>
class StringCopier {
public:
StringCopier(TJsonBuffer& jb) : _jb(&jb) {}
StringCopier(TMemoryPool& memoryPool) : _memoryPool(&memoryPool) {}
typedef typename TJsonBuffer::String String;
typedef typename TMemoryPool::String String;
String startString() {
return _jb->startString();
return _memoryPool->startString();
}
private:
TJsonBuffer* _jb;
TMemoryPool* _memoryPool;
};
} // namespace Internals
} // namespace ArduinoJson

View File

@ -28,7 +28,7 @@ class StringMover {
TChar* _startPtr;
};
StringMover(TChar* buffer) : _ptr(buffer) {}
StringMover(TChar* ptr) : _ptr(ptr) {}
String startString() {
return String(&_ptr);

View File

@ -10,35 +10,35 @@
namespace ArduinoJson {
namespace Internals {
template <typename TJsonBuffer, typename TInput, typename Enable = void>
template <typename TMemoryPool, typename TInput, typename Enable = void>
struct StringStorage {
typedef StringCopier<TJsonBuffer> type;
typedef StringCopier<TMemoryPool> type;
static type create(TJsonBuffer& jb, TInput&) {
static type create(TMemoryPool& jb, TInput&) {
return type(jb);
}
};
template <typename TJsonBuffer, typename TChar>
struct StringStorage<TJsonBuffer, TChar*,
template <typename TMemoryPool, typename TChar>
struct StringStorage<TMemoryPool, TChar*,
typename enable_if<!is_const<TChar>::value>::type> {
typedef StringMover<TChar> type;
static type create(TJsonBuffer&, TChar* input) {
static type create(TMemoryPool&, TChar* input) {
return type(input);
}
};
template <typename TJsonBuffer, typename TInput>
typename StringStorage<TJsonBuffer, TInput>::type makeStringStorage(
TJsonBuffer& jb, TInput& input) {
return StringStorage<TJsonBuffer, TInput>::create(jb, input);
template <typename TMemoryPool, typename TInput>
typename StringStorage<TMemoryPool, TInput>::type makeStringStorage(
TMemoryPool& jb, TInput& input) {
return StringStorage<TMemoryPool, TInput>::create(jb, input);
}
template <typename TJsonBuffer, typename TChar>
typename StringStorage<TJsonBuffer, TChar*>::type makeStringStorage(
TJsonBuffer& jb, TChar* input) {
return StringStorage<TJsonBuffer, TChar*>::create(jb, input);
template <typename TMemoryPool, typename TChar>
typename StringStorage<TMemoryPool, TChar*>::type makeStringStorage(
TMemoryPool& jb, TChar* input) {
return StringStorage<TMemoryPool, TChar*>::create(jb, input);
}
} // namespace Internals
} // namespace ArduinoJson

View File

@ -14,10 +14,10 @@ class ArduinoString {
ArduinoString(const ::String& str) : _str(&str) {}
template <typename Buffer>
const char* save(Buffer* buffer) const {
const char* save(Buffer* memoryPool) const {
if (is_null()) return NULL;
size_t n = _str->length() + 1;
void* dup = buffer->alloc(n);
void* dup = memoryPool->alloc(n);
if (dup != NULL) memcpy(dup, _str->c_str(), n);
return static_cast<const char*>(dup);
}

View File

@ -15,7 +15,7 @@ class FixedSizeFlashString {
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strcmp_P(actual, expected) == 0;
return strncmp_P(expected, actual, _size) == 0;
}
bool is_null() const {
@ -23,9 +23,9 @@ class FixedSizeFlashString {
}
template <typename Buffer>
const char* save(Buffer* buffer) const {
const char* save(Buffer* memoryPool) const {
if (!_str) return NULL;
void* dup = buffer->alloc(_size);
void* dup = memoryPool->alloc(_size);
if (dup != NULL) memcpy_P(dup, (const char*)_str, _size);
return static_cast<const char*>(dup);
}

View File

@ -4,6 +4,8 @@
#pragma once
#include <string.h> // strcmp
namespace ArduinoJson {
namespace Internals {
@ -22,9 +24,9 @@ class FixedSizeRamString {
}
template <typename Buffer>
const char* save(Buffer* buffer) const {
const char* save(Buffer* memoryPool) const {
if (!_str) return NULL;
void* dup = buffer->alloc(_size);
void* dup = memoryPool->alloc(_size);
if (!dup) return NULL;
memcpy(dup, _str, _size);
return static_cast<const char*>(dup);

View File

@ -14,9 +14,9 @@ class StlString {
StlString(const std::string& str) : _str(&str) {}
template <typename Buffer>
const char* save(Buffer* buffer) const {
const char* save(Buffer* memoryPool) const {
size_t n = _str->length() + 1;
void* dup = buffer->alloc(n);
void* dup = memoryPool->alloc(n);
if (dup != NULL) memcpy(dup, _str->c_str(), n);
return static_cast<const char*>(dup);
}
@ -26,6 +26,7 @@ class StlString {
}
bool equals(const char* expected) const {
if (!expected) return false;
return *_str == expected;
}

View File

@ -14,7 +14,7 @@ class ZeroTerminatedFlashString {
bool equals(const char* expected) const {
const char* actual = reinterpret_cast<const char*>(_str);
if (!actual || !expected) return actual == expected;
return strcmp_P(actual, expected) == 0;
return strcmp_P(expected, actual) == 0;
}
bool is_null() const {
@ -22,10 +22,10 @@ class ZeroTerminatedFlashString {
}
template <typename Buffer>
const char* save(Buffer* buffer) const {
const char* save(Buffer* memoryPool) const {
if (!_str) return NULL;
size_t n = size() + 1; // copy the terminator
void* dup = buffer->alloc(n);
void* dup = memoryPool->alloc(n);
if (dup != NULL) memcpy_P(dup, (const char*)_str, n);
return static_cast<const char*>(dup);
}

View File

@ -22,10 +22,10 @@ class ZeroTerminatedRamString {
}
template <typename Buffer>
const char* save(Buffer* buffer) const {
const char* save(Buffer* memoryPool) const {
if (!_str) return NULL;
size_t n = size() + 1;
void* dup = buffer->alloc(n);
void* dup = memoryPool->alloc(n);
if (!dup) return NULL;
memcpy(dup, _str, n);
return static_cast<const char*>(dup);

View File

@ -4,7 +4,7 @@
#pragma once
#define ARDUINOJSON_VERSION "6.2.2-beta"
#define ARDUINOJSON_VERSION "6.4.0-beta"
#define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 2
#define ARDUINOJSON_VERSION_REVISION 2
#define ARDUINOJSON_VERSION_MINOR 4
#define ARDUINOJSON_VERSION_REVISION 0

View File

@ -38,7 +38,9 @@ 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
@ -53,6 +55,11 @@ 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()
@ -64,10 +71,11 @@ if(MSVC)
)
endif()
add_subdirectory(DynamicJsonBuffer)
add_subdirectory(DynamicMemoryPool)
add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray)
add_subdirectory(JsonDeserializer)
add_subdirectory(JsonDocument)
add_subdirectory(JsonObject)
add_subdirectory(JsonSerializer)
add_subdirectory(JsonVariant)
@ -76,4 +84,4 @@ add_subdirectory(Misc)
add_subdirectory(MsgPackDeserializer)
add_subdirectory(MsgPackSerializer)
add_subdirectory(Numbers)
add_subdirectory(StaticJsonBuffer)
add_subdirectory(StaticMemoryPool)

View File

@ -1,13 +0,0 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(DynamicJsonBufferTests
alloc.cpp
no_memory.cpp
size.cpp
startString.cpp
)
target_link_libraries(DynamicJsonBufferTests catch)
add_test(DynamicJsonBuffer DynamicJsonBufferTests)

View File

@ -1,29 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicJsonBuffer.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("DynamicJsonBuffer::size()") {
DynamicJsonBuffer buffer;
SECTION("Initial size is 0") {
REQUIRE(0 == buffer.size());
}
SECTION("Increases after alloc()") {
buffer.alloc(1);
REQUIRE(1U <= buffer.size());
buffer.alloc(1);
REQUIRE(2U <= buffer.size());
}
SECTION("Goes back to 0 after clear()") {
buffer.alloc(1);
buffer.clear();
REQUIRE(0 == buffer.size());
}
}

View File

@ -0,0 +1,13 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(DynamicMemoryPoolTests
alloc.cpp
no_memory.cpp
size.cpp
startString.cpp
)
target_link_libraries(DynamicMemoryPoolTests catch)
add_test(DynamicMemoryPool DynamicMemoryPoolTests)

View File

@ -18,7 +18,7 @@ std::stringstream allocatorLog;
struct SpyingAllocator : DefaultAllocator {
void* allocate(size_t n) {
allocatorLog << "A" << (n - DynamicJsonBuffer::EmptyBlockSize);
allocatorLog << "A" << (n - DynamicMemoryPool::EmptyBlockSize);
return DefaultAllocator::allocate(n);
}
void deallocate(void* p) {
@ -27,20 +27,20 @@ struct SpyingAllocator : DefaultAllocator {
}
};
TEST_CASE("DynamicJsonBuffer::alloc()") {
TEST_CASE("DynamicMemoryPool::alloc()") {
SECTION("Returns different pointers") {
DynamicJsonBuffer buffer;
void* p1 = buffer.alloc(1);
void* p2 = buffer.alloc(2);
DynamicMemoryPool memoryPool;
void* p1 = memoryPool.alloc(1);
void* p2 = memoryPool.alloc(2);
REQUIRE(p1 != p2);
}
SECTION("Doubles allocation size when full") {
allocatorLog.str("");
{
DynamicJsonBufferBase<SpyingAllocator> buffer(1);
buffer.alloc(1);
buffer.alloc(1);
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.alloc(1);
memoryPool.alloc(1);
}
REQUIRE(allocatorLog.str() == "A1A2FF");
}
@ -48,11 +48,11 @@ TEST_CASE("DynamicJsonBuffer::alloc()") {
SECTION("Resets allocation size after clear()") {
allocatorLog.str("");
{
DynamicJsonBufferBase<SpyingAllocator> buffer(1);
buffer.alloc(1);
buffer.alloc(1);
buffer.clear();
buffer.alloc(1);
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.alloc(1);
memoryPool.alloc(1);
memoryPool.clear();
memoryPool.alloc(1);
}
REQUIRE(allocatorLog.str() == "A1A2FFA1F");
}
@ -60,15 +60,15 @@ TEST_CASE("DynamicJsonBuffer::alloc()") {
SECTION("Makes a big allocation when needed") {
allocatorLog.str("");
{
DynamicJsonBufferBase<SpyingAllocator> buffer(1);
buffer.alloc(42);
DynamicMemoryPoolBase<SpyingAllocator> memoryPool(1);
memoryPool.alloc(42);
}
REQUIRE(allocatorLog.str() == "A42F");
}
SECTION("Alignment") {
// make room for two but not three
DynamicJsonBuffer tinyBuf(2 * sizeof(void*) + 1);
DynamicMemoryPool tinyBuf(2 * sizeof(void*) + 1);
REQUIRE(isAligned(tinyBuf.alloc(1))); // this on is aligned by design
REQUIRE(isAligned(tinyBuf.alloc(1))); // this one fits in the first block

View File

@ -14,8 +14,8 @@ struct NoMemoryAllocator {
void deallocate(void*) {}
};
TEST_CASE("DynamicJsonBuffer no memory") {
DynamicJsonBufferBase<NoMemoryAllocator> _jsonBuffer;
TEST_CASE("DynamicMemoryPool no memory") {
DynamicMemoryPoolBase<NoMemoryAllocator> _memoryPool;
SECTION("FixCodeCoverage") {
// call this function to fix code coverage
@ -33,8 +33,8 @@ TEST_CASE("DynamicJsonBuffer no memory") {
// }
SECTION("startString()") {
DynamicJsonBufferBase<NoMemoryAllocator>::String str =
_jsonBuffer.startString();
DynamicMemoryPoolBase<NoMemoryAllocator>::String str =
_memoryPool.startString();
str.append('!');
REQUIRE(0 == str.c_str());
}

View File

@ -0,0 +1,29 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("DynamicMemoryPool::size()") {
DynamicMemoryPool memoryPool;
SECTION("Initial size is 0") {
REQUIRE(0 == memoryPool.size());
}
SECTION("Increases after alloc()") {
memoryPool.alloc(1);
REQUIRE(1U <= memoryPool.size());
memoryPool.alloc(1);
REQUIRE(2U <= memoryPool.size());
}
SECTION("Goes back to 0 after clear()") {
memoryPool.alloc(1);
memoryPool.clear();
REQUIRE(0 == memoryPool.size());
}
}

View File

@ -2,16 +2,16 @@
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Memory/DynamicJsonBuffer.hpp>
#include <ArduinoJson/Memory/DynamicMemoryPool.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("DynamicJsonBuffer::startString()") {
TEST_CASE("DynamicMemoryPool::startString()") {
SECTION("WorksWhenBufferIsBigEnough") {
DynamicJsonBuffer jsonBuffer(6);
DynamicMemoryPool memoryPool(6);
DynamicJsonBuffer::String str = jsonBuffer.startString();
DynamicMemoryPool::String str = memoryPool.startString();
str.append('h');
str.append('e');
str.append('l');
@ -22,9 +22,9 @@ TEST_CASE("DynamicJsonBuffer::startString()") {
}
SECTION("GrowsWhenBufferIsTooSmall") {
DynamicJsonBuffer jsonBuffer(5);
DynamicMemoryPool memoryPool(5);
DynamicJsonBuffer::String str = jsonBuffer.startString();
DynamicMemoryPool::String str = memoryPool.startString();
str.append('h');
str.append('e');
str.append('l');
@ -35,15 +35,15 @@ TEST_CASE("DynamicJsonBuffer::startString()") {
}
SECTION("SizeIncreases") {
DynamicJsonBuffer jsonBuffer(5);
DynamicMemoryPool memoryPool(5);
DynamicJsonBuffer::String str = jsonBuffer.startString();
REQUIRE(0 == jsonBuffer.size());
DynamicMemoryPool::String str = memoryPool.startString();
REQUIRE(0 == memoryPool.size());
str.append('h');
REQUIRE(1 == jsonBuffer.size());
REQUIRE(1 == memoryPool.size());
str.c_str();
REQUIRE(2 == jsonBuffer.size());
REQUIRE(2 == memoryPool.size());
}
}

View File

@ -7,57 +7,69 @@
TEST_CASE("JsonArray::add()") {
DynamicJsonDocument doc;
JsonArray _array = doc.to<JsonArray>();
JsonArray array = doc.to<JsonArray>();
SECTION("int") {
_array.add(123);
REQUIRE(123 == _array[0].as<int>());
REQUIRE(_array[0].is<int>());
REQUIRE(_array[0].is<double>());
array.add(123);
REQUIRE(123 == array[0].as<int>());
REQUIRE(array[0].is<int>());
REQUIRE(array[0].is<double>());
}
SECTION("double") {
_array.add(123.45);
REQUIRE(123.45 == _array[0].as<double>());
REQUIRE(_array[0].is<double>());
REQUIRE_FALSE(_array[0].is<bool>());
array.add(123.45);
REQUIRE(123.45 == array[0].as<double>());
REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<bool>());
}
SECTION("bool") {
_array.add(true);
REQUIRE(true == _array[0].as<bool>());
REQUIRE(_array[0].is<bool>());
REQUIRE_FALSE(_array[0].is<int>());
array.add(true);
REQUIRE(true == array[0].as<bool>());
REQUIRE(array[0].is<bool>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("const char*") {
const char* str = "hello";
_array.add(str);
REQUIRE(str == _array[0].as<std::string>());
REQUIRE(_array[0].is<const char*>());
REQUIRE_FALSE(_array[0].is<int>());
array.add(str);
REQUIRE(str == array[0].as<std::string>());
REQUIRE(array[0].is<const char*>());
REQUIRE_FALSE(array[0].is<int>());
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("vla") {
int i = 16;
char vla[i];
strcpy(vla, "world");
array.add(vla);
REQUIRE(std::string("world") == array[0]);
}
#endif
SECTION("nested array") {
DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
_array.add(arr);
array.add(arr);
REQUIRE(arr == _array[0].as<JsonArray>());
REQUIRE(_array[0].is<JsonArray>());
REQUIRE_FALSE(_array[0].is<int>());
REQUIRE(arr == array[0].as<JsonArray>());
REQUIRE(array[0].is<JsonArray>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("nested object") {
DynamicJsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
_array.add(obj);
array.add(obj);
REQUIRE(obj == _array[0].as<JsonObject>());
REQUIRE(_array[0].is<JsonObject>());
REQUIRE_FALSE(_array[0].is<int>());
REQUIRE(obj == array[0].as<JsonObject>());
REQUIRE(array[0].is<JsonObject>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("array subscript") {
@ -66,9 +78,9 @@ TEST_CASE("JsonArray::add()") {
JsonArray arr = doc2.to<JsonArray>();
arr.add(str);
_array.add(arr[0]);
array.add(arr[0]);
REQUIRE(str == _array[0]);
REQUIRE(str == array[0]);
}
SECTION("object subscript") {
@ -77,49 +89,49 @@ TEST_CASE("JsonArray::add()") {
JsonObject obj = doc2.to<JsonObject>();
obj["x"] = str;
_array.add(obj["x"]);
array.add(obj["x"]);
REQUIRE(str == _array[0]);
REQUIRE(str == array[0]);
}
SECTION("should not duplicate const char*") {
_array.add("world");
array.add("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char*") {
_array.add(const_cast<char*>("world"));
array.add(const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
_array.add(std::string("world"));
array.add(std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should not duplicate serialized(const char*)") {
_array.add(serialized("{}"));
array.add(serialized("{}"));
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate serialized(char*)") {
_array.add(serialized(const_cast<char*>("{}")));
array.add(serialized(const_cast<char*>("{}")));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate serialized(std::string)") {
_array.add(serialized(std::string("{}")));
array.add(serialized(std::string("{}")));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 2;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate serialized(std::string)") {
_array.add(serialized(std::string("\0XX", 3)));
array.add(serialized(std::string("\0XX", 3)));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3;
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -19,7 +19,7 @@ TEST_CASE("JsonArray::copyFrom()") {
REQUIRE(std::string("[1,2,3]") == json);
}
SECTION("OneDimension_JsonBufferTooSmall") {
SECTION("OneDimension_MemoryPoolTooSmall") {
const size_t SIZE = JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
JsonArray array = doc.to<JsonArray>();
@ -46,16 +46,19 @@ TEST_CASE("JsonArray::copyFrom()") {
REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json);
}
SECTION("TwoDimensions_JsonBufferTooSmall") {
SECTION("TwoDimensions_MemoryPoolTooSmall") {
const size_t SIZE =
JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
JsonArray array = doc.to<JsonArray>();
char json[32];
char json[32] = "";
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
CAPTURE(SIZE)
bool ok = array.copyFrom(source);
REQUIRE_FALSE(ok);
CAPTURE(doc.memoryUsage());
CHECK_FALSE(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5]]") == json);

View File

@ -17,9 +17,9 @@ TEST_CASE("JsonArray::isNull()") {
REQUIRE(array.isNull() == false);
}
SECTION("returns true when allocation fails") {
StaticJsonDocument<1> doc;
JsonArray array = doc.to<JsonArray>();
REQUIRE(array.isNull() == true);
}
/* SECTION("returns true when allocation fails") {
StaticJsonDocument<1> doc;
JsonArray array = doc.to<JsonArray>();
REQUIRE(array.isNull() == true);
}*/
}

View File

@ -30,8 +30,4 @@ TEST_CASE("JsonArray::begin()/end()") {
SECTION("Mutable") {
run_iterator_test<JsonArray::iterator>();
}
SECTION("Const") {
run_iterator_test<JsonArray::const_iterator>();
}
}

View File

@ -9,57 +9,70 @@ using namespace Catch::Matchers;
TEST_CASE("JsonArray::set()") {
DynamicJsonDocument doc;
JsonArray _array = doc.to<JsonArray>();
_array.add(0);
JsonArray array = doc.to<JsonArray>();
array.add(0);
SECTION("int") {
_array.set(0, 123);
REQUIRE(123 == _array[0].as<int>());
REQUIRE(_array[0].is<int>());
REQUIRE_FALSE(_array[0].is<bool>());
array.set(0, 123);
REQUIRE(123 == array[0].as<int>());
REQUIRE(array[0].is<int>());
REQUIRE_FALSE(array[0].is<bool>());
}
SECTION("double") {
_array.set(0, 123.45);
REQUIRE(123.45 == _array[0].as<double>());
REQUIRE(_array[0].is<double>());
REQUIRE_FALSE(_array[0].is<int>());
array.set(0, 123.45);
REQUIRE(123.45 == array[0].as<double>());
REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("bool") {
_array.set(0, true);
REQUIRE(true == _array[0].as<bool>());
REQUIRE(_array[0].is<bool>());
REQUIRE_FALSE(_array[0].is<int>());
array.set(0, true);
REQUIRE(true == array[0].as<bool>());
REQUIRE(array[0].is<bool>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("const char*") {
_array.set(0, "hello");
REQUIRE_THAT(_array[0].as<const char*>(), Equals("hello"));
REQUIRE(_array[0].is<const char*>());
REQUIRE_FALSE(_array[0].is<int>());
array.set(0, "hello");
REQUIRE_THAT(array[0].as<const char*>(), Equals("hello"));
REQUIRE(array[0].is<const char*>());
REQUIRE_FALSE(array[0].is<int>());
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set()") {
int i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array.set(0, vla);
REQUIRE(std::string("world") == array[0]);
}
#endif
SECTION("nested array") {
DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
_array.set(0, arr);
array.set(0, arr);
REQUIRE(arr == _array[0].as<JsonArray>());
REQUIRE(_array[0].is<JsonArray>());
REQUIRE_FALSE(_array[0].is<int>());
REQUIRE(arr == array[0].as<JsonArray>());
REQUIRE(array[0].is<JsonArray>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("nested object") {
DynamicJsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
_array.set(0, obj);
array.set(0, obj);
REQUIRE(obj == _array[0].as<JsonObject>());
REQUIRE(_array[0].is<JsonObject>());
REQUIRE_FALSE(_array[0].is<int>());
REQUIRE(obj == array[0].as<JsonObject>());
REQUIRE(array[0].is<JsonObject>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("array subscript") {
@ -67,9 +80,9 @@ TEST_CASE("JsonArray::set()") {
JsonArray arr = doc2.to<JsonArray>();
arr.add("hello");
_array.set(0, arr[0]);
array.set(0, arr[0]);
REQUIRE_THAT(_array[0].as<char*>(), Equals("hello"));
REQUIRE_THAT(array[0].as<char*>(), Equals("hello"));
}
SECTION("object subscript") {
@ -77,25 +90,25 @@ TEST_CASE("JsonArray::set()") {
JsonObject obj = doc2.to<JsonObject>();
obj["x"] = "hello";
_array.set(0, obj["x"]);
array.set(0, obj["x"]);
REQUIRE_THAT(_array[0].as<char*>(), Equals("hello"));
REQUIRE_THAT(array[0].as<char*>(), Equals("hello"));
}
SECTION("should not duplicate const char*") {
_array.set(0, "world");
array.set(0, "world");
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char*") {
_array.set(0, const_cast<char*>("world"));
array.set(0, const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
_array.set(0, std::string("world"));
array.set(0, std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -8,85 +8,85 @@
TEST_CASE("JsonArray::operator[]") {
DynamicJsonDocument doc;
JsonArray _array = doc.to<JsonArray>();
_array.add(0);
JsonArray array = doc.to<JsonArray>();
array.add(0);
SECTION("int") {
_array[0] = 123;
REQUIRE(123 == _array[0].as<int>());
REQUIRE(true == _array[0].is<int>());
REQUIRE(false == _array[0].is<bool>());
array[0] = 123;
REQUIRE(123 == array[0].as<int>());
REQUIRE(true == array[0].is<int>());
REQUIRE(false == array[0].is<bool>());
}
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
SECTION("long long") {
_array[0] = 9223372036854775807;
REQUIRE(9223372036854775807 == _array[0].as<long long>());
REQUIRE(true == _array[0].is<int>());
REQUIRE(false == _array[0].is<bool>());
array[0] = 9223372036854775807;
REQUIRE(9223372036854775807 == array[0].as<long long>());
REQUIRE(true == array[0].is<int>());
REQUIRE(false == array[0].is<bool>());
}
#endif
SECTION("double") {
_array[0] = 123.45;
REQUIRE(123.45 == _array[0].as<double>());
REQUIRE(true == _array[0].is<double>());
REQUIRE(false == _array[0].is<int>());
array[0] = 123.45;
REQUIRE(123.45 == array[0].as<double>());
REQUIRE(true == array[0].is<double>());
REQUIRE(false == array[0].is<int>());
}
SECTION("bool") {
_array[0] = true;
REQUIRE(true == _array[0].as<bool>());
REQUIRE(true == _array[0].is<bool>());
REQUIRE(false == _array[0].is<int>());
array[0] = true;
REQUIRE(true == array[0].as<bool>());
REQUIRE(true == array[0].is<bool>());
REQUIRE(false == array[0].is<int>());
}
SECTION("const char*") {
const char* str = "hello";
_array[0] = str;
REQUIRE(str == _array[0].as<const char*>());
REQUIRE(str == _array[0].as<char*>()); // <- short hand
REQUIRE(true == _array[0].is<const char*>());
REQUIRE(false == _array[0].is<int>());
array[0] = str;
REQUIRE(str == array[0].as<const char*>());
REQUIRE(str == array[0].as<char*>()); // <- short hand
REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>());
}
SECTION("nested array") {
DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
JsonArray arr2 = doc2.to<JsonArray>();
_array[0] = arr;
array[0] = arr2;
REQUIRE(arr == _array[0].as<JsonArray>());
REQUIRE(arr == _array[0].as<JsonArray>()); // <- short hand
// REQUIRE(arr == _array[0].as<const JsonArray>());
// REQUIRE(arr == _array[0].as<const JsonArray>()); // <- short hand
REQUIRE(true == _array[0].is<JsonArray>());
REQUIRE(false == _array[0].is<int>());
REQUIRE(arr2 == array[0].as<JsonArray>());
REQUIRE(arr2 == array[0].as<JsonArray>()); // <- short hand
// REQUIRE(arr2 == array[0].as<const JsonArray>());
// REQUIRE(arr2 == array[0].as<const JsonArray>()); // <- short hand
REQUIRE(true == array[0].is<JsonArray>());
REQUIRE(false == array[0].is<int>());
}
SECTION("nested object") {
DynamicJsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
_array[0] = obj;
array[0] = obj;
REQUIRE(obj == _array[0].as<JsonObject>());
REQUIRE(obj == _array[0].as<const JsonObject>()); // <- short hand
REQUIRE(true == _array[0].is<JsonObject>());
REQUIRE(false == _array[0].is<int>());
REQUIRE(obj == array[0].as<JsonObject>());
REQUIRE(obj == array[0].as<const JsonObject>()); // <- short hand
REQUIRE(true == array[0].is<JsonObject>());
REQUIRE(false == array[0].is<int>());
}
SECTION("array subscript") {
DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
JsonArray arr2 = doc2.to<JsonArray>();
const char* str = "hello";
arr.add(str);
arr2.add(str);
_array[0] = arr[0];
array[0] = arr2[0];
REQUIRE(str == _array[0]);
REQUIRE(str == array[0]);
}
SECTION("object subscript") {
@ -96,26 +96,55 @@ TEST_CASE("JsonArray::operator[]") {
obj["x"] = str;
_array[0] = obj["x"];
array[0] = obj["x"];
REQUIRE(str == _array[0]);
REQUIRE(str == array[0]);
}
SECTION("should not duplicate const char*") {
_array[0] = "world";
array[0] = "world";
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char*") {
_array[0] = const_cast<char*>("world");
array[0] = const_cast<char*>("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
_array[0] = std::string("world");
array[0] = std::string("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("array[0].to<JsonObject>()") {
JsonObject obj = array[0].to<JsonObject>();
REQUIRE(obj.isNull() == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(VLA)") {
int i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0].set(vla);
REQUIRE(std::string("world") == array[0]);
}
SECTION("operator=(VLA)") {
int i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0] = vla;
REQUIRE(std::string("world") == array[0]);
}
#endif
}

View File

@ -3,15 +3,14 @@
# MIT License
add_executable(JsonDeserializerTests
DeserializationError.cpp
deserializeJsonArray.cpp
deserializeJsonArrayStatic.cpp
deserializeJsonObject.cpp
deserializeJsonObjectStatic.cpp
deserializeJsonValue.cpp
DeserializationError.cpp
input_types.cpp
nestingLimit.cpp
std_istream.cpp
std_string.cpp
)
target_link_libraries(JsonDeserializerTests catch)

View File

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

View File

@ -87,6 +87,14 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
REQUIRE(doc.as<bool>() == false);
}
SECTION("0") {
DeserializationError err = deserializeJson(doc, "0");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<int>() == true);
REQUIRE(doc.as<int>() == 0);
REQUIRE(doc.as<std::string>() == "0"); // issue #808
}
SECTION("NaN") {
DeserializationError err = deserializeJson(doc, "NaN");
REQUIRE(err == DeserializationError::Ok);

View File

@ -6,6 +6,35 @@
#include <catch.hpp>
#include <sstream>
TEST_CASE("deserializeJson(const std::string&)") {
DynamicJsonDocument doc;
SECTION("should accept const string") {
const std::string input("[42]");
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("should accept temporary string") {
DeserializationError err = deserializeJson(doc, std::string("[42]"));
REQUIRE(err == DeserializationError::Ok);
}
SECTION("should duplicate content") {
std::string input("[\"hello\"]");
DeserializationError err = deserializeJson(doc, input);
input[2] = 'X'; // alter the string tomake sure we made a copy
JsonArray array = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(std::string("hello") == array[0]);
}
}
TEST_CASE("deserializeJson(std::istream&)") {
DynamicJsonDocument doc;
@ -71,3 +100,16 @@ TEST_CASE("deserializeJson(std::istream&)") {
REQUIRE('1' == char(json.get()));
}
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
TEST_CASE("deserializeJson(VLA)") {
int i = 9;
char vla[i];
strcpy(vla, "{\"a\":42}");
StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
DeserializationError err = deserializeJson(doc, vla);
REQUIRE(err == DeserializationError::Ok);
}
#endif

View File

@ -1,35 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("deserializeJson(const std::string&)") {
DynamicJsonDocument doc;
SECTION("should accept const string") {
const std::string input("[42]");
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("should accept temporary string") {
DeserializationError err = deserializeJson(doc, std::string("[42]"));
REQUIRE(err == DeserializationError::Ok);
}
SECTION("should duplicate content") {
std::string input("[\"hello\"]");
DeserializationError err = deserializeJson(doc, input);
input[2] = 'X'; // alter the string tomake sure we made a copy
JsonArray array = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(std::string("hello") == array[0]);
}
}

View File

@ -0,0 +1,11 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(JsonDocumentTests
DynamicJsonDocument.cpp
StaticJsonDocument.cpp
)
target_link_libraries(JsonDocumentTests catch)
add_test(JsonDocument JsonDocumentTests)

View File

@ -0,0 +1,41 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("DynamicJsonDocument") {
DynamicJsonDocument doc;
SECTION("serializeJson()") {
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
SECTION("memoryUsage()") {
SECTION("starts at zero") {
REQUIRE(doc.memoryUsage() == 0);
}
SECTION("JSON_ARRAY_SIZE(0)") {
doc.to<JsonArray>();
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
}
SECTION("JSON_ARRAY_SIZE(1)") {
doc.to<JsonArray>().add(42);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
}
SECTION("JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(0)") {
doc.to<JsonArray>().createNestedArray();
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(0));
}
}
}

View File

@ -0,0 +1,20 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("StaticJsonDocument") {
StaticJsonDocument<200> doc;
SECTION("serializeJson()") {
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
}

View File

@ -4,8 +4,11 @@
add_executable(JsonObjectTests
containsKey.cpp
createNestedArray.cpp
createNestedObject.cpp
get.cpp
invalid.cpp
is.cpp
isNull.cpp
iterator.cpp
remove.cpp

View File

@ -8,23 +8,29 @@
TEST_CASE("JsonObject::containsKey()") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj.set("hello", 42);
SECTION("ContainsKeyReturnsFalseForNonExistingKey") {
obj.set("hello", 42);
SECTION("returns false if key not present") {
REQUIRE(false == obj.containsKey("world"));
}
SECTION("ContainsKeyReturnsTrueForDefinedValue") {
obj.set("hello", 42);
SECTION("returns true if key present") {
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("ContainsKeyReturnsFalseAfterRemove") {
obj.set("hello", 42);
SECTION("returns false after remove()") {
obj.remove("hello");
REQUIRE(false == obj.containsKey("hello"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
}

View File

@ -0,0 +1,27 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonObject::createNestedArray()") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
SECTION("key is a const char*") {
JsonArray arr = obj.createNestedArray("hello");
REQUIRE(arr.isNull() == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
JsonArray arr = obj.createNestedArray(vla);
REQUIRE(arr.isNull() == false);
}
#endif
}

View File

@ -0,0 +1,25 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonObject::createNestedObject()") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
SECTION("key is a const char*") {
obj.createNestedObject("hello");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
obj.createNestedObject(vla);
}
#endif
}

View File

@ -11,9 +11,20 @@ TEST_CASE("JsonObject::get()") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
SECTION("GetConstCharPointer_GivenStringLiteral") {
SECTION("get<const char*>(const char*)") {
obj.set("hello", "world");
const char* value = obj.get<const char*>("hello");
REQUIRE_THAT(value, Equals("world"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("get<const char*>(VLA)") {
obj.set("hello", "world");
int i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(std::string("world") == obj.get<char*>(vla));
}
#endif
}

28
test/JsonObject/is.cpp Normal file
View File

@ -0,0 +1,28 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonObject::is<T>()") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["int"] = 42;
obj["str"] = "hello";
SECTION("is<int>(const char*)") {
REQUIRE(true == obj.is<int>("int"));
REQUIRE(false == obj.is<int>("str"));
}
#if HAS_VARIABLE_LENGTH_ARRAY
SECTION("is<T>(VLA)") {
int i = 16;
char vla[i];
strcpy(vla, "int");
REQUIRE(true == obj.is<int>(vla));
}
#endif
}

View File

@ -17,9 +17,9 @@ TEST_CASE("JsonObject::isNull()") {
REQUIRE(array.isNull() == false);
}
SECTION("returns true when allocation fails") {
StaticJsonDocument<1> doc;
JsonObject array = doc.to<JsonObject>();
REQUIRE(array.isNull() == true);
}
/* SECTION("returns true when allocation fails") {
StaticJsonDocument<1> doc;
JsonObject array = doc.to<JsonObject>();
REQUIRE(array.isNull() == true);
}*/
}

View File

@ -16,36 +16,33 @@ TEST_CASE("JsonObject::begin()/end()") {
SECTION("NonConstIterator") {
JsonObject::iterator it = obj.begin();
REQUIRE(obj.end() != it);
REQUIRE_THAT(it->key, Equals("ab"));
REQUIRE(12 == it->value);
it->key = "a.b";
it->value = 1.2;
REQUIRE_THAT(it->key(), Equals("ab"));
REQUIRE(12 == it->value());
++it;
REQUIRE(obj.end() != it);
REQUIRE_THAT(it->key, Equals("cd"));
REQUIRE(34 == it->value);
it->key = "c.d";
it->value = 3.4;
REQUIRE_THAT(it->key(), Equals("cd"));
REQUIRE(34 == it->value());
++it;
REQUIRE(obj.end() == it);
REQUIRE(2 == obj.size());
REQUIRE(1.2 == obj["a.b"]);
REQUIRE(3.4 == obj["c.d"]);
}
SECTION("ConstIterator") {
const JsonObject const_object = obj;
JsonObject::const_iterator it = const_object.begin();
// SECTION("ConstIterator") {
// const JsonObject const_object = obj;
// JsonObject::iterator it = const_object.begin();
REQUIRE(const_object.end() != it);
REQUIRE_THAT(it->key, Equals("ab"));
REQUIRE(12 == it->value);
++it;
REQUIRE(const_object.end() != it);
REQUIRE_THAT(it->key, Equals("cd"));
REQUIRE(34 == it->value);
++it;
REQUIRE(const_object.end() == it);
// REQUIRE(const_object.end() != it);
// REQUIRE_THAT(it->key(), Equals("ab"));
// REQUIRE(12 == it->value());
// ++it;
// REQUIRE(const_object.end() != it);
// REQUIRE_THAT(it->key(), Equals("cd"));
// REQUIRE(34 == it->value());
// ++it;
// REQUIRE(const_object.end() == it);
// }
SECTION("Dereferencing end() is safe") {
REQUIRE(obj.end()->key() == 0);
REQUIRE(obj.end()->value().isNull());
}
}

View File

@ -9,34 +9,64 @@
TEST_CASE("JsonObject::remove()") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["a"] = 0;
obj["b"] = 1;
obj["c"] = 2;
std::string result;
SECTION("SizeDecreased_WhenValuesAreRemoved") {
obj["hello"] = 1;
obj.remove("hello");
REQUIRE(0 == obj.size());
}
SECTION("SizeUntouched_WhenRemoveIsCalledWithAWrongKey") {
obj["hello"] = 1;
obj.remove("world");
REQUIRE(1 == obj.size());
}
SECTION("RemoveByIterator") {
obj["a"] = 0;
obj["b"] = 1;
obj["c"] = 2;
for (JsonObject::iterator it = obj.begin(); it != obj.end(); ++it) {
if (it->value == 1) obj.remove(it);
SECTION("remove(key)") {
SECTION("Remove first") {
obj.remove("a");
serializeJson(obj, result);
REQUIRE("{\"b\":1,\"c\":2}" == result);
}
std::string result;
SECTION("Remove middle") {
obj.remove("b");
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"c\":2}" == result);
}
SECTION("Remove last") {
obj.remove("c");
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"b\":1}" == result);
}
}
SECTION("remove(iterator)") {
JsonObject::iterator it = obj.begin();
SECTION("Remove first") {
obj.remove(it);
serializeJson(obj, result);
REQUIRE("{\"b\":1,\"c\":2}" == result);
}
SECTION("Remove middle") {
++it;
obj.remove(it);
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"c\":2}" == result);
}
SECTION("Remove last") {
it += 2;
obj.remove(it);
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"b\":1}" == result);
}
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a vla") {
int i = 16;
char vla[i];
strcpy(vla, "b");
obj.remove(vla);
serializeJson(obj, result);
REQUIRE("{\"a\":0,\"c\":2}" == result);
}
#endif
}

View File

@ -42,6 +42,38 @@ TEST_CASE("JsonObject::set()") {
REQUIRE_FALSE(obj["hello"].is<long>());
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
obj.set(vla, "world");
REQUIRE(std::string("world") == obj["hello"]);
}
SECTION("value is a VLA") {
int i = 16;
char vla[i];
strcpy(vla, "world");
obj.set("hello", vla);
REQUIRE(std::string("world") == obj["hello"]);
}
SECTION("key and value are VLAs") {
int i = 16;
char vla[i];
strcpy(vla, "world");
obj.set(vla, vla);
REQUIRE(std::string("world") == obj["world"]);
}
#endif
SECTION("nested array") {
DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();

View File

@ -19,9 +19,21 @@ TEST_CASE("JsonObject::size()") {
REQUIRE(1 == obj.size());
}
SECTION("doesn't increase when the smae key is added twice") {
SECTION("decreases when values are removed") {
obj.set("hello", 42);
obj.remove("hello");
REQUIRE(0 == obj.size());
}
SECTION("doesn't increase when the same key is added twice") {
obj["hello"] = 1;
obj["hello"] = 2;
REQUIRE(1 == obj.size());
}
SECTION("doesn't decrease when another key is removed") {
obj["hello"] = 1;
obj.remove("world");
REQUIRE(1 == obj.size());
}
}

View File

@ -159,4 +159,64 @@ TEST_CASE("JsonObject::operator[]") {
REQUIRE(obj.size() == 1);
REQUIRE(obj[null] == 0);
}
SECTION("obj[key].to<JsonArray>()") {
JsonArray arr = obj["hello"].to<JsonArray>();
REQUIRE(arr.isNull() == false);
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("obj[VLA] = str") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
obj[vla] = "world";
REQUIRE(std::string("world") == obj["hello"]);
}
SECTION("obj[str] = VLA") { // issue #416
int i = 32;
char vla[i];
strcpy(vla, "world");
obj["hello"] = vla;
REQUIRE(std::string("world") == obj["hello"].as<char*>());
}
SECTION("obj.set(VLA, str)") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
obj[vla] = "world";
REQUIRE(std::string("world") == obj["hello"]);
}
SECTION("obj.set(str, VLA)") {
int i = 32;
char vla[i];
strcpy(vla, "world");
obj["hello"].set(vla);
REQUIRE(std::string("world") == obj["hello"].as<char*>());
}
SECTION("obj[VLA]") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
deserializeJson(doc, "{\"hello\":\"world\"}");
obj = doc.as<JsonObject>();
REQUIRE(std::string("world") == obj[vla]);
}
#endif
}

View File

@ -8,6 +8,7 @@ add_executable(JsonSerializerTests
JsonObject.cpp
JsonObjectPretty.cpp
JsonVariant.cpp
misc.cpp
std_stream.cpp
std_string.cpp
)

View File

@ -6,9 +6,12 @@
#include <catch.hpp>
#include <limits>
void check(JsonVariant variant, const std::string &expected) {
template <typename T>
void check(T value, const std::string &expected) {
DynamicJsonDocument doc;
doc.to<JsonVariant>().set(value);
char buffer[256] = "";
size_t returnValue = serializeJson(variant, buffer, sizeof(buffer));
size_t returnValue = serializeJson(doc, buffer, sizeof(buffer));
REQUIRE(expected == buffer);
REQUIRE(expected.size() == returnValue);
}
@ -22,14 +25,30 @@ TEST_CASE("serializeJson(JsonVariant)") {
check(static_cast<char *>(0), "null");
}
SECTION("String") {
SECTION("const char*") {
check("hello", "\"hello\"");
}
SECTION("string") {
check(std::string("hello"), "\"hello\"");
}
SECTION("SerializedValue<const char*>") {
check(serialized("[1,2]"), "[1,2]");
}
SECTION("SerializedValue<std::string>") {
check(serialized(std::string("[1,2]")), "[1,2]");
}
SECTION("Double") {
check(3.1415927, "3.1415927");
}
SECTION("Zero") {
check(0, "0");
}
SECTION("Integer") {
check(42, "42");
}

View File

@ -0,0 +1,46 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include <limits>
template <typename T>
void check(T value, const std::string &expected) {
DynamicJsonDocument doc;
doc.to<JsonVariant>().set(value);
char buffer[256] = "";
size_t returnValue = serializeJson(doc, buffer, sizeof(buffer));
REQUIRE(expected == buffer);
REQUIRE(expected.size() == returnValue);
}
TEST_CASE("serializeJson(JsonObjectSubscript)") {
DynamicJsonDocument doc;
deserializeJson(doc, "{\"hello\":42}");
JsonObject obj = doc.as<JsonObject>();
std::string result;
serializeJson(obj["hello"], result);
REQUIRE(result == "42");
}
TEST_CASE("serializeJson(JsonArraySubscript)") {
DynamicJsonDocument doc;
deserializeJson(doc, "[42]");
JsonArray arr = doc.as<JsonArray>();
std::string result;
serializeJson(arr[0], result);
REQUIRE(result == "42");
}
TEST_CASE("serializeJson(JsonVariantSubscript)") {
DynamicJsonDocument doc;
deserializeJson(doc, "[42]");
JsonVariant var = doc.as<JsonVariant>();
std::string result;
serializeJson(var[0], result);
REQUIRE(result == "42");
}

View File

@ -11,16 +11,18 @@ TEST_CASE("operator<<(std::ostream)") {
std::ostringstream os;
SECTION("JsonVariant containing false") {
JsonVariant variant = false;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(false);
os << variant;
REQUIRE("false" == os.str());
}
SECTION("JsonVariant containing string") {
JsonVariant variant = "coucou";
JsonVariant variant = doc.to<JsonVariant>();
variant.set("coucou");
os << variant;
REQUIRE("\"coucou\"" == os.str());

View File

@ -9,223 +9,153 @@
static const char* null = 0;
TEST_CASE("JsonVariant::as()") {
SECTION("DoubleAsBool") {
JsonVariant variant = 4.2;
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("not set") {
REQUIRE(false == variant.as<bool>());
REQUIRE(0 == variant.as<int>());
REQUIRE(0.0f == variant.as<float>());
REQUIRE(0 == variant.as<char*>());
REQUIRE("null" == variant.as<std::string>());
}
SECTION("set(4.2)") {
variant.set(4.2);
REQUIRE(variant.as<bool>());
REQUIRE(0 == variant.as<const char*>());
REQUIRE(variant.as<std::string>() == "4.2");
REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<unsigned>() == 4U);
}
SECTION("DoubleAsCstr") {
JsonVariant variant = 4.2;
REQUIRE_FALSE(variant.as<const char*>());
SECTION("set(0.0)") {
variant.set(0.0);
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<long>() == 0L);
}
SECTION("DoubleAsString") {
JsonVariant variant = 4.2;
REQUIRE(std::string("4.2") == variant.as<std::string>());
SECTION("set(false)") {
variant.set(false);
REQUIRE(false == variant.as<bool>());
REQUIRE(variant.as<double>() == 0.0);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<std::string>() == "false");
}
SECTION("DoubleAsLong") {
JsonVariant variant = 4.2;
REQUIRE(4L == variant.as<long>());
}
SECTION("set(true)") {
variant.set(true);
SECTION("DoubleAsUnsigned") {
JsonVariant variant = 4.2;
REQUIRE(4U == variant.as<unsigned>());
}
SECTION("DoubleZeroAsBool") {
JsonVariant variant = 0.0;
REQUIRE_FALSE(variant.as<bool>());
}
SECTION("DoubleZeroAsLong") {
JsonVariant variant = 0.0;
REQUIRE(0L == variant.as<long>());
}
SECTION("FalseAsBool") {
JsonVariant variant = false;
REQUIRE_FALSE(variant.as<bool>());
}
SECTION("FalseAsDouble") {
JsonVariant variant = false;
REQUIRE(0.0 == variant.as<double>());
}
SECTION("FalseAsLong") {
JsonVariant variant = false;
REQUIRE(0L == variant.as<long>());
}
SECTION("FalseAsString") {
JsonVariant variant = false;
REQUIRE(std::string("false") == variant.as<std::string>());
}
SECTION("TrueAsBool") {
JsonVariant variant = true;
REQUIRE(variant.as<bool>());
REQUIRE(variant.as<double>() == 1.0);
REQUIRE(variant.as<long>() == 1L);
REQUIRE(variant.as<std::string>() == "true");
}
SECTION("TrueAsDouble") {
JsonVariant variant = true;
REQUIRE(1.0 == variant.as<double>());
SECTION("set(42L)") {
variant.set(42L);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == 42.0);
REQUIRE(variant.as<std::string>() == "42");
}
SECTION("TrueAsLong") {
JsonVariant variant = true;
REQUIRE(1L == variant.as<long>());
SECTION("set(-42L)") {
variant.set(-42L);
REQUIRE(variant.as<double>() == -42.0);
REQUIRE(variant.as<std::string>() == "-42");
}
SECTION("TrueAsString") {
JsonVariant variant = true;
REQUIRE(std::string("true") == variant.as<std::string>());
SECTION("set(0L)") {
variant.set(0L);
SECTION("as<bool>()") {
REQUIRE(false == variant.as<bool>());
}
SECTION("as<double>()") {
REQUIRE(variant.as<double>() == 0.0);
}
}
SECTION("LongAsBool") {
JsonVariant variant = 42L;
SECTION("set(null)") {
variant.set(null);
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<double>() == 0.0);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<std::string>() == "null");
}
SECTION("set(\"42\")") {
variant.set("42");
REQUIRE(variant.as<bool>());
REQUIRE(variant.as<long>() == 42L);
}
SECTION("LongZeroAsBool") {
JsonVariant variant = 0L;
REQUIRE_FALSE(variant.as<bool>());
SECTION("set(\"hello\")") {
variant.set("hello");
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<const char*>() == std::string("hello"));
REQUIRE(variant.as<char*>() == std::string("hello"));
REQUIRE(variant.as<std::string>() == std::string("hello"));
}
SECTION("PositiveLongAsDouble") {
JsonVariant variant = 42L;
REQUIRE(42.0 == variant.as<double>());
}
SECTION("set(\"true\")") {
variant.set("true");
SECTION("NegativeLongAsDouble") {
JsonVariant variant = -42L;
REQUIRE(-42.0 == variant.as<double>());
}
SECTION("LongAsString") {
JsonVariant variant = 42L;
REQUIRE(std::string("42") == variant.as<std::string>());
}
SECTION("LongZeroAsDouble") {
JsonVariant variant = 0L;
REQUIRE(0.0 == variant.as<double>());
}
SECTION("NullAsBool") {
JsonVariant variant = null;
REQUIRE_FALSE(variant.as<bool>());
}
SECTION("NullAsDouble") {
JsonVariant variant = null;
REQUIRE(0.0 == variant.as<double>());
}
SECTION("NullAsLong") {
JsonVariant variant = null;
REQUIRE(0L == variant.as<long>());
}
SECTION("NullAsString") {
JsonVariant variant = null;
REQUIRE(std::string("null") == variant.as<std::string>());
}
SECTION("NumberStringAsBool") {
JsonVariant variant = "42";
REQUIRE(variant.as<bool>());
REQUIRE(variant.as<long>() == 1L);
}
SECTION("NumberStringAsLong") {
JsonVariant variant = "42";
REQUIRE(42L == variant.as<long>());
}
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
SECTION("NumberStringAsInt64Negative") {
JsonVariant variant = "-9223372036854775808";
REQUIRE(-9223372036854775807 - 1 == variant.as<long long>());
}
SECTION("NumberStringAsInt64Positive") {
JsonVariant variant = "9223372036854775807";
REQUIRE(9223372036854775807 == variant.as<long long>());
}
#endif
SECTION("RandomStringAsBool") {
JsonVariant variant = "hello";
REQUIRE_FALSE(variant.as<bool>());
}
SECTION("RandomStringAsLong") {
JsonVariant variant = "hello";
REQUIRE(0L == variant.as<long>());
}
SECTION("RandomStringAsConstCharPtr") {
JsonVariant variant = "hello";
REQUIRE(std::string("hello") == variant.as<const char*>());
}
SECTION("RandomStringAsCharPtr") {
JsonVariant variant = "hello";
REQUIRE(std::string("hello") == variant.as<char*>());
}
SECTION("RandomStringAsString") {
JsonVariant variant = "hello";
REQUIRE(std::string("hello") == variant.as<std::string>());
}
SECTION("TrueStringAsBool") {
JsonVariant variant = "true";
REQUIRE(variant.as<bool>());
}
SECTION("TrueStringAsLong") {
JsonVariant variant = "true";
REQUIRE(1L == variant.as<long>());
}
SECTION("ObjectAsString") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
SECTION("to<JsonObject>()") {
JsonObject obj = variant.to<JsonObject>();
obj["key"] = "value";
JsonVariant variant = obj;
REQUIRE(std::string("{\"key\":\"value\"}") == variant.as<std::string>());
SECTION("as<std::string>()") {
REQUIRE(variant.as<std::string>() == std::string("{\"key\":\"value\"}"));
}
SECTION("ObjectAsJsonObject") {
JsonObject o = variant.as<JsonObject>();
REQUIRE(o.size() == 1);
REQUIRE(o["key"] == std::string("value"));
}
}
SECTION("ArrayAsString") {
DynamicJsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
SECTION("to<JsonArray>()") {
JsonArray arr = variant.to<JsonArray>();
arr.add(4);
arr.add(2);
JsonVariant variant = arr;
REQUIRE(std::string("[4,2]") == variant.as<std::string>());
SECTION("as<std::string>()") {
REQUIRE(variant.as<std::string>() == std::string("[4,2]"));
}
SECTION("as<JsonArray>()") {
JsonArray a = variant.as<JsonArray>();
REQUIRE(a.size() == 2);
REQUIRE(a[0] == 4);
REQUIRE(a[1] == 2);
}
}
SECTION("ArrayAsJsonArray") {
DynamicJsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
JsonVariant variant = arr;
REQUIRE(arr == variant.as<JsonArray>());
REQUIRE(arr == variant.as<JsonArray>()); // <- shorthand
#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
SECTION("Smallest int64 negative") {
variant.set("-9223372036854775808");
REQUIRE(variant.as<long long>() == -9223372036854775807 - 1);
}
SECTION("ObjectAsJsonObject") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
JsonVariant variant = obj;
REQUIRE(obj == variant.as<JsonObject>());
REQUIRE(obj == variant.as<JsonObject>()); // <- shorthand
SECTION("Biggerst int64 positive") {
variant.set("9223372036854775807");
REQUIRE(variant.as<long long>() == 9223372036854775807);
}
#endif
}

View File

@ -8,46 +8,58 @@
static const char* null = 0;
template <typename T>
void checkEquals(JsonVariant a, T b) {
REQUIRE(b == a);
REQUIRE(a == b);
REQUIRE(b <= a);
REQUIRE(a <= b);
REQUIRE(b >= a);
REQUIRE(a >= b);
void checkEquals(T a, T b) {
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE_FALSE(b != a);
REQUIRE_FALSE(a != b);
REQUIRE_FALSE(b > a);
REQUIRE_FALSE(a > b);
REQUIRE_FALSE(b < a);
REQUIRE_FALSE(a < b);
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(JsonVariant a, T b) {
REQUIRE(a > b);
REQUIRE(b < a);
REQUIRE(a != b);
REQUIRE(b != a);
void checkGreater(T a, T b) {
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE_FALSE(a < b);
REQUIRE_FALSE(b > a);
REQUIRE_FALSE(a == b);
REQUIRE_FALSE(b == 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(JsonVariant a, T b) {
REQUIRE(a < b);
REQUIRE(b > a);
REQUIRE(a != b);
REQUIRE(b != a);
void checkLower(T a, T b) {
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE_FALSE(a > b);
REQUIRE_FALSE(b < a);
REQUIRE_FALSE(a == b);
REQUIRE_FALSE(b == 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>
@ -99,7 +111,9 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("null") {
JsonVariant variant = null;
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(null);
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
@ -139,7 +153,9 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("String") {
JsonVariant variant = "hello";
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set("hello");
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
@ -163,10 +179,47 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE_FALSE(null == variant);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA equals") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
DynamicJsonDocument doc;
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;
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, doc2, doc3;
JsonVariant variant1 = doc1.to<JsonVariant>();
JsonVariant variant2 = doc2.to<JsonVariant>();
JsonVariant variant3 = doc3.to<JsonVariant>();
SECTION("IntegerInVariant") {
JsonVariant variant1 = 42;
JsonVariant variant2 = 42;
JsonVariant variant3 = 666;
variant1.set(42);
variant2.set(42);
variant3.set(666);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
@ -176,9 +229,9 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("StringInVariant") {
JsonVariant variant1 = "0hello" + 1; // make sure they have
JsonVariant variant2 = "1hello" + 1; // different addresses
JsonVariant variant3 = "world";
variant1.set("0hello" + 1); // make sure they have
variant2.set("1hello" + 1); // different addresses
variant3.set("world");
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
@ -188,9 +241,9 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("DoubleInVariant") {
JsonVariant variant1 = 42.0;
JsonVariant variant2 = 42.0;
JsonVariant variant3 = 666.0;
variant1.set(42.0);
variant2.set(42.0);
variant3.set(666.0);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
@ -200,9 +253,9 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("BoolInVariant") {
JsonVariant variant1 = true;
JsonVariant variant2 = true;
JsonVariant variant3 = false;
variant1.set(true);
variant2.set(true);
variant3.set(false);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
@ -212,14 +265,11 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("ArrayInVariant") {
DynamicJsonDocument doc1;
JsonArray array1 = doc1.to<JsonArray>();
DynamicJsonDocument doc2;
JsonArray array2 = doc2.to<JsonArray>();
JsonArray array1 = variant1.to<JsonArray>();
JsonArray array2 = variant2.to<JsonArray>();
JsonVariant variant1 = array1;
JsonVariant variant2 = array1;
JsonVariant variant3 = array2;
array1.add(42);
array2.add(42);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
@ -229,14 +279,11 @@ TEST_CASE("JsonVariant comparisons") {
}
SECTION("ObjectInVariant") {
DynamicJsonDocument doc1;
JsonObject obj1 = doc1.to<JsonObject>();
DynamicJsonDocument doc2;
JsonObject obj2 = doc2.to<JsonObject>();
JsonObject obj1 = variant1.to<JsonObject>();
JsonObject obj2 = variant2.to<JsonObject>();
JsonVariant variant1 = obj1;
JsonVariant variant2 = obj1;
JsonVariant variant3 = obj2;
obj1["hello"] = "world";
obj2["hello"] = "world";
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
@ -245,22 +292,22 @@ TEST_CASE("JsonVariant comparisons") {
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("VariantsOfDifferentTypes") {
DynamicJsonDocument doc1;
JsonObject obj = doc1.to<JsonObject>();
// SECTION("VariantsOfDifferentTypes") {
// DynamicJsonDocument doc1;
// JsonObject obj = doc1.to<JsonObject>();
DynamicJsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
JsonVariant variants[] = {
true, 42, 666.667, "hello", arr, obj,
};
size_t n = sizeof(variants) / sizeof(variants[0]);
// DynamicJsonDocument doc2;
// 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]);
}
}
}
// 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]);
// }
// }
// }
}

View File

@ -5,61 +5,80 @@
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant copy") {
JsonVariant _variant1;
JsonVariant _variant2;
TEST_CASE("JsonVariant::set(JsonVariant)") {
DynamicJsonDocument doc1;
DynamicJsonDocument doc2;
JsonVariant var1 = doc1.to<JsonVariant>();
JsonVariant var2 = doc2.to<JsonVariant>();
SECTION("IntegersAreCopiedByValue") {
_variant1 = 123;
_variant2 = _variant1;
_variant1 = 456;
SECTION("stores JsonArray by copy") {
JsonArray arr = doc2.to<JsonArray>();
REQUIRE(123 == _variant2.as<int>());
arr.add(42);
var1.set(doc2.as<JsonVariant>());
arr[0] = 666;
REQUIRE(var1.as<std::string>() == "[42]");
}
SECTION("DoublesAreCopiedByValue") {
_variant1 = 123.45;
_variant2 = _variant1;
_variant1 = 456.78;
SECTION("stores JsonObject by copy") {
JsonObject obj = doc2.to<JsonObject>();
REQUIRE(123.45 == _variant2.as<double>());
obj["value"] = 42;
var1.set(doc2.as<JsonVariant>());
obj["value"] = 666;
REQUIRE(var1.as<std::string>() == "{\"value\":42}");
}
SECTION("BooleansAreCopiedByValue") {
_variant1 = true;
_variant2 = _variant1;
_variant1 = false;
SECTION("stores const char* by reference") {
var1.set("hello!!");
var2.set(var1);
REQUIRE(_variant2.as<bool>());
REQUIRE(doc1.memoryUsage() == 0);
REQUIRE(doc2.memoryUsage() == 0);
}
SECTION("StringsAreCopiedByValue") {
_variant1 = "hello";
_variant2 = _variant1;
_variant1 = "world";
SECTION("stores char* by copy") {
char str[] = "hello!!";
REQUIRE(std::string("hello") == _variant2.as<const char*>());
var1.set(str);
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
}
SECTION("ObjectsAreCopiedByReference") {
DynamicJsonDocument doc;
JsonObject object = doc.to<JsonObject>();
SECTION("stores std::string by copy") {
var1.set(std::string("hello!!"));
var2.set(var1);
_variant1 = object;
object["hello"] = "world";
REQUIRE(1 == _variant1.as<JsonObject>().size());
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
}
SECTION("ArraysAreCopiedByReference") {
DynamicJsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("stores Serialized<const char*> by reference") {
var1.set(serialized("hello!!", 8));
var2.set(var1);
_variant1 = array;
REQUIRE(doc1.memoryUsage() == 0);
REQUIRE(doc2.memoryUsage() == 0);
}
array.add("world");
SECTION("stores Serialized<char*> by copy") {
char str[] = "hello!!";
var1.set(serialized(str, 8));
var2.set(var1);
REQUIRE(1 == _variant1.as<JsonArray>().size());
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
}
SECTION("stores Serialized<std::string> by copy") {
var1.set(serialized(std::string("hello!!!")));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
}
}

View File

@ -5,7 +5,11 @@
#include <ArduinoJson.h>
#include <catch.hpp>
void checkIsArray(JsonVariant var) {
void checkIsArray(JsonArray value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<JsonArray>());
REQUIRE(var.is<JsonArray>());
REQUIRE(var.is<const JsonArray>());
@ -16,48 +20,65 @@ void checkIsArray(JsonVariant var) {
REQUIRE_FALSE(var.is<float>());
REQUIRE_FALSE(var.is<int>());
REQUIRE_FALSE(var.is<long>());
REQUIRE_FALSE(var.is<const char*>());
REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonObject>());
}
void checkIsBool(JsonVariant var) {
void checkIsBool(bool value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<bool>());
REQUIRE_FALSE(var.is<double>());
REQUIRE_FALSE(var.is<float>());
REQUIRE_FALSE(var.is<int>());
REQUIRE_FALSE(var.is<long>());
REQUIRE_FALSE(var.is<const char*>());
REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonArray>());
REQUIRE_FALSE(var.is<JsonObject>());
}
void checkIsFloat(JsonVariant var) {
void checkIsFloat(double value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<double>());
REQUIRE(var.is<float>());
REQUIRE_FALSE(var.is<bool>());
REQUIRE_FALSE(var.is<int>());
REQUIRE_FALSE(var.is<long>());
REQUIRE_FALSE(var.is<const char*>());
REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonArray>());
REQUIRE_FALSE(var.is<JsonObject>());
}
void checkIsInteger(JsonVariant var) {
template <typename T>
void checkIsInteger(T value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<long>());
REQUIRE(var.is<int>());
REQUIRE(var.is<float>());
REQUIRE(var.is<double>());
REQUIRE_FALSE(var.is<bool>());
REQUIRE_FALSE(var.is<const char*>());
REQUIRE_FALSE(var.is<const char *>());
REQUIRE_FALSE(var.is<JsonArray>());
REQUIRE_FALSE(var.is<JsonObject>());
}
void checkIsString(JsonVariant var) {
REQUIRE(var.is<const char*>());
void checkIsString(const char *value) {
DynamicJsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.is<const char *>());
REQUIRE_FALSE(var.is<bool>());
REQUIRE_FALSE(var.is<int>());

View File

@ -6,39 +6,42 @@
#include <catch.hpp>
TEST_CASE("JsonVariant::isNull()") {
SECTION("ReturnsFalse_WhenUndefined") {
JsonVariant variant;
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("return true when Undefined") {
REQUIRE(variant.isNull() == true);
}
SECTION("ReturnsTrue_WhenInteger") {
JsonVariant variant = 0;
SECTION("return false when Integer") {
variant.set(42);
REQUIRE(variant.isNull() == false);
}
SECTION("ReturnsTrue_WhenEmptyArray") {
DynamicJsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("return false when EmptyArray") {
DynamicJsonDocument doc2;
JsonArray array = doc2.to<JsonArray>();
JsonVariant variant = array;
variant.set(array);
REQUIRE(variant.isNull() == false);
}
SECTION("ReturnsTrue_WhenEmptyObject") {
DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
SECTION("return false when EmptyObject") {
DynamicJsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
JsonVariant variant = obj;
variant.set(obj);
REQUIRE(variant.isNull() == false);
}
SECTION("ReturnsFalse_WhenInvalidArray") {
JsonVariant variant = JsonArray();
REQUIRE(variant.isNull() == true);
}
SECTION("ReturnsFalse_WhenInvalidObject") {
JsonVariant variant = JsonObject();
REQUIRE(variant.isNull() == true);
}
/* SECTION("return true when InvalidArray") {
variant.set(JsonArray());
REQUIRE(variant.isNull() == true);
}
*/
/* SECTION("return true when InvalidObject") {
variant.set(JsonObject());
REQUIRE(variant.isNull() == true);
}*/
}

View File

@ -5,78 +5,84 @@
#include <ArduinoJson.h>
#include <catch.hpp>
static const JsonVariant undefined;
static const JsonVariant null = static_cast<const char*>(0);
TEST_CASE("JsonVariant::operator|()") {
SECTION("undefined | const char*") {
std::string result = undefined | "default";
REQUIRE(result == "default");
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("undefined") {
SECTION("undefined | const char*") {
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("undefined | int") {
int result = variant | 42;
REQUIRE(result == 42);
}
SECTION("undefined | bool") {
bool result = variant | true;
REQUIRE(result == true);
}
}
SECTION("undefined | int") {
int result = undefined | 42;
REQUIRE(result == 42);
}
SECTION("null") {
variant.set(static_cast<const char*>(0));
SECTION("undefined | bool") {
bool result = undefined | true;
REQUIRE(result == true);
}
SECTION("null | const char*") {
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("null | const char*") {
std::string result = null | "default";
REQUIRE(result == "default");
}
SECTION("null | int") {
int result = variant | 42;
REQUIRE(result == 42);
}
SECTION("null | int") {
int result = null | 42;
REQUIRE(result == 42);
}
SECTION("null | bool") {
bool result = null | true;
REQUIRE(result == true);
SECTION("null | bool") {
bool result = variant | true;
REQUIRE(result == true);
}
}
SECTION("int | const char*") {
JsonVariant variant = 42;
variant.set(42);
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("int | int") {
JsonVariant variant = 0;
variant.set(0);
int result = variant | 666;
REQUIRE(result == 0);
}
SECTION("double | int") {
JsonVariant variant = 42.0;
variant.set(42.0);
int result = variant | 666;
REQUIRE(result == 42);
}
SECTION("bool | bool") {
JsonVariant variant = false;
variant.set(false);
bool result = variant | true;
REQUIRE(result == false);
}
SECTION("int | bool") {
JsonVariant variant = 0;
variant.set(0);
bool result = variant | true;
REQUIRE(result == true);
}
SECTION("const char* | const char*") {
JsonVariant variant = "not default";
variant.set("not default");
std::string result = variant | "default";
REQUIRE(result == "not default");
}
SECTION("const char* | int") {
JsonVariant variant = "not default";
variant.set("not default");
int result = variant | 42;
REQUIRE(result == 42);
}

View File

@ -9,7 +9,10 @@
template <typename T>
void checkValue(T expected) {
JsonVariant variant = expected;
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(expected);
REQUIRE(expected == variant.as<T>());
}
@ -21,11 +24,15 @@ void checkReference(T &expected) {
template <typename T>
void checkNumericType() {
DynamicJsonDocument docMin, docMax;
JsonVariant variantMin = docMin.to<JsonVariant>();
JsonVariant variantMax = docMax.to<JsonVariant>();
T min = std::numeric_limits<T>::min();
T max = std::numeric_limits<T>::max();
JsonVariant variantMin(min);
JsonVariant variantMax(max);
variantMin.set(min);
variantMax.set(max);
REQUIRE(min == variantMin.as<T>());
REQUIRE(max == variantMax.as<T>());
@ -41,9 +48,12 @@ TEST_CASE("JsonVariant set()/get()") {
SECTION("Null") {
checkValue<const char *>(NULL);
}
SECTION("String") {
SECTION("const char*") {
checkValue<const char *>("hello");
}
SECTION("std::string") {
checkValue<std::string>("hello");
}
SECTION("False") {
checkValue<bool>(false);
@ -128,3 +138,74 @@ TEST_CASE("JsonVariant set()/get()") {
checkValue<JsonObject>(object);
}
}
TEST_CASE("JsonVariant and strings") {
DynamicJsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("stores const char* by reference") {
char str[16];
strcpy(str, "hello");
variant.set(static_cast<const char *>(str));
strcpy(str, "world");
REQUIRE(variant == "world");
}
SECTION("stores char* by copy") {
char str[16];
strcpy(str, "hello");
variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
}
SECTION("stores unsigned char* by copy") {
char str[16];
strcpy(str, "hello");
variant.set(reinterpret_cast<unsigned char *>(str));
strcpy(str, "world");
REQUIRE(variant == "hello");
}
SECTION("stores signed char* by copy") {
char str[16];
strcpy(str, "hello");
variant.set(reinterpret_cast<signed char *>(str));
strcpy(str, "world");
REQUIRE(variant == "hello");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("stores VLA by copy") {
int n = 16;
char str[n];
strcpy(str, "hello");
variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
}
#endif
SECTION("stores std::string by copy") {
std::string str;
str = "hello";
variant.set(str);
str.replace(0, 5, "world");
REQUIRE(variant == "hello");
}
// TODO: string
// TODO: serialized()
}

Some files were not shown because too many files have changed in this diff Show More