From 6b985b2d1fa37ec5ad01139d20be387e31032d04 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Thu, 4 Oct 2018 11:16:23 +0200 Subject: [PATCH] Fixed object keys not being duplicated --- CHANGELOG.md | 2 + src/ArduinoJson.hpp | 2 + src/ArduinoJson/Data/JsonVariantContent.hpp | 42 ------------- src/ArduinoJson/Data/JsonVariantData.hpp | 52 ++++++++++++++- src/ArduinoJson/Data/JsonVariantType.hpp | 23 ------- src/ArduinoJson/Json/JsonDeserializer.hpp | 34 +++++----- src/ArduinoJson/JsonObject.hpp | 63 ++++++++++++------- src/ArduinoJson/JsonPair.hpp | 38 +++++++++-- src/ArduinoJson/JsonVariant.hpp | 44 +++++++------ src/ArduinoJson/Memory/DynamicMemoryPool.hpp | 11 ++-- src/ArduinoJson/Memory/StaticMemoryPool.hpp | 11 ++-- .../MsgPack/MsgPackDeserializer.hpp | 12 ++-- .../StringStorage/StringCopier.hpp | 4 +- src/ArduinoJson/StringStorage/StringMover.hpp | 10 +-- src/ArduinoJson/Strings/ArduinoString.hpp | 8 +-- .../Strings/FixedSizeFlashString.hpp | 6 +- .../Strings/FixedSizeRamString.hpp | 6 +- src/ArduinoJson/Strings/StlString.hpp | 6 +- .../Strings/StringInMemoryPool.hpp | 16 +++++ src/ArduinoJson/Strings/StringTypes.hpp | 2 + .../Strings/ZeroTerminatedFlashString.hpp | 6 +- .../Strings/ZeroTerminatedRamString.hpp | 32 ++++------ .../Strings/ZeroTerminatedRamStringConst.hpp | 43 +++++++++++++ test/DynamicMemoryPool/no_memory.cpp | 7 ++- test/DynamicMemoryPool/startString.cpp | 14 +++-- test/JsonObject/CMakeLists.txt | 1 + test/JsonObject/copy.cpp | 59 +++++++++++++++++ test/JsonObject/iterator.cpp | 6 +- test/JsonVariant/copy.cpp | 12 ++-- test/StaticMemoryPool/startString.cpp | 14 +++-- 30 files changed, 368 insertions(+), 218 deletions(-) delete mode 100644 src/ArduinoJson/Data/JsonVariantContent.hpp delete mode 100644 src/ArduinoJson/Data/JsonVariantType.hpp create mode 100644 src/ArduinoJson/Strings/StringInMemoryPool.hpp create mode 100644 src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp create mode 100644 test/JsonObject/copy.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index b1293c1e..a2468fb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) ----------- diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index 11e8127b..5f1d0fde 100644 --- a/src/ArduinoJson.hpp +++ b/src/ArduinoJson.hpp @@ -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; diff --git a/src/ArduinoJson/Data/JsonVariantContent.hpp b/src/ArduinoJson/Data/JsonVariantContent.hpp deleted file mode 100644 index 63597c76..00000000 --- a/src/ArduinoJson/Data/JsonVariantContent.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#include // 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 diff --git a/src/ArduinoJson/Data/JsonVariantData.hpp b/src/ArduinoJson/Data/JsonVariantData.hpp index af9dcabb..dae37138 100644 --- a/src/ArduinoJson/Data/JsonVariantData.hpp +++ b/src/ArduinoJson/Data/JsonVariantData.hpp @@ -4,14 +4,60 @@ #pragma once -#include "JsonVariantContent.hpp" -#include "JsonVariantType.hpp" +#include // 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; }; diff --git a/src/ArduinoJson/Data/JsonVariantType.hpp b/src/ArduinoJson/Data/JsonVariantType.hpp deleted file mode 100644 index b83efce0..00000000 --- a/src/ArduinoJson/Data/JsonVariantType.hpp +++ /dev/null @@ -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 diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index 34c72872..d956ca56 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -16,6 +16,9 @@ namespace ARDUINOJSON_NAMESPACE { template class JsonDeserializer { + typedef typename remove_reference::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::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::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; } diff --git a/src/ArduinoJson/JsonObject.hpp b/src/ArduinoJson/JsonObject.hpp index 00d580c5..d2edb0a4 100644 --- a/src/ArduinoJson/JsonObject.hpp +++ b/src/ArduinoJson/JsonObject.hpp @@ -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(it->key().c_str()), it->value()); } return ok; } @@ -103,7 +106,7 @@ class JsonObject { template FORCE_INLINE typename JsonVariantAs::type get( const TString& key) const { - return get_impl(key); + return get_impl(makeString(key)); } // // TValue get(TKey) const; @@ -112,7 +115,7 @@ class JsonObject { // std::string, String, JsonArray, JsonObject template FORCE_INLINE typename JsonVariantAs::type get(TString* key) const { - return get_impl(key); + return get_impl(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 FORCE_INLINE void remove(const TString& key) { - remove_impl(key); + remove_impl(makeString(key)); } // // void remove(TKey); // TKey = char*, const char*, char[], const char[], const FlashStringHelper* template FORCE_INLINE void remove(TString* key) { - remove_impl(key); + remove_impl(makeString(key)); } // Sets the specified key with the specified value. @@ -247,12 +250,16 @@ class JsonObject { template FORCE_INLINE JsonVariant set(TString* key) { - return set_impl(key); + return set_impl(makeString(key)); } template FORCE_INLINE JsonVariant set(const TString& key) { - return set_impl(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 FORCE_INLINE bool containsKey_impl(TStringRef key) const { - return findSlot(key) != 0; + return findSlot(makeString(key)) != 0; } template @@ -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 FORCE_INLINE Slot* findSlot(TStringRef key) const { - return const_cast(this)->findSlot(key); + return const_cast(this)->findSlot(key); } - template + template FORCE_INLINE typename JsonVariantAs::type get_impl( TStringRef key) const { - Slot* slot = findSlot(key); + Slot* slot = findSlot(key); return slot ? JsonVariant(_memoryPool, &slot->value).as() : TValue(); } template FORCE_INLINE bool is_impl(TStringRef key) const { - Slot* slot = findSlot(key); + Slot* slot = findSlot(makeString(key)); return slot ? JsonVariant(_memoryPool, &slot->value).is() : false; } template FORCE_INLINE void remove_impl(TStringRef key) { if (!_data) return; - Slot* slot = findSlot(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(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 + 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 - 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 diff --git a/src/ArduinoJson/JsonPair.hpp b/src/ArduinoJson/JsonPair.hpp index 644d4d11..f9b33f94 100644 --- a/src/ArduinoJson/JsonPair.hpp +++ b/src/ArduinoJson/JsonPair.hpp @@ -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 diff --git a/src/ArduinoJson/JsonVariant.hpp b/src/ArduinoJson/JsonVariant.hpp index 7800ccae..45e035b0 100644 --- a/src/ArduinoJson/JsonVariant.hpp +++ b/src/ArduinoJson/JsonVariant.hpp @@ -125,32 +125,14 @@ class JsonVariant : public JsonVariantBase { template FORCE_INLINE bool set(const T &value, typename enable_if::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 FORCE_INLINE bool set(T *value, typename enable_if::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 { 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 { typename enable_if::value, JsonVariant>::type to(); private: + template + 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; }; diff --git a/src/ArduinoJson/Memory/DynamicMemoryPool.hpp b/src/ArduinoJson/Memory/DynamicMemoryPool.hpp index 7d5519e1..da307304 100644 --- a/src/ArduinoJson/Memory/DynamicMemoryPool.hpp +++ b/src/ArduinoJson/Memory/DynamicMemoryPool.hpp @@ -4,6 +4,7 @@ #pragma once +#include "../Strings/StringInMemoryPool.hpp" #include "MemoryPool.hpp" #include @@ -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: diff --git a/src/ArduinoJson/Memory/StaticMemoryPool.hpp b/src/ArduinoJson/Memory/StaticMemoryPool.hpp index 5c15ab44..ef8157af 100644 --- a/src/ArduinoJson/Memory/StaticMemoryPool.hpp +++ b/src/ArduinoJson/Memory/StaticMemoryPool.hpp @@ -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(_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: diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 7c2c390d..92e87e29 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -15,6 +15,9 @@ namespace ARDUINOJSON_NAMESPACE { template class MsgPackDeserializer { + typedef typename remove_reference::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::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(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()) return DeserializationError::NotSupported; - JsonVariant value = object.set(key.as()); + JsonVariant value = object.set(StringInMemoryPool(key.as())); if (value.isInvalid()) return DeserializationError::NoMemory; err = parse(value); diff --git a/src/ArduinoJson/StringStorage/StringCopier.hpp b/src/ArduinoJson/StringStorage/StringCopier.hpp index 20220e8e..576acf24 100644 --- a/src/ArduinoJson/StringStorage/StringCopier.hpp +++ b/src/ArduinoJson/StringStorage/StringCopier.hpp @@ -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(); } diff --git a/src/ArduinoJson/StringStorage/StringMover.hpp b/src/ArduinoJson/StringStorage/StringMover.hpp index 06986e40..3abdaf34 100644 --- a/src/ArduinoJson/StringStorage/StringMover.hpp +++ b/src/ArduinoJson/StringStorage/StringMover.hpp @@ -9,15 +9,15 @@ namespace ARDUINOJSON_NAMESPACE { template 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(_startPtr); } @@ -29,8 +29,8 @@ class StringMover { StringMover(TChar* ptr) : _ptr(ptr) {} - String startString() { - return String(&_ptr); + StringBuilder startString() { + return StringBuilder(&_ptr); } private: diff --git a/src/ArduinoJson/Strings/ArduinoString.hpp b/src/ArduinoJson/Strings/ArduinoString.hpp index 9402de58..eccb2c96 100644 --- a/src/ArduinoJson/Strings/ArduinoString.hpp +++ b/src/ArduinoJson/Strings/ArduinoString.hpp @@ -12,16 +12,16 @@ class ArduinoString { public: ArduinoString(const ::String& str) : _str(&str) {} - template - const char* save(Buffer* memoryPool) const { - if (is_null()) return NULL; + template + 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(dup); } - bool is_null() const { + bool isNull() const { // Arduino's String::c_str() can return NULL return !_str->c_str(); } diff --git a/src/ArduinoJson/Strings/FixedSizeFlashString.hpp b/src/ArduinoJson/Strings/FixedSizeFlashString.hpp index 3fb5c8e2..41e6de1a 100644 --- a/src/ArduinoJson/Strings/FixedSizeFlashString.hpp +++ b/src/ArduinoJson/Strings/FixedSizeFlashString.hpp @@ -17,12 +17,12 @@ class FixedSizeFlashString { return strncmp_P(expected, actual, _size) == 0; } - bool is_null() const { + bool isNull() const { return !_str; } - template - const char* save(Buffer* memoryPool) const { + template + 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); diff --git a/src/ArduinoJson/Strings/FixedSizeRamString.hpp b/src/ArduinoJson/Strings/FixedSizeRamString.hpp index 8b599369..15066922 100644 --- a/src/ArduinoJson/Strings/FixedSizeRamString.hpp +++ b/src/ArduinoJson/Strings/FixedSizeRamString.hpp @@ -18,12 +18,12 @@ class FixedSizeRamString { return strcmp(actual, expected) == 0; } - bool is_null() const { + bool isNull() const { return !_str; } - template - const char* save(Buffer* memoryPool) const { + template + const char* save(TMemoryPool* memoryPool) const { if (!_str) return NULL; void* dup = memoryPool->alloc(_size); if (!dup) return NULL; diff --git a/src/ArduinoJson/Strings/StlString.hpp b/src/ArduinoJson/Strings/StlString.hpp index a2bbfda9..bdf9dcba 100644 --- a/src/ArduinoJson/Strings/StlString.hpp +++ b/src/ArduinoJson/Strings/StlString.hpp @@ -12,15 +12,15 @@ class StlString { public: StlString(const std::string& str) : _str(&str) {} - template - const char* save(Buffer* memoryPool) const { + template + 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(dup); } - bool is_null() const { + bool isNull() const { return false; } diff --git a/src/ArduinoJson/Strings/StringInMemoryPool.hpp b/src/ArduinoJson/Strings/StringInMemoryPool.hpp new file mode 100644 index 00000000..5b1f9950 --- /dev/null +++ b/src/ArduinoJson/Strings/StringInMemoryPool.hpp @@ -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 diff --git a/src/ArduinoJson/Strings/StringTypes.hpp b/src/ArduinoJson/Strings/StringTypes.hpp index a4675e5c..0f055563 100644 --- a/src/ArduinoJson/Strings/StringTypes.hpp +++ b/src/ArduinoJson/Strings/StringTypes.hpp @@ -18,7 +18,9 @@ struct IsString : IsString {}; } // namespace ARDUINOJSON_NAMESPACE #include "FixedSizeRamString.hpp" +#include "StringInMemoryPool.hpp" #include "ZeroTerminatedRamString.hpp" +#include "ZeroTerminatedRamStringConst.hpp" #if ARDUINOJSON_ENABLE_STD_STRING #include "StlString.hpp" diff --git a/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp b/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp index f5f7cf3f..5601d1be 100644 --- a/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp +++ b/src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp @@ -16,12 +16,12 @@ class ZeroTerminatedFlashString { return strcmp_P(expected, actual) == 0; } - bool is_null() const { + bool isNull() const { return !_str; } - template - const char* save(Buffer* memoryPool) const { + template + const char* save(TMemoryPool* memoryPool) const { if (!_str) return NULL; size_t n = size() + 1; // copy the terminator void* dup = memoryPool->alloc(n); diff --git a/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp b/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp index 79f11330..d36a7d64 100644 --- a/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp +++ b/src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp @@ -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(_str); - if (!actual || !expected) return actual == expected; - return strcmp(actual, expected) == 0; - } - - bool is_null() const { - return !_str; - } - - template - const char* save(Buffer* memoryPool) const { + template + 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(dup); } - - size_t size() const { - return strlen(reinterpret_cast(_str)); - } - - private: - const char* _str; }; template @@ -43,6 +29,10 @@ inline ZeroTerminatedRamString makeString(const TChar* str) { return ZeroTerminatedRamString(reinterpret_cast(str)); } +inline ZeroTerminatedRamString makeString(char* str) { + return ZeroTerminatedRamString(str); +} + template struct IsString { static const bool value = sizeof(TChar) == 1; diff --git a/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp b/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp new file mode 100644 index 00000000..964cbc6d --- /dev/null +++ b/src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp @@ -0,0 +1,43 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include // size_t +#include // 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 + 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 diff --git a/test/DynamicMemoryPool/no_memory.cpp b/test/DynamicMemoryPool/no_memory.cpp index 6df934be..afc82d7f 100644 --- a/test/DynamicMemoryPool/no_memory.cpp +++ b/test/DynamicMemoryPool/no_memory.cpp @@ -17,6 +17,8 @@ struct NoMemoryAllocator { TEST_CASE("DynamicMemoryPool no memory") { DynamicMemoryPoolBase _memoryPool; + typedef DynamicMemoryPoolBase::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::String str = - _memoryPool.startString(); + StringBuilder str = _memoryPool.startString(); str.append('!'); - REQUIRE(0 == str.c_str()); + REQUIRE(str.complete().isNull()); } } diff --git a/test/DynamicMemoryPool/startString.cpp b/test/DynamicMemoryPool/startString.cpp index 43e600b9..c891b4ce 100644 --- a/test/DynamicMemoryPool/startString.cpp +++ b/test/DynamicMemoryPool/startString.cpp @@ -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()); } } diff --git a/test/JsonObject/CMakeLists.txt b/test/JsonObject/CMakeLists.txt index b4f03f52..dbed2e90 100644 --- a/test/JsonObject/CMakeLists.txt +++ b/test/JsonObject/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(JsonObjectTests containsKey.cpp + copy.cpp createNestedArray.cpp createNestedObject.cpp get.cpp diff --git a/test/JsonObject/copy.cpp b/test/JsonObject/copy.cpp new file mode 100644 index 00000000..8691fe9b --- /dev/null +++ b/test/JsonObject/copy.cpp @@ -0,0 +1,59 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("JsonObject::copyFrom()") { + DynamicJsonDocument doc1; + DynamicJsonDocument doc2; + + JsonObject obj1 = doc1.to(); + JsonObject obj2 = doc2.to(); + + 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")); + } +} diff --git a/test/JsonObject/iterator.cpp b/test/JsonObject/iterator.cpp index a54d3689..5c1096f8 100644 --- a/test/JsonObject/iterator.cpp +++ b/test/JsonObject/iterator.cpp @@ -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()); } } diff --git a/test/JsonVariant/copy.cpp b/test/JsonVariant/copy.cpp index 3da18385..d81be221 100644 --- a/test/JsonVariant/copy.cpp +++ b/test/JsonVariant/copy.cpp @@ -13,21 +13,21 @@ TEST_CASE("JsonVariant::set(JsonVariant)") { SECTION("stores JsonArray by copy") { JsonArray arr = doc2.to(); - arr.add(42); - var1.set(doc2.as()); - arr[0] = 666; + var1.set(arr); + + arr[0] = 666; REQUIRE(var1.as() == "[42]"); } SECTION("stores JsonObject by copy") { JsonObject obj = doc2.to(); - obj["value"] = 42; - var1.set(doc2.as()); - obj["value"] = 666; + var1.set(obj); + + obj["value"] = 666; REQUIRE(var1.as() == "{\"value\":42}"); } diff --git a/test/StaticMemoryPool/startString.cpp b/test/StaticMemoryPool/startString.cpp index b8993097..38837aec 100644 --- a/test/StaticMemoryPool/startString.cpp +++ b/test/StaticMemoryPool/startString.cpp @@ -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()); } }