diff --git a/CHANGELOG.md b/CHANGELOG.md index cb58722ad..f47f752fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ArduinoJson: change log HEAD ---- -* Added support for custom writer classes (issue #1088) +* Added support for custom writer/reader classes (issue #1088) * Added conversion from `JsonArray` and `JsonObject` to `bool`, to be consistent with `JsonVariant` * Fixed `deserializeJson()` when input contains duplicate keys (issue #1095) * Improved `deserializeMsgPack()` speed by reading several bytes at once diff --git a/extras/tests/CMakeLists.txt b/extras/tests/CMakeLists.txt index 2d9755843..61c5b0490 100644 --- a/extras/tests/CMakeLists.txt +++ b/extras/tests/CMakeLists.txt @@ -72,6 +72,7 @@ if(MSVC) ) endif() +include_directories(Helpers) add_subdirectory(ElementProxy) add_subdirectory(IntegrationTests) add_subdirectory(JsonArray) diff --git a/extras/tests/Helpers/CustomReader.hpp b/extras/tests/Helpers/CustomReader.hpp new file mode 100644 index 000000000..0a426f547 --- /dev/null +++ b/extras/tests/Helpers/CustomReader.hpp @@ -0,0 +1,26 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include + +class CustomReader { + std::stringstream _stream; + + public: + CustomReader(const char* input) : _stream(input) {} + + int read() { + return _stream.get(); + } + + size_t readBytes(char* buffer, size_t length) { + _stream.read(buffer, static_cast(length)); + return static_cast(_stream.gcount()); + } + + private: + CustomReader(const CustomReader&); +}; diff --git a/extras/tests/JsonDeserializer/input_types.cpp b/extras/tests/JsonDeserializer/input_types.cpp index 9b7fe4732..326305b4b 100644 --- a/extras/tests/JsonDeserializer/input_types.cpp +++ b/extras/tests/JsonDeserializer/input_types.cpp @@ -6,6 +6,8 @@ #include #include +#include "CustomReader.hpp" + TEST_CASE("deserializeJson(const std::string&)") { DynamicJsonDocument doc(4096); @@ -113,3 +115,14 @@ TEST_CASE("deserializeJson(VLA)") { REQUIRE(err == DeserializationError::Ok); } #endif + +TEST_CASE("deserializeJson(CustomReader)") { + DynamicJsonDocument doc(4096); + CustomReader reader("[4,2]"); + DeserializationError err = deserializeJson(doc, reader); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.size() == 2); + REQUIRE(doc[0] == 4); + REQUIRE(doc[1] == 2); +} diff --git a/extras/tests/Misc/Readers.cpp b/extras/tests/Misc/Readers.cpp index b5a27cc08..6e22d1b18 100644 --- a/extras/tests/Misc/Readers.cpp +++ b/extras/tests/Misc/Readers.cpp @@ -2,15 +2,15 @@ // Copyright Benoit Blanchon 2014-2019 // MIT License -#include +#include #include using namespace ARDUINOJSON_NAMESPACE; -TEST_CASE("StdStreamReader") { +TEST_CASE("Reader") { SECTION("read()") { std::istringstream src("\x01\xFF"); - StdStreamReader reader(src); + Reader reader(src); REQUIRE(reader.read() == 0x01); REQUIRE(reader.read() == 0xFF); @@ -19,7 +19,7 @@ TEST_CASE("StdStreamReader") { SECTION("readBytes() all at once") { std::istringstream src("ABC"); - StdStreamReader reader(src); + Reader reader(src); char buffer[8] = "abcd"; REQUIRE(reader.readBytes(buffer, 4) == 3); @@ -32,7 +32,7 @@ TEST_CASE("StdStreamReader") { SECTION("readBytes() in two parts") { std::istringstream src("ABCDEF"); - StdStreamReader reader(src); + Reader reader(src); char buffer[12] = "abcdefg"; REQUIRE(reader.readBytes(buffer, 4) == 4); @@ -48,9 +48,9 @@ TEST_CASE("StdStreamReader") { } } -TEST_CASE("SafeCharPointerReader") { +TEST_CASE("BoundedReader") { SECTION("read") { - SafeCharPointerReader reader("\x01\xFF", 2); + BoundedReader reader("\x01\xFF", 2); REQUIRE(reader.read() == 0x01); REQUIRE(reader.read() == 0xFF); REQUIRE(reader.read() == -1); @@ -58,7 +58,7 @@ TEST_CASE("SafeCharPointerReader") { } SECTION("readBytes() all at once") { - SafeCharPointerReader reader("ABCD", 3); + BoundedReader reader("ABCD", 3); char buffer[8] = "abcd"; REQUIRE(reader.readBytes(buffer, 4) == 3); @@ -70,7 +70,7 @@ TEST_CASE("SafeCharPointerReader") { } SECTION("readBytes() in two parts") { - SafeCharPointerReader reader("ABCDEF", 6); + BoundedReader reader("ABCDEF", 6); char buffer[8] = "abcdefg"; REQUIRE(reader.readBytes(buffer, 4) == 4); @@ -86,9 +86,9 @@ TEST_CASE("SafeCharPointerReader") { } } -TEST_CASE("UnsafeCharPointerReader") { +TEST_CASE("Reader") { SECTION("read()") { - UnsafeCharPointerReader reader("\x01\xFF\x00\x12"); + Reader reader("\x01\xFF\x00\x12"); REQUIRE(reader.read() == 0x01); REQUIRE(reader.read() == 0xFF); REQUIRE(reader.read() == 0); @@ -96,7 +96,7 @@ TEST_CASE("UnsafeCharPointerReader") { } SECTION("readBytes() all at once") { - UnsafeCharPointerReader reader("ABCD"); + Reader reader("ABCD"); char buffer[8] = "abcd"; REQUIRE(reader.readBytes(buffer, 3) == 3); @@ -108,7 +108,7 @@ TEST_CASE("UnsafeCharPointerReader") { } SECTION("readBytes() in two parts") { - UnsafeCharPointerReader reader("ABCDEF"); + Reader reader("ABCDEF"); char buffer[8] = "abcdefg"; REQUIRE(reader.readBytes(buffer, 4) == 4); diff --git a/extras/tests/MixedConfiguration/enable_progmem_1.cpp b/extras/tests/MixedConfiguration/enable_progmem_1.cpp index b812e8613..becc23d79 100644 --- a/extras/tests/MixedConfiguration/enable_progmem_1.cpp +++ b/extras/tests/MixedConfiguration/enable_progmem_1.cpp @@ -86,11 +86,11 @@ TEST_CASE("memcpy_P") { CHECK(dst[3] == 0); } -TEST_CASE("SafeCharPointerReader") { - using ARDUINOJSON_NAMESPACE::SafeFlashStringReader; +TEST_CASE("BoundedReader") { + using namespace ARDUINOJSON_NAMESPACE; SECTION("read") { - SafeFlashStringReader reader(F("\x01\xFF"), 2); + BoundedReader reader(F("\x01\xFF"), 2); REQUIRE(reader.read() == 0x01); REQUIRE(reader.read() == 0xFF); REQUIRE(reader.read() == -1); @@ -98,7 +98,7 @@ TEST_CASE("SafeCharPointerReader") { } SECTION("readBytes() all at once") { - SafeFlashStringReader reader(F("ABCD"), 3); + BoundedReader reader(F("ABCD"), 3); char buffer[8] = "abcd"; REQUIRE(reader.readBytes(buffer, 4) == 3); @@ -110,7 +110,7 @@ TEST_CASE("SafeCharPointerReader") { } SECTION("readBytes() in two parts") { - SafeFlashStringReader reader(F("ABCDEF"), 6); + BoundedReader reader(F("ABCDEF"), 6); char buffer[8] = "abcdefg"; REQUIRE(reader.readBytes(buffer, 4) == 4); @@ -126,11 +126,11 @@ TEST_CASE("SafeCharPointerReader") { } } -TEST_CASE("UnsafeFlashStringReader") { - using ARDUINOJSON_NAMESPACE::UnsafeFlashStringReader; +TEST_CASE("Reader") { + using namespace ARDUINOJSON_NAMESPACE; SECTION("read()") { - UnsafeFlashStringReader reader(F("\x01\xFF\x00\x12")); + Reader reader(F("\x01\xFF\x00\x12")); REQUIRE(reader.read() == 0x01); REQUIRE(reader.read() == 0xFF); REQUIRE(reader.read() == 0); @@ -138,7 +138,7 @@ TEST_CASE("UnsafeFlashStringReader") { } SECTION("readBytes() all at once") { - UnsafeFlashStringReader reader(F("ABCD")); + Reader reader(F("ABCD")); char buffer[8] = "abcd"; REQUIRE(reader.readBytes(buffer, 3) == 3); @@ -150,7 +150,7 @@ TEST_CASE("UnsafeFlashStringReader") { } SECTION("readBytes() in two parts") { - UnsafeFlashStringReader reader(F("ABCDEF")); + Reader reader(F("ABCDEF")); char buffer[8] = "abcdefg"; REQUIRE(reader.readBytes(buffer, 4) == 4); diff --git a/extras/tests/MsgPackDeserializer/input_types.cpp b/extras/tests/MsgPackDeserializer/input_types.cpp index 3b20add45..20481abd3 100644 --- a/extras/tests/MsgPackDeserializer/input_types.cpp +++ b/extras/tests/MsgPackDeserializer/input_types.cpp @@ -5,6 +5,8 @@ #include #include +#include "CustomReader.hpp" + TEST_CASE("deserializeMsgPack(const std::string&)") { DynamicJsonDocument doc(4096); @@ -80,3 +82,14 @@ TEST_CASE("deserializeMsgPack(VLA)") { REQUIRE(err == DeserializationError::Ok); } #endif + +TEST_CASE("deserializeMsgPack(CustomReader)") { + DynamicJsonDocument doc(4096); + CustomReader reader("\x92\xA5Hello\xA5world"); + DeserializationError err = deserializeMsgPack(doc, reader); + + REQUIRE(err == DeserializationError::Ok); + REQUIRE(doc.size() == 2); + REQUIRE(doc[0] == "Hello"); + REQUIRE(doc[1] == "world"); +} diff --git a/src/ArduinoJson/Deserialization/CharPointerReader.hpp b/src/ArduinoJson/Deserialization/CharPointerReader.hpp deleted file mode 100644 index a8fb38f03..000000000 --- a/src/ArduinoJson/Deserialization/CharPointerReader.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 -// MIT License - -#pragma once - -#include -#include - -namespace ARDUINOJSON_NAMESPACE { - -template -struct IsCharOrVoid { - static const bool value = - is_same::value || is_same::value || - is_same::value || is_same::value; -}; - -template -struct IsCharOrVoid : IsCharOrVoid {}; - -class UnsafeCharPointerReader { - const char* _ptr; - - public: - explicit UnsafeCharPointerReader(const char* ptr) - : _ptr(ptr ? ptr : reinterpret_cast("")) {} - - int read() { - return static_cast(*_ptr++); - } - - size_t readBytes(char* buffer, size_t length) { - for (size_t i = 0; i < length; i++) buffer[i] = *_ptr++; - return length; - } -}; - -class SafeCharPointerReader : public IteratorReader { - public: - explicit SafeCharPointerReader(const char* ptr, size_t len) - : IteratorReader(ptr, ptr + len) {} -}; - -template -inline typename enable_if::value, - UnsafeCharPointerReader>::type -makeReader(TChar* input) { - return UnsafeCharPointerReader(reinterpret_cast(input)); -} - -template -inline - typename enable_if::value, SafeCharPointerReader>::type - makeReader(TChar* input, size_t n) { - return SafeCharPointerReader(reinterpret_cast(input), n); -} - -#if ARDUINOJSON_ENABLE_ARDUINO_STRING -inline SafeCharPointerReader makeReader(const ::String& input) { - return SafeCharPointerReader(input.c_str(), input.length()); -} -#endif - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Deserialization/Reader.hpp b/src/ArduinoJson/Deserialization/Reader.hpp new file mode 100644 index 000000000..a6beeba78 --- /dev/null +++ b/src/ArduinoJson/Deserialization/Reader.hpp @@ -0,0 +1,55 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include + +#include // for size_t + +namespace ARDUINOJSON_NAMESPACE { + +// The default reader is a simple wrapper for Readers that are not copiable +template +struct Reader { + public: + Reader(TSource& source) : _source(&source) {} + + int read() { + return _source->read(); + } + + size_t readBytes(char* buffer, size_t length) { + return _source->readBytes(buffer, length); + } + + private: + TSource* _source; +}; + +template +struct BoundedReader { + // no default implementation because we need to pass the size to the + // constructor +}; +} // namespace ARDUINOJSON_NAMESPACE + +#include +#include + +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM +#include +#endif + +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +#include +#endif + +#if ARDUINOJSON_ENABLE_PROGMEM +#include +#endif + +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif diff --git a/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp b/src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp similarity index 62% rename from src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp rename to src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp index 2135ba249..dac436bfd 100644 --- a/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp +++ b/src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp @@ -4,19 +4,17 @@ #pragma once -#include - -#if ARDUINOJSON_ENABLE_ARDUINO_STREAM - #include namespace ARDUINOJSON_NAMESPACE { -struct ArduinoStreamReader { +template +struct Reader::value>::type> { Stream& _stream; public: - explicit ArduinoStreamReader(Stream& stream) : _stream(stream) {} + explicit Reader(Stream& stream) : _stream(stream) {} int read() { // don't use _stream.read() as it ignores the timeout @@ -29,9 +27,4 @@ struct ArduinoStreamReader { } }; -inline ArduinoStreamReader makeReader(Stream& input) { - return ArduinoStreamReader(input); -} } // namespace ARDUINOJSON_NAMESPACE - -#endif diff --git a/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp b/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp new file mode 100644 index 000000000..107839793 --- /dev/null +++ b/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp @@ -0,0 +1,17 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +namespace ARDUINOJSON_NAMESPACE { + +template +struct Reader::value>::type> + : BoundedReader { + explicit Reader(const ::String& s) + : BoundedReader(s.c_str(), s.length()) {} +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Deserialization/FlashStringReader.hpp b/src/ArduinoJson/Deserialization/Readers/FlashReader.hpp similarity index 60% rename from src/ArduinoJson/Deserialization/FlashStringReader.hpp rename to src/ArduinoJson/Deserialization/Readers/FlashReader.hpp index 0891eace7..5c96c40ba 100644 --- a/src/ArduinoJson/Deserialization/FlashStringReader.hpp +++ b/src/ArduinoJson/Deserialization/Readers/FlashReader.hpp @@ -4,16 +4,14 @@ #pragma once -#include - -#if ARDUINOJSON_ENABLE_PROGMEM - namespace ARDUINOJSON_NAMESPACE { -class UnsafeFlashStringReader { + +template <> +struct Reader { const char* _ptr; public: - explicit UnsafeFlashStringReader(const __FlashStringHelper* ptr) + explicit Reader(const __FlashStringHelper* ptr) : _ptr(reinterpret_cast(ptr)) {} int read() { @@ -27,12 +25,13 @@ class UnsafeFlashStringReader { } }; -class SafeFlashStringReader { +template <> +struct BoundedReader { const char* _ptr; const char* _end; public: - explicit SafeFlashStringReader(const __FlashStringHelper* ptr, size_t size) + explicit BoundedReader(const __FlashStringHelper* ptr, size_t size) : _ptr(reinterpret_cast(ptr)), _end(_ptr + size) {} int read() { @@ -50,15 +49,4 @@ class SafeFlashStringReader { return length; } }; - -inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) { - return UnsafeFlashStringReader(input); -} - -inline SafeFlashStringReader makeReader(const __FlashStringHelper* input, - size_t size) { - return SafeFlashStringReader(input, size); -} } // namespace ARDUINOJSON_NAMESPACE - -#endif diff --git a/src/ArduinoJson/Deserialization/IteratorReader.hpp b/src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp similarity index 60% rename from src/ArduinoJson/Deserialization/IteratorReader.hpp rename to src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp index c52a7b0bf..70b3d4d99 100644 --- a/src/ArduinoJson/Deserialization/IteratorReader.hpp +++ b/src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp @@ -4,8 +4,6 @@ #pragma once -#include - namespace ARDUINOJSON_NAMESPACE { template @@ -30,10 +28,16 @@ class IteratorReader { } }; -template -inline IteratorReader makeReader( - const TInput& input) { - return IteratorReader(input.begin(), - input.end()); -} +template +struct void_ { + typedef void type; +}; + +template +struct Reader::type> + : IteratorReader { + explicit Reader(const TSource& source) + : IteratorReader(source.begin(), + source.end()) {} +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Deserialization/Readers/RamReader.hpp b/src/ArduinoJson/Deserialization/Readers/RamReader.hpp new file mode 100644 index 000000000..9101d4ef6 --- /dev/null +++ b/src/ArduinoJson/Deserialization/Readers/RamReader.hpp @@ -0,0 +1,50 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +struct IsCharOrVoid { + static const bool value = + is_same::value || is_same::value || + is_same::value || is_same::value; +}; + +template +struct IsCharOrVoid : IsCharOrVoid {}; + +template +struct Reader::value>::type> { + const char* _ptr; + + public: + explicit Reader(const void* ptr) + : _ptr(ptr ? reinterpret_cast(ptr) : "") {} + + int read() { + return static_cast(*_ptr++); + } + + size_t readBytes(char* buffer, size_t length) { + for (size_t i = 0; i < length; i++) buffer[i] = *_ptr++; + return length; + } +}; + +template +struct BoundedReader::value>::type> + : public IteratorReader { + public: + explicit BoundedReader(const void* ptr, size_t len) + : IteratorReader(reinterpret_cast(ptr), + reinterpret_cast(ptr) + len) {} +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp b/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp new file mode 100644 index 000000000..7bcbfaa15 --- /dev/null +++ b/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp @@ -0,0 +1,29 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +struct Reader::value>::type> { + public: + explicit Reader(std::istream& stream) : _stream(&stream) {} + + int read() { + return _stream->get(); + } + + size_t readBytes(char* buffer, size_t length) { + _stream->read(buffer, static_cast(length)); + return static_cast(_stream->gcount()); + } + + private: + std::istream* _stream; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Deserialization/StdStreamReader.hpp b/src/ArduinoJson/Deserialization/StdStreamReader.hpp deleted file mode 100644 index bf6cbb93d..000000000 --- a/src/ArduinoJson/Deserialization/StdStreamReader.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 -// MIT License - -#pragma once - -#include - -#if ARDUINOJSON_ENABLE_STD_STREAM - -#include - -namespace ARDUINOJSON_NAMESPACE { - -class StdStreamReader { - std::istream& _stream; - char _current; - - public: - explicit StdStreamReader(std::istream& stream) - : _stream(stream), _current(0) {} - - int read() { - return _stream.get(); - } - - size_t readBytes(char* buffer, size_t length) { - _stream.read(buffer, static_cast(length)); - return static_cast(_stream.gcount()); - } - - private: - StdStreamReader& operator=(const StdStreamReader&); // Visual Studio C4512 -}; - -inline StdStreamReader makeReader(std::istream& input) { - return StdStreamReader(input); -} -} // namespace ARDUINOJSON_NAMESPACE - -#endif diff --git a/src/ArduinoJson/Deserialization/deserialize.hpp b/src/ArduinoJson/Deserialization/deserialize.hpp index a5bae37e4..eb3a9924b 100644 --- a/src/ArduinoJson/Deserialization/deserialize.hpp +++ b/src/ArduinoJson/Deserialization/deserialize.hpp @@ -4,13 +4,9 @@ #pragma once -#include -#include #include -#include -#include #include -#include +#include #include namespace ARDUINOJSON_NAMESPACE { @@ -25,26 +21,17 @@ TDeserializer makeDeserializer(MemoryPool &pool, // deserialize(JsonDocument&, const std::string&); // deserialize(JsonDocument&, const String&); +// deserialize(JsonDocument&, char*); +// deserialize(JsonDocument&, const char*); +// deserialize(JsonDocument&, const __FlashStringHelper*); template