Просмотр исходного кода

Fixed object keys not being duplicated

Benoit Blanchon 7 лет назад
Родитель
Сommit
6b985b2d1f
30 измененных файлов с 368 добавлено и 218 удалено
  1. 2 0
      CHANGELOG.md
  2. 2 0
      src/ArduinoJson.hpp
  3. 0 42
      src/ArduinoJson/Data/JsonVariantContent.hpp
  4. 49 3
      src/ArduinoJson/Data/JsonVariantData.hpp
  5. 0 23
      src/ArduinoJson/Data/JsonVariantType.hpp
  6. 17 17
      src/ArduinoJson/Json/JsonDeserializer.hpp
  7. 39 24
      src/ArduinoJson/JsonObject.hpp
  8. 32 6
      src/ArduinoJson/JsonPair.hpp
  9. 24 20
      src/ArduinoJson/JsonVariant.hpp
  10. 6 5
      src/ArduinoJson/Memory/DynamicMemoryPool.hpp
  11. 6 5
      src/ArduinoJson/Memory/StaticMemoryPool.hpp
  12. 7 5
      src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
  13. 2 2
      src/ArduinoJson/StringStorage/StringCopier.hpp
  14. 5 5
      src/ArduinoJson/StringStorage/StringMover.hpp
  15. 4 4
      src/ArduinoJson/Strings/ArduinoString.hpp
  16. 3 3
      src/ArduinoJson/Strings/FixedSizeFlashString.hpp
  17. 3 3
      src/ArduinoJson/Strings/FixedSizeRamString.hpp
  18. 3 3
      src/ArduinoJson/Strings/StlString.hpp
  19. 16 0
      src/ArduinoJson/Strings/StringInMemoryPool.hpp
  20. 2 0
      src/ArduinoJson/Strings/StringTypes.hpp
  21. 3 3
      src/ArduinoJson/Strings/ZeroTerminatedFlashString.hpp
  22. 11 21
      src/ArduinoJson/Strings/ZeroTerminatedRamString.hpp
  23. 43 0
      src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp
  24. 4 3
      test/DynamicMemoryPool/no_memory.cpp
  25. 8 6
      test/DynamicMemoryPool/startString.cpp
  26. 1 0
      test/JsonObject/CMakeLists.txt
  27. 59 0
      test/JsonObject/copy.cpp
  28. 3 3
      test/JsonObject/iterator.cpp
  29. 6 6
      test/JsonVariant/copy.cpp
  30. 8 6
      test/StaticMemoryPool/startString.cpp

+ 2 - 0
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)
 -----------

+ 2 - 0
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;

+ 0 - 42
src/ArduinoJson/Data/JsonVariantContent.hpp

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

+ 49 - 3
src/ArduinoJson/Data/JsonVariantData.hpp

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

+ 0 - 23
src/ArduinoJson/Data/JsonVariantType.hpp

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

+ 17 - 17
src/ArduinoJson/Json/JsonDeserializer.hpp

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

+ 39 - 24
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<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

+ 32 - 6
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

+ 24 - 20
src/ArduinoJson/JsonVariant.hpp

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

+ 6 - 5
src/ArduinoJson/Memory/DynamicMemoryPool.hpp

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

+ 6 - 5
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<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:

+ 7 - 5
src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp

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

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

+ 5 - 5
src/ArduinoJson/StringStorage/StringMover.hpp

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

+ 4 - 4
src/ArduinoJson/Strings/ArduinoString.hpp

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

+ 3 - 3
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 <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);

+ 3 - 3
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 <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;

+ 3 - 3
src/ArduinoJson/Strings/StlString.hpp

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

+ 2 - 0
src/ArduinoJson/Strings/StringTypes.hpp

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

+ 3 - 3
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 <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);

+ 11 - 21
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) {}
-
-  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;
-  }
+  ZeroTerminatedRamString(const char* str)
+      : ZeroTerminatedRamStringConst(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 - 0
src/ArduinoJson/Strings/ZeroTerminatedRamStringConst.hpp

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

+ 4 - 3
test/DynamicMemoryPool/no_memory.cpp

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

+ 8 - 6
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());
   }
 }

+ 1 - 0
test/JsonObject/CMakeLists.txt

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

+ 59 - 0
test/JsonObject/copy.cpp

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

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

+ 6 - 6
test/JsonVariant/copy.cpp

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