diff --git a/CHANGELOG.md b/CHANGELOG.md index 5def537c..de905099 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ HEAD * Added support for custom writer 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 v6.12.0 (2019-09-05) ------- diff --git a/extras/tests/Misc/CMakeLists.txt b/extras/tests/Misc/CMakeLists.txt index 0f70acc0..73ac51d6 100644 --- a/extras/tests/Misc/CMakeLists.txt +++ b/extras/tests/Misc/CMakeLists.txt @@ -5,7 +5,7 @@ add_executable(MiscTests conflicts.cpp FloatParts.cpp - StreamReader.cpp + Readers.cpp StringAdapters.cpp StringWriter.cpp TypeTraits.cpp diff --git a/extras/tests/Misc/Readers.cpp b/extras/tests/Misc/Readers.cpp new file mode 100644 index 00000000..b5a27cc0 --- /dev/null +++ b/extras/tests/Misc/Readers.cpp @@ -0,0 +1,166 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#include +#include + +using namespace ARDUINOJSON_NAMESPACE; + +TEST_CASE("StdStreamReader") { + SECTION("read()") { + std::istringstream src("\x01\xFF"); + StdStreamReader reader(src); + + REQUIRE(reader.read() == 0x01); + REQUIRE(reader.read() == 0xFF); + REQUIRE(reader.read() == -1); + } + + SECTION("readBytes() all at once") { + std::istringstream src("ABC"); + StdStreamReader reader(src); + + char buffer[8] = "abcd"; + REQUIRE(reader.readBytes(buffer, 4) == 3); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'd'); + } + + SECTION("readBytes() in two parts") { + std::istringstream src("ABCDEF"); + StdStreamReader reader(src); + + char buffer[12] = "abcdefg"; + REQUIRE(reader.readBytes(buffer, 4) == 4); + REQUIRE(reader.readBytes(buffer + 4, 4) == 2); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'D'); + REQUIRE(buffer[4] == 'E'); + REQUIRE(buffer[5] == 'F'); + REQUIRE(buffer[6] == 'g'); + } +} + +TEST_CASE("SafeCharPointerReader") { + SECTION("read") { + SafeCharPointerReader reader("\x01\xFF", 2); + REQUIRE(reader.read() == 0x01); + REQUIRE(reader.read() == 0xFF); + REQUIRE(reader.read() == -1); + REQUIRE(reader.read() == -1); + } + + SECTION("readBytes() all at once") { + SafeCharPointerReader reader("ABCD", 3); + + char buffer[8] = "abcd"; + REQUIRE(reader.readBytes(buffer, 4) == 3); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'd'); + } + + SECTION("readBytes() in two parts") { + SafeCharPointerReader reader("ABCDEF", 6); + + char buffer[8] = "abcdefg"; + REQUIRE(reader.readBytes(buffer, 4) == 4); + REQUIRE(reader.readBytes(buffer + 4, 4) == 2); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'D'); + REQUIRE(buffer[4] == 'E'); + REQUIRE(buffer[5] == 'F'); + REQUIRE(buffer[6] == 'g'); + } +} + +TEST_CASE("UnsafeCharPointerReader") { + SECTION("read()") { + UnsafeCharPointerReader reader("\x01\xFF\x00\x12"); + REQUIRE(reader.read() == 0x01); + REQUIRE(reader.read() == 0xFF); + REQUIRE(reader.read() == 0); + REQUIRE(reader.read() == 0x12); + } + + SECTION("readBytes() all at once") { + UnsafeCharPointerReader reader("ABCD"); + + char buffer[8] = "abcd"; + REQUIRE(reader.readBytes(buffer, 3) == 3); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'd'); + } + + SECTION("readBytes() in two parts") { + UnsafeCharPointerReader reader("ABCDEF"); + + char buffer[8] = "abcdefg"; + REQUIRE(reader.readBytes(buffer, 4) == 4); + REQUIRE(reader.readBytes(buffer + 4, 2) == 2); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'D'); + REQUIRE(buffer[4] == 'E'); + REQUIRE(buffer[5] == 'F'); + REQUIRE(buffer[6] == 'g'); + } +} + +TEST_CASE("IteratorReader") { + SECTION("read()") { + std::string src("\x01\xFF"); + IteratorReader reader(src.begin(), src.end()); + + REQUIRE(reader.read() == 0x01); + REQUIRE(reader.read() == 0xFF); + REQUIRE(reader.read() == -1); + } + + SECTION("readBytes() all at once") { + std::string src("ABC"); + IteratorReader reader(src.begin(), src.end()); + + char buffer[8] = "abcd"; + REQUIRE(reader.readBytes(buffer, 4) == 3); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'd'); + } + + SECTION("readBytes() in two parts") { + std::string src("ABCDEF"); + IteratorReader reader(src.begin(), src.end()); + + char buffer[12] = "abcdefg"; + REQUIRE(reader.readBytes(buffer, 4) == 4); + REQUIRE(reader.readBytes(buffer + 4, 4) == 2); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'D'); + REQUIRE(buffer[4] == 'E'); + REQUIRE(buffer[5] == 'F'); + REQUIRE(buffer[6] == 'g'); + } +} diff --git a/extras/tests/Misc/StreamReader.cpp b/extras/tests/Misc/StreamReader.cpp deleted file mode 100644 index ab4c244a..00000000 --- a/extras/tests/Misc/StreamReader.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2019 -// MIT License - -#include -#include - -using namespace ARDUINOJSON_NAMESPACE; - -TEST_CASE("StdStreamReader") { - std::istringstream src("\x01\xFF"); - StdStreamReader reader(src); - - REQUIRE(reader.read() == 0x01); - REQUIRE(reader.read() == 0xFF); - REQUIRE(reader.read() == -1); -} - -TEST_CASE("SafeCharPointerReader") { - SafeCharPointerReader reader("\x01\xFF", 2); - - REQUIRE(reader.read() == 0x01); - REQUIRE(reader.read() == 0xFF); - REQUIRE(reader.read() == -1); -} - -TEST_CASE("UnsafeCharPointerReader") { - UnsafeCharPointerReader reader("\x01\xFF"); - - REQUIRE(reader.read() == 0x01); - REQUIRE(reader.read() == 0xFF); - REQUIRE(reader.read() == 0); -} diff --git a/extras/tests/MixedConfiguration/enable_progmem_1.cpp b/extras/tests/MixedConfiguration/enable_progmem_1.cpp index a228d746..b812e861 100644 --- a/extras/tests/MixedConfiguration/enable_progmem_1.cpp +++ b/extras/tests/MixedConfiguration/enable_progmem_1.cpp @@ -85,3 +85,83 @@ TEST_CASE("memcpy_P") { CHECK(dst[2] == 'C'); CHECK(dst[3] == 0); } + +TEST_CASE("SafeCharPointerReader") { + using ARDUINOJSON_NAMESPACE::SafeFlashStringReader; + + SECTION("read") { + SafeFlashStringReader reader(F("\x01\xFF"), 2); + REQUIRE(reader.read() == 0x01); + REQUIRE(reader.read() == 0xFF); + REQUIRE(reader.read() == -1); + REQUIRE(reader.read() == -1); + } + + SECTION("readBytes() all at once") { + SafeFlashStringReader reader(F("ABCD"), 3); + + char buffer[8] = "abcd"; + REQUIRE(reader.readBytes(buffer, 4) == 3); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'd'); + } + + SECTION("readBytes() in two parts") { + SafeFlashStringReader reader(F("ABCDEF"), 6); + + char buffer[8] = "abcdefg"; + REQUIRE(reader.readBytes(buffer, 4) == 4); + REQUIRE(reader.readBytes(buffer + 4, 4) == 2); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'D'); + REQUIRE(buffer[4] == 'E'); + REQUIRE(buffer[5] == 'F'); + REQUIRE(buffer[6] == 'g'); + } +} + +TEST_CASE("UnsafeFlashStringReader") { + using ARDUINOJSON_NAMESPACE::UnsafeFlashStringReader; + + SECTION("read()") { + UnsafeFlashStringReader reader(F("\x01\xFF\x00\x12")); + REQUIRE(reader.read() == 0x01); + REQUIRE(reader.read() == 0xFF); + REQUIRE(reader.read() == 0); + REQUIRE(reader.read() == 0x12); + } + + SECTION("readBytes() all at once") { + UnsafeFlashStringReader reader(F("ABCD")); + + char buffer[8] = "abcd"; + REQUIRE(reader.readBytes(buffer, 3) == 3); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'd'); + } + + SECTION("readBytes() in two parts") { + UnsafeFlashStringReader reader(F("ABCDEF")); + + char buffer[8] = "abcdefg"; + REQUIRE(reader.readBytes(buffer, 4) == 4); + REQUIRE(reader.readBytes(buffer + 4, 2) == 2); + + REQUIRE(buffer[0] == 'A'); + REQUIRE(buffer[1] == 'B'); + REQUIRE(buffer[2] == 'C'); + REQUIRE(buffer[3] == 'D'); + REQUIRE(buffer[4] == 'E'); + REQUIRE(buffer[5] == 'F'); + REQUIRE(buffer[6] == 'g'); + } +} diff --git a/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp b/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp index 33b45e7b..2135ba24 100644 --- a/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp +++ b/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp @@ -23,6 +23,10 @@ struct ArduinoStreamReader { char c; return _stream.readBytes(&c, 1) ? c : -1; } + + size_t readBytes(char* buffer, size_t length) { + return _stream.readBytes(buffer, length); + } }; inline ArduinoStreamReader makeReader(Stream& input) { diff --git a/src/ArduinoJson/Deserialization/CharPointerReader.hpp b/src/ArduinoJson/Deserialization/CharPointerReader.hpp index 154473bf..a8fb38f0 100644 --- a/src/ArduinoJson/Deserialization/CharPointerReader.hpp +++ b/src/ArduinoJson/Deserialization/CharPointerReader.hpp @@ -4,6 +4,7 @@ #pragma once +#include #include namespace ARDUINOJSON_NAMESPACE { @@ -28,22 +29,17 @@ class UnsafeCharPointerReader { 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 { - const char* _ptr; - const char* _end; - +class SafeCharPointerReader : public IteratorReader { public: explicit SafeCharPointerReader(const char* ptr, size_t len) - : _ptr(ptr ? ptr : reinterpret_cast("")), _end(_ptr + len) {} - - int read() { - if (_ptr < _end) - return static_cast(*_ptr++); - else - return -1; - } + : IteratorReader(ptr, ptr + len) {} }; template diff --git a/src/ArduinoJson/Deserialization/FlashStringReader.hpp b/src/ArduinoJson/Deserialization/FlashStringReader.hpp index eca2b2a2..0891eace 100644 --- a/src/ArduinoJson/Deserialization/FlashStringReader.hpp +++ b/src/ArduinoJson/Deserialization/FlashStringReader.hpp @@ -19,6 +19,12 @@ class UnsafeFlashStringReader { int read() { return pgm_read_byte(_ptr++); } + + size_t readBytes(char* buffer, size_t length) { + memcpy_P(buffer, _ptr, length); + _ptr += length; + return length; + } }; class SafeFlashStringReader { @@ -35,6 +41,14 @@ class SafeFlashStringReader { else return -1; } + + size_t readBytes(char* buffer, size_t length) { + size_t available = static_cast(_end - _ptr); + if (available < length) length = available; + memcpy_P(buffer, _ptr, length); + _ptr += length; + return length; + } }; inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) { diff --git a/src/ArduinoJson/Deserialization/IteratorReader.hpp b/src/ArduinoJson/Deserialization/IteratorReader.hpp index 618c1dd2..c52a7b0b 100644 --- a/src/ArduinoJson/Deserialization/IteratorReader.hpp +++ b/src/ArduinoJson/Deserialization/IteratorReader.hpp @@ -22,6 +22,12 @@ class IteratorReader { else return -1; } + + size_t readBytes(char* buffer, size_t length) { + size_t i = 0; + while (i < length && _ptr < _end) buffer[i++] = *_ptr++; + return i; + } }; template diff --git a/src/ArduinoJson/Deserialization/StdStreamReader.hpp b/src/ArduinoJson/Deserialization/StdStreamReader.hpp index 4d816528..bf6cbb93 100644 --- a/src/ArduinoJson/Deserialization/StdStreamReader.hpp +++ b/src/ArduinoJson/Deserialization/StdStreamReader.hpp @@ -24,6 +24,11 @@ class StdStreamReader { 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 }; diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 3af43a97..9e642eee 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -142,10 +142,7 @@ class MsgPackDeserializer { } bool readBytes(uint8_t *p, size_t n) { - for (size_t i = 0; i < n; i++) { - if (!readByte(p[i])) return false; - } - return true; + return _reader.readBytes(reinterpret_cast(p), n) == n; } template