Copy JsonArray and JsonObject, instead of storing pointers (fixes #780)

This commit is contained in:
Benoit Blanchon
2018-09-11 16:05:56 +02:00
parent 2998a55f0b
commit b106b1ed14
52 changed files with 971 additions and 978 deletions

View File

@ -1,11 +1,16 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
HEAD
----
* Copy `JsonArray` and `JsonObject`, instead of storing pointers (issue #780)
v6.3.0-beta (2018-08-31) v6.3.0-beta (2018-08-31)
----------- -----------
* Implemented reference semantics for `JsonVariant` * Implemented reference semantics for `JsonVariant`
* Replace `JsonPair`'s `key` and `value` with `key()` and `value()` * Replaced `JsonPair`'s `key` and `value` with `key()` and `value()`
* Fixed `serializeJson(obj[key], dst)` (issue #794) * Fixed `serializeJson(obj[key], dst)` (issue #794)
> ### BREAKING CHANGES > ### BREAKING CHANGES

View File

@ -11,22 +11,34 @@
namespace ArduinoJson { namespace ArduinoJson {
namespace Internals { namespace Internals {
// Forward declarations struct JsonObjectData {
struct JsonArrayData; struct Slot* head;
struct JsonObjectData; 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. // The enum JsonVariantType determines which member is in use.
union JsonVariantContent { union JsonVariantContent {
JsonFloat asFloat; // used for double and float JsonFloat asFloat;
JsonUInt asInteger; // used for bool, char, short, int and longs JsonUInt asInteger;
JsonArrayData* asArray; // asArray cannot be null JsonArrayData asArray;
JsonObjectData* asObject; // asObject cannot be null JsonObjectData asObject;
const char* asString; // asString can be null const char* asString;
struct { struct {
const char* data; const char* data;
size_t size; size_t size;
} asRaw; } asRaw;
}; };
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -16,6 +16,10 @@ struct JsonVariantData {
JsonVariantType type; JsonVariantType type;
JsonVariantContent content; JsonVariantContent content;
JsonVariantData() {
type = JSON_NULL;
}
void setBoolean(bool value) { void setBoolean(bool value) {
type = JSON_BOOLEAN; type = JSON_BOOLEAN;
content.asInteger = static_cast<JsonUInt>(value); content.asInteger = static_cast<JsonUInt>(value);
@ -43,13 +47,24 @@ struct JsonVariantData {
content.asInteger = value; content.asInteger = value;
} }
void setString(const char *value) { void setOwnedString(const char *value) {
type = JSON_STRING; type = JSON_OWNED_STRING;
content.asString = value; content.asString = value;
} }
void setRaw(const char *data, size_t size) { void setLinkedString(const char *value) {
type = JSON_RAW; 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.data = data;
content.asRaw.size = size; content.asRaw.size = size;
} }
@ -58,66 +73,70 @@ struct JsonVariantData {
type = JSON_NULL; type = JSON_NULL;
} }
void setArray(JsonArrayData &array) { JsonArrayData *toArray() {
type = JSON_ARRAY; type = JSON_ARRAY;
content.asArray = &array; content.asArray.head = 0;
content.asArray.tail = 0;
return &content.asArray;
} }
void setObject(JsonObjectData &object) { JsonObjectData *toObject() {
type = JSON_OBJECT; type = JSON_OBJECT;
content.asObject = &object; content.asObject.head = 0;
content.asObject.tail = 0;
return &content.asObject;
} }
JsonArrayData *asArray() const { JsonArrayData *asArray() {
return type == JSON_ARRAY ? content.asArray : 0; return type == JSON_ARRAY ? &content.asArray : 0;
} }
JsonObjectData *asObject() const { JsonObjectData *asObject() {
return type == JSON_OBJECT ? content.asObject : 0; return type == JSON_OBJECT ? &content.asObject : 0;
} }
template <typename T> template <typename T>
T asInteger() const { T asInteger() const {
switch (type) { switch (type) {
case JSON_NULL:
case JSON_RAW:
return 0;
case JSON_POSITIVE_INTEGER: case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN: case JSON_BOOLEAN:
return T(content.asInteger); return T(content.asInteger);
case JSON_NEGATIVE_INTEGER: case JSON_NEGATIVE_INTEGER:
return T(~content.asInteger + 1); return T(~content.asInteger + 1);
case JSON_STRING: case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return parseInteger<T>(content.asString); return parseInteger<T>(content.asString);
default: case JSON_FLOAT:
return T(content.asFloat); return T(content.asFloat);
default:
return 0;
} }
} }
template <typename T> template <typename T>
T asFloat() const { T asFloat() const {
switch (type) { switch (type) {
case JSON_NULL:
case JSON_RAW:
return 0;
case JSON_POSITIVE_INTEGER: case JSON_POSITIVE_INTEGER:
case JSON_BOOLEAN: case JSON_BOOLEAN:
return static_cast<T>(content.asInteger); return static_cast<T>(content.asInteger);
case JSON_NEGATIVE_INTEGER: case JSON_NEGATIVE_INTEGER:
return -static_cast<T>(content.asInteger); return -static_cast<T>(content.asInteger);
case JSON_STRING: case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return parseFloat<T>(content.asString); return parseFloat<T>(content.asString);
default: case JSON_FLOAT:
return static_cast<T>(content.asFloat); return static_cast<T>(content.asFloat);
default:
return 0;
} }
} }
const char *asString() const { const char *asString() const {
return type == JSON_STRING ? content.asString : NULL; return isString() ? content.asString : NULL;
} }
bool isArray() const { bool isArray() const {
return type == Internals::JSON_ARRAY; return type == JSON_ARRAY;
} }
bool isBoolean() const { bool isBoolean() const {
@ -138,43 +157,11 @@ struct JsonVariantData {
} }
bool isObject() const { bool isObject() const {
return type == Internals::JSON_OBJECT; return type == JSON_OBJECT;
} }
bool isString() const { bool isString() const {
return type == Internals::JSON_STRING; return type == JSON_LINKED_STRING || type == JSON_OWNED_STRING;
}
template <typename Visitor>
void visit(Visitor &visitor) const {
switch (type) {
case JSON_FLOAT:
return visitor.acceptFloat(content.asFloat);
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_RAW:
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:
return visitor.acceptNull();
}
} }
}; };
} // namespace Internals } // namespace Internals

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. // Enumerated type to know the current type of a JsonVariant.
// The value determines which member of JsonVariantContent is used. // The value determines which member of JsonVariantContent is used.
enum JsonVariantType { enum JsonVariantType {
JSON_NULL, // JsonVariant has not been initialized JSON_NULL,
JSON_RAW, // JsonVariant contains a raw string that should not be escaped JSON_LINKED_RAW,
JSON_STRING, // JsonVariant stores a const char* JSON_OWNED_RAW,
JSON_BOOLEAN, // JsonVariant stores a bool JSON_LINKED_STRING,
JSON_POSITIVE_INTEGER, // JsonVariant stores an JsonUInt JSON_OWNED_STRING,
JSON_NEGATIVE_INTEGER, // JsonVariant stores an JsonUInt that must be negated JSON_BOOLEAN,
JSON_ARRAY, // JsonVariant stores a pointer to a JsonArrayData JSON_POSITIVE_INTEGER,
JSON_OBJECT, // JsonVariant stores a pointer to a JsonObjectData JSON_NEGATIVE_INTEGER,
JSON_FLOAT // JsonVariant stores a JsonFloat JSON_ARRAY,
JSON_OBJECT,
JSON_FLOAT
}; };
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -1,83 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../Memory/MemoryPool.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;
List() : _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(MemoryPool *memoryPool) {
node_type *newNode = new (memoryPool) 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;
}
}
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/AllocableInMemoryPool.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::AllocableInMemoryPool {
ListNode() NOEXCEPT : 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

@ -36,7 +36,7 @@ deserialize(TDocument &doc, const TString &input) {
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input), doc.memoryPool(), makeReader(input),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariantData>()); .parse(doc.template to<JsonVariant>());
} }
// //
// DeserializationError deserialize(TDocument& doc, TChar* input); // DeserializationError deserialize(TDocument& doc, TChar* input);
@ -49,7 +49,7 @@ DeserializationError deserialize(TDocument &doc, TChar *input) {
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input), doc.memoryPool(), makeReader(input),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariantData>()); .parse(doc.template to<JsonVariant>());
} }
// //
// DeserializationError deserialize(TDocument& doc, TChar* input, size_t // DeserializationError deserialize(TDocument& doc, TChar* input, size_t
@ -64,7 +64,7 @@ DeserializationError deserialize(TDocument &doc, TChar *input,
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input, inputSize), doc.memoryPool(), makeReader(input, inputSize),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariantData>()); .parse(doc.template to<JsonVariant>());
} }
// //
// DeserializationError deserialize(TDocument& doc, TStream input); // DeserializationError deserialize(TDocument& doc, TStream input);
@ -77,7 +77,7 @@ DeserializationError deserialize(TDocument &doc, TStream &input) {
return makeDeserializer<TDeserializer>( return makeDeserializer<TDeserializer>(
doc.memoryPool(), makeReader(input), doc.memoryPool(), makeReader(input),
makeStringStorage(doc.memoryPool(), input), doc.nestingLimit) makeStringStorage(doc.memoryPool(), input), doc.nestingLimit)
.parse(doc.template to<JsonVariantData>()); .parse(doc.template to<JsonVariant>());
} }
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -4,8 +4,7 @@
#pragma once #pragma once
#include "JsonArray.hpp" #include "Data/JsonVariantTo.hpp"
#include "JsonObject.hpp"
#include "JsonVariant.hpp" #include "JsonVariant.hpp"
#include "Memory/DynamicMemoryPool.hpp" #include "Memory/DynamicMemoryPool.hpp"
@ -30,49 +29,10 @@ class DynamicJsonDocument {
return getVariant().as<T>(); return getVariant().as<T>();
} }
// JsonObject to<JsonObject>()
template <typename T> template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonObject>::value, typename Internals::JsonVariantTo<T>::type to() {
JsonObject>::type _memoryPool.clear();
to() { return getVariant().to<T>();
clear();
JsonObject object(&_memoryPool);
getVariant().set(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(&_memoryPool);
getVariant().set(array);
return array;
}
// JsonVariant to<JsonVariant>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
JsonVariant>::type
to() {
clear();
return getVariant();
}
// JsonVariantData& to<JsonVariantData>()
template <typename T>
typename Internals::enable_if<
Internals::is_same<T, Internals::JsonVariantData>::value,
Internals::JsonVariantData&>::type
to() {
clear();
return _rootData;
}
Internals::DynamicMemoryPool& memoryPool() {
return _memoryPool;
} }
void clear() { void clear() {
@ -85,8 +45,12 @@ class DynamicJsonDocument {
} }
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void accept(Visitor& visitor) const {
return _rootData.visit(visitor); return getVariant().accept(visitor);
}
Internals::DynamicMemoryPool& memoryPool() {
return _memoryPool;
} }
private: private:

View File

@ -25,7 +25,7 @@ class JsonDeserializer {
_stringStorage(stringStorage), _stringStorage(stringStorage),
_nestingLimit(nestingLimit), _nestingLimit(nestingLimit),
_loaded(false) {} _loaded(false) {}
DeserializationError parse(JsonVariantData &variant) { DeserializationError parse(JsonVariant variant) {
DeserializationError err = skipSpacesAndComments(); DeserializationError err = skipSpacesAndComments();
if (err) return err; if (err) return err;
@ -65,12 +65,11 @@ class JsonDeserializer {
return true; return true;
} }
DeserializationError parseArray(JsonVariantData &variant) { DeserializationError parseArray(JsonVariant variant) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
JsonArrayData *array = new (_memoryPool) JsonArrayData; JsonArray array = variant.to<JsonArray>();
if (!array) return DeserializationError::NoMemory; if (array.isNull()) return DeserializationError::NoMemory;
variant.setArray(*array);
// Check opening braket // Check opening braket
if (!eat('[')) return DeserializationError::InvalidInput; if (!eat('[')) return DeserializationError::InvalidInput;
@ -85,12 +84,12 @@ class JsonDeserializer {
// Read each value // Read each value
for (;;) { for (;;) {
// Allocate slot in array // Allocate slot in array
JsonVariantData *value = array->addSlot(_memoryPool); JsonVariant value = array.add();
if (!value) return DeserializationError::NoMemory; if (value.isInvalid()) return DeserializationError::NoMemory;
// 1 - Parse value // 1 - Parse value
_nestingLimit--; _nestingLimit--;
err = parse(*value); err = parse(value);
_nestingLimit++; _nestingLimit++;
if (err) return err; if (err) return err;
@ -104,12 +103,11 @@ class JsonDeserializer {
} }
} }
DeserializationError parseObject(JsonVariantData &variant) { DeserializationError parseObject(JsonVariant variant) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
JsonObjectData *object = new (_memoryPool) JsonObjectData; JsonObject object = variant.to<JsonObject>();
if (!object) return DeserializationError::NoMemory; if (object.isNull()) return DeserializationError::NoMemory;
variant.setObject(*object);
// Check opening brace // Check opening brace
if (!eat('{')) return DeserializationError::InvalidInput; if (!eat('{')) return DeserializationError::InvalidInput;
@ -134,12 +132,12 @@ class JsonDeserializer {
if (!eat(':')) return DeserializationError::InvalidInput; if (!eat(':')) return DeserializationError::InvalidInput;
// Allocate slot in object // Allocate slot in object
JsonVariantData *value = object->addSlot(_memoryPool, key); JsonVariant value = object.set(key);
if (!value) return DeserializationError::NoMemory; if (value.isInvalid()) return DeserializationError::NoMemory;
// Parse value // Parse value
_nestingLimit--; _nestingLimit--;
err = parse(*value); err = parse(value);
_nestingLimit++; _nestingLimit++;
if (err) return err; if (err) return err;
@ -157,7 +155,7 @@ class JsonDeserializer {
} }
} }
DeserializationError parseValue(JsonVariantData &variant) { DeserializationError parseValue(JsonVariant variant) {
if (isQuote(current())) { if (isQuote(current())) {
return parseStringValue(variant); return parseStringValue(variant);
} else { } else {
@ -173,11 +171,11 @@ class JsonDeserializer {
} }
} }
DeserializationError parseStringValue(JsonVariantData &variant) { DeserializationError parseStringValue(JsonVariant variant) {
const char *value; const char *value;
DeserializationError err = parseQuotedString(&value); DeserializationError err = parseQuotedString(&value);
if (err) return err; if (err) return err;
variant.setString(value); variant.set(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -235,7 +233,7 @@ class JsonDeserializer {
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError parseNumericValue(JsonVariantData &result) { DeserializationError parseNumericValue(JsonVariant result) {
char buffer[64]; char buffer[64];
uint8_t n = 0; uint8_t n = 0;
@ -248,15 +246,15 @@ class JsonDeserializer {
buffer[n] = 0; buffer[n] = 0;
if (isInteger(buffer)) { if (isInteger(buffer)) {
result.setInteger(parseInteger<JsonInteger>(buffer)); result.set(parseInteger<JsonInteger>(buffer));
} else if (isFloat(buffer)) { } else if (isFloat(buffer)) {
result.setFloat(parseFloat<JsonFloat>(buffer)); result.set(parseFloat<JsonFloat>(buffer));
} else if (!strcmp(buffer, "true")) { } else if (!strcmp(buffer, "true")) {
result.setBoolean(true); result.set(true);
} else if (!strcmp(buffer, "false")) { } else if (!strcmp(buffer, "false")) {
result.setBoolean(false); result.set(false);
} else if (!strcmp(buffer, "null")) { } else if (!strcmp(buffer, "null")) {
result.setNull(); // already null
} else { } else {
return DeserializationError::InvalidInput; return DeserializationError::InvalidInput;
} }

View File

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

View File

@ -4,13 +4,17 @@
#pragma once #pragma once
#include "JsonArrayData.hpp" #include "Data/JsonVariantData.hpp"
#include "JsonArrayIterator.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 { namespace ArduinoJson {
class JsonObject; class JsonObject;
namespace Internals { namespace Internals {
class JsonArraySubscript; class JsonArraySubscript;
} }
@ -25,29 +29,52 @@ class JsonArray {
FORCE_INLINE JsonArray(Internals::MemoryPool* buf, FORCE_INLINE JsonArray(Internals::MemoryPool* buf,
Internals::JsonArrayData* arr) Internals::JsonArrayData* arr)
: _memoryPool(buf), _data(arr) {} : _memoryPool(buf), _data(arr) {}
FORCE_INLINE explicit JsonArray(Internals::MemoryPool* buf)
: _memoryPool(buf), _data(new (buf) Internals::JsonArrayData()) {}
// Adds the specified value at the end of the array. // Adds the specified value at the end of the array.
// //
// bool add(TValue); // bool add(TValue);
// TValue = bool, long, int, short, float, double, serialized, JsonVariant, // TValue = bool, long, int, short, float, double, serialized, JsonVariant,
// std::string, String, JsonArrayData, JsonObject // std::string, String, JsonObject
template <typename T> template <typename T>
FORCE_INLINE bool add(const T& value) { FORCE_INLINE bool add(const T& value) {
return add_impl<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); // bool add(TValue);
// TValue = char*, const char*, const FlashStringHelper* // TValue = char*, const char*, const FlashStringHelper*
template <typename T> template <typename T>
FORCE_INLINE bool add(T* value) { FORCE_INLINE bool add(T* value) {
return add_impl<T*>(value); return add().set(value);
}
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 { FORCE_INLINE iterator begin() const {
if (!_data) return iterator(); if (!_data) return iterator();
return iterator(_memoryPool, _data->begin()); return iterator(_memoryPool, _data->head);
} }
FORCE_INLINE iterator end() const { FORCE_INLINE iterator end() const {
@ -83,6 +110,15 @@ class JsonArray {
return ok; 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 // Exports a 1D array
template <typename T, size_t N> template <typename T, size_t N>
FORCE_INLINE size_t copyTo(T (&array)[N]) const { FORCE_INLINE size_t copyTo(T (&array)[N]) const {
@ -115,8 +151,17 @@ class JsonArray {
FORCE_INLINE const Internals::JsonArraySubscript operator[]( FORCE_INLINE const Internals::JsonArraySubscript operator[](
size_t index) const; size_t index) const;
FORCE_INLINE bool operator==(const JsonArray& rhs) const { FORCE_INLINE bool operator==(JsonArray rhs) const {
return _data == rhs._data; 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. // Gets the value at the specified index.
@ -137,7 +182,18 @@ class JsonArray {
// Removes element at specified position. // Removes element at specified position.
FORCE_INLINE void remove(iterator it) { FORCE_INLINE void remove(iterator it) {
if (!_data) return; if (!_data) return;
_data->remove(it.internal());
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. // Removes element at specified index.
@ -163,10 +219,24 @@ class JsonArray {
if (!_data) return false; if (!_data) return false;
return set_impl<T*>(index, value); return set_impl<T*>(index, value);
} }
// 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);
}
FORCE_INLINE size_t size() const { FORCE_INLINE size_t size() const {
if (!_data) return 0; if (!_data) return 0;
return _data->size(); Internals::Slot* slot = _data->head;
size_t n = 0;
while (slot) {
slot = slot->next;
n++;
}
return n;
} }
FORCE_INLINE bool isNull() const { FORCE_INLINE bool isNull() const {
@ -174,11 +244,11 @@ class JsonArray {
} }
template <typename Visitor> template <typename Visitor>
FORCE_INLINE void visit(Visitor& visitor) const { FORCE_INLINE void accept(Visitor& visitor) const {
if (_data) if (_data)
visitor.acceptArray(*_data); visitor.visitArray(*this);
else else
visitor.acceptNull(); visitor.visitNull();
} }
private: private:
@ -191,10 +261,7 @@ class JsonArray {
template <typename TValueRef> template <typename TValueRef>
FORCE_INLINE bool add_impl(TValueRef value) { FORCE_INLINE bool add_impl(TValueRef value) {
if (!_data) return false; return add().set(value);
iterator it = iterator(_memoryPool, _data->add(_memoryPool));
if (it == end()) return false;
return it->set(value);
} }
Internals::MemoryPool* _memoryPool; Internals::MemoryPool* _memoryPool;

View File

@ -1,28 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/JsonVariantData.hpp"
#include "Data/List.hpp"
#include "Memory/AllocableInMemoryPool.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 StaticMemoryPool.
#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<JsonVariantData>, AllocableInMemoryPool {
JsonVariantData* addSlot(MemoryPool* memoryPool) {
iterator it = add(memoryPool);
return it != end() ? &*it : 0;
}
};
} // namespace Internals
} // namespace ArduinoJson

View File

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

View File

@ -4,7 +4,7 @@
#pragma once #pragma once
#include "Data/ListIterator.hpp" #include "Data/Slot.hpp"
#include "JsonVariant.hpp" #include "JsonVariant.hpp"
namespace ArduinoJson { namespace ArduinoJson {
@ -28,45 +28,46 @@ class JsonVariantPtr {
}; };
class JsonArrayIterator { class JsonArrayIterator {
typedef Internals::ListIterator<Internals::JsonVariantData> internal_iterator;
public: public:
JsonArrayIterator() {} JsonArrayIterator() : _slot(0) {}
explicit JsonArrayIterator(Internals::MemoryPool *memoryPool, explicit JsonArrayIterator(Internals::MemoryPool *memoryPool,
internal_iterator iterator) Internals::Slot *iterator)
: _iterator(iterator), _memoryPool(memoryPool) {} : _memoryPool(memoryPool), _slot(iterator) {}
JsonVariant operator*() const { JsonVariant operator*() const {
return JsonVariant(_memoryPool, &*_iterator); return JsonVariant(_memoryPool, &_slot->value);
} }
JsonVariantPtr operator->() { JsonVariantPtr operator->() {
return JsonVariantPtr(_memoryPool, &*_iterator); return JsonVariantPtr(_memoryPool, &_slot->value);
} }
bool operator==(const JsonArrayIterator &other) const { bool operator==(const JsonArrayIterator &other) const {
return _iterator == other._iterator; return _slot == other._slot;
} }
bool operator!=(const JsonArrayIterator &other) const { bool operator!=(const JsonArrayIterator &other) const {
return _iterator != other._iterator; return _slot != other._slot;
} }
JsonArrayIterator &operator++() { JsonArrayIterator &operator++() {
++_iterator; _slot = _slot->next;
return *this; return *this;
} }
JsonArrayIterator &operator+=(size_t distance) { JsonArrayIterator &operator+=(size_t distance) {
_iterator += distance; while (distance && _slot) {
_slot = _slot->next;
distance--;
}
return *this; return *this;
} }
internal_iterator internal() { Internals::Slot *internal() {
return _iterator; return _slot;
} }
private: private:
internal_iterator _iterator;
Internals::MemoryPool *_memoryPool; Internals::MemoryPool *_memoryPool;
Internals::Slot *_slot;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -57,6 +57,11 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
return _array.is<T>(_index); 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 // Replaces the value
// //
// bool set(const TValue&) // bool set(const TValue&)
@ -75,8 +80,8 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
} }
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void accept(Visitor& visitor) const {
return _array.get<JsonVariant>(_index).visit(visitor); return _array.get<JsonVariant>(_index).accept(visitor);
} }
private: private:

View File

@ -4,14 +4,17 @@
#pragma once #pragma once
#include "./JsonObjectData.hpp"
#include "./JsonObjectIterator.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 { namespace ArduinoJson {
class JsonObject { class JsonObject {
friend class JsonVariant; friend class JsonVariant;
typedef Internals::JsonObjectData::iterator internal_iterator;
public: public:
typedef JsonObjectIterator iterator; typedef JsonObjectIterator iterator;
@ -20,12 +23,15 @@ class JsonObject {
FORCE_INLINE JsonObject(Internals::MemoryPool* buf, FORCE_INLINE JsonObject(Internals::MemoryPool* buf,
Internals::JsonObjectData* object) Internals::JsonObjectData* object)
: _memoryPool(buf), _data(object) {} : _memoryPool(buf), _data(object) {}
FORCE_INLINE explicit JsonObject(Internals::MemoryPool* buf)
: _memoryPool(buf), _data(new (buf) Internals::JsonObjectData()) {}
FORCE_INLINE iterator begin() const { FORCE_INLINE iterator begin() const {
if (!_data) return iterator(); if (!_data) return iterator();
return iterator(_memoryPool, _data->begin()); return iterator(_memoryPool, _data->head);
}
void clear() {
_data->head = 0;
_data->tail = 0;
} }
// Tells weither the specified key is present and associated with a value. // Tells weither the specified key is present and associated with a value.
@ -44,6 +50,15 @@ class JsonObject {
return containsKey_impl<TString*>(key); return containsKey_impl<TString*>(key);
} }
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;
}
FORCE_INLINE iterator end() const { FORCE_INLINE iterator end() const {
return iterator(); return iterator();
} }
@ -155,13 +170,26 @@ class JsonObject {
return Internals::JsonObjectSubscript<TString*>(*this, key); return Internals::JsonObjectSubscript<TString*>(*this, key);
} }
FORCE_INLINE bool operator==(const JsonObject& rhs) const { FORCE_INLINE bool operator==(JsonObject rhs) const {
return _data == rhs._data; 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;
} }
FORCE_INLINE void remove(iterator it) { FORCE_INLINE void remove(iterator it) {
if (!_data) return; if (!_data) return;
_data->remove(it.internal()); 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. // Removes the specified key and the associated value.
@ -188,7 +216,7 @@ class JsonObject {
// std::string, String, JsonArray, JsonObject // std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString> template <typename TValue, typename TString>
FORCE_INLINE bool set(const TString& key, const TValue& value) { FORCE_INLINE bool set(const TString& key, const TValue& value) {
return set_impl<const TString&, const TValue&>(key, value); return set(key).set(value);
} }
// //
// bool set(TKey, TValue); // bool set(TKey, TValue);
@ -196,7 +224,7 @@ class JsonObject {
// TValue = char*, const char*, const FlashStringHelper* // TValue = char*, const char*, const FlashStringHelper*
template <typename TValue, typename TString> template <typename TValue, typename TString>
FORCE_INLINE bool set(const TString& key, TValue* value) { FORCE_INLINE bool set(const TString& key, TValue* value) {
return set_impl<const TString&, TValue*>(key, value); return set(key).set(value);
} }
// //
// bool set(TKey, const TValue&); // bool set(TKey, const TValue&);
@ -205,7 +233,7 @@ class JsonObject {
// std::string, String, JsonArray, JsonObject // std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString> template <typename TValue, typename TString>
FORCE_INLINE bool set(TString* key, const TValue& value) { FORCE_INLINE bool set(TString* key, const TValue& value) {
return set_impl<TString*, const TValue&>(key, value); return set(key).set(value);
} }
// //
// bool set(TKey, TValue); // bool set(TKey, TValue);
@ -213,12 +241,28 @@ class JsonObject {
// TValue = char*, const char*, const FlashStringHelper* // TValue = char*, const char*, const FlashStringHelper*
template <typename TValue, typename TString> template <typename TValue, typename TString>
FORCE_INLINE bool set(TString* key, TValue* value) { FORCE_INLINE bool set(TString* key, TValue* value) {
return set_impl<TString*, TValue*>(key, value); return set(key).set(value);
}
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 { FORCE_INLINE size_t size() const {
if (!_data) return 0; if (!_data) return 0;
return _data->size(); size_t n = 0;
Internals::Slot* slot = _data->head;
while (slot) {
n++;
slot = slot->next;
}
return n;
} }
FORCE_INLINE bool isNull() const { FORCE_INLINE bool isNull() const {
@ -226,17 +270,17 @@ class JsonObject {
} }
template <typename Visitor> template <typename Visitor>
FORCE_INLINE void visit(Visitor& visitor) const { FORCE_INLINE void accept(Visitor& visitor) const {
if (_data) if (_data)
visitor.acceptObject(*_data); visitor.visitObject(*this);
else else
visitor.acceptNull(); visitor.visitNull();
} }
private: private:
template <typename TStringRef> template <typename TStringRef>
FORCE_INLINE bool containsKey_impl(TStringRef key) const { FORCE_INLINE bool containsKey_impl(TStringRef key) const {
return findKey<TStringRef>(key) != _data->end(); return findSlot<TStringRef>(key) != 0;
} }
template <typename TStringRef> template <typename TStringRef>
@ -247,77 +291,95 @@ class JsonObject {
// Returns the list node that matches the specified key. // Returns the list node that matches the specified key.
template <typename TStringRef> template <typename TStringRef>
internal_iterator findKey(TStringRef key) { Internals::Slot* findSlot(TStringRef key) {
if (!_data) return internal_iterator(); if (!_data) return 0;
internal_iterator it; Internals::Slot* slot = _data->head;
for (it = _data->begin(); it != _data->end(); ++it) { while (slot) {
if (Internals::makeString(key).equals(it->key)) break; if (Internals::makeString(key).equals(slot->key)) break;
slot = slot->next;
} }
return it; return slot;
} }
template <typename TStringRef> template <typename TStringRef>
FORCE_INLINE internal_iterator findKey(TStringRef key) const { FORCE_INLINE Internals::Slot* findSlot(TStringRef key) const {
return const_cast<JsonObject*>(this)->findKey<TStringRef>(key); return const_cast<JsonObject*>(this)->findSlot<TStringRef>(key);
} }
template <typename TStringRef, typename TValue> template <typename TStringRef, typename TValue>
FORCE_INLINE typename Internals::JsonVariantAs<TValue>::type get_impl( FORCE_INLINE typename Internals::JsonVariantAs<TValue>::type get_impl(
TStringRef key) const { TStringRef key) const {
internal_iterator it = findKey<TStringRef>(key); Internals::Slot* slot = findSlot<TStringRef>(key);
return it != _data->end() return slot ? JsonVariant(_memoryPool, &slot->value).as<TValue>()
? JsonVariant(_memoryPool, &it->value).as<TValue>()
: TValue(); : TValue();
} }
template <typename TStringRef, typename TValue> template <typename TStringRef, typename TValue>
FORCE_INLINE bool is_impl(TStringRef key) const { FORCE_INLINE bool is_impl(TStringRef key) const {
internal_iterator it = findKey<TStringRef>(key); Internals::Slot* slot = findSlot<TStringRef>(key);
return it != _data->end() return slot ? JsonVariant(_memoryPool, &slot->value).is<TValue>() : false;
? JsonVariant(_memoryPool, &it->value).is<TValue>()
: false;
} }
template <typename TStringRef> template <typename TStringRef>
FORCE_INLINE void remove_impl(TStringRef key) { FORCE_INLINE void remove_impl(TStringRef key) {
if (!_data) return; 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> template <typename TStringRef>
FORCE_INLINE bool set_impl(TStringRef key, TValueRef value) { FORCE_INLINE JsonVariant set_impl(TStringRef key) {
if (!_data) return false; if (!_data) return JsonVariant();
// ignore null key // ignore null key
if (Internals::makeString(key).is_null()) return false; if (Internals::makeString(key).is_null()) return JsonVariant();
// search a matching key // search a matching key
internal_iterator it = findKey<TStringRef>(key); Internals::Slot* slot = findSlot<TStringRef>(key);
if (it == _data->end()) { if (!slot) {
// add the key // add the key
// TODO: use JsonPairData directly, we don't need an iterator slot = new (_memoryPool) Internals::Slot();
it = _data->add(_memoryPool); if (!slot) return JsonVariant();
if (it == _data->end()) return false;
if (!set_key(it, key)) return false; 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;
} }
// save the value if (!set_key(slot, key)) return JsonVariant();
return JsonVariant(_memoryPool, &it->value).set(value);
} }
FORCE_INLINE bool set_key(internal_iterator& it, const char* key) { return JsonVariant(_memoryPool, &slot->value);
it->key = key; }
FORCE_INLINE bool set_key(Internals::Slot* slot, const char* key) {
slot->key = key;
return true; return true;
} }
template <typename T> template <typename T>
FORCE_INLINE bool set_key(internal_iterator& it, const T& key) { FORCE_INLINE bool set_key(Internals::Slot* slot, const T& key) {
const char* dup = Internals::makeString(key).save(_memoryPool); const char* dup = Internals::makeString(key).save(_memoryPool);
if (!dup) return false; if (!dup) return false;
it->key = dup; slot->key = dup;
return true; return true;
} }
mutable Internals::MemoryPool* _memoryPool; mutable Internals::MemoryPool* _memoryPool;
mutable Internals::JsonObjectData* _data; mutable Internals::JsonObjectData* _data;
}; }; // namespace ArduinoJson
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -1,30 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "Data/List.hpp"
#include "JsonPair.hpp"
#include "Memory/AllocableInMemoryPool.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 StaticMemoryPool.
#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<JsonPairData>, AllocableInMemoryPool {
JsonVariantData* addSlot(MemoryPool* memoryPool, const char* key) {
iterator it = add(memoryPool);
if (it == end()) return 0;
it->key = key;
return &it->value;
}
};
} // namespace Internals
} // namespace ArduinoJson

View File

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

View File

@ -4,15 +4,14 @@
#pragma once #pragma once
#include "Data/ListIterator.hpp"
#include "JsonPair.hpp" #include "JsonPair.hpp"
namespace ArduinoJson { namespace ArduinoJson {
class JsonPairPtr { class JsonPairPtr {
public: public:
JsonPairPtr(Internals::MemoryPool *memoryPool, Internals::JsonPairData *data) JsonPairPtr(Internals::MemoryPool *memoryPool, Internals::Slot *slot)
: _pair(memoryPool, data) {} : _pair(memoryPool, slot) {}
const JsonPair *operator->() const { const JsonPair *operator->() const {
return &_pair; return &_pair;
@ -26,47 +25,48 @@ class JsonPairPtr {
JsonPair _pair; JsonPair _pair;
}; };
// A read-write forward iterator for JsonArray
class JsonObjectIterator { class JsonObjectIterator {
typedef Internals::ListIterator<Internals::JsonPairData> internal_iterator;
public: public:
JsonObjectIterator() {} JsonObjectIterator() : _slot(0) {}
explicit JsonObjectIterator(Internals::MemoryPool *memoryPool, explicit JsonObjectIterator(Internals::MemoryPool *memoryPool,
internal_iterator iterator) Internals::Slot *slot)
: _memoryPool(memoryPool), _iterator(iterator) {} : _memoryPool(memoryPool), _slot(slot) {}
JsonPair operator*() const { JsonPair operator*() const {
return JsonPair(_memoryPool, &*_iterator); return JsonPair(_memoryPool, _slot);
} }
JsonPairPtr operator->() { JsonPairPtr operator->() {
return JsonPairPtr(_memoryPool, &*_iterator); return JsonPairPtr(_memoryPool, _slot);
} }
bool operator==(const JsonObjectIterator &other) const { bool operator==(const JsonObjectIterator &other) const {
return _iterator == other._iterator; return _slot == other._slot;
} }
bool operator!=(const JsonObjectIterator &other) const { bool operator!=(const JsonObjectIterator &other) const {
return _iterator != other._iterator; return _slot != other._slot;
} }
JsonObjectIterator &operator++() { JsonObjectIterator &operator++() {
++_iterator; if (_slot) _slot = _slot->next;
return *this; return *this;
} }
JsonObjectIterator &operator+=(size_t distance) { JsonObjectIterator &operator+=(size_t distance) {
_iterator += distance; while (_slot && distance > 0) {
_slot = _slot->next;
distance--;
}
return *this; return *this;
} }
internal_iterator internal() { Internals::Slot *internal() {
return _iterator; return _slot;
} }
private: private:
Internals::MemoryPool *_memoryPool; Internals::MemoryPool *_memoryPool;
internal_iterator _iterator; Internals::Slot *_slot;
}; };
} // namespace ArduinoJson } // namespace ArduinoJson

View File

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

View File

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

View File

@ -92,7 +92,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
// set(SerializedValue<const char *>) // set(SerializedValue<const char *>)
FORCE_INLINE bool set(Internals::SerializedValue<const char *> value) { FORCE_INLINE bool set(Internals::SerializedValue<const char *> value) {
if (!_data) return false; if (!_data) return false;
_data->setRaw(value.data(), value.size()); _data->setLinkedRaw(value.data(), value.size());
return true; return true;
} }
@ -108,7 +108,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
const char *dup = const char *dup =
Internals::makeString(value.data(), value.size()).save(_memoryPool); Internals::makeString(value.data(), value.size()).save(_memoryPool);
if (dup) if (dup)
_data->setRaw(dup, value.size()); _data->setOwnedRaw(dup, value.size());
else else
_data->setNull(); _data->setNull();
return true; return true;
@ -124,7 +124,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
if (!_data) return false; if (!_data) return false;
const char *dup = Internals::makeString(value).save(_memoryPool); const char *dup = Internals::makeString(value).save(_memoryPool);
if (dup) { if (dup) {
_data->setString(dup); _data->setOwnedString(dup);
return true; return true;
} else { } else {
_data->setNull(); _data->setNull();
@ -141,7 +141,7 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
if (!_data) return false; if (!_data) return false;
const char *dup = Internals::makeString(value).save(_memoryPool); const char *dup = Internals::makeString(value).save(_memoryPool);
if (dup) { if (dup) {
_data->setString(dup); _data->setOwnedString(dup);
return true; return true;
} else { } else {
_data->setNull(); _data->setNull();
@ -152,22 +152,15 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
// set(const char*); // set(const char*);
FORCE_INLINE bool set(const char *value) { FORCE_INLINE bool set(const char *value) {
if (!_data) return false; if (!_data) return false;
_data->setString(value); _data->setLinkedString(value);
return true; return true;
} }
FORCE_INLINE bool set(const JsonVariant &value) { bool set(const JsonVariant &value);
if (!_data) return false;
if (value._data)
*_data = *value._data;
else
_data->setNull();
return true;
}
FORCE_INLINE bool set(const JsonArray &array); FORCE_INLINE bool set(JsonArray array);
FORCE_INLINE bool set(const Internals::JsonArraySubscript &); FORCE_INLINE bool set(const Internals::JsonArraySubscript &);
FORCE_INLINE bool set(const JsonObject &object); FORCE_INLINE bool set(JsonObject object);
template <typename TString> template <typename TString>
FORCE_INLINE bool set(const Internals::JsonObjectSubscript<TString> &); FORCE_INLINE bool set(const Internals::JsonObjectSubscript<TString> &);
@ -327,19 +320,37 @@ class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
return _data && _data->isObject(); return _data && _data->isObject();
} }
// Returns true if the variant has a value
FORCE_INLINE bool isNull() const { FORCE_INLINE bool isNull() const {
return _data == 0 || _data->isNull(); return _data == 0 || _data->isNull();
} }
template <typename Visitor> FORCE_INLINE bool isInvalid() const {
FORCE_INLINE void visit(Visitor &visitor) const { return _data == 0;
if (_data)
_data->visit(visitor);
else
visitor.acceptNull();
} }
template <typename Visitor>
void accept(Visitor &visitor) const;
// 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: private:
Internals::MemoryPool *_memoryPool; Internals::MemoryPool *_memoryPool;
Internals::JsonVariantData *_data; Internals::JsonVariantData *_data;

View File

@ -5,8 +5,6 @@
#pragma once #pragma once
#include "Configuration.hpp" #include "Configuration.hpp"
#include "JsonArrayData.hpp"
#include "JsonObjectData.hpp"
#include "JsonVariant.hpp" #include "JsonVariant.hpp"
#include "Numbers/parseFloat.hpp" #include "Numbers/parseFloat.hpp"
#include "Numbers/parseInteger.hpp" #include "Numbers/parseInteger.hpp"
@ -15,26 +13,16 @@
namespace ArduinoJson { namespace ArduinoJson {
inline bool JsonVariant::set(const JsonArray& array) { inline bool JsonVariant::set(JsonArray array) {
if (!_data) return false; return to<JsonArray>().copyFrom(array);
if (array._data)
_data->setArray(*array._data);
else
_data->setNull();
return true;
} }
inline bool JsonVariant::set(const Internals::JsonArraySubscript& value) { inline bool JsonVariant::set(const Internals::JsonArraySubscript& value) {
return set(value.as<JsonVariant>()); return set(value.as<JsonVariant>());
} }
inline bool JsonVariant::set(const JsonObject& object) { inline bool JsonVariant::set(JsonObject object) {
if (!_data) return false; return to<JsonObject>().copyFrom(object);
if (object._data)
_data->setObject(*object._data);
else
_data->setNull();
return true;
} }
template <typename TString> template <typename TString>
@ -43,6 +31,28 @@ inline bool JsonVariant::set(
return set(value.template as<JsonVariant>()); 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;
}
}
template <typename T> template <typename T>
inline typename Internals::enable_if< inline typename Internals::enable_if<
Internals::is_same<typename Internals::remove_const<T>::type, Internals::is_same<typename Internals::remove_const<T>::type,
@ -60,4 +70,69 @@ inline typename Internals::enable_if<
JsonVariant::as() const { JsonVariant::as() const {
return _data ? JsonObject(_memoryPool, _data->asObject()) : JsonObject(); return _data ? JsonObject(_memoryPool, _data->asObject()) : JsonObject();
} }
template <typename T>
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>
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;
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 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 visitor.visitNull();
}
}
} // namespace ArduinoJson } // namespace ArduinoJson

View File

@ -12,7 +12,6 @@ namespace Internals {
class AllocableInMemoryPool { class AllocableInMemoryPool {
public: public:
void *operator new(size_t n, MemoryPool *memoryPool) NOEXCEPT { void *operator new(size_t n, MemoryPool *memoryPool) NOEXCEPT {
if (!memoryPool) return NULL;
return memoryPool->alloc(n); return memoryPool->alloc(n);
} }

View File

@ -155,9 +155,9 @@ class DynamicMemoryPoolBase : public MemoryPool {
// Implements a MemoryPool with dynamic memory allocation. // Implements a MemoryPool with dynamic memory allocation.
// You are strongly encouraged to consider using StaticMemoryPool which is much // You are strongly encouraged to consider using StaticMemoryPool which is much
// more suitable for embedded systems. // more suitable for embedded systems.
typedef Internals::DynamicMemoryPoolBase<Internals::DefaultAllocator> typedef DynamicMemoryPoolBase<DefaultAllocator> DynamicMemoryPool;
DynamicMemoryPool;
} // namespace Internals } // namespace Internals
} // namespace ArduinoJson
#if defined(__clang__) #if defined(__clang__)
#pragma clang diagnostic pop #pragma clang diagnostic pop
@ -166,4 +166,3 @@ typedef Internals::DynamicMemoryPoolBase<Internals::DefaultAllocator>
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#endif #endif
} // namespace ArduinoJson

View File

@ -107,12 +107,12 @@ class StaticMemoryPoolBase : public MemoryPool {
// The template paramenter CAPACITY specifies the capacity of the memoryPool in // The template paramenter CAPACITY specifies the capacity of the memoryPool in
// bytes. // bytes.
template <size_t CAPACITY> template <size_t CAPACITY>
class StaticMemoryPool : public Internals::StaticMemoryPoolBase { class StaticMemoryPool : public StaticMemoryPoolBase {
static const size_t ACTUAL_CAPACITY = Internals::Max<1, CAPACITY>::value; static const size_t ACTUAL_CAPACITY = Max<1, CAPACITY>::value;
public: public:
explicit StaticMemoryPool() explicit StaticMemoryPool()
: Internals::StaticMemoryPoolBase(_buffer, ACTUAL_CAPACITY) {} : StaticMemoryPoolBase(_buffer, ACTUAL_CAPACITY) {}
private: private:
char _buffer[ACTUAL_CAPACITY]; char _buffer[ACTUAL_CAPACITY];

View File

@ -24,17 +24,17 @@ class MsgPackDeserializer {
_stringStorage(stringStorage), _stringStorage(stringStorage),
_nestingLimit(nestingLimit) {} _nestingLimit(nestingLimit) {}
DeserializationError parse(JsonVariantData &variant) { DeserializationError parse(JsonVariant variant) {
uint8_t code; uint8_t code;
if (!readByte(code)) return DeserializationError::IncompleteInput; if (!readByte(code)) return DeserializationError::IncompleteInput;
if ((code & 0x80) == 0) { if ((code & 0x80) == 0) {
variant.setInteger(code); variant.set(code);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
if ((code & 0xe0) == 0xe0) { if ((code & 0xe0) == 0xe0) {
variant.setInteger(static_cast<int8_t>(code)); variant.set(static_cast<int8_t>(code));
return DeserializationError::Ok; return DeserializationError::Ok;
} }
@ -48,15 +48,15 @@ class MsgPackDeserializer {
switch (code) { switch (code) {
case 0xc0: case 0xc0:
variant.setNull(); // already null
return DeserializationError::Ok; return DeserializationError::Ok;
case 0xc2: case 0xc2:
variant.setBoolean(false); variant.set(false);
return DeserializationError::Ok; return DeserializationError::Ok;
case 0xc3: case 0xc3:
variant.setBoolean(true); variant.set(true);
return DeserializationError::Ok; return DeserializationError::Ok;
case 0xcc: case 0xcc:
@ -171,54 +171,54 @@ class MsgPackDeserializer {
} }
template <typename T> template <typename T>
DeserializationError readInteger(JsonVariantData &variant) { DeserializationError readInteger(JsonVariant variant) {
T value; T value;
if (!readInteger(value)) return DeserializationError::IncompleteInput; if (!readInteger(value)) return DeserializationError::IncompleteInput;
variant.setInteger(value); variant.set(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat( typename enable_if<sizeof(T) == 4, DeserializationError>::type readFloat(
JsonVariantData &variant) { JsonVariant variant) {
T value; T value;
if (!readBytes(value)) return DeserializationError::IncompleteInput; if (!readBytes(value)) return DeserializationError::IncompleteInput;
fixEndianess(value); fixEndianess(value);
variant.setFloat(value); variant.set(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble( typename enable_if<sizeof(T) == 8, DeserializationError>::type readDouble(
JsonVariantData &variant) { JsonVariant variant) {
T value; T value;
if (!readBytes(value)) return DeserializationError::IncompleteInput; if (!readBytes(value)) return DeserializationError::IncompleteInput;
fixEndianess(value); fixEndianess(value);
variant.setFloat(value); variant.set(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble( typename enable_if<sizeof(T) == 4, DeserializationError>::type readDouble(
JsonVariantData &variant) { JsonVariant variant) {
uint8_t i[8]; // input is 8 bytes uint8_t i[8]; // input is 8 bytes
T value; // output is 4 bytes T value; // output is 4 bytes
uint8_t *o = reinterpret_cast<uint8_t *>(&value); uint8_t *o = reinterpret_cast<uint8_t *>(&value);
if (!readBytes(i, 8)) return DeserializationError::IncompleteInput; if (!readBytes(i, 8)) return DeserializationError::IncompleteInput;
doubleToFloat(i, o); doubleToFloat(i, o);
fixEndianess(value); fixEndianess(value);
variant.setFloat(value); variant.set(value);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename T> template <typename T>
DeserializationError readString(JsonVariantData &variant) { DeserializationError readString(JsonVariant variant) {
T size; T size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readString(variant, size); return readString(variant, size);
} }
DeserializationError readString(JsonVariantData &variant, size_t n) { DeserializationError readString(JsonVariant variant, size_t n) {
typename remove_reference<TStringStorage>::type::String str = typename remove_reference<TStringStorage>::type::String str =
_stringStorage.startString(); _stringStorage.startString();
for (; n; --n) { for (; n; --n) {
@ -228,33 +228,31 @@ class MsgPackDeserializer {
} }
const char *s = str.c_str(); const char *s = str.c_str();
if (s == NULL) return DeserializationError::NoMemory; if (s == NULL) return DeserializationError::NoMemory;
variant.setString(s); variant.set(s);
return DeserializationError::Ok; return DeserializationError::Ok;
} }
template <typename TSize> template <typename TSize>
DeserializationError readArray(JsonVariantData &variant) { DeserializationError readArray(JsonVariant variant) {
TSize size; TSize size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readArray(variant, size); return readArray(variant, size);
} }
DeserializationError readArray(JsonVariantData &variant, size_t n) { DeserializationError readArray(JsonVariant variant, size_t n) {
JsonArrayData *array = new (_memoryPool) JsonArrayData; JsonArray array = variant.to<JsonArray>();
if (!array) return DeserializationError::NoMemory; if (array.isNull()) return DeserializationError::NoMemory;
return readArray(array, n);
variant.setArray(*array);
return readArray(*array, n);
} }
DeserializationError readArray(JsonArrayData &array, size_t n) { DeserializationError readArray(JsonArray array, size_t n) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
--_nestingLimit; --_nestingLimit;
for (; n; --n) { for (; n; --n) {
JsonVariantData *value = array.addSlot(_memoryPool); JsonVariant value = array.add();
if (!value) return DeserializationError::NoMemory; if (value.isInvalid()) return DeserializationError::NoMemory;
DeserializationError err = parse(*value); DeserializationError err = parse(value);
if (err) return err; if (err) return err;
} }
++_nestingLimit; ++_nestingLimit;
@ -262,33 +260,33 @@ class MsgPackDeserializer {
} }
template <typename TSize> template <typename TSize>
DeserializationError readObject(JsonVariantData &variant) { DeserializationError readObject(JsonVariant variant) {
TSize size; TSize size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readObject(variant, size); return readObject(variant, size);
} }
DeserializationError readObject(JsonVariantData &variant, size_t n) { DeserializationError readObject(JsonVariant variant, size_t n) {
JsonObjectData *object = new (_memoryPool) JsonObjectData; JsonObject object = variant.to<JsonObject>();
if (!object) return DeserializationError::NoMemory; if (object.isNull()) return DeserializationError::NoMemory;
variant.setObject(*object);
return readObject(*object, n); return readObject(object, n);
} }
DeserializationError readObject(JsonObjectData &object, size_t n) { DeserializationError readObject(JsonObject object, size_t n) {
if (_nestingLimit == 0) return DeserializationError::TooDeep; if (_nestingLimit == 0) return DeserializationError::TooDeep;
--_nestingLimit; --_nestingLimit;
for (; n; --n) { for (; n; --n) {
JsonVariantData key; JsonVariantData keyData;
JsonVariant key(_memoryPool, &keyData);
DeserializationError err = parse(key); DeserializationError err = parse(key);
if (err) return err; if (err) return err;
if (!key.isString()) return DeserializationError::NotSupported; if (!keyData.isString()) return DeserializationError::NotSupported;
JsonVariantData *value = object.addSlot(_memoryPool, key.asString()); JsonVariant value = object.set(keyData.asString());
if (!value) return DeserializationError::NoMemory; if (value.isInvalid()) return DeserializationError::NoMemory;
err = parse(*value); err = parse(value);
if (err) return err; if (err) return err;
} }
++_nestingLimit; ++_nestingLimit;

View File

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

View File

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

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "Data/JsonVariantTo.hpp"
#include "JsonVariant.hpp" #include "JsonVariant.hpp"
#include "Memory/StaticMemoryPool.hpp" #include "Memory/StaticMemoryPool.hpp"
@ -30,45 +31,10 @@ class StaticJsonDocument {
return getVariant().template as<T>(); return getVariant().template as<T>();
} }
// JsonObject to<JsonObject>()
template <typename T> template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonObject>::value, typename Internals::JsonVariantTo<T>::type to() {
JsonObject>::type _memoryPool.clear();
to() { return getVariant().template to<T>();
clear();
JsonObject object(&_memoryPool);
getVariant().set(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(&_memoryPool);
getVariant().set(array);
return array;
}
// JsonVariant to<JsonVariant>()
template <typename T>
typename Internals::enable_if<Internals::is_same<T, JsonVariant>::value,
JsonVariant>::type
to() {
clear();
return getVariant();
}
// JsonVariantData& to<JsonVariantData>()
template <typename T>
typename Internals::enable_if<
Internals::is_same<T, Internals::JsonVariantData>::value,
Internals::JsonVariantData&>::type
to() {
clear();
return _rootData;
} }
void clear() { void clear() {
@ -81,8 +47,8 @@ class StaticJsonDocument {
} }
template <typename Visitor> template <typename Visitor>
void visit(Visitor& visitor) const { void accept(Visitor& visitor) const {
return getVariant().visit(visitor); return getVariant().accept(visitor);
} }
private: private:

View File

@ -51,11 +51,14 @@ TEST_CASE("JsonArray::copyFrom()") {
JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2); JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc; StaticJsonDocument<SIZE> doc;
JsonArray array = doc.to<JsonArray>(); JsonArray array = doc.to<JsonArray>();
char json[32]; char json[32] = "";
int source[][3] = {{1, 2, 3}, {4, 5, 6}}; int source[][3] = {{1, 2, 3}, {4, 5, 6}};
CAPTURE(SIZE)
bool ok = array.copyFrom(source); bool ok = array.copyFrom(source);
REQUIRE_FALSE(ok); CAPTURE(doc.memoryUsage());
CHECK_FALSE(ok);
serializeJson(array, json, sizeof(json)); serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5]]") == json); REQUIRE(std::string("[[1,2,3],[4,5]]") == json);

View File

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

View File

@ -119,6 +119,11 @@ TEST_CASE("JsonArray::operator[]") {
REQUIRE(expectedSize == doc.memoryUsage()); REQUIRE(expectedSize == doc.memoryUsage());
} }
SECTION("array[0].to<JsonObject>()") {
JsonObject obj = array[0].to<JsonObject>();
REQUIRE(obj.isNull() == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(VLA)") { SECTION("set(VLA)") {
int i = 16; int i = 16;

View File

@ -17,4 +17,25 @@ TEST_CASE("DynamicJsonDocument") {
REQUIRE(json == "{\"hello\":\"world\"}"); 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

@ -10,7 +10,8 @@ TEST_CASE("JsonObject::createNestedArray()") {
JsonObject obj = doc.to<JsonObject>(); JsonObject obj = doc.to<JsonObject>();
SECTION("key is a const char*") { SECTION("key is a const char*") {
obj.createNestedArray("hello"); JsonArray arr = obj.createNestedArray("hello");
REQUIRE(arr.isNull() == false);
} }
#ifdef HAS_VARIABLE_LENGTH_ARRAY #ifdef HAS_VARIABLE_LENGTH_ARRAY
@ -19,7 +20,8 @@ TEST_CASE("JsonObject::createNestedArray()") {
char vla[i]; char vla[i];
strcpy(vla, "hello"); strcpy(vla, "hello");
obj.createNestedArray(vla); JsonArray arr = obj.createNestedArray(vla);
REQUIRE(arr.isNull() == false);
} }
#endif #endif
} }

View File

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

View File

@ -40,4 +40,9 @@ TEST_CASE("JsonObject::begin()/end()") {
// ++it; // ++it;
// REQUIRE(const_object.end() == it); // REQUIRE(const_object.end() == it);
// } // }
SECTION("Dereferencing end() is safe") {
REQUIRE(obj.end()->key() == 0);
REQUIRE(obj.end()->value().isNull());
}
} }

View File

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

View File

@ -19,9 +19,21 @@ TEST_CASE("JsonObject::size()") {
REQUIRE(1 == obj.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"] = 1;
obj["hello"] = 2; obj["hello"] = 2;
REQUIRE(1 == obj.size()); 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

@ -160,6 +160,12 @@ TEST_CASE("JsonObject::operator[]") {
REQUIRE(obj[null] == 0); 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) && \ #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR) !defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("obj[VLA] = str") { SECTION("obj[VLA] = str") {

View File

@ -5,6 +5,7 @@
add_executable(JsonVariantTests add_executable(JsonVariantTests
as.cpp as.cpp
compare.cpp compare.cpp
copy.cpp
is.cpp is.cpp
isnull.cpp isnull.cpp
or.cpp or.cpp

View File

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

View File

@ -265,13 +265,11 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("ArrayInVariant") { SECTION("ArrayInVariant") {
DynamicJsonDocument docArr1, docArr2; JsonArray array1 = variant1.to<JsonArray>();
JsonArray array1 = docArr1.to<JsonArray>(); JsonArray array2 = variant2.to<JsonArray>();
JsonArray array2 = docArr2.to<JsonArray>();
variant1.set(array1); array1.add(42);
variant2.set(array1); array2.add(42);
variant3.set(array2);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);
@ -281,13 +279,11 @@ TEST_CASE("JsonVariant comparisons") {
} }
SECTION("ObjectInVariant") { SECTION("ObjectInVariant") {
DynamicJsonDocument docObj1, docObj2; JsonObject obj1 = variant1.to<JsonObject>();
JsonObject obj1 = docObj1.to<JsonObject>(); JsonObject obj2 = variant2.to<JsonObject>();
JsonObject obj2 = docObj2.to<JsonObject>();
variant1.set(obj1); obj1["hello"] = "world";
variant2.set(obj1); obj2["hello"] = "world";
variant3.set(obj2);
REQUIRE(variant1 == variant2); REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2); REQUIRE_FALSE(variant1 != variant2);

84
test/JsonVariant/copy.cpp Normal file
View File

@ -0,0 +1,84 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant::set(JsonVariant)") {
DynamicJsonDocument doc1;
DynamicJsonDocument doc2;
JsonVariant var1 = doc1.to<JsonVariant>();
JsonVariant var2 = doc2.to<JsonVariant>();
SECTION("stores JsonArray by copy") {
JsonArray arr = doc2.to<JsonArray>();
arr.add(42);
var1.set(doc2.as<JsonVariant>());
arr[0] = 666;
REQUIRE(var1.as<std::string>() == "[42]");
}
SECTION("stores JsonObject by copy") {
JsonObject obj = doc2.to<JsonObject>();
obj["value"] = 42;
var1.set(doc2.as<JsonVariant>());
obj["value"] = 666;
REQUIRE(var1.as<std::string>() == "{\"value\":42}");
}
SECTION("stores const char* by reference") {
var1.set("hello!!");
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 0);
REQUIRE(doc2.memoryUsage() == 0);
}
SECTION("stores char* by copy") {
char str[] = "hello!!";
var1.set(str);
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
}
SECTION("stores std::string by copy") {
var1.set(std::string("hello!!"));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 8);
REQUIRE(doc2.memoryUsage() == 8);
}
SECTION("stores Serialized<const char*> by reference") {
var1.set(serialized("hello!!", 8));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 0);
REQUIRE(doc2.memoryUsage() == 0);
}
SECTION("stores Serialized<char*> by copy") {
char str[] = "hello!!";
var1.set(serialized(str, 8));
var2.set(var1);
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

@ -35,13 +35,13 @@ TEST_CASE("JsonVariant::isNull()") {
REQUIRE(variant.isNull() == false); REQUIRE(variant.isNull() == false);
} }
SECTION("return true when InvalidArray") { /* SECTION("return true when InvalidArray") {
variant.set(JsonArray()); variant.set(JsonArray());
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
} }
*/
SECTION("return true when InvalidObject") { /* SECTION("return true when InvalidObject") {
variant.set(JsonObject()); variant.set(JsonObject());
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
} }*/
} }

View File

@ -23,9 +23,7 @@ TEST_CASE("JsonVariant::operator[]") {
} }
SECTION("The JsonVariant is a JsonArray") { SECTION("The JsonVariant is a JsonArray") {
DynamicJsonDocument doc2; JsonArray array = var.to<JsonArray>();
JsonArray array = doc2.to<JsonArray>();
var.set(array);
SECTION("get value") { SECTION("get value") {
array.add("element at index 0"); array.add("element at index 0");
@ -62,9 +60,7 @@ TEST_CASE("JsonVariant::operator[]") {
} }
SECTION("The JsonVariant is a JsonObject") { SECTION("The JsonVariant is a JsonObject") {
DynamicJsonDocument doc2; JsonObject object = var.to<JsonObject>();
JsonObject object = doc2.to<JsonObject>();
var.set(object);
SECTION("get value") { SECTION("get value") {
object["a"] = "element at key \"a\""; object["a"] = "element at key \"a\"";
@ -92,6 +88,11 @@ TEST_CASE("JsonVariant::operator[]") {
REQUIRE(1 == var.size()); REQUIRE(1 == var.size());
REQUIRE(std::string("world") == var["hello"]); REQUIRE(std::string("world") == var["hello"]);
} }
SECTION("var[key].to<JsonArray>()") {
JsonArray arr = var["hello"].to<JsonArray>();
REQUIRE(arr.isNull() == false);
}
} }
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \ #if defined(HAS_VARIABLE_LENGTH_ARRAY) && \

View File

@ -8,47 +8,39 @@
TEST_CASE("JsonVariant undefined") { TEST_CASE("JsonVariant undefined") {
JsonVariant variant; JsonVariant variant;
SECTION("AsLongReturns0") { SECTION("as<long>()") {
REQUIRE(0 == variant.as<long>()); REQUIRE(0 == variant.as<long>());
} }
SECTION("AsUnsignedReturns0") { SECTION("as<unsigned>()") {
REQUIRE(0 == variant.as<unsigned>()); REQUIRE(0 == variant.as<unsigned>());
} }
SECTION("AsStringReturnsNull") { SECTION("as<char*>()") {
REQUIRE(0 == variant.as<char*>()); REQUIRE(0 == variant.as<char*>());
} }
SECTION("AsDoubleReturns0") { SECTION("as<double>()") {
REQUIRE(0 == variant.as<double>()); REQUIRE(0 == variant.as<double>());
} }
SECTION("AsBoolReturnsFalse") { SECTION("as<bool>()") {
REQUIRE(false == variant.as<bool>()); REQUIRE(false == variant.as<bool>());
} }
SECTION("AsArrayReturnInvalid") { SECTION("as<JsonArray>()") {
REQUIRE(JsonArray() == variant.as<JsonArray>()); REQUIRE(variant.as<JsonArray>().isNull());
} }
SECTION("AsConstArrayReturnInvalid") { SECTION("as<const JsonArray>()") {
REQUIRE(JsonArray() == variant.as<const JsonArray>()); REQUIRE(variant.as<const JsonArray>().isNull());
} }
SECTION("AsObjectReturnInvalid") { SECTION("as<JsonObject>()") {
REQUIRE(JsonObject() == variant.as<JsonObject>()); REQUIRE(variant.as<JsonObject>().isNull());
} }
SECTION("AsConstObjectReturnInvalid") { SECTION("as<const JsonObject>()") {
REQUIRE(JsonObject() == variant.as<const JsonObject>()); REQUIRE(variant.as<const JsonObject>().isNull());
}
SECTION("AsArrayWrapperReturnInvalid") {
REQUIRE(JsonArray() == variant.as<JsonArray>());
}
SECTION("AsObjectWrapperReturnInvalid") {
REQUIRE(JsonObject() == variant.as<JsonObject>());
} }
} }