Fixed object keys not being duplicated

This commit is contained in:
Benoit Blanchon
2018-10-04 11:16:23 +02:00
parent 527dc19794
commit 6b985b2d1f
30 changed files with 368 additions and 218 deletions

View File

@ -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)
-----------

View File

@ -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;

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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:

View File

@ -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:

View File

@ -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);

View File

@ -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();
}

View File

@ -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:

View File

@ -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();
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View 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

View File

@ -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"

View File

@ -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);

View File

@ -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;

View 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

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -4,6 +4,7 @@
add_executable(JsonObjectTests
containsKey.cpp
copy.cpp
createNestedArray.cpp
createNestedObject.cpp
get.cpp

59
test/JsonObject/copy.cpp Normal file
View 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"));
}
}

View File

@ -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());
}
}

View File

@ -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}");
}

View File

@ -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());
}
}