Эх сурвалжийг харах

`RawJson()` accepts any kind of string and obeys to duplication rules

Benoit Blanchon 8 жил өмнө
parent
commit
bae179ed67

+ 8 - 6
CHANGELOG.md

@@ -5,17 +5,19 @@ HEAD
 ----
 
 * Changed the rules of string duplication (issue #658)
+* `RawJson()` accepts any kind of string and obeys to the same rules for duplication
 * Changed the return type of `strdup()` to `const char*` to prevent double duplication
 * Marked `strdup()` as deprecated
 
 > ### New rules for string duplication
 >
-> | type         | duplication |
-> |:-------------|:------------|
-> | const char*  | no          |
-> | char*        | ~~no~~ yes  |
-> | String       | yes         |
-> | std::string  | yes         |
+> | type                       | duplication |
+> |:---------------------------|:------------|
+> | const char*                | no          |
+> | char*                      | ~~no~~ yes  |
+> | String                     | yes         |
+> | std::string                | yes         |
+> | const __FlashStringHelper* | yes         |
 >
 > These new rules make `JsonBuffer::strdup()` useless.
 

+ 3 - 0
examples/ProgmemExample/ProgmemExample.ino

@@ -37,6 +37,9 @@ void setup() {
   // JsonBuffer.
   root["sensor"] = F("gps");
 
+  // It works with RawJson too:
+  root["sensor"] = RawJson(F("\"gps\""));
+
   // You can compare the content of a JsonVariant to a Flash String
   if (root["sensor"] == F("gps")) {
     // ...

+ 3 - 0
examples/StringExample/StringExample.ino

@@ -40,6 +40,9 @@ void setup() {
   // WARNING: the content of the String will be duplicated in the JsonBuffer.
   root["sensor"] = sensor;
 
+  // It works with RawJson too:
+  root["sensor"] = RawJson(sensor);
+
   // You can also concatenate strings
   // WARNING: the content of the String will be duplicated in the JsonBuffer.
   root[String("sen") + "sor"] = String("gp") + "s";

+ 22 - 4
src/ArduinoJson/Data/ValueSaver.hpp

@@ -23,11 +23,29 @@ struct ValueSaver {
 
 template <typename Source>
 struct ValueSaver<Source, typename TypeTraits::EnableIf<
-                              TypeTraits::IsString<Source>::value>::type> {
+                              StringTraits<Source>::should_duplicate>::type> {
   template <typename Destination>
-  static bool save(JsonBuffer* buffer, Destination& destination,
-                   Source source) {
-    return StringTraits<Source>::save(source, destination, buffer);
+  static bool save(JsonBuffer* buffer, Destination& dest, Source source) {
+    if (!StringTraits<Source>::is_null(source)) {
+      typename StringTraits<Source>::duplicate_t dup =
+          StringTraits<Source>::duplicate(source, buffer);
+      if (!dup) return false;
+      dest = dup;
+    } else {
+      dest = reinterpret_cast<const char*>(0);
+    }
+    return true;
+  }
+};
+
+// const char*, const signed char*, const unsigned char*
+template <typename Char>
+struct ValueSaver<Char*, typename TypeTraits::EnableIf<
+                             !StringTraits<Char*>::should_duplicate>::type> {
+  template <typename Destination>
+  static bool save(JsonBuffer*, Destination& dest, Char* source) {
+    dest = reinterpret_cast<const char*>(source);
+    return true;
   }
 };
 }

+ 1 - 1
src/ArduinoJson/JsonObjectSubscript.hpp

@@ -46,7 +46,7 @@ class JsonObjectSubscript
   // operator=(TValue);
   // TValue = char*, const char*, const FlashStringHelper*
   template <typename TValue>
-  FORCE_INLINE this_type& operator=(const TValue* src) {
+  FORCE_INLINE this_type& operator=(TValue* src) {
     _object.set(_key, src);
     return *this;
   }

+ 1 - 1
src/ArduinoJson/JsonVariant.hpp

@@ -117,7 +117,7 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
   }
 
   // Create a JsonVariant containing an unparsed string
-  JsonVariant(RawJson value) {
+  JsonVariant(Internals::RawJsonString<const char *> value) {
     _type = Internals::JSON_UNPARSED;
     _content.asString = value;
   }

+ 5 - 4
src/ArduinoJson/JsonVariantComparisons.hpp

@@ -104,7 +104,7 @@ class JsonVariantComparisons {
   }
 
   template <typename TString>
-  typename TypeTraits::EnableIf<TypeTraits::IsString<TString>::value,
+  typename TypeTraits::EnableIf<Internals::StringTraits<TString>::has_equals,
                                 bool>::type
   equals(const TString &comparand) const {
     const char *value = as<const char *>();
@@ -112,9 +112,10 @@ class JsonVariantComparisons {
   }
 
   template <typename TComparand>
-  typename TypeTraits::EnableIf<!TypeTraits::IsVariant<TComparand>::value &&
-                                    !TypeTraits::IsString<TComparand>::value,
-                                bool>::type
+  typename TypeTraits::EnableIf<
+      !TypeTraits::IsVariant<TComparand>::value &&
+          !Internals::StringTraits<TComparand>::has_equals,
+      bool>::type
   equals(const TComparand &comparand) const {
     return as<TComparand>() == comparand;
   }

+ 30 - 4
src/ArduinoJson/RawJson.hpp

@@ -6,15 +6,41 @@
 
 namespace ArduinoJson {
 
+namespace Internals {
 // A special type of data that can be used to insert pregenerated JSON portions.
-class RawJson {
+template <typename T>
+class RawJsonString {
  public:
-  explicit RawJson(const char* str) : _str(str) {}
-  operator const char*() const {
+  explicit RawJsonString(T str) : _str(str) {}
+  operator T() const {
     return _str;
   }
 
  private:
-  const char* _str;
+  T _str;
 };
+
+template <typename String>
+struct StringTraits<RawJsonString<String>, void> {
+  static bool is_null(RawJsonString<String> source) {
+    return StringTraits<String>::is_null(static_cast<String>(source));
+  }
+
+  typedef RawJsonString<const char*> duplicate_t;
+
+  template <typename Buffer>
+  static duplicate_t duplicate(RawJsonString<String> source, Buffer* buffer) {
+    return duplicate_t(StringTraits<String>::duplicate(source, buffer));
+  }
+
+  static const bool has_append = false;
+  static const bool has_equals = false;
+  static const bool should_duplicate = StringTraits<String>::should_duplicate;
+};
+}
+
+template <typename T>
+inline Internals::RawJsonString<T> RawJson(T str) {
+  return Internals::RawJsonString<T>(str);
+}
 }

+ 2 - 4
src/ArduinoJson/Serialization/JsonPrintable.hpp

@@ -29,8 +29,7 @@ template <typename T>
 class JsonPrintable {
  public:
   template <typename Print>
-  typename TypeTraits::EnableIf<!TypeTraits::IsString<Print>::value,
-                                size_t>::type
+  typename TypeTraits::EnableIf<!StringTraits<Print>::has_append, size_t>::type
   printTo(Print &print) const {
     JsonWriter<Print> writer(print);
     JsonSerializer<JsonWriter<Print> >::serialize(downcast(), writer);
@@ -79,8 +78,7 @@ class JsonPrintable {
   }
 
   template <typename Print>
-  typename TypeTraits::EnableIf<!TypeTraits::IsString<Print>::value,
-                                size_t>::type
+  typename TypeTraits::EnableIf<!StringTraits<Print>::has_append, size_t>::type
   prettyPrintTo(Print &print) const {
     IndentedPrint<Print> indentedPrint(print);
     return prettyPrintTo(indentedPrint);

+ 3 - 0
src/ArduinoJson/StringTraits/ArduinoStream.hpp

@@ -43,6 +43,9 @@ struct ArduinoStreamTraits {
       return c;
     }
   };
+
+  static const bool has_append = false;
+  static const bool has_equals = false;
 };
 
 template <typename TStream>

+ 12 - 39
src/ArduinoJson/StringTraits/CharPointer.hpp

@@ -33,58 +33,31 @@ struct CharPointerTraits {
     return strcmp(reinterpret_cast<const char*>(str), expected) == 0;
   }
 
-  // TODO: remove
+  static bool is_null(const TChar* str) {
+    return !str;
+  }
+
+  typedef const char* duplicate_t;
+
   template <typename Buffer>
-  static char* duplicate(const TChar* str, Buffer* buffer) {
+  static duplicate_t duplicate(const TChar* str, Buffer* buffer) {
     if (!str) return NULL;
     size_t size = strlen(reinterpret_cast<const char*>(str)) + 1;
     void* dup = buffer->alloc(size);
     if (dup != NULL) memcpy(dup, str, size);
-    return static_cast<char*>(dup);
+    return static_cast<duplicate_t>(dup);
   }
 
   static const bool has_append = false;
   static const bool has_equals = true;
-};
-
-// const char*, const unsigned char*, const signed char*
-template <typename TChar>
-struct StringTraits<TChar*, typename TypeTraits::EnableIf<
-                                TypeTraits::IsChar<TChar>::value &&
-                                TypeTraits::IsConst<TChar>::value>::type>
-    : CharPointerTraits<TChar> {
-  // Just save the pointer
-  template <typename Buffer, typename Destination>
-  static typename TypeTraits::EnableIf<TypeTraits::IsConst<TChar>::value,
-                                       bool>::type
-  save(const TChar* source, Destination& dest, Buffer*) {
-    dest = reinterpret_cast<const char*>(source);
-    return true;
-  }
+  static const bool should_duplicate = !TypeTraits::IsConst<TChar>::value;
 };
 
 // char*, unsigned char*, signed char*
+// const char*, const unsigned char*, const signed char*
 template <typename TChar>
 struct StringTraits<TChar*, typename TypeTraits::EnableIf<
-                                TypeTraits::IsChar<TChar>::value &&
-                                !TypeTraits::IsConst<TChar>::value>::type>
-    : CharPointerTraits<TChar> {
-  // Make a copy of the string
-  template <typename Buffer, typename Destination>
-  static typename TypeTraits::EnableIf<!TypeTraits::IsConst<TChar>::value,
-                                       bool>::type
-  save(const TChar* source, Destination& dest, Buffer* buffer) {
-    if (source) {
-      size_t size = strlen(reinterpret_cast<const char*>(source)) + 1;
-      void* dup = buffer->alloc(size);
-      if (!dup) return false;
-      memcpy(dup, source, size);
-      dest = reinterpret_cast<const char*>(dup);
-    } else {
-      dest = reinterpret_cast<const char*>(source);
-    }
-    return true;
-  }
-};
+                                TypeTraits::IsChar<TChar>::value>::type>
+    : CharPointerTraits<TChar> {};
 }
 }

+ 9 - 17
src/ArduinoJson/StringTraits/FlashString.hpp

@@ -34,32 +34,24 @@ struct StringTraits<const __FlashStringHelper*, void> {
     return strcmp_P(expected, (const char*)str) == 0;
   }
 
-  // TODO: remove
+  static bool is_null(const __FlashStringHelper* str) {
+    return !str;
+  }
+
+  typedef const char* duplicate_t;
+
   template <typename Buffer>
-  static char* duplicate(const __FlashStringHelper* str, Buffer* buffer) {
+  static duplicate_t duplicate(const __FlashStringHelper* str, Buffer* buffer) {
     if (!str) return NULL;
     size_t size = strlen_P((const char*)str) + 1;
     void* dup = buffer->alloc(size);
     if (dup != NULL) memcpy_P(dup, (const char*)str, size);
-    return static_cast<char*>(dup);
-  }
-
-  template <typename Buffer, typename Destination>
-  static bool save(const __FlashStringHelper* source, Destination& dest,
-                   Buffer* buffer) {
-    if (source) {
-      size_t size = strlen_P((const char*)source) + 1;
-      void* dup = buffer->alloc(size);
-      if (dup != NULL) memcpy_P(dup, (const char*)source, size);
-      dest = reinterpret_cast<const char*>(dup);
-    } else {
-      dest = reinterpret_cast<const char*>(source);
-    }
-    return true;
+    return static_cast<duplicate_t>(dup);
   }
 
   static const bool has_append = false;
   static const bool has_equals = true;
+  static const bool should_duplicate = true;
 };
 }
 }

+ 3 - 0
src/ArduinoJson/StringTraits/StdStream.hpp

@@ -42,6 +42,9 @@ struct StdStreamTraits {
       return _stream.eof() ? '\0' : static_cast<char>(_stream.get());
     }
   };
+
+  static const bool has_append = false;
+  static const bool has_equals = false;
 };
 
 template <typename TStream>

+ 7 - 15
src/ArduinoJson/StringTraits/StdString.hpp

@@ -19,29 +19,20 @@ namespace Internals {
 
 template <typename TString>
 struct StdStringTraits {
-  // TODO: remove
+  typedef const char* duplicate_t;
+
   template <typename Buffer>
-  static char* duplicate(const TString& str, Buffer* buffer) {
+  static duplicate_t duplicate(const TString& str, Buffer* buffer) {
     if (!str.c_str()) return NULL;  // <- Arduino string can return NULL
     size_t size = str.length() + 1;
     void* dup = buffer->alloc(size);
     if (dup != NULL) memcpy(dup, str.c_str(), size);
-    return static_cast<char*>(dup);
+    return static_cast<duplicate_t>(dup);
   }
 
-  template <typename Buffer, typename Destination>
-  static bool save(const TString& str, Destination& dest, Buffer* buffer) {
+  static bool is_null(const TString& str) {
     // Arduino's String::c_str() can return NULL
-    if (str.c_str()) {
-      size_t size = str.length() + 1;
-      void* dup = buffer->alloc(size);
-      if (!dup) return false;
-      memcpy(dup, str.c_str(), size);
-      dest = reinterpret_cast<const char*>(dup);
-    } else {
-      dest = str.c_str();
-    }
-    return true;
+    return !str.c_str();
   }
 
   struct Reader : CharPointerTraits<char>::Reader {
@@ -62,6 +53,7 @@ struct StdStringTraits {
 
   static const bool has_append = true;
   static const bool has_equals = true;
+  static const bool should_duplicate = true;
 };
 
 #if ARDUINOJSON_ENABLE_ARDUINO_STRING

+ 4 - 16
src/ArduinoJson/StringTraits/StringTraits.hpp

@@ -16,7 +16,10 @@ namespace ArduinoJson {
 namespace Internals {
 
 template <typename TString, typename Enable = void>
-struct StringTraits {};
+struct StringTraits {
+  static const bool has_append = false;
+  static const bool has_equals = false;
+};
 
 template <typename TString>
 struct StringTraits<const TString, void> : StringTraits<TString> {};
@@ -31,18 +34,3 @@ struct StringTraits<TString&, void> : StringTraits<TString> {};
 #include "FlashString.hpp"
 #include "StdStream.hpp"
 #include "StdString.hpp"
-
-namespace ArduinoJson {
-namespace TypeTraits {
-template <typename T, typename Enable = void>
-struct IsString {
-  static const bool value = false;
-};
-
-template <typename T>
-struct IsString<T, typename TypeTraits::EnableIf<
-                       Internals::StringTraits<T>::has_equals>::type> {
-  static const bool value = Internals::StringTraits<T>::has_equals;
-};
-}
-}

+ 13 - 1
test/JsonArray/add.cpp

@@ -84,7 +84,7 @@ TEST_CASE("JsonArray::add()") {
     REQUIRE(expectedSize == _jsonBuffer.size());
   }
 
-  SECTION("should duplicate  char*") {
+  SECTION("should duplicate char*") {
     _array.add(const_cast<char*>("world"));
     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
     REQUIRE(expectedSize == _jsonBuffer.size());
@@ -95,4 +95,16 @@ TEST_CASE("JsonArray::add()") {
     const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
     REQUIRE(expectedSize == _jsonBuffer.size());
   }
+
+  SECTION("should not duplicate RawJson(const char*)") {
+    _array.add(RawJson("{}"));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1);
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate RawJson(char*)") {
+    _array.add(RawJson(const_cast<char*>("{}")));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
 }

+ 14 - 4
test/JsonArray/printTo.cpp

@@ -8,10 +8,10 @@
 static void check(JsonArray &array, std::string expected) {
   std::string actual;
   size_t actualLen = array.printTo(actual);
-  size_t measuredLen = array.measureLength();
-  CHECK(actualLen == expected.size());
-  CHECK(measuredLen == expected.size());
   REQUIRE(expected == actual);
+  REQUIRE(actualLen == expected.size());
+  size_t measuredLen = array.measureLength();
+  REQUIRE(measuredLen == expected.size());
 }
 
 TEST_CASE("JsonArray::printTo()") {
@@ -67,12 +67,22 @@ TEST_CASE("JsonArray::printTo()") {
     check(array, "[1,2]");
   }
 
-  SECTION("RawJson") {
+  SECTION("RawJson(const char*)") {
     array.add(RawJson("{\"key\":\"value\"}"));
 
     check(array, "[{\"key\":\"value\"}]");
   }
 
+  SECTION("RawJson(char*)") {
+    DynamicJsonBuffer jb2;
+    JsonArray &arr = jb2.createArray();
+
+    char tmp[] = "{\"key\":\"value\"}";
+    arr.add(RawJson(tmp));
+
+    check(arr, "[{\"key\":\"value\"}]");
+  }
+
   SECTION("OneIntegerOverCapacity") {
     array.add(1);
     array.add(2);

+ 1 - 0
test/Misc/CMakeLists.txt

@@ -8,6 +8,7 @@ add_executable(MiscTests
 	std_stream.cpp
 	std_string.cpp
 	StringBuilder.cpp
+	StringTraits.cpp
 	TypeTraits.cpp
 	unsigned_char.cpp
 	vla.cpp

+ 22 - 0
test/Misc/StringTraits.cpp

@@ -0,0 +1,22 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+template <typename String>
+bool should_duplicate() {
+  return StringTraits<String>::should_duplicate;
+}
+
+TEST_CASE("StringTraits") {
+  SECTION("should_duplicate") {
+    REQUIRE(false == should_duplicate<const char*>());
+    REQUIRE(true == should_duplicate<char*>());
+    REQUIRE(true == should_duplicate<RawJsonString<char*> >());
+    REQUIRE(false == should_duplicate<RawJsonString<const char*> >());
+  }
+}

+ 0 - 7
test/Misc/TypeTraits.cpp

@@ -4,7 +4,6 @@
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
-#include <sstream>
 
 using namespace ArduinoJson::TypeTraits;
 
@@ -31,12 +30,6 @@ TEST_CASE("TypeTraits") {
     REQUIRE(static_cast<bool>(IsVariant<JsonVariant>::value));
   }
 
-  SECTION("IsString") {
-    REQUIRE((IsString<const char*>::value));
-    REQUIRE((IsString<std::string>::value));
-    REQUIRE_FALSE((IsString<double>::value));
-  }
-
   SECTION("IsConst") {
     REQUIRE_FALSE((IsConst<char>::value));
     REQUIRE((IsConst<const char>::value));