diff --git a/CHANGELOG.md b/CHANGELOG.md index 09933380..21142424 100644 --- a/CHANGELOG.md +++ b/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. diff --git a/examples/ProgmemExample/ProgmemExample.ino b/examples/ProgmemExample/ProgmemExample.ino index 287c5067..15be8ed1 100644 --- a/examples/ProgmemExample/ProgmemExample.ino +++ b/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")) { // ... diff --git a/examples/StringExample/StringExample.ino b/examples/StringExample/StringExample.ino index d070d551..d5994ffb 100644 --- a/examples/StringExample/StringExample.ino +++ b/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"; diff --git a/src/ArduinoJson/Data/ValueSaver.hpp b/src/ArduinoJson/Data/ValueSaver.hpp index 0a9e1883..0e996e6a 100644 --- a/src/ArduinoJson/Data/ValueSaver.hpp +++ b/src/ArduinoJson/Data/ValueSaver.hpp @@ -23,11 +23,29 @@ struct ValueSaver { template struct ValueSaver::value>::type> { + StringTraits::should_duplicate>::type> { template - static bool save(JsonBuffer* buffer, Destination& destination, - Source source) { - return StringTraits::save(source, destination, buffer); + static bool save(JsonBuffer* buffer, Destination& dest, Source source) { + if (!StringTraits::is_null(source)) { + typename StringTraits::duplicate_t dup = + StringTraits::duplicate(source, buffer); + if (!dup) return false; + dest = dup; + } else { + dest = reinterpret_cast(0); + } + return true; + } +}; + +// const char*, const signed char*, const unsigned char* +template +struct ValueSaver::should_duplicate>::type> { + template + static bool save(JsonBuffer*, Destination& dest, Char* source) { + dest = reinterpret_cast(source); + return true; } }; } diff --git a/src/ArduinoJson/JsonObjectSubscript.hpp b/src/ArduinoJson/JsonObjectSubscript.hpp index 57544e65..ede6bdcf 100644 --- a/src/ArduinoJson/JsonObjectSubscript.hpp +++ b/src/ArduinoJson/JsonObjectSubscript.hpp @@ -46,7 +46,7 @@ class JsonObjectSubscript // operator=(TValue); // TValue = char*, const char*, const FlashStringHelper* template - FORCE_INLINE this_type& operator=(const TValue* src) { + FORCE_INLINE this_type& operator=(TValue* src) { _object.set(_key, src); return *this; } diff --git a/src/ArduinoJson/JsonVariant.hpp b/src/ArduinoJson/JsonVariant.hpp index f2d7ff95..415af172 100644 --- a/src/ArduinoJson/JsonVariant.hpp +++ b/src/ArduinoJson/JsonVariant.hpp @@ -117,7 +117,7 @@ class JsonVariant : public JsonVariantBase { } // Create a JsonVariant containing an unparsed string - JsonVariant(RawJson value) { + JsonVariant(Internals::RawJsonString value) { _type = Internals::JSON_UNPARSED; _content.asString = value; } diff --git a/src/ArduinoJson/JsonVariantComparisons.hpp b/src/ArduinoJson/JsonVariantComparisons.hpp index 156d0db3..74eeae43 100644 --- a/src/ArduinoJson/JsonVariantComparisons.hpp +++ b/src/ArduinoJson/JsonVariantComparisons.hpp @@ -104,7 +104,7 @@ class JsonVariantComparisons { } template - typename TypeTraits::EnableIf::value, + typename TypeTraits::EnableIf::has_equals, bool>::type equals(const TString &comparand) const { const char *value = as(); @@ -112,9 +112,10 @@ class JsonVariantComparisons { } template - typename TypeTraits::EnableIf::value && - !TypeTraits::IsString::value, - bool>::type + typename TypeTraits::EnableIf< + !TypeTraits::IsVariant::value && + !Internals::StringTraits::has_equals, + bool>::type equals(const TComparand &comparand) const { return as() == comparand; } diff --git a/src/ArduinoJson/RawJson.hpp b/src/ArduinoJson/RawJson.hpp index 5477278f..4beb980e 100644 --- a/src/ArduinoJson/RawJson.hpp +++ b/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 +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 +struct StringTraits, void> { + static bool is_null(RawJsonString source) { + return StringTraits::is_null(static_cast(source)); + } + + typedef RawJsonString duplicate_t; + + template + static duplicate_t duplicate(RawJsonString source, Buffer* buffer) { + return duplicate_t(StringTraits::duplicate(source, buffer)); + } + + static const bool has_append = false; + static const bool has_equals = false; + static const bool should_duplicate = StringTraits::should_duplicate; }; } + +template +inline Internals::RawJsonString RawJson(T str) { + return Internals::RawJsonString(str); +} +} diff --git a/src/ArduinoJson/Serialization/JsonPrintable.hpp b/src/ArduinoJson/Serialization/JsonPrintable.hpp index 9ea4bfb4..650cff54 100644 --- a/src/ArduinoJson/Serialization/JsonPrintable.hpp +++ b/src/ArduinoJson/Serialization/JsonPrintable.hpp @@ -29,8 +29,7 @@ template class JsonPrintable { public: template - typename TypeTraits::EnableIf::value, - size_t>::type + typename TypeTraits::EnableIf::has_append, size_t>::type printTo(Print &print) const { JsonWriter writer(print); JsonSerializer >::serialize(downcast(), writer); @@ -79,8 +78,7 @@ class JsonPrintable { } template - typename TypeTraits::EnableIf::value, - size_t>::type + typename TypeTraits::EnableIf::has_append, size_t>::type prettyPrintTo(Print &print) const { IndentedPrint indentedPrint(print); return prettyPrintTo(indentedPrint); diff --git a/src/ArduinoJson/StringTraits/ArduinoStream.hpp b/src/ArduinoJson/StringTraits/ArduinoStream.hpp index 0fe08100..80d383db 100644 --- a/src/ArduinoJson/StringTraits/ArduinoStream.hpp +++ b/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 diff --git a/src/ArduinoJson/StringTraits/CharPointer.hpp b/src/ArduinoJson/StringTraits/CharPointer.hpp index 9231d6c4..20864f70 100644 --- a/src/ArduinoJson/StringTraits/CharPointer.hpp +++ b/src/ArduinoJson/StringTraits/CharPointer.hpp @@ -33,58 +33,31 @@ struct CharPointerTraits { return strcmp(reinterpret_cast(str), expected) == 0; } - // TODO: remove + static bool is_null(const TChar* str) { + return !str; + } + + typedef const char* duplicate_t; + template - 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(str)) + 1; void* dup = buffer->alloc(size); if (dup != NULL) memcpy(dup, str, size); - return static_cast(dup); + return static_cast(dup); } static const bool has_append = false; static const bool has_equals = true; -}; - -// const char*, const unsigned char*, const signed char* -template -struct StringTraits::value && - TypeTraits::IsConst::value>::type> - : CharPointerTraits { - // Just save the pointer - template - static typename TypeTraits::EnableIf::value, - bool>::type - save(const TChar* source, Destination& dest, Buffer*) { - dest = reinterpret_cast(source); - return true; - } + static const bool should_duplicate = !TypeTraits::IsConst::value; }; // char*, unsigned char*, signed char* +// const char*, const unsigned char*, const signed char* template struct StringTraits::value && - !TypeTraits::IsConst::value>::type> - : CharPointerTraits { - // Make a copy of the string - template - static typename TypeTraits::EnableIf::value, - bool>::type - save(const TChar* source, Destination& dest, Buffer* buffer) { - if (source) { - size_t size = strlen(reinterpret_cast(source)) + 1; - void* dup = buffer->alloc(size); - if (!dup) return false; - memcpy(dup, source, size); - dest = reinterpret_cast(dup); - } else { - dest = reinterpret_cast(source); - } - return true; - } -}; + TypeTraits::IsChar::value>::type> + : CharPointerTraits {}; } } diff --git a/src/ArduinoJson/StringTraits/FlashString.hpp b/src/ArduinoJson/StringTraits/FlashString.hpp index d706f35a..95f555d2 100644 --- a/src/ArduinoJson/StringTraits/FlashString.hpp +++ b/src/ArduinoJson/StringTraits/FlashString.hpp @@ -34,32 +34,24 @@ struct StringTraits { 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 - 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(dup); - } - - template - 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(dup); - } else { - dest = reinterpret_cast(source); - } - return true; + return static_cast(dup); } static const bool has_append = false; static const bool has_equals = true; + static const bool should_duplicate = true; }; } } diff --git a/src/ArduinoJson/StringTraits/StdStream.hpp b/src/ArduinoJson/StringTraits/StdStream.hpp index ef203391..3a410aeb 100644 --- a/src/ArduinoJson/StringTraits/StdStream.hpp +++ b/src/ArduinoJson/StringTraits/StdStream.hpp @@ -42,6 +42,9 @@ struct StdStreamTraits { return _stream.eof() ? '\0' : static_cast(_stream.get()); } }; + + static const bool has_append = false; + static const bool has_equals = false; }; template diff --git a/src/ArduinoJson/StringTraits/StdString.hpp b/src/ArduinoJson/StringTraits/StdString.hpp index 719f7647..39124dac 100644 --- a/src/ArduinoJson/StringTraits/StdString.hpp +++ b/src/ArduinoJson/StringTraits/StdString.hpp @@ -19,29 +19,20 @@ namespace Internals { template struct StdStringTraits { - // TODO: remove + typedef const char* duplicate_t; + template - 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(dup); + return static_cast(dup); } - template - 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(dup); - } else { - dest = str.c_str(); - } - return true; + return !str.c_str(); } struct Reader : CharPointerTraits::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 diff --git a/src/ArduinoJson/StringTraits/StringTraits.hpp b/src/ArduinoJson/StringTraits/StringTraits.hpp index 568a4221..dd5694b2 100644 --- a/src/ArduinoJson/StringTraits/StringTraits.hpp +++ b/src/ArduinoJson/StringTraits/StringTraits.hpp @@ -16,7 +16,10 @@ namespace ArduinoJson { namespace Internals { template -struct StringTraits {}; +struct StringTraits { + static const bool has_append = false; + static const bool has_equals = false; +}; template struct StringTraits : StringTraits {}; @@ -31,18 +34,3 @@ struct StringTraits : StringTraits {}; #include "FlashString.hpp" #include "StdStream.hpp" #include "StdString.hpp" - -namespace ArduinoJson { -namespace TypeTraits { -template -struct IsString { - static const bool value = false; -}; - -template -struct IsString::has_equals>::type> { - static const bool value = Internals::StringTraits::has_equals; -}; -} -} diff --git a/test/JsonArray/add.cpp b/test/JsonArray/add.cpp index 1a39833f..2e10754e 100644 --- a/test/JsonArray/add.cpp +++ b/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("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("{}"))); + const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3; + REQUIRE(expectedSize == _jsonBuffer.size()); + } } diff --git a/test/JsonArray/printTo.cpp b/test/JsonArray/printTo.cpp index 73f14ac6..6b7f003c 100644 --- a/test/JsonArray/printTo.cpp +++ b/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); diff --git a/test/Misc/CMakeLists.txt b/test/Misc/CMakeLists.txt index 5b572601..0b54bd7b 100644 --- a/test/Misc/CMakeLists.txt +++ b/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 diff --git a/test/Misc/StringTraits.cpp b/test/Misc/StringTraits.cpp new file mode 100644 index 00000000..db18648c --- /dev/null +++ b/test/Misc/StringTraits.cpp @@ -0,0 +1,22 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +using namespace ArduinoJson::Internals; + +template +bool should_duplicate() { + return StringTraits::should_duplicate; +} + +TEST_CASE("StringTraits") { + SECTION("should_duplicate") { + REQUIRE(false == should_duplicate()); + REQUIRE(true == should_duplicate()); + REQUIRE(true == should_duplicate >()); + REQUIRE(false == should_duplicate >()); + } +} diff --git a/test/Misc/TypeTraits.cpp b/test/Misc/TypeTraits.cpp index 6f170eb0..2f8e7e8c 100644 --- a/test/Misc/TypeTraits.cpp +++ b/test/Misc/TypeTraits.cpp @@ -4,7 +4,6 @@ #include #include -#include using namespace ArduinoJson::TypeTraits; @@ -31,12 +30,6 @@ TEST_CASE("TypeTraits") { REQUIRE(static_cast(IsVariant::value)); } - SECTION("IsString") { - REQUIRE((IsString::value)); - REQUIRE((IsString::value)); - REQUIRE_FALSE((IsString::value)); - } - SECTION("IsConst") { REQUIRE_FALSE((IsConst::value)); REQUIRE((IsConst::value));