mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-15 19:42:12 +02:00
Fixed object keys not being duplicated
This commit is contained in:
@ -6,6 +6,8 @@ HEAD
|
||||
|
||||
* Added implicit conversion from `JsonArray` and `JsonObject` to `JsonVariant`
|
||||
* Allow mixed configuration in compilation units (issue #809)
|
||||
* Fixed object keys not being duplicated
|
||||
* `JsonPair::key()` now returns a `JsonKey`
|
||||
|
||||
v6.4.0-beta (2018-09-11)
|
||||
-----------
|
||||
|
@ -31,7 +31,9 @@ using ARDUINOJSON_NAMESPACE::DynamicJsonDocument;
|
||||
using ARDUINOJSON_NAMESPACE::JsonArray;
|
||||
using ARDUINOJSON_NAMESPACE::JsonFloat;
|
||||
using ARDUINOJSON_NAMESPACE::JsonInteger;
|
||||
using ARDUINOJSON_NAMESPACE::JsonKey;
|
||||
using ARDUINOJSON_NAMESPACE::JsonObject;
|
||||
using ARDUINOJSON_NAMESPACE::JsonPair;
|
||||
using ARDUINOJSON_NAMESPACE::JsonUInt;
|
||||
using ARDUINOJSON_NAMESPACE::JsonVariant;
|
||||
using ARDUINOJSON_NAMESPACE::serialized;
|
||||
|
@ -1,42 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h> // size_t
|
||||
|
||||
#include "JsonFloat.hpp"
|
||||
#include "JsonInteger.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
struct JsonObjectData {
|
||||
struct Slot* head;
|
||||
struct Slot* tail;
|
||||
};
|
||||
|
||||
struct JsonArrayData {
|
||||
struct Slot* head;
|
||||
struct Slot* tail;
|
||||
};
|
||||
|
||||
struct RawData {
|
||||
const char* data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// A union that defines the actual content of a JsonVariantData.
|
||||
// The enum JsonVariantType determines which member is in use.
|
||||
union JsonVariantContent {
|
||||
JsonFloat asFloat;
|
||||
JsonUInt asInteger;
|
||||
JsonArrayData asArray;
|
||||
JsonObjectData asObject;
|
||||
const char* asString;
|
||||
struct {
|
||||
const char* data;
|
||||
size_t size;
|
||||
} asRaw;
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -4,14 +4,60 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "JsonVariantContent.hpp"
|
||||
#include "JsonVariantType.hpp"
|
||||
#include <stdlib.h> // size_t
|
||||
|
||||
#include "JsonFloat.hpp"
|
||||
#include "JsonInteger.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
enum JsonVariantType {
|
||||
JSON_NULL,
|
||||
JSON_LINKED_RAW,
|
||||
JSON_OWNED_RAW,
|
||||
JSON_LINKED_STRING,
|
||||
JSON_OWNED_STRING,
|
||||
JSON_BOOLEAN,
|
||||
JSON_POSITIVE_INTEGER,
|
||||
JSON_NEGATIVE_INTEGER,
|
||||
JSON_ARRAY,
|
||||
JSON_OBJECT,
|
||||
JSON_FLOAT
|
||||
};
|
||||
|
||||
struct JsonObjectData {
|
||||
struct Slot *head;
|
||||
struct Slot *tail;
|
||||
};
|
||||
|
||||
struct JsonArrayData {
|
||||
struct Slot *head;
|
||||
struct Slot *tail;
|
||||
};
|
||||
|
||||
struct RawData {
|
||||
const char *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// A union that defines the actual content of a JsonVariantData.
|
||||
// The enum JsonVariantType determines which member is in use.
|
||||
union JsonVariantContent {
|
||||
JsonFloat asFloat;
|
||||
JsonUInt asInteger;
|
||||
JsonArrayData asArray;
|
||||
JsonObjectData asObject;
|
||||
const char *asString;
|
||||
struct {
|
||||
const char *data;
|
||||
size_t size;
|
||||
} asRaw;
|
||||
};
|
||||
|
||||
// this struct must be a POD type to prevent error calling offsetof on clang
|
||||
struct JsonVariantData {
|
||||
JsonVariantType type;
|
||||
bool keyIsStatic : 1;
|
||||
JsonVariantType type : 7;
|
||||
JsonVariantContent content;
|
||||
};
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
// Enumerated type to know the current type of a JsonVariant.
|
||||
// The value determines which member of JsonVariantContent is used.
|
||||
enum JsonVariantType {
|
||||
JSON_NULL,
|
||||
JSON_LINKED_RAW,
|
||||
JSON_OWNED_RAW,
|
||||
JSON_LINKED_STRING,
|
||||
JSON_OWNED_STRING,
|
||||
JSON_BOOLEAN,
|
||||
JSON_POSITIVE_INTEGER,
|
||||
JSON_NEGATIVE_INTEGER,
|
||||
JSON_ARRAY,
|
||||
JSON_OBJECT,
|
||||
JSON_FLOAT
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -16,6 +16,9 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TReader, typename TStringStorage>
|
||||
class JsonDeserializer {
|
||||
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
||||
StringBuilder;
|
||||
|
||||
public:
|
||||
JsonDeserializer(MemoryPool &memoryPool, TReader reader,
|
||||
TStringStorage stringStorage, uint8_t nestingLimit)
|
||||
@ -121,8 +124,8 @@ class JsonDeserializer {
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
// Parse key
|
||||
const char *key;
|
||||
err = parseKey(&key);
|
||||
StringInMemoryPool key;
|
||||
err = parseKey(key);
|
||||
if (err) return err;
|
||||
|
||||
// Skip spaces
|
||||
@ -162,7 +165,7 @@ class JsonDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
DeserializationError parseKey(const char **key) {
|
||||
DeserializationError parseKey(StringInMemoryPool &key) {
|
||||
if (isQuote(current())) {
|
||||
return parseQuotedString(key);
|
||||
} else {
|
||||
@ -171,18 +174,16 @@ class JsonDeserializer {
|
||||
}
|
||||
|
||||
DeserializationError parseStringValue(JsonVariant variant) {
|
||||
const char *value;
|
||||
DeserializationError err = parseQuotedString(&value);
|
||||
StringInMemoryPool value;
|
||||
DeserializationError err = parseQuotedString(value);
|
||||
if (err) return err;
|
||||
variant.set(value);
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
DeserializationError parseQuotedString(const char **result) {
|
||||
typename remove_reference<TStringStorage>::type::String str =
|
||||
_stringStorage.startString();
|
||||
|
||||
char stopChar = current();
|
||||
DeserializationError parseQuotedString(StringInMemoryPool &result) {
|
||||
StringBuilder str = _stringStorage.startString();
|
||||
const char stopChar = current();
|
||||
|
||||
move();
|
||||
for (;;) {
|
||||
@ -205,14 +206,13 @@ class JsonDeserializer {
|
||||
str.append(c);
|
||||
}
|
||||
|
||||
*result = str.c_str();
|
||||
if (*result == NULL) return DeserializationError::NoMemory;
|
||||
result = str.complete();
|
||||
if (result.isNull()) return DeserializationError::NoMemory;
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
DeserializationError parseNonQuotedString(const char **result) {
|
||||
typename remove_reference<TStringStorage>::type::String str =
|
||||
_stringStorage.startString();
|
||||
DeserializationError parseNonQuotedString(StringInMemoryPool &result) {
|
||||
StringBuilder str = _stringStorage.startString();
|
||||
|
||||
char c = current();
|
||||
if (c == '\0') return DeserializationError::IncompleteInput;
|
||||
@ -227,8 +227,8 @@ class JsonDeserializer {
|
||||
return DeserializationError::InvalidInput;
|
||||
}
|
||||
|
||||
*result = str.c_str();
|
||||
if (*result == NULL) return DeserializationError::NoMemory;
|
||||
result = str.complete();
|
||||
if (result.isNull()) return DeserializationError::NoMemory;
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,10 @@ class JsonObject {
|
||||
bool ok = _data != 0;
|
||||
clear();
|
||||
for (iterator it = src.begin(); it != src.end(); ++it) {
|
||||
ok &= set(it->key(), it->value());
|
||||
if (it->key().isStatic())
|
||||
ok &= set(it->key().c_str(), it->value());
|
||||
else
|
||||
ok &= set(const_cast<char*>(it->key().c_str()), it->value());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -103,7 +106,7 @@ class JsonObject {
|
||||
template <typename TValue, typename TString>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get(
|
||||
const TString& key) const {
|
||||
return get_impl<const TString&, TValue>(key);
|
||||
return get_impl<TValue>(makeString(key));
|
||||
}
|
||||
//
|
||||
// TValue get<TValue>(TKey) const;
|
||||
@ -112,7 +115,7 @@ class JsonObject {
|
||||
// std::string, String, JsonArray, JsonObject
|
||||
template <typename TValue, typename TString>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get(TString* key) const {
|
||||
return get_impl<TString*, TValue>(key);
|
||||
return get_impl<TValue>(makeString(key));
|
||||
}
|
||||
|
||||
// Checks the type of the value associated with the specified key.
|
||||
@ -199,14 +202,14 @@ class JsonObject {
|
||||
// TKey = const std::string&, const String&
|
||||
template <typename TString>
|
||||
FORCE_INLINE void remove(const TString& key) {
|
||||
remove_impl<const TString&>(key);
|
||||
remove_impl(makeString(key));
|
||||
}
|
||||
//
|
||||
// void remove(TKey);
|
||||
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
|
||||
template <typename TString>
|
||||
FORCE_INLINE void remove(TString* key) {
|
||||
remove_impl<TString*>(key);
|
||||
remove_impl(makeString(key));
|
||||
}
|
||||
|
||||
// Sets the specified key with the specified value.
|
||||
@ -247,12 +250,16 @@ class JsonObject {
|
||||
|
||||
template <typename TString>
|
||||
FORCE_INLINE JsonVariant set(TString* key) {
|
||||
return set_impl<TString*>(key);
|
||||
return set_impl(makeString(key));
|
||||
}
|
||||
|
||||
template <typename TString>
|
||||
FORCE_INLINE JsonVariant set(const TString& key) {
|
||||
return set_impl<const TString&>(key);
|
||||
return set_impl(makeString(key));
|
||||
}
|
||||
|
||||
FORCE_INLINE JsonVariant set(const StringInMemoryPool& key) {
|
||||
return set_impl(key);
|
||||
}
|
||||
|
||||
FORCE_INLINE size_t size() const {
|
||||
@ -281,7 +288,7 @@ class JsonObject {
|
||||
private:
|
||||
template <typename TStringRef>
|
||||
FORCE_INLINE bool containsKey_impl(TStringRef key) const {
|
||||
return findSlot<TStringRef>(key) != 0;
|
||||
return findSlot(makeString(key)) != 0;
|
||||
}
|
||||
|
||||
template <typename TStringRef>
|
||||
@ -296,34 +303,34 @@ class JsonObject {
|
||||
if (!_data) return 0;
|
||||
Slot* slot = _data->head;
|
||||
while (slot) {
|
||||
if (makeString(key).equals(slot->key)) break;
|
||||
if (key.equals(slot->key)) break;
|
||||
slot = slot->next;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
template <typename TStringRef>
|
||||
FORCE_INLINE Slot* findSlot(TStringRef key) const {
|
||||
return const_cast<JsonObject*>(this)->findSlot<TStringRef>(key);
|
||||
return const_cast<JsonObject*>(this)->findSlot(key);
|
||||
}
|
||||
|
||||
template <typename TStringRef, typename TValue>
|
||||
template <typename TValue, typename TStringRef>
|
||||
FORCE_INLINE typename JsonVariantAs<TValue>::type get_impl(
|
||||
TStringRef key) const {
|
||||
Slot* slot = findSlot<TStringRef>(key);
|
||||
Slot* slot = findSlot(key);
|
||||
return slot ? JsonVariant(_memoryPool, &slot->value).as<TValue>()
|
||||
: TValue();
|
||||
}
|
||||
|
||||
template <typename TStringRef, typename TValue>
|
||||
FORCE_INLINE bool is_impl(TStringRef key) const {
|
||||
Slot* slot = findSlot<TStringRef>(key);
|
||||
Slot* slot = findSlot(makeString(key));
|
||||
return slot ? JsonVariant(_memoryPool, &slot->value).is<TValue>() : false;
|
||||
}
|
||||
|
||||
template <typename TStringRef>
|
||||
FORCE_INLINE void remove_impl(TStringRef key) {
|
||||
if (!_data) return;
|
||||
Slot* slot = findSlot<TStringRef>(key);
|
||||
Slot* slot = findSlot(key);
|
||||
if (!slot) return;
|
||||
if (slot->prev)
|
||||
slot->prev->next = slot->next;
|
||||
@ -340,10 +347,10 @@ class JsonObject {
|
||||
if (!_data) return JsonVariant();
|
||||
|
||||
// ignore null key
|
||||
if (makeString(key).is_null()) return JsonVariant();
|
||||
if (key.isNull()) return JsonVariant();
|
||||
|
||||
// search a matching key
|
||||
Slot* slot = findSlot<TStringRef>(key);
|
||||
Slot* slot = findSlot(key);
|
||||
if (!slot) {
|
||||
// add the key
|
||||
slot = new (_memoryPool) Slot();
|
||||
@ -367,20 +374,28 @@ class JsonObject {
|
||||
return JsonVariant(_memoryPool, &slot->value);
|
||||
}
|
||||
|
||||
FORCE_INLINE bool set_key(Slot* slot, const char* key) {
|
||||
slot->key = key;
|
||||
template <typename TStringRef>
|
||||
FORCE_INLINE bool set_key(Slot* slot, TStringRef key) {
|
||||
const char* dup = key.save(_memoryPool);
|
||||
if (!dup) return false;
|
||||
slot->key = dup;
|
||||
slot->value.keyIsStatic = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set_key(Slot* slot, const T& key) {
|
||||
const char* dup = makeString(key).save(_memoryPool);
|
||||
if (!dup) return false;
|
||||
slot->key = dup;
|
||||
FORCE_INLINE bool set_key(Slot* slot, ZeroTerminatedRamStringConst key) {
|
||||
slot->key = key.save(_memoryPool);
|
||||
slot->value.keyIsStatic = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool set_key(Slot* slot, StringInMemoryPool key) {
|
||||
slot->key = key.save(_memoryPool);
|
||||
slot->value.keyIsStatic = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
mutable MemoryPool* _memoryPool;
|
||||
mutable JsonObjectData* _data;
|
||||
}; // namespace ARDUINOJSON_NAMESPACE
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -8,19 +8,45 @@
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class JsonKey {
|
||||
public:
|
||||
JsonKey(Slot* slot) : _slot(slot) {}
|
||||
|
||||
operator const char*() const {
|
||||
return c_str();
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
return _slot ? _slot->key : 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return _slot == 0 || _slot->key == 0;
|
||||
}
|
||||
|
||||
bool isStatic() const {
|
||||
return _slot ? _slot->value.keyIsStatic : true;
|
||||
}
|
||||
|
||||
friend bool operator==(JsonKey lhs, const char* rhs) {
|
||||
if (lhs.isNull()) return rhs == 0;
|
||||
return rhs ? !strcmp(lhs, rhs) : false;
|
||||
}
|
||||
|
||||
private:
|
||||
Slot* _slot;
|
||||
};
|
||||
|
||||
// A key value pair for JsonObjectData.
|
||||
class JsonPair {
|
||||
public:
|
||||
JsonPair(MemoryPool* memoryPool, Slot* slot) {
|
||||
JsonPair(MemoryPool* memoryPool, Slot* slot) : _key(slot) {
|
||||
if (slot) {
|
||||
_key = slot->key;
|
||||
_value = JsonVariant(memoryPool, &slot->value);
|
||||
} else {
|
||||
_key = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* key() const {
|
||||
JsonKey key() const {
|
||||
return _key;
|
||||
}
|
||||
|
||||
@ -29,7 +55,7 @@ class JsonPair {
|
||||
}
|
||||
|
||||
private:
|
||||
const char* _key;
|
||||
JsonKey _key;
|
||||
JsonVariant _value;
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -125,32 +125,14 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set(const T &value,
|
||||
typename enable_if<IsString<T>::value>::type * = 0) {
|
||||
if (!_data) return false;
|
||||
const char *dup = makeString(value).save(_memoryPool);
|
||||
if (dup) {
|
||||
_data->type = JSON_OWNED_STRING;
|
||||
_data->content.asString = dup;
|
||||
return true;
|
||||
} else {
|
||||
_data->type = JSON_NULL;
|
||||
return false;
|
||||
}
|
||||
return setString(makeString(value));
|
||||
}
|
||||
|
||||
// set(char*)
|
||||
template <typename T>
|
||||
FORCE_INLINE bool set(T *value,
|
||||
typename enable_if<IsString<T *>::value>::type * = 0) {
|
||||
if (!_data) return false;
|
||||
const char *dup = makeString(value).save(_memoryPool);
|
||||
if (dup) {
|
||||
_data->type = JSON_OWNED_STRING;
|
||||
_data->content.asString = dup;
|
||||
return true;
|
||||
} else {
|
||||
_data->type = JSON_NULL;
|
||||
return false;
|
||||
}
|
||||
return setString(makeString(value));
|
||||
}
|
||||
|
||||
// set(const char*);
|
||||
@ -161,6 +143,14 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
|
||||
return true;
|
||||
}
|
||||
|
||||
// set(const char*);
|
||||
FORCE_INLINE bool set(StringInMemoryPool value) {
|
||||
if (!_data) return false;
|
||||
_data->type = JSON_OWNED_STRING;
|
||||
_data->content.asString = value.save(_memoryPool);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set(const JsonVariant &value);
|
||||
|
||||
FORCE_INLINE bool set(JsonArray array);
|
||||
@ -367,6 +357,20 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
|
||||
typename enable_if<is_same<T, JsonVariant>::value, JsonVariant>::type to();
|
||||
|
||||
private:
|
||||
template <typename TStringRef>
|
||||
bool setString(TStringRef value) {
|
||||
if (!_data) return false;
|
||||
const char *dup = value.save(_memoryPool);
|
||||
if (dup) {
|
||||
_data->type = JSON_OWNED_STRING;
|
||||
_data->content.asString = dup;
|
||||
return true;
|
||||
} else {
|
||||
_data->type = JSON_NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryPool *_memoryPool;
|
||||
JsonVariantData *_data;
|
||||
};
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Strings/StringInMemoryPool.hpp"
|
||||
#include "MemoryPool.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -77,9 +78,9 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
_head = 0;
|
||||
}
|
||||
|
||||
class String {
|
||||
class StringBuilder {
|
||||
public:
|
||||
String(DynamicMemoryPoolBase* parent)
|
||||
explicit StringBuilder(DynamicMemoryPoolBase* parent)
|
||||
: _parent(parent), _start(NULL), _length(0) {}
|
||||
|
||||
void append(char c) {
|
||||
@ -97,7 +98,7 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
_length++;
|
||||
}
|
||||
|
||||
const char* c_str() {
|
||||
StringInMemoryPool complete() {
|
||||
append(0);
|
||||
return _start;
|
||||
}
|
||||
@ -108,8 +109,8 @@ class DynamicMemoryPoolBase : public MemoryPool {
|
||||
size_t _length;
|
||||
};
|
||||
|
||||
String startString() {
|
||||
return String(this);
|
||||
StringBuilder startString() {
|
||||
return StringBuilder(this);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -5,15 +5,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Polyfills/mpl/max.hpp"
|
||||
#include "../Strings/StringInMemoryPool.hpp"
|
||||
#include "MemoryPool.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class StaticMemoryPoolBase : public MemoryPool {
|
||||
public:
|
||||
class String {
|
||||
class StringBuilder {
|
||||
public:
|
||||
String(StaticMemoryPoolBase* parent) : _parent(parent) {
|
||||
explicit StringBuilder(StaticMemoryPoolBase* parent) : _parent(parent) {
|
||||
_start = parent->_buffer + parent->_size;
|
||||
}
|
||||
|
||||
@ -24,7 +25,7 @@ class StaticMemoryPoolBase : public MemoryPool {
|
||||
}
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
StringInMemoryPool complete() const {
|
||||
if (_parent->canAlloc(1)) {
|
||||
char* last = static_cast<char*>(_parent->doAlloc(1));
|
||||
*last = '\0';
|
||||
@ -65,8 +66,8 @@ class StaticMemoryPoolBase : public MemoryPool {
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
String startString() {
|
||||
return String(this);
|
||||
StringBuilder startString() {
|
||||
return StringBuilder(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -15,6 +15,9 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TReader, typename TStringStorage>
|
||||
class MsgPackDeserializer {
|
||||
typedef typename remove_reference<TStringStorage>::type::StringBuilder
|
||||
StringBuilder;
|
||||
|
||||
public:
|
||||
MsgPackDeserializer(MemoryPool &memoryPool, TReader reader,
|
||||
TStringStorage stringStorage, uint8_t nestingLimit)
|
||||
@ -218,15 +221,14 @@ class MsgPackDeserializer {
|
||||
}
|
||||
|
||||
DeserializationError readString(JsonVariant variant, size_t n) {
|
||||
typename remove_reference<TStringStorage>::type::String str =
|
||||
_stringStorage.startString();
|
||||
StringBuilder str = _stringStorage.startString();
|
||||
for (; n; --n) {
|
||||
uint8_t c;
|
||||
if (!readBytes(c)) return DeserializationError::IncompleteInput;
|
||||
str.append(static_cast<char>(c));
|
||||
}
|
||||
const char *s = str.c_str();
|
||||
if (s == NULL) return DeserializationError::NoMemory;
|
||||
StringInMemoryPool s = str.complete();
|
||||
if (s.isNull()) return DeserializationError::NoMemory;
|
||||
variant.set(s);
|
||||
return DeserializationError::Ok;
|
||||
}
|
||||
@ -281,7 +283,7 @@ class MsgPackDeserializer {
|
||||
if (err) return err;
|
||||
if (!key.is<char *>()) return DeserializationError::NotSupported;
|
||||
|
||||
JsonVariant value = object.set(key.as<char *>());
|
||||
JsonVariant value = object.set(StringInMemoryPool(key.as<char *>()));
|
||||
if (value.isInvalid()) return DeserializationError::NoMemory;
|
||||
|
||||
err = parse(value);
|
||||
|
@ -11,9 +11,9 @@ class StringCopier {
|
||||
public:
|
||||
StringCopier(TMemoryPool& memoryPool) : _memoryPool(&memoryPool) {}
|
||||
|
||||
typedef typename TMemoryPool::String String;
|
||||
typedef typename TMemoryPool::StringBuilder StringBuilder;
|
||||
|
||||
String startString() {
|
||||
StringBuilder startString() {
|
||||
return _memoryPool->startString();
|
||||
}
|
||||
|
||||
|
@ -9,15 +9,15 @@ namespace ARDUINOJSON_NAMESPACE {
|
||||
template <typename TChar>
|
||||
class StringMover {
|
||||
public:
|
||||
class String {
|
||||
class StringBuilder {
|
||||
public:
|
||||
String(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
|
||||
StringBuilder(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
|
||||
|
||||
void append(char c) {
|
||||
*(*_writePtr)++ = TChar(c);
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
StringInMemoryPool complete() const {
|
||||
*(*_writePtr)++ = 0;
|
||||
return reinterpret_cast<const char*>(_startPtr);
|
||||
}
|
||||
@ -29,8 +29,8 @@ class StringMover {
|
||||
|
||||
StringMover(TChar* ptr) : _ptr(ptr) {}
|
||||
|
||||
String startString() {
|
||||
return String(&_ptr);
|
||||
StringBuilder startString() {
|
||||
return StringBuilder(&_ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -12,16 +12,16 @@ class ArduinoString {
|
||||
public:
|
||||
ArduinoString(const ::String& str) : _str(&str) {}
|
||||
|
||||
template <typename Buffer>
|
||||
const char* save(Buffer* memoryPool) const {
|
||||
if (is_null()) return NULL;
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
if (isNull()) return NULL;
|
||||
size_t n = _str->length() + 1;
|
||||
void* dup = memoryPool->alloc(n);
|
||||
if (dup != NULL) memcpy(dup, _str->c_str(), n);
|
||||
return static_cast<const char*>(dup);
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
bool isNull() const {
|
||||
// Arduino's String::c_str() can return NULL
|
||||
return !_str->c_str();
|
||||
}
|
||||
|
@ -17,12 +17,12 @@ class FixedSizeFlashString {
|
||||
return strncmp_P(expected, actual, _size) == 0;
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
const char* save(Buffer* memoryPool) const {
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
void* dup = memoryPool->alloc(_size);
|
||||
if (dup != NULL) memcpy_P(dup, (const char*)_str, _size);
|
||||
|
@ -18,12 +18,12 @@ class FixedSizeRamString {
|
||||
return strcmp(actual, expected) == 0;
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
const char* save(Buffer* memoryPool) const {
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
void* dup = memoryPool->alloc(_size);
|
||||
if (!dup) return NULL;
|
||||
|
@ -12,15 +12,15 @@ class StlString {
|
||||
public:
|
||||
StlString(const std::string& str) : _str(&str) {}
|
||||
|
||||
template <typename Buffer>
|
||||
const char* save(Buffer* memoryPool) const {
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
size_t n = _str->length() + 1;
|
||||
void* dup = memoryPool->alloc(n);
|
||||
if (dup != NULL) memcpy(dup, _str->c_str(), n);
|
||||
return static_cast<const char*>(dup);
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
bool isNull() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
16
src/ArduinoJson/Strings/StringInMemoryPool.hpp
Normal file
16
src/ArduinoJson/Strings/StringInMemoryPool.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ZeroTerminatedRamStringConst.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class StringInMemoryPool : public ZeroTerminatedRamStringConst {
|
||||
public:
|
||||
StringInMemoryPool(const char* str = 0) : ZeroTerminatedRamStringConst(str) {}
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -18,7 +18,9 @@ struct IsString<T&> : IsString<T> {};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
#include "FixedSizeRamString.hpp"
|
||||
#include "StringInMemoryPool.hpp"
|
||||
#include "ZeroTerminatedRamString.hpp"
|
||||
#include "ZeroTerminatedRamStringConst.hpp"
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STD_STRING
|
||||
#include "StlString.hpp"
|
||||
|
@ -16,12 +16,12 @@ class ZeroTerminatedFlashString {
|
||||
return strcmp_P(expected, actual) == 0;
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
const char* save(Buffer* memoryPool) const {
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
size_t n = size() + 1; // copy the terminator
|
||||
void* dup = memoryPool->alloc(n);
|
||||
|
@ -4,24 +4,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ZeroTerminatedRamStringConst.hpp"
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class ZeroTerminatedRamString {
|
||||
class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst {
|
||||
public:
|
||||
ZeroTerminatedRamString(const char* str) : _str(str) {}
|
||||
ZeroTerminatedRamString(const char* str)
|
||||
: ZeroTerminatedRamStringConst(str) {}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
const char* actual = reinterpret_cast<const char*>(_str);
|
||||
if (!actual || !expected) return actual == expected;
|
||||
return strcmp(actual, expected) == 0;
|
||||
}
|
||||
|
||||
bool is_null() const {
|
||||
return !_str;
|
||||
}
|
||||
|
||||
template <typename Buffer>
|
||||
const char* save(Buffer* memoryPool) const {
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool* memoryPool) const {
|
||||
if (!_str) return NULL;
|
||||
size_t n = size() + 1;
|
||||
void* dup = memoryPool->alloc(n);
|
||||
@ -29,13 +22,6 @@ class ZeroTerminatedRamString {
|
||||
memcpy(dup, _str, n);
|
||||
return static_cast<const char*>(dup);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return strlen(reinterpret_cast<const char*>(_str));
|
||||
}
|
||||
|
||||
private:
|
||||
const char* _str;
|
||||
};
|
||||
|
||||
template <typename TChar>
|
||||
@ -43,6 +29,10 @@ inline ZeroTerminatedRamString makeString(const TChar* str) {
|
||||
return ZeroTerminatedRamString(reinterpret_cast<const char*>(str));
|
||||
}
|
||||
|
||||
inline ZeroTerminatedRamString makeString(char* str) {
|
||||
return ZeroTerminatedRamString(str);
|
||||
}
|
||||
|
||||
template <typename TChar>
|
||||
struct IsString<TChar*> {
|
||||
static const bool value = sizeof(TChar) == 1;
|
||||
|
43
src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp
Normal file
43
src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h> // size_t
|
||||
#include <string.h> // strcmp
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class ZeroTerminatedRamStringConst {
|
||||
public:
|
||||
ZeroTerminatedRamStringConst(const char* str) : _str(str) {}
|
||||
|
||||
bool equals(const char* expected) const {
|
||||
const char* actual = _str;
|
||||
if (!actual || !expected) return actual == expected;
|
||||
return strcmp(actual, expected) == 0;
|
||||
}
|
||||
|
||||
bool isNull() const {
|
||||
return !_str;
|
||||
}
|
||||
|
||||
template <typename TMemoryPool>
|
||||
const char* save(TMemoryPool*) const {
|
||||
return _str;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return strlen(_str);
|
||||
}
|
||||
|
||||
protected:
|
||||
const char* _str;
|
||||
};
|
||||
|
||||
inline ZeroTerminatedRamStringConst makeString(const char* str) {
|
||||
return ZeroTerminatedRamStringConst(str);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
@ -17,6 +17,8 @@ struct NoMemoryAllocator {
|
||||
TEST_CASE("DynamicMemoryPool no memory") {
|
||||
DynamicMemoryPoolBase<NoMemoryAllocator> _memoryPool;
|
||||
|
||||
typedef DynamicMemoryPoolBase<NoMemoryAllocator>::StringBuilder StringBuilder;
|
||||
|
||||
SECTION("FixCodeCoverage") {
|
||||
// call this function to fix code coverage
|
||||
NoMemoryAllocator().deallocate(NULL);
|
||||
@ -33,9 +35,8 @@ TEST_CASE("DynamicMemoryPool no memory") {
|
||||
// }
|
||||
|
||||
SECTION("startString()") {
|
||||
DynamicMemoryPoolBase<NoMemoryAllocator>::String str =
|
||||
_memoryPool.startString();
|
||||
StringBuilder str = _memoryPool.startString();
|
||||
str.append('!');
|
||||
REQUIRE(0 == str.c_str());
|
||||
REQUIRE(str.complete().isNull());
|
||||
}
|
||||
}
|
||||
|
@ -7,43 +7,45 @@
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
typedef DynamicMemoryPool::StringBuilder StringBuilder;
|
||||
|
||||
TEST_CASE("DynamicMemoryPool::startString()") {
|
||||
SECTION("WorksWhenBufferIsBigEnough") {
|
||||
DynamicMemoryPool memoryPool(6);
|
||||
|
||||
DynamicMemoryPool::String str = memoryPool.startString();
|
||||
StringBuilder str = memoryPool.startString();
|
||||
str.append('h');
|
||||
str.append('e');
|
||||
str.append('l');
|
||||
str.append('l');
|
||||
str.append('o');
|
||||
|
||||
REQUIRE(std::string("hello") == str.c_str());
|
||||
REQUIRE(str.complete().equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("GrowsWhenBufferIsTooSmall") {
|
||||
DynamicMemoryPool memoryPool(5);
|
||||
|
||||
DynamicMemoryPool::String str = memoryPool.startString();
|
||||
StringBuilder str = memoryPool.startString();
|
||||
str.append('h');
|
||||
str.append('e');
|
||||
str.append('l');
|
||||
str.append('l');
|
||||
str.append('o');
|
||||
|
||||
REQUIRE(std::string("hello") == str.c_str());
|
||||
REQUIRE(str.complete().equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("SizeIncreases") {
|
||||
DynamicMemoryPool memoryPool(5);
|
||||
|
||||
DynamicMemoryPool::String str = memoryPool.startString();
|
||||
StringBuilder str = memoryPool.startString();
|
||||
REQUIRE(0 == memoryPool.size());
|
||||
|
||||
str.append('h');
|
||||
REQUIRE(1 == memoryPool.size());
|
||||
|
||||
str.c_str();
|
||||
str.complete();
|
||||
REQUIRE(2 == memoryPool.size());
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
add_executable(JsonObjectTests
|
||||
containsKey.cpp
|
||||
copy.cpp
|
||||
createNestedArray.cpp
|
||||
createNestedObject.cpp
|
||||
get.cpp
|
||||
|
59
test/JsonObject/copy.cpp
Normal file
59
test/JsonObject/copy.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2018
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::copyFrom()") {
|
||||
DynamicJsonDocument doc1;
|
||||
DynamicJsonDocument doc2;
|
||||
|
||||
JsonObject obj1 = doc1.to<JsonObject>();
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
SECTION("doesn't copy static string in key or value") {
|
||||
obj1["hello"] = "world";
|
||||
|
||||
obj2.copyFrom(obj1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
|
||||
REQUIRE(obj2["hello"] == std::string("world"));
|
||||
}
|
||||
|
||||
SECTION("copy local string value") {
|
||||
obj1["hello"] = std::string("world");
|
||||
|
||||
obj2.copyFrom(obj1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
|
||||
REQUIRE(obj2["hello"] == std::string("world"));
|
||||
}
|
||||
|
||||
SECTION("copy local key") {
|
||||
obj1[std::string("hello")] = "world";
|
||||
|
||||
obj2.copyFrom(obj1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
|
||||
REQUIRE(obj2["hello"] == std::string("world"));
|
||||
}
|
||||
|
||||
SECTION("copy string from deserializeJson()") {
|
||||
deserializeJson(doc1, "{'hello':'world'}");
|
||||
|
||||
obj2.copyFrom(obj1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
|
||||
REQUIRE(obj2["hello"] == std::string("world"));
|
||||
}
|
||||
|
||||
SECTION("copy string from deserializeMsgPack()") {
|
||||
deserializeMsgPack(doc1, "\x81\xA5hello\xA5world");
|
||||
|
||||
obj2.copyFrom(obj1);
|
||||
|
||||
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
|
||||
REQUIRE(obj2["hello"] == std::string("world"));
|
||||
}
|
||||
}
|
@ -16,11 +16,11 @@ TEST_CASE("JsonObject::begin()/end()") {
|
||||
SECTION("NonConstIterator") {
|
||||
JsonObject::iterator it = obj.begin();
|
||||
REQUIRE(obj.end() != it);
|
||||
REQUIRE_THAT(it->key(), Equals("ab"));
|
||||
REQUIRE(it->key() == "ab");
|
||||
REQUIRE(12 == it->value());
|
||||
++it;
|
||||
REQUIRE(obj.end() != it);
|
||||
REQUIRE_THAT(it->key(), Equals("cd"));
|
||||
REQUIRE(it->key() == "cd");
|
||||
REQUIRE(34 == it->value());
|
||||
++it;
|
||||
REQUIRE(obj.end() == it);
|
||||
@ -42,7 +42,7 @@ TEST_CASE("JsonObject::begin()/end()") {
|
||||
// }
|
||||
|
||||
SECTION("Dereferencing end() is safe") {
|
||||
REQUIRE(obj.end()->key() == 0);
|
||||
REQUIRE(obj.end()->key().isNull());
|
||||
REQUIRE(obj.end()->value().isNull());
|
||||
}
|
||||
}
|
||||
|
@ -13,21 +13,21 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
|
||||
|
||||
SECTION("stores JsonArray by copy") {
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
|
||||
arr.add(42);
|
||||
var1.set(doc2.as<JsonVariant>());
|
||||
arr[0] = 666;
|
||||
|
||||
var1.set(arr);
|
||||
|
||||
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;
|
||||
|
||||
var1.set(obj);
|
||||
|
||||
obj["value"] = 666;
|
||||
REQUIRE(var1.as<std::string>() == "{\"value\":42}");
|
||||
}
|
||||
|
||||
|
@ -8,42 +8,44 @@
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
TEST_CASE("StaticMemoryPool::startString()") {
|
||||
typedef StaticMemoryPoolBase::StringBuilder StringBuilder;
|
||||
|
||||
SECTION("WorksWhenBufferIsBigEnough") {
|
||||
StaticMemoryPool<6> memoryPool;
|
||||
|
||||
StaticMemoryPoolBase::String str = memoryPool.startString();
|
||||
StringBuilder str = memoryPool.startString();
|
||||
str.append('h');
|
||||
str.append('e');
|
||||
str.append('l');
|
||||
str.append('l');
|
||||
str.append('o');
|
||||
|
||||
REQUIRE(std::string("hello") == str.c_str());
|
||||
REQUIRE(str.complete().equals("hello"));
|
||||
}
|
||||
|
||||
SECTION("ReturnsNullWhenTooSmall") {
|
||||
StaticMemoryPool<5> memoryPool;
|
||||
|
||||
StaticMemoryPoolBase::String str = memoryPool.startString();
|
||||
StringBuilder str = memoryPool.startString();
|
||||
str.append('h');
|
||||
str.append('e');
|
||||
str.append('l');
|
||||
str.append('l');
|
||||
str.append('o');
|
||||
|
||||
REQUIRE(0 == str.c_str());
|
||||
REQUIRE(str.complete().isNull());
|
||||
}
|
||||
|
||||
SECTION("SizeIncreases") {
|
||||
StaticMemoryPool<5> memoryPool;
|
||||
|
||||
StaticMemoryPoolBase::String str = memoryPool.startString();
|
||||
StringBuilder str = memoryPool.startString();
|
||||
REQUIRE(0 == memoryPool.size());
|
||||
|
||||
str.append('h');
|
||||
REQUIRE(1 == memoryPool.size());
|
||||
|
||||
str.c_str();
|
||||
str.complete();
|
||||
REQUIRE(2 == memoryPool.size());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user