Improved deserializeMsgPack() speed by reading several bytes at once

This commit is contained in:
Benoit Blanchon
2019-10-14 12:02:26 +02:00
parent fd8f4eb3a6
commit 16fe3c0acc
11 changed files with 286 additions and 50 deletions

View File

@ -7,6 +7,7 @@ HEAD
* Added support for custom writer classes (issue #1088) * Added support for custom writer classes (issue #1088)
* Added conversion from `JsonArray` and `JsonObject` to `bool`, to be consistent with `JsonVariant` * Added conversion from `JsonArray` and `JsonObject` to `bool`, to be consistent with `JsonVariant`
* Fixed `deserializeJson()` when input contains duplicate keys (issue #1095) * Fixed `deserializeJson()` when input contains duplicate keys (issue #1095)
* Improved `deserializeMsgPack()` speed by reading several bytes at once
v6.12.0 (2019-09-05) v6.12.0 (2019-09-05)
------- -------

View File

@ -5,7 +5,7 @@
add_executable(MiscTests add_executable(MiscTests
conflicts.cpp conflicts.cpp
FloatParts.cpp FloatParts.cpp
StreamReader.cpp Readers.cpp
StringAdapters.cpp StringAdapters.cpp
StringWriter.cpp StringWriter.cpp
TypeTraits.cpp TypeTraits.cpp

View File

@ -0,0 +1,166 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
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<std::string::const_iterator> 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<std::string::const_iterator> 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<std::string::const_iterator> 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');
}
}

View File

@ -1,33 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
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);
}

View File

@ -85,3 +85,83 @@ TEST_CASE("memcpy_P") {
CHECK(dst[2] == 'C'); CHECK(dst[2] == 'C');
CHECK(dst[3] == 0); 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');
}
}

View File

@ -23,6 +23,10 @@ struct ArduinoStreamReader {
char c; char c;
return _stream.readBytes(&c, 1) ? c : -1; 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) { inline ArduinoStreamReader makeReader(Stream& input) {

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <ArduinoJson/Deserialization/IteratorReader.hpp>
#include <ArduinoJson/Namespace.hpp> #include <ArduinoJson/Namespace.hpp>
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
@ -28,22 +29,17 @@ class UnsafeCharPointerReader {
int read() { int read() {
return static_cast<unsigned char>(*_ptr++); return static_cast<unsigned char>(*_ptr++);
} }
size_t readBytes(char* buffer, size_t length) {
for (size_t i = 0; i < length; i++) buffer[i] = *_ptr++;
return length;
}
}; };
class SafeCharPointerReader { class SafeCharPointerReader : public IteratorReader<const char*> {
const char* _ptr;
const char* _end;
public: public:
explicit SafeCharPointerReader(const char* ptr, size_t len) explicit SafeCharPointerReader(const char* ptr, size_t len)
: _ptr(ptr ? ptr : reinterpret_cast<const char*>("")), _end(_ptr + len) {} : IteratorReader<const char*>(ptr, ptr + len) {}
int read() {
if (_ptr < _end)
return static_cast<unsigned char>(*_ptr++);
else
return -1;
}
}; };
template <typename TChar> template <typename TChar>

View File

@ -19,6 +19,12 @@ class UnsafeFlashStringReader {
int read() { int read() {
return pgm_read_byte(_ptr++); return pgm_read_byte(_ptr++);
} }
size_t readBytes(char* buffer, size_t length) {
memcpy_P(buffer, _ptr, length);
_ptr += length;
return length;
}
}; };
class SafeFlashStringReader { class SafeFlashStringReader {
@ -35,6 +41,14 @@ class SafeFlashStringReader {
else else
return -1; return -1;
} }
size_t readBytes(char* buffer, size_t length) {
size_t available = static_cast<size_t>(_end - _ptr);
if (available < length) length = available;
memcpy_P(buffer, _ptr, length);
_ptr += length;
return length;
}
}; };
inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) { inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) {

View File

@ -22,6 +22,12 @@ class IteratorReader {
else else
return -1; 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 <typename TInput> template <typename TInput>

View File

@ -24,6 +24,11 @@ class StdStreamReader {
return _stream.get(); return _stream.get();
} }
size_t readBytes(char* buffer, size_t length) {
_stream.read(buffer, static_cast<std::streamsize>(length));
return static_cast<size_t>(_stream.gcount());
}
private: private:
StdStreamReader& operator=(const StdStreamReader&); // Visual Studio C4512 StdStreamReader& operator=(const StdStreamReader&); // Visual Studio C4512
}; };

View File

@ -142,10 +142,7 @@ class MsgPackDeserializer {
} }
bool readBytes(uint8_t *p, size_t n) { bool readBytes(uint8_t *p, size_t n) {
for (size_t i = 0; i < n; i++) { return _reader.readBytes(reinterpret_cast<char *>(p), n) == n;
if (!readByte(p[i])) return false;
}
return true;
} }
template <typename T> template <typename T>