mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 00:32:37 +01:00 
			
		
		
		
	RawJson() accepts any kind of string and obeys to duplication rules
				
					
				
			This commit is contained in:
		
							
								
								
									
										14
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								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. | ||||
|  | ||||
|   | ||||
| @@ -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")) { | ||||
|     // ... | ||||
|   | ||||
| @@ -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"; | ||||
|   | ||||
| @@ -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; | ||||
|   } | ||||
| }; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   } | ||||
|   | ||||
| @@ -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; | ||||
|   } | ||||
|   | ||||
| @@ -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; | ||||
|   } | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -43,6 +43,9 @@ struct ArduinoStreamTraits { | ||||
|       return c; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   static const bool has_append = false; | ||||
|   static const bool has_equals = false; | ||||
| }; | ||||
|  | ||||
| template <typename TStream> | ||||
|   | ||||
| @@ -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> {}; | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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()); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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
									
								
								test/Misc/StringTraits.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								test/Misc/StringTraits.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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*> >()); | ||||
|   } | ||||
| } | ||||
| @@ -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)); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user