forked from bblanchon/ArduinoJson
Added support for Printable
(closes #1444)
This commit is contained in:
@ -5,6 +5,7 @@ HEAD
|
||||
----
|
||||
|
||||
* Added support for custom converters (issue #687)
|
||||
* Added support for `Printable` (issue #1444)
|
||||
* Removed support for `char` values, see below (issue #1498)
|
||||
* `deserializeJson()` leaves `\uXXXX` unchanged instead of returning `NotSupported`
|
||||
* `deserializeMsgPack()` inserts `null` instead of returning `NotSupported`
|
||||
|
@ -4,5 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/Print.h"
|
||||
#include "api/Stream.h"
|
||||
#include "api/String.h"
|
||||
|
33
extras/tests/Helpers/api/Print.h
Normal file
33
extras/tests/Helpers/api/Print.h
Normal file
@ -0,0 +1,33 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
class Print {
|
||||
public:
|
||||
virtual ~Print() {}
|
||||
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
virtual size_t write(const uint8_t *buffer, size_t size) = 0;
|
||||
|
||||
size_t write(const char *str) {
|
||||
if (!str)
|
||||
return 0;
|
||||
return write(reinterpret_cast<const uint8_t *>(str), strlen(str));
|
||||
}
|
||||
|
||||
size_t write(const char *buffer, size_t size) {
|
||||
return write(reinterpret_cast<const uint8_t *>(buffer), size);
|
||||
}
|
||||
};
|
||||
|
||||
class Printable {
|
||||
public:
|
||||
virtual ~Printable() {}
|
||||
virtual size_t printTo(Print &p) const = 0;
|
||||
};
|
@ -7,6 +7,7 @@ add_executable(MiscTests
|
||||
conflicts.cpp
|
||||
FloatParts.cpp
|
||||
JsonString.cpp
|
||||
printable.cpp
|
||||
Readers.cpp
|
||||
StringAdapters.cpp
|
||||
StringWriter.cpp
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
|
112
extras/tests/Misc/printable.cpp
Normal file
112
extras/tests/Misc/printable.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2021
|
||||
// MIT License
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
struct PrintOneCharacterAtATime {
|
||||
static size_t printStringTo(const std::string& s, Print& p) {
|
||||
size_t result = 0;
|
||||
for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
|
||||
size_t n = p.write(uint8_t(*it));
|
||||
if (n == 0)
|
||||
break;
|
||||
result += n;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct PrintAllAtOnce {
|
||||
static size_t printStringTo(const std::string& s, Print& p) {
|
||||
return p.write(s.data(), s.size());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PrintPolicy>
|
||||
struct PrintableString : public Printable {
|
||||
PrintableString(const char* s) : _str(s), _total(0) {}
|
||||
|
||||
virtual size_t printTo(Print& p) const {
|
||||
size_t result = PrintPolicy::printStringTo(_str, p);
|
||||
_total += result;
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t totalBytesWritten() const {
|
||||
return _total;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _str;
|
||||
mutable size_t _total;
|
||||
};
|
||||
|
||||
TEST_CASE("Printable") {
|
||||
SECTION("Enough space for the whole string") {
|
||||
StaticJsonDocument<64> doc;
|
||||
doc.set(666);
|
||||
|
||||
SECTION("Via Print::write(char)") {
|
||||
PrintableString<PrintOneCharacterAtATime> printable = "Hello World!";
|
||||
CHECK(doc.set(printable) == true);
|
||||
CHECK(doc.as<std::string>() == "Hello World!");
|
||||
CHECK(printable.totalBytesWritten() == 12);
|
||||
CHECK(doc.overflowed() == false);
|
||||
CHECK(doc.memoryUsage() == 13);
|
||||
}
|
||||
|
||||
SECTION("Via Print::write(const char* size_t)") {
|
||||
PrintableString<PrintAllAtOnce> printable = "Hello World!";
|
||||
CHECK(doc.set(printable) == true);
|
||||
CHECK(doc.as<std::string>() == "Hello World!");
|
||||
CHECK(printable.totalBytesWritten() == 12);
|
||||
CHECK(doc.overflowed() == false);
|
||||
CHECK(doc.memoryUsage() == 13);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Too small memory pool") {
|
||||
StaticJsonDocument<8> doc;
|
||||
|
||||
SECTION("Via Print::write(char)") {
|
||||
PrintableString<PrintOneCharacterAtATime> printable = "Hello World!";
|
||||
CHECK(doc.set(printable) == false);
|
||||
CHECK(doc.isNull());
|
||||
CHECK(printable.totalBytesWritten() == 8);
|
||||
CHECK(doc.overflowed() == true);
|
||||
CHECK(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
SECTION("Via Print::write(const char* size_t)") {
|
||||
PrintableString<PrintAllAtOnce> printable = "Hello World!";
|
||||
CHECK(doc.set(printable) == false);
|
||||
CHECK(doc.isNull());
|
||||
CHECK(printable.totalBytesWritten() == 0);
|
||||
CHECK(doc.overflowed() == true);
|
||||
CHECK(doc.memoryUsage() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Null variant") {
|
||||
JsonVariant var;
|
||||
PrintableString<PrintOneCharacterAtATime> printable = "Hello World!";
|
||||
CHECK(var.set(printable) == false);
|
||||
CHECK(var.isNull());
|
||||
CHECK(printable.totalBytesWritten() == 0);
|
||||
}
|
||||
|
||||
SECTION("String deduplication") {
|
||||
StaticJsonDocument<128> doc;
|
||||
doc.add(PrintableString<PrintOneCharacterAtATime>("Hello World!"));
|
||||
doc.add(PrintableString<PrintAllAtOnce>("Hello World!"));
|
||||
REQUIRE(doc.size() == 2);
|
||||
CHECK(doc[0] == "Hello World!");
|
||||
CHECK(doc[1] == "Hello World!");
|
||||
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 13);
|
||||
}
|
||||
}
|
@ -206,4 +206,67 @@ struct Converter<decltype(nullptr)> {
|
||||
|
||||
#endif
|
||||
|
||||
#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
|
||||
|
||||
class MemoryPoolPrint : public Print {
|
||||
public:
|
||||
MemoryPoolPrint(MemoryPool* pool) : _pool(pool), _size(0) {
|
||||
pool->getFreeZone(&_string, &_capacity);
|
||||
}
|
||||
|
||||
const char* c_str() {
|
||||
if (_size >= _capacity)
|
||||
return 0;
|
||||
|
||||
_string[_size++] = 0; // TODO: test overflow
|
||||
return _pool->saveStringFromFreeZone(_size);
|
||||
}
|
||||
|
||||
size_t write(uint8_t c) {
|
||||
if (_size >= _capacity)
|
||||
return 0;
|
||||
|
||||
_string[_size++] = char(c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t write(const uint8_t* buffer, size_t size) {
|
||||
if (_size + size >= _capacity) {
|
||||
_size = _capacity; // mark as overflowed
|
||||
return 0;
|
||||
}
|
||||
memcpy(&_string[_size], buffer, size);
|
||||
_size += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
bool overflowed() const {
|
||||
return _size >= _capacity;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* _pool;
|
||||
size_t _size;
|
||||
char* _string;
|
||||
size_t _capacity;
|
||||
};
|
||||
|
||||
inline bool convertToJson(VariantRef variant, const ::Printable& value) {
|
||||
MemoryPool* pool = getPool(variant);
|
||||
VariantData* data = getData(variant);
|
||||
if (!pool || !data)
|
||||
return false;
|
||||
MemoryPoolPrint print(pool);
|
||||
value.printTo(print);
|
||||
if (print.overflowed()) {
|
||||
pool->markAsOverflowed();
|
||||
data->setNull();
|
||||
return false;
|
||||
}
|
||||
data->setOwnedString(print.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
@ -244,16 +244,26 @@ class VariantData {
|
||||
setType(VALUE_IS_NULL);
|
||||
}
|
||||
|
||||
void setStringPointer(const char *s, storage_policies::store_by_copy) {
|
||||
void setOwnedString(const char *s) {
|
||||
ARDUINOJSON_ASSERT(s != 0);
|
||||
setType(VALUE_IS_OWNED_STRING);
|
||||
_content.asString = s;
|
||||
}
|
||||
|
||||
void setStringPointer(const char *s, storage_policies::store_by_address) {
|
||||
void setLinkedString(const char *s) {
|
||||
ARDUINOJSON_ASSERT(s != 0);
|
||||
setType(VALUE_IS_LINKED_STRING);
|
||||
_content.asString = s;
|
||||
}
|
||||
|
||||
void setStringPointer(const char *s, storage_policies::store_by_copy) {
|
||||
setOwnedString(s);
|
||||
}
|
||||
|
||||
void setStringPointer(const char *s, storage_policies::store_by_address) {
|
||||
setLinkedString(s);
|
||||
}
|
||||
|
||||
template <typename TAdaptedString>
|
||||
bool setString(TAdaptedString value, MemoryPool *pool) {
|
||||
return setString(value, pool, typename TAdaptedString::storage_policy());
|
||||
|
Reference in New Issue
Block a user