From 1e3d4789981e75b10e439b2e4a6df78d7466408f Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Tue, 14 Jan 2020 14:50:44 +0100 Subject: [PATCH] Improved speed of serializeXxx() when writing to a String --- CHANGELOG.md | 1 + extras/tests/Helpers/WString.h | 21 +++++++----- extras/tests/Misc/StringWriter.cpp | 33 +++++++++++++++++++ src/ArduinoJson/Configuration.hpp | 4 +++ .../Writers/ArduinoStringWriter.hpp | 30 +++++++++++++---- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa670e66..5f612403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ HEAD (This file is used for syntax highlighting in the Arduino IDE) * Fixed `variant.is()` * Fixed value returned by `serializeJson()`, `serializeJsonPretty()`, and `serializeMsgPack()` when writing to a `String` +* Improved speed of `serializeJson()`, `serializeJsonPretty()`, and `serializeMsgPack()` when writing to a `String` > ### BREAKING CHANGES > diff --git a/extras/tests/Helpers/WString.h b/extras/tests/Helpers/WString.h index db66e892..18122f7e 100644 --- a/extras/tests/Helpers/WString.h +++ b/extras/tests/Helpers/WString.h @@ -9,16 +9,10 @@ // Reproduces Arduino's String class class String { public: - String& operator+=(char c) { - _str += c; + String& operator+=(const char* rhs) { + _str += rhs; return *this; } - String& operator+=(int); // no used, just to add ambiguity - - unsigned char reserve(size_t capacity) { - _str.reserve(capacity); - return 1; - } size_t length() const { return _str.size(); @@ -28,12 +22,21 @@ class String { return _str.c_str(); } + bool operator==(const char* s) const { + return _str == s; + } + + friend std::ostream& operator<<(std::ostream& lhs, const ::String& rhs) { + lhs << rhs._str; + return lhs; + } + private: std::string _str; }; class StringSumHelper; -bool operator==(const std::string& lhs, const ::String& rhs) { +inline bool operator==(const std::string& lhs, const ::String& rhs) { return lhs == rhs.c_str(); } diff --git a/extras/tests/Misc/StringWriter.cpp b/extras/tests/Misc/StringWriter.cpp index 4c2fc239..15ff9212 100644 --- a/extras/tests/Misc/StringWriter.cpp +++ b/extras/tests/Misc/StringWriter.cpp @@ -3,6 +3,7 @@ // MIT License #define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +#define ARDUINOJSON_STRING_BUFFER_SIZE 5 #include #include #include "custom_string.hpp" @@ -59,7 +60,39 @@ TEST_CASE("Writer") { TEST_CASE("Writer") { ::String output; Writer< ::String> sb(output); + common_tests(sb, output); + + SECTION("Writes characters to temporary buffer") { + // accumulate in buffer + sb.write('a'); + sb.write('b'); + sb.write('c'); + REQUIRE(output == ""); + + // flush when full + sb.write('d'); + REQUIRE(output == "abcd"); + + // flush on destruction + sb.write('e'); + sb.~Writer(); + REQUIRE(output == "abcde"); + } + + SECTION("Writes strings to temporary buffer") { + // accumulate in buffer + print(sb, "abc"); + REQUIRE(output == ""); + + // flush when full, and continue to accumulate + print(sb, "de"); + REQUIRE(output == "abcd"); + + // flush on destruction + sb.~Writer(); + REQUIRE(output == "abcde"); + } } TEST_CASE("Writer") { diff --git a/src/ArduinoJson/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index b99d25ab..7eb9c5dc 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/src/ArduinoJson/Configuration.hpp @@ -204,3 +204,7 @@ #ifndef ARDUINOJSON_TAB #define ARDUINOJSON_TAB " " #endif + +#ifndef ARDUINOJSON_STRING_BUFFER_SIZE +#define ARDUINOJSON_STRING_BUFFER_SIZE 32 +#endif diff --git a/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp b/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp index d0efeec2..121de1f0 100644 --- a/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp +++ b/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp @@ -10,26 +10,42 @@ namespace ARDUINOJSON_NAMESPACE { template <> class Writer< ::String, void> { + static const size_t bufferCapacity = ARDUINOJSON_STRING_BUFFER_SIZE; + public: - explicit Writer(::String &str) : _str(&str) {} + explicit Writer(::String &str) : _destination(&str) { + _size = 0; + } + + ~Writer() { + flush(); + } size_t write(uint8_t c) { - _str->operator+=(static_cast(c)); + ARDUINOJSON_ASSERT(_size < bufferCapacity); + _buffer[_size++] = static_cast(c); + if (_size + 1 >= bufferCapacity) flush(); return 1; } size_t write(const uint8_t *s, size_t n) { - // CAUTION: Arduino String doesn't have append() - // and old version doesn't have size() either - _str->reserve(_str->length() + n); for (size_t i = 0; i < n; i++) { - _str->operator+=(static_cast(*s++)); + write(s[i]); } return n; } private: - ::String *_str; + void flush() { + ARDUINOJSON_ASSERT(_size < bufferCapacity); + _buffer[_size] = 0; + *_destination += _buffer; + _size = 0; + } + + ::String *_destination; + char _buffer[bufferCapacity]; + size_t _size; }; } // namespace ARDUINOJSON_NAMESPACE