forked from bblanchon/ArduinoJson
Added string deduplication (closes #1303)
This commit is contained in:
@ -5,6 +5,7 @@ HEAD
|
|||||||
----
|
----
|
||||||
|
|
||||||
* Added comparisons (`>`, `>=`, `==`, `!=`, `<`, and `<=`) between `JsonVariant`s
|
* Added comparisons (`>`, `>=`, `==`, `!=`, `<`, and `<=`) between `JsonVariant`s
|
||||||
|
* Added string deduplication (issue #1303)
|
||||||
|
|
||||||
v6.15.2 (2020-05-15)
|
v6.15.2 (2020-05-15)
|
||||||
-------
|
-------
|
||||||
|
@ -31,6 +31,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
|
|||||||
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
|
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
|
||||||
* [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/?utm_source=github&utm_medium=readme)
|
* [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/?utm_source=github&utm_medium=readme)
|
||||||
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme)
|
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme)
|
||||||
|
* Deduplicates strings
|
||||||
* Versatile
|
* Versatile
|
||||||
* [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
|
* [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
|
||||||
* Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme)
|
* Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme)
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
// Reproduces Arduino's String class
|
// Reproduces Arduino's String class
|
||||||
class String {
|
class String {
|
||||||
public:
|
public:
|
||||||
|
String() {}
|
||||||
|
explicit String(const char* s) : _str(s) {}
|
||||||
|
|
||||||
String& operator+=(const char* rhs) {
|
String& operator+=(const char* rhs) {
|
||||||
_str += rhs;
|
_str += rhs;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -239,7 +239,7 @@ TEST_CASE("Filtering") {
|
|||||||
10,
|
10,
|
||||||
DeserializationError::Ok,
|
DeserializationError::Ok,
|
||||||
"[{\"example\":1},{\"example\":3}]",
|
"[{\"example\":1},{\"example\":3}]",
|
||||||
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"[',2,3]",
|
"[',2,3]",
|
||||||
|
@ -74,16 +74,16 @@ TEST_CASE("Invalid JSON string") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Not enough room to duplicate the string") {
|
TEST_CASE("Not enough room to save the key") {
|
||||||
DynamicJsonDocument doc(JSON_OBJECT_SIZE(0));
|
DynamicJsonDocument doc(JSON_OBJECT_SIZE(1) + 8);
|
||||||
|
|
||||||
SECTION("Quoted string") {
|
SECTION("Quoted string") {
|
||||||
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
REQUIRE(deserializeJson(doc, "{\"accuracy\":1}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Non-quoted string") {
|
SECTION("Non-quoted string") {
|
||||||
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
REQUIRE(deserializeJson(doc, "{accuracy:1}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ TEST_CASE("StaticJsonDocument") {
|
|||||||
|
|
||||||
SECTION("garbageCollect()") {
|
SECTION("garbageCollect()") {
|
||||||
StaticJsonDocument<256> doc;
|
StaticJsonDocument<256> doc;
|
||||||
doc[std::string("example")] = std::string("example");
|
doc[std::string("example")] = std::string("jukebox");
|
||||||
doc.remove("example");
|
doc.remove("example");
|
||||||
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);
|
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
add_executable(MemoryPoolTests
|
add_executable(MemoryPoolTests
|
||||||
allocVariant.cpp
|
allocVariant.cpp
|
||||||
allocString.cpp
|
|
||||||
clear.cpp
|
clear.cpp
|
||||||
|
saveString.cpp
|
||||||
size.cpp
|
size.cpp
|
||||||
StringCopier.cpp
|
StringCopier.cpp
|
||||||
)
|
)
|
||||||
|
@ -38,8 +38,47 @@ TEST_CASE("StringCopier") {
|
|||||||
|
|
||||||
str.startString(&pool);
|
str.startString(&pool);
|
||||||
str.append('h');
|
str.append('h');
|
||||||
str.commit(&pool);
|
str.save(&pool);
|
||||||
|
|
||||||
REQUIRE(1 == pool.size());
|
REQUIRE(1 == pool.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* addStringToPool(MemoryPool* pool, const char* s) {
|
||||||
|
StringCopier str;
|
||||||
|
str.startString(pool);
|
||||||
|
str.append(s);
|
||||||
|
str.append('\0');
|
||||||
|
return str.save(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("StringCopier::save() deduplicates strings") {
|
||||||
|
char buffer[4096];
|
||||||
|
MemoryPool pool(buffer, 4096);
|
||||||
|
|
||||||
|
SECTION("Basic") {
|
||||||
|
const char* s1 = addStringToPool(&pool, "hello");
|
||||||
|
const char* s2 = addStringToPool(&pool, "world");
|
||||||
|
const char* s3 = addStringToPool(&pool, "hello");
|
||||||
|
|
||||||
|
REQUIRE(s1 == s3);
|
||||||
|
REQUIRE(s2 != s3);
|
||||||
|
REQUIRE(pool.size() == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Requires terminator") {
|
||||||
|
const char* s1 = addStringToPool(&pool, "hello world");
|
||||||
|
const char* s2 = addStringToPool(&pool, "hello");
|
||||||
|
|
||||||
|
REQUIRE(s2 != s1);
|
||||||
|
REQUIRE(pool.size() == 12 + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Don't overrun") {
|
||||||
|
const char* s1 = addStringToPool(&pool, "hello world");
|
||||||
|
const char* s2 = addStringToPool(&pool, "wor");
|
||||||
|
|
||||||
|
REQUIRE(s2 != s1);
|
||||||
|
REQUIRE(pool.size() == 12 + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
// ArduinoJson - arduinojson.org
|
|
||||||
// Copyright Benoit Blanchon 2014-2020
|
|
||||||
// MIT License
|
|
||||||
|
|
||||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
|
||||||
#include <catch.hpp>
|
|
||||||
|
|
||||||
using namespace ARDUINOJSON_NAMESPACE;
|
|
||||||
|
|
||||||
TEST_CASE("MemoryPool::allocFrozenString()") {
|
|
||||||
const size_t poolCapacity = 64;
|
|
||||||
const size_t longestString = poolCapacity;
|
|
||||||
char buffer[poolCapacity];
|
|
||||||
MemoryPool pool(buffer, poolCapacity);
|
|
||||||
|
|
||||||
SECTION("Returns different addresses") {
|
|
||||||
char *a = pool.allocFrozenString(1);
|
|
||||||
char *b = pool.allocFrozenString(1);
|
|
||||||
REQUIRE(a != b);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Returns NULL when full") {
|
|
||||||
void *p1 = pool.allocFrozenString(longestString);
|
|
||||||
REQUIRE(p1 != 0);
|
|
||||||
|
|
||||||
void *p2 = pool.allocFrozenString(1);
|
|
||||||
REQUIRE(p2 == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Returns NULL when pool is too small") {
|
|
||||||
void *p = pool.allocFrozenString(longestString + 1);
|
|
||||||
REQUIRE(0 == p);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Returns NULL when buffer is NULL") {
|
|
||||||
MemoryPool pool2(0, poolCapacity);
|
|
||||||
REQUIRE(0 == pool2.allocFrozenString(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Returns NULL when capacity is 0") {
|
|
||||||
MemoryPool pool2(buffer, 0);
|
|
||||||
REQUIRE(0 == pool2.allocFrozenString(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Returns same address after clear()") {
|
|
||||||
void *a = pool.allocFrozenString(1);
|
|
||||||
pool.clear();
|
|
||||||
void *b = pool.allocFrozenString(1);
|
|
||||||
|
|
||||||
REQUIRE(a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Can use full capacity when fresh") {
|
|
||||||
void *a = pool.allocFrozenString(longestString);
|
|
||||||
|
|
||||||
REQUIRE(a != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Can use full capacity after clear") {
|
|
||||||
pool.allocFrozenString(longestString);
|
|
||||||
pool.clear();
|
|
||||||
|
|
||||||
void *a = pool.allocFrozenString(longestString);
|
|
||||||
|
|
||||||
REQUIRE(a != 0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@
|
|||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||||
|
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
using namespace ARDUINOJSON_NAMESPACE;
|
using namespace ARDUINOJSON_NAMESPACE;
|
||||||
@ -21,8 +22,8 @@ TEST_CASE("MemoryPool::clear()") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Discards allocated strings") {
|
SECTION("Discards allocated strings") {
|
||||||
pool.allocFrozenString(10);
|
pool.saveString(adaptString(const_cast<char *>("123456789")));
|
||||||
REQUIRE(pool.size() > 0);
|
REQUIRE(pool.size() == 10);
|
||||||
|
|
||||||
pool.clear();
|
pool.clear();
|
||||||
|
|
||||||
|
81
extras/tests/MemoryPool/saveString.cpp
Normal file
81
extras/tests/MemoryPool/saveString.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// ArduinoJson - arduinojson.org
|
||||||
|
// Copyright Benoit Blanchon 2014-2020
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||||
|
#include <ArduinoJson/Strings/StringAdapters.hpp>
|
||||||
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
using namespace ARDUINOJSON_NAMESPACE;
|
||||||
|
|
||||||
|
static const char *saveString(MemoryPool &pool, const char *s) {
|
||||||
|
return pool.saveString(adaptString(const_cast<char *>(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("MemoryPool::saveString()") {
|
||||||
|
char buffer[32];
|
||||||
|
MemoryPool pool(buffer, 32);
|
||||||
|
|
||||||
|
SECTION("Duplicates different strings") {
|
||||||
|
const char *a = saveString(pool, "hello");
|
||||||
|
const char *b = saveString(pool, "world");
|
||||||
|
REQUIRE(a != b);
|
||||||
|
REQUIRE(pool.size() == 6 + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Deduplicates identical strings") {
|
||||||
|
const char *a = saveString(pool, "hello");
|
||||||
|
const char *b = saveString(pool, "hello");
|
||||||
|
REQUIRE(a == b);
|
||||||
|
REQUIRE(pool.size() == 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Returns NULL when full") {
|
||||||
|
REQUIRE(pool.capacity() == 32);
|
||||||
|
|
||||||
|
const void *p1 = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
REQUIRE(p1 != 0);
|
||||||
|
REQUIRE(pool.size() == 32);
|
||||||
|
|
||||||
|
const void *p2 = saveString(pool, "b");
|
||||||
|
REQUIRE(p2 == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Returns NULL when pool is too small") {
|
||||||
|
const void *p = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
REQUIRE(0 == p);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Returns NULL when buffer is NULL") {
|
||||||
|
MemoryPool pool2(0, 32);
|
||||||
|
REQUIRE(0 == saveString(pool2, "a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Returns NULL when capacity is 0") {
|
||||||
|
MemoryPool pool2(buffer, 0);
|
||||||
|
REQUIRE(0 == saveString(pool2, "a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Returns same address after clear()") {
|
||||||
|
const void *a = saveString(pool, "hello");
|
||||||
|
pool.clear();
|
||||||
|
const void *b = saveString(pool, "world");
|
||||||
|
|
||||||
|
REQUIRE(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can use full capacity when fresh") {
|
||||||
|
const void *a = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
|
||||||
|
REQUIRE(a != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can use full capacity after clear") {
|
||||||
|
saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
pool.clear();
|
||||||
|
|
||||||
|
const void *a = saveString(pool, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||||||
|
|
||||||
|
REQUIRE(a != 0);
|
||||||
|
}
|
||||||
|
}
|
@ -22,24 +22,6 @@ TEST_CASE("MemoryPool::size()") {
|
|||||||
REQUIRE(0 == pool.size());
|
REQUIRE(0 == pool.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Decreases after freezeString()") {
|
|
||||||
StringSlot a = pool.allocExpandableString();
|
|
||||||
pool.freezeString(a, 1);
|
|
||||||
REQUIRE(pool.size() == 1);
|
|
||||||
|
|
||||||
StringSlot b = pool.allocExpandableString();
|
|
||||||
pool.freezeString(b, 1);
|
|
||||||
REQUIRE(pool.size() == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Increases after allocFrozenString()") {
|
|
||||||
pool.allocFrozenString(1);
|
|
||||||
REQUIRE(pool.size() == 1);
|
|
||||||
|
|
||||||
pool.allocFrozenString(2);
|
|
||||||
REQUIRE(pool.size() == 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Doesn't grow when memory pool is full") {
|
SECTION("Doesn't grow when memory pool is full") {
|
||||||
const size_t variantCount = sizeof(buffer) / sizeof(VariantSlot);
|
const size_t variantCount = sizeof(buffer) / sizeof(VariantSlot);
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "progmem_emulation.hpp"
|
#include "progmem_emulation.hpp"
|
||||||
#include "weird_strcmp.hpp"
|
#include "weird_strcmp.hpp"
|
||||||
|
|
||||||
|
#include <ArduinoJson/Strings/ArduinoStringAdapter.hpp>
|
||||||
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
|
||||||
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
|
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
|
||||||
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
|
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
|
||||||
@ -114,6 +115,21 @@ TEST_CASE("std::string") {
|
|||||||
CHECK(adapter.size() == 5);
|
CHECK(adapter.size() == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Arduino String") {
|
||||||
|
::String str("bravo");
|
||||||
|
ArduinoStringAdapter adapter = adaptString(str);
|
||||||
|
|
||||||
|
CHECK(adapter.compare(NULL) > 0);
|
||||||
|
CHECK(adapter.compare("alpha") > 0);
|
||||||
|
CHECK(adapter.compare("bravo") == 0);
|
||||||
|
CHECK(adapter.compare("charlie") < 0);
|
||||||
|
|
||||||
|
CHECK(adapter.equals("bravo"));
|
||||||
|
CHECK_FALSE(adapter.equals("charlie"));
|
||||||
|
|
||||||
|
CHECK(adapter.size() == 5);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("custom_string") {
|
TEST_CASE("custom_string") {
|
||||||
custom_string str("bravo");
|
custom_string str("bravo");
|
||||||
StlStringAdapter<custom_string> adapter = adaptString(str);
|
StlStringAdapter<custom_string> adapter = adaptString(str);
|
||||||
|
@ -18,6 +18,8 @@ add_executable(MixedConfigurationTests
|
|||||||
enable_nan_0.cpp
|
enable_nan_0.cpp
|
||||||
enable_nan_1.cpp
|
enable_nan_1.cpp
|
||||||
enable_progmem_1.cpp
|
enable_progmem_1.cpp
|
||||||
|
enable_string_deduplication_0.cpp
|
||||||
|
enable_string_deduplication_1.cpp
|
||||||
use_double_0.cpp
|
use_double_0.cpp
|
||||||
use_double_1.cpp
|
use_double_1.cpp
|
||||||
use_long_long_0.cpp
|
use_long_long_0.cpp
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
// ArduinoJson - arduinojson.org
|
||||||
|
// Copyright Benoit Blanchon 2014-2020
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
#include "progmem_emulation.hpp"
|
||||||
|
|
||||||
|
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
|
||||||
|
#define ARDUINOJSON_ENABLE_PROGMEM 1
|
||||||
|
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 0
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
|
||||||
|
SECTION("deserializeJson()") {
|
||||||
|
SECTION("Deduplicate values") {
|
||||||
|
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||||
|
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Deduplicate keys") {
|
||||||
|
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 16);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
|
||||||
|
CHECK(key1 != key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("JsonDocument") {
|
||||||
|
SECTION("values") {
|
||||||
|
SECTION("std::string") {
|
||||||
|
doc.add(std::string("example"));
|
||||||
|
doc.add(std::string("example"));
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||||
|
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("char*") {
|
||||||
|
char value[] = "example";
|
||||||
|
doc.add(value);
|
||||||
|
doc.add(value);
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||||
|
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Arduino String") {
|
||||||
|
doc.add(String("example"));
|
||||||
|
doc.add(String("example"));
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||||
|
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Flash string") {
|
||||||
|
doc.add(F("example"));
|
||||||
|
doc.add(F("example"));
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
|
||||||
|
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("keys") {
|
||||||
|
SECTION("std::string") {
|
||||||
|
doc[0][std::string("example")] = 1;
|
||||||
|
doc[1][std::string("example")] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 != key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("char*") {
|
||||||
|
char key[] = "example";
|
||||||
|
doc[0][key] = 1;
|
||||||
|
doc[1][key] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 != key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Arduino String") {
|
||||||
|
doc[0][String("example")] = 1;
|
||||||
|
doc[1][String("example")] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 != key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Flash string") {
|
||||||
|
doc[0][F("example")] = 1;
|
||||||
|
doc[1][F("example")] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 != key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
// ArduinoJson - arduinojson.org
|
||||||
|
// Copyright Benoit Blanchon 2014-2020
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
#include "progmem_emulation.hpp"
|
||||||
|
|
||||||
|
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
|
||||||
|
#define ARDUINOJSON_ENABLE_PROGMEM 1
|
||||||
|
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
|
||||||
|
StaticJsonDocument<1024> doc;
|
||||||
|
|
||||||
|
SECTION("deserializeJson()") {
|
||||||
|
SECTION("Deduplicate values") {
|
||||||
|
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||||
|
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Deduplicate keys") {
|
||||||
|
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 8);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 == key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("JsonDocument") {
|
||||||
|
SECTION("values") {
|
||||||
|
SECTION("std::string") {
|
||||||
|
doc.add(std::string("example"));
|
||||||
|
doc.add(std::string("example"));
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||||
|
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("char*") {
|
||||||
|
char value[] = "example";
|
||||||
|
doc.add(value);
|
||||||
|
doc.add(value);
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||||
|
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Arduino String") {
|
||||||
|
doc.add(String("example"));
|
||||||
|
doc.add(String("example"));
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||||
|
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Flash string") {
|
||||||
|
doc.add(F("example"));
|
||||||
|
doc.add(F("example"));
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
|
||||||
|
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("keys") {
|
||||||
|
SECTION("std::string") {
|
||||||
|
doc[0][std::string("example")] = 1;
|
||||||
|
doc[1][std::string("example")] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 == key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("char*") {
|
||||||
|
char key[] = "example";
|
||||||
|
doc[0][key] = 1;
|
||||||
|
doc[1][key] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 == key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Arduino String") {
|
||||||
|
doc[0][String("example")] = 1;
|
||||||
|
doc[1][String("example")] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 == key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Flash string") {
|
||||||
|
doc[0][F("example")] = 1;
|
||||||
|
doc[1][F("example")] = 2;
|
||||||
|
|
||||||
|
CHECK(doc.memoryUsage() ==
|
||||||
|
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
|
||||||
|
|
||||||
|
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||||
|
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||||
|
CHECK(key1 == key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -215,6 +215,10 @@
|
|||||||
#define ARDUINOJSON_TAB " "
|
#define ARDUINOJSON_TAB " "
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||||
|
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef ARDUINOJSON_STRING_BUFFER_SIZE
|
#ifndef ARDUINOJSON_STRING_BUFFER_SIZE
|
||||||
#define ARDUINOJSON_STRING_BUFFER_SIZE 32
|
#define ARDUINOJSON_STRING_BUFFER_SIZE 32
|
||||||
#endif
|
#endif
|
||||||
|
@ -212,8 +212,6 @@ class JsonDeserializer {
|
|||||||
|
|
||||||
// Read each key value pair
|
// Read each key value pair
|
||||||
for (;;) {
|
for (;;) {
|
||||||
_stringStorage.startString(_pool);
|
|
||||||
|
|
||||||
// Parse key
|
// Parse key
|
||||||
err = parseKey();
|
err = parseKey();
|
||||||
if (err)
|
if (err)
|
||||||
@ -233,7 +231,9 @@ class JsonDeserializer {
|
|||||||
if (memberFilter.allow()) {
|
if (memberFilter.allow()) {
|
||||||
VariantData *variant = object.getMember(adaptString(key));
|
VariantData *variant = object.getMember(adaptString(key));
|
||||||
if (!variant) {
|
if (!variant) {
|
||||||
_stringStorage.commit(_pool);
|
// Save key in memory pool.
|
||||||
|
// This MUST be done before adding the slot.
|
||||||
|
key = _stringStorage.save(_pool);
|
||||||
|
|
||||||
// Allocate slot in object
|
// Allocate slot in object
|
||||||
VariantSlot *slot = object.addSlot(_pool);
|
VariantSlot *slot = object.addSlot(_pool);
|
||||||
@ -325,6 +325,7 @@ class JsonDeserializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DeserializationError parseKey() {
|
DeserializationError parseKey() {
|
||||||
|
_stringStorage.startString(_pool);
|
||||||
if (isQuote(current())) {
|
if (isQuote(current())) {
|
||||||
return parseQuotedString();
|
return parseQuotedString();
|
||||||
} else {
|
} else {
|
||||||
@ -337,8 +338,8 @@ class JsonDeserializer {
|
|||||||
DeserializationError err = parseQuotedString();
|
DeserializationError err = parseQuotedString();
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
_stringStorage.commit(_pool);
|
const char *value = _stringStorage.save(_pool);
|
||||||
variant.setOwnedString(make_not_null(_stringStorage.c_str()));
|
variant.setOwnedString(make_not_null(value));
|
||||||
return DeserializationError::Ok;
|
return DeserializationError::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,40 +51,43 @@ class MemoryPool {
|
|||||||
return allocRight<VariantSlot>();
|
return allocRight<VariantSlot>();
|
||||||
}
|
}
|
||||||
|
|
||||||
char* allocFrozenString(size_t n) {
|
|
||||||
if (!canAlloc(n))
|
|
||||||
return 0;
|
|
||||||
char* s = _left;
|
|
||||||
_left += n;
|
|
||||||
checkInvariants();
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TAdaptedString>
|
template <typename TAdaptedString>
|
||||||
char* saveString(const TAdaptedString& str) {
|
const char* saveString(const TAdaptedString& str) {
|
||||||
if (str.isNull())
|
if (str.isNull())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||||
|
const char* existingCopy = findString(str.begin());
|
||||||
|
if (existingCopy)
|
||||||
|
return existingCopy;
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t n = str.size();
|
size_t n = str.size();
|
||||||
char* dup = allocFrozenString(n + 1);
|
|
||||||
if (dup) {
|
char* newCopy = allocString(n + 1);
|
||||||
str.copyTo(dup, n);
|
if (newCopy) {
|
||||||
dup[n] = 0; // force null-terminator
|
str.copyTo(newCopy, n);
|
||||||
|
newCopy[n] = 0; // force null-terminator
|
||||||
}
|
}
|
||||||
|
return newCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getFreeZone(char** zoneStart, size_t* zoneSize) const {
|
||||||
|
*zoneStart = _left;
|
||||||
|
*zoneSize = size_t(_right - _left);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* saveStringFromFreeZone(size_t len) {
|
||||||
|
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||||
|
const char* dup = findString(_left);
|
||||||
|
if (dup)
|
||||||
return dup;
|
return dup;
|
||||||
}
|
#endif
|
||||||
|
|
||||||
StringSlot allocExpandableString() {
|
const char* str = _left;
|
||||||
StringSlot s;
|
_left += len;
|
||||||
s.value = _left;
|
|
||||||
s.size = size_t(_right - _left);
|
|
||||||
checkInvariants();
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void freezeString(StringSlot& s, size_t newSize) {
|
|
||||||
_left = (s.value + newSize);
|
|
||||||
s.size = newSize;
|
|
||||||
checkInvariants();
|
checkInvariants();
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
@ -100,18 +103,6 @@ class MemoryPool {
|
|||||||
return _begin <= p && p < _end;
|
return _begin <= p && p < _end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T* allocRight() {
|
|
||||||
return reinterpret_cast<T*>(allocRight(sizeof(T)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void* allocRight(size_t bytes) {
|
|
||||||
if (!canAlloc(bytes))
|
|
||||||
return 0;
|
|
||||||
_right -= bytes;
|
|
||||||
return _right;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workaround for missing placement new
|
// Workaround for missing placement new
|
||||||
void* operator new(size_t, void* p) {
|
void* operator new(size_t, void* p) {
|
||||||
return p;
|
return p;
|
||||||
@ -163,6 +154,46 @@ class MemoryPool {
|
|||||||
ARDUINOJSON_ASSERT(isAligned(_right));
|
ARDUINOJSON_ASSERT(isAligned(_right));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
|
||||||
|
template <typename TIterator>
|
||||||
|
const char* findString(TIterator str) {
|
||||||
|
for (char* next = _begin; next < _left; ++next) {
|
||||||
|
char* begin = next;
|
||||||
|
|
||||||
|
// try to match
|
||||||
|
for (TIterator it = str; *it == *next; ++it) {
|
||||||
|
if (*next++ == 0)
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jump to next terminator
|
||||||
|
while (*next) ++next;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char* allocString(size_t n) {
|
||||||
|
if (!canAlloc(n))
|
||||||
|
return 0;
|
||||||
|
char* s = _left;
|
||||||
|
_left += n;
|
||||||
|
checkInvariants();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* allocRight() {
|
||||||
|
return reinterpret_cast<T*>(allocRight(sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* allocRight(size_t bytes) {
|
||||||
|
if (!canAlloc(bytes))
|
||||||
|
return 0;
|
||||||
|
_right -= bytes;
|
||||||
|
return _right;
|
||||||
|
}
|
||||||
|
|
||||||
char *_begin, *_left, *_right, *_end;
|
char *_begin, *_left, *_right, *_end;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -248,8 +248,8 @@ class MsgPackDeserializer {
|
|||||||
_stringStorage.append('\0');
|
_stringStorage.append('\0');
|
||||||
if (!_stringStorage.isValid())
|
if (!_stringStorage.isValid())
|
||||||
return DeserializationError::NoMemory;
|
return DeserializationError::NoMemory;
|
||||||
_stringStorage.commit(_pool);
|
|
||||||
result = _stringStorage.c_str();
|
result = _stringStorage.save(_pool);
|
||||||
return DeserializationError::Ok;
|
return DeserializationError::Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,16 +16,17 @@
|
|||||||
#define ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, H) \
|
#define ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, H) \
|
||||||
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \
|
ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT4(A, B, C, D), \
|
||||||
ARDUINOJSON_CONCAT4(E, F, G, H))
|
ARDUINOJSON_CONCAT4(E, F, G, H))
|
||||||
#define ARDUINOJSON_CONCAT12(A, B, C, D, E, F, G, H, I, J, K, L) \
|
#define ARDUINOJSON_CONCAT13(A, B, C, D, E, F, G, H, I, J, K, L, M) \
|
||||||
ARDUINOJSON_CONCAT8(A, B, C, D, E, F, G, \
|
ARDUINOJSON_CONCAT8(A, B, C, D, E, ARDUINOJSON_CONCAT4(F, G, H, I), \
|
||||||
ARDUINOJSON_CONCAT4(H, I, J, ARDUINOJSON_CONCAT2(K, L)))
|
ARDUINOJSON_CONCAT2(J, K), ARDUINOJSON_CONCAT2(L, M))
|
||||||
|
|
||||||
#define ARDUINOJSON_NAMESPACE \
|
#define ARDUINOJSON_NAMESPACE \
|
||||||
ARDUINOJSON_CONCAT12( \
|
ARDUINOJSON_CONCAT13( \
|
||||||
ArduinoJson, ARDUINOJSON_VERSION_MAJOR, ARDUINOJSON_VERSION_MINOR, \
|
ArduinoJson, ARDUINOJSON_VERSION_MAJOR, ARDUINOJSON_VERSION_MINOR, \
|
||||||
ARDUINOJSON_VERSION_REVISION, _, ARDUINOJSON_USE_LONG_LONG, \
|
ARDUINOJSON_VERSION_REVISION, _, ARDUINOJSON_USE_LONG_LONG, \
|
||||||
ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_DECODE_UNICODE, \
|
ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_DECODE_UNICODE, \
|
||||||
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
|
ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \
|
||||||
ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_ENABLE_COMMENTS)
|
ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_ENABLE_COMMENTS, \
|
||||||
|
ARDUINOJSON_ENABLE_STRING_DEDUPLICATION)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -11,13 +11,13 @@ namespace ARDUINOJSON_NAMESPACE {
|
|||||||
class StringCopier {
|
class StringCopier {
|
||||||
public:
|
public:
|
||||||
void startString(MemoryPool* pool) {
|
void startString(MemoryPool* pool) {
|
||||||
_slot = pool->allocExpandableString();
|
pool->getFreeZone(&_ptr, &_capacity);
|
||||||
_size = 0;
|
_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit(MemoryPool* pool) {
|
const char* save(MemoryPool* pool) {
|
||||||
ARDUINOJSON_ASSERT(_slot.value);
|
ARDUINOJSON_ASSERT(_ptr);
|
||||||
pool->freezeString(_slot, _size);
|
return pool->saveStringFromFreeZone(_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void append(const char* s) {
|
void append(const char* s) {
|
||||||
@ -29,27 +29,28 @@ class StringCopier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void append(char c) {
|
void append(char c) {
|
||||||
if (!_slot.value)
|
if (!_ptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_size >= _slot.size) {
|
if (_size >= _capacity) {
|
||||||
_slot.value = 0;
|
_ptr = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_slot.value[_size++] = c;
|
_ptr[_size++] = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValid() {
|
bool isValid() {
|
||||||
return _slot.value != 0;
|
return _ptr != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* c_str() {
|
const char* c_str() {
|
||||||
return _slot.value;
|
return _ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
char* _ptr;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
StringSlot _slot;
|
size_t _capacity;
|
||||||
};
|
};
|
||||||
} // namespace ARDUINOJSON_NAMESPACE
|
} // namespace ARDUINOJSON_NAMESPACE
|
||||||
|
@ -16,7 +16,9 @@ class StringMover {
|
|||||||
_startPtr = _writePtr;
|
_startPtr = _writePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit(MemoryPool*) const {}
|
const char* save(MemoryPool*) const {
|
||||||
|
return _startPtr;
|
||||||
|
}
|
||||||
|
|
||||||
void append(char c) {
|
void append(char c) {
|
||||||
*_writePtr++ = c;
|
*_writePtr++ = c;
|
||||||
|
@ -39,6 +39,10 @@ class ArduinoStringAdapter {
|
|||||||
return _str->length();
|
return _str->length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* begin() const {
|
||||||
|
return _str->c_str();
|
||||||
|
}
|
||||||
|
|
||||||
typedef storage_policies::store_by_copy storage_policy;
|
typedef storage_policies::store_by_copy storage_policy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -39,6 +39,10 @@ class ConstRamStringAdapter {
|
|||||||
return _str;
|
return _str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* begin() const {
|
||||||
|
return _str;
|
||||||
|
}
|
||||||
|
|
||||||
typedef storage_policies::store_by_address storage_policy;
|
typedef storage_policies::store_by_address storage_policy;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ArduinoJson/Polyfills/pgmspace.hpp>
|
#include <ArduinoJson/Polyfills/pgmspace.hpp>
|
||||||
|
#include <ArduinoJson/Strings/FlashStringIterator.hpp>
|
||||||
#include <ArduinoJson/Strings/IsString.hpp>
|
#include <ArduinoJson/Strings/IsString.hpp>
|
||||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||||
|
|
||||||
@ -42,6 +43,10 @@ class FlashStringAdapter {
|
|||||||
return strlen_P(reinterpret_cast<const char*>(_str));
|
return strlen_P(reinterpret_cast<const char*>(_str));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlashStringIterator begin() const {
|
||||||
|
return FlashStringIterator(_str);
|
||||||
|
}
|
||||||
|
|
||||||
typedef storage_policies::store_by_copy storage_policy;
|
typedef storage_policies::store_by_copy storage_policy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
44
src/ArduinoJson/Strings/FlashStringIterator.hpp
Normal file
44
src/ArduinoJson/Strings/FlashStringIterator.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// ArduinoJson - arduinojson.org
|
||||||
|
// Copyright Benoit Blanchon 2014-2020
|
||||||
|
// MIT License
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ARDUINOJSON_NAMESPACE {
|
||||||
|
|
||||||
|
class FlashStringIterator {
|
||||||
|
public:
|
||||||
|
explicit FlashStringIterator(const __FlashStringHelper* ptr)
|
||||||
|
: _ptr(reinterpret_cast<const char*>(ptr)) {}
|
||||||
|
|
||||||
|
explicit FlashStringIterator(const char* ptr) : _ptr(ptr) {}
|
||||||
|
|
||||||
|
FlashStringIterator operator+(ptrdiff_t d) const {
|
||||||
|
return FlashStringIterator(_ptr + d);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t operator-(FlashStringIterator other) const {
|
||||||
|
return _ptr - other._ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlashStringIterator operator++(int) {
|
||||||
|
return FlashStringIterator(_ptr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlashStringIterator operator++() {
|
||||||
|
return FlashStringIterator(++_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(FlashStringIterator other) const {
|
||||||
|
return _ptr != other._ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char operator*() const {
|
||||||
|
return char(pgm_read_byte(_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* _ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ARDUINOJSON_NAMESPACE
|
@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ArduinoJson/Namespace.hpp>
|
#include <ArduinoJson/Namespace.hpp>
|
||||||
|
#include <ArduinoJson/Strings/FlashStringIterator.hpp>
|
||||||
#include <ArduinoJson/Strings/IsString.hpp>
|
#include <ArduinoJson/Strings/IsString.hpp>
|
||||||
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
#include <ArduinoJson/Strings/StoragePolicy.hpp>
|
||||||
|
|
||||||
@ -41,6 +42,10 @@ class SizedFlashStringAdapter {
|
|||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FlashStringIterator begin() const {
|
||||||
|
return FlashStringIterator(_str);
|
||||||
|
}
|
||||||
|
|
||||||
typedef storage_policies::store_by_copy storage_policy;
|
typedef storage_policies::store_by_copy storage_policy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -36,6 +36,10 @@ class SizedRamStringAdapter {
|
|||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* begin() const {
|
||||||
|
return _str;
|
||||||
|
}
|
||||||
|
|
||||||
typedef storage_policies::store_by_copy storage_policy;
|
typedef storage_policies::store_by_copy storage_policy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -41,6 +41,10 @@ class StlStringAdapter {
|
|||||||
return _str->size();
|
return _str->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* begin() const {
|
||||||
|
return _str->c_str();
|
||||||
|
}
|
||||||
|
|
||||||
typedef storage_policies::store_by_copy storage_policy;
|
typedef storage_policies::store_by_copy storage_policy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -192,7 +192,7 @@ class VariantData {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool setOwnedRaw(SerializedValue<T> value, MemoryPool *pool) {
|
bool setOwnedRaw(SerializedValue<T> value, MemoryPool *pool) {
|
||||||
char *dup = pool->saveString(adaptString(value.data(), value.size()));
|
const char *dup = pool->saveString(adaptString(value.data(), value.size()));
|
||||||
if (dup) {
|
if (dup) {
|
||||||
setType(VALUE_IS_OWNED_RAW);
|
setType(VALUE_IS_OWNED_RAW);
|
||||||
_content.asRaw.data = dup;
|
_content.asRaw.data = dup;
|
||||||
|
Reference in New Issue
Block a user