Pass StringNode* to VariantData

This commit is contained in:
Benoit Blanchon
2023-05-02 18:29:19 +02:00
parent 5c0338970c
commit 167ea08c53
13 changed files with 90 additions and 60 deletions

View File

@ -93,39 +93,46 @@ TEST_CASE("StringCopier") {
} }
} }
static const char* addStringToPool(MemoryPool& pool, const char* s) { static StringNode* addStringToPool(MemoryPool& pool, const char* s) {
StringCopier str(&pool); StringCopier str(&pool);
str.startString(); str.startString();
str.append(s); str.append(s);
return str.save().c_str(); return str.save();
} }
TEST_CASE("StringCopier::save() deduplicates strings") { TEST_CASE("StringCopier::save() deduplicates strings") {
MemoryPool pool(4096); MemoryPool pool(4096);
SECTION("Basic") { SECTION("Basic") {
const char* s1 = addStringToPool(pool, "hello"); auto s1 = addStringToPool(pool, "hello");
const char* s2 = addStringToPool(pool, "world"); auto s2 = addStringToPool(pool, "world");
const char* s3 = addStringToPool(pool, "hello"); auto s3 = addStringToPool(pool, "hello");
REQUIRE(s1 == s3); REQUIRE(s1 == s3);
REQUIRE(s2 != s3); REQUIRE(s2 != s3);
REQUIRE(s1->references == 2);
REQUIRE(s2->references == 1);
REQUIRE(s3->references == 2);
REQUIRE(pool.size() == 2 * sizeofString(5)); REQUIRE(pool.size() == 2 * sizeofString(5));
} }
SECTION("Requires terminator") { SECTION("Requires terminator") {
const char* s1 = addStringToPool(pool, "hello world"); auto s1 = addStringToPool(pool, "hello world");
const char* s2 = addStringToPool(pool, "hello"); auto s2 = addStringToPool(pool, "hello");
REQUIRE(s2 != s1); REQUIRE(s2 != s1);
REQUIRE(s1->references == 1);
REQUIRE(s2->references == 1);
REQUIRE(pool.size() == sizeofString(11) + sizeofString(5)); REQUIRE(pool.size() == sizeofString(11) + sizeofString(5));
} }
SECTION("Don't overrun") { SECTION("Don't overrun") {
const char* s1 = addStringToPool(pool, "hello world"); auto s1 = addStringToPool(pool, "hello world");
const char* s2 = addStringToPool(pool, "wor"); auto s2 = addStringToPool(pool, "wor");
REQUIRE(s2 != s1); REQUIRE(s2 != s1);
REQUIRE(s1->references == 1);
REQUIRE(s2->references == 1);
REQUIRE(pool.size() == sizeofString(11) + sizeofString(3)); REQUIRE(pool.size() == sizeofString(11) + sizeofString(3));
} }
} }

View File

@ -10,11 +10,11 @@
using namespace ArduinoJson::detail; using namespace ArduinoJson::detail;
static const char* saveString(MemoryPool& pool, const char* s) { static StringNode* saveString(MemoryPool& pool, const char* s) {
return pool.saveString(adaptString(s)); return pool.saveString(adaptString(s));
} }
static const char* saveString(MemoryPool& pool, const char* s, size_t n) { static StringNode* saveString(MemoryPool& pool, const char* s, size_t n) {
return pool.saveString(adaptString(s, n)); return pool.saveString(adaptString(s, n));
} }
@ -22,35 +22,47 @@ TEST_CASE("MemoryPool::saveString()") {
MemoryPool pool(32); MemoryPool pool(32);
SECTION("Duplicates different strings") { SECTION("Duplicates different strings") {
const char* a = saveString(pool, "hello"); auto a = saveString(pool, "hello");
const char* b = saveString(pool, "world"); auto b = saveString(pool, "world");
REQUIRE(a != b); REQUIRE(a->data != b->data);
REQUIRE(a->length == 5);
REQUIRE(b->length == 5);
REQUIRE(a->references == 1);
REQUIRE(b->references == 1);
REQUIRE(pool.size() == 2 * sizeofString(5)); REQUIRE(pool.size() == 2 * sizeofString(5));
} }
SECTION("Deduplicates identical strings") { SECTION("Deduplicates identical strings") {
const char* a = saveString(pool, "hello"); auto a = saveString(pool, "hello");
const char* b = saveString(pool, "hello"); auto b = saveString(pool, "hello");
REQUIRE(a == b); REQUIRE(a == b);
REQUIRE(a->length == 5);
REQUIRE(a->references == 2);
REQUIRE(pool.size() == sizeofString(5)); REQUIRE(pool.size() == sizeofString(5));
} }
SECTION("Deduplicates identical strings that contain NUL") { SECTION("Deduplicates identical strings that contain NUL") {
const char* a = saveString(pool, "hello\0world", 11); auto a = saveString(pool, "hello\0world", 11);
const char* b = saveString(pool, "hello\0world", 11); auto b = saveString(pool, "hello\0world", 11);
REQUIRE(a == b); REQUIRE(a == b);
REQUIRE(a->length == 11);
REQUIRE(a->references == 2);
REQUIRE(pool.size() == sizeofString(11)); REQUIRE(pool.size() == sizeofString(11));
} }
SECTION("Don't stop on first NUL") { SECTION("Don't stop on first NUL") {
const char* a = saveString(pool, "hello"); auto a = saveString(pool, "hello");
const char* b = saveString(pool, "hello\0world", 11); auto b = saveString(pool, "hello\0world", 11);
REQUIRE(a != b); REQUIRE(a != b);
REQUIRE(a->length == 5);
REQUIRE(b->length == 11);
REQUIRE(a->references == 1);
REQUIRE(b->references == 1);
REQUIRE(pool.size() == sizeofString(5) + sizeofString(11)); REQUIRE(pool.size() == sizeofString(5) + sizeofString(11));
} }
SECTION("Returns NULL when allocation fails") { SECTION("Returns NULL when allocation fails") {
MemoryPool pool2(32, FailingAllocator::instance()); MemoryPool pool2(32, FailingAllocator::instance());
REQUIRE(0 == saveString(pool2, "a")); REQUIRE(saveString(pool2, "a") == nullptr);
} }
} }

View File

@ -28,12 +28,12 @@ inline VariantData* collectionAddMember(CollectionData* obj, TAdaptedString key,
if (!slot) if (!slot)
return nullptr; return nullptr;
if (key.isLinked()) if (key.isLinked())
slot->setKey(JsonString(key.data(), key.size(), JsonString::Linked)); slot->setKey(key.data());
else { else {
auto storedKey = pool->saveString(key); auto storedKey = pool->saveString(key);
if (!storedKey) if (!storedKey)
return nullptr; return nullptr;
slot->setKey(JsonString(storedKey, key.size(), JsonString::Copied)); slot->setKey(storedKey);
} }
obj->add(slot); obj->add(slot);
return slot->data(); return slot->data();

View File

@ -277,14 +277,14 @@ class JsonDeserializer {
VariantSlot* slot = object.get(adaptString(key.c_str())); VariantSlot* slot = object.get(adaptString(key.c_str()));
if (!slot) { if (!slot) {
// Save key in memory pool. // Save key in memory pool.
key = stringStorage_.save(); auto savedKey = stringStorage_.save();
// Allocate slot in object // Allocate slot in object
slot = pool_->allocVariant(); slot = pool_->allocVariant();
if (!slot) if (!slot)
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
slot->setKey(key); slot->setKey(savedKey);
object.add(slot); object.add(slot);
} }

View File

@ -107,14 +107,14 @@ class MemoryPool {
} }
template <typename TAdaptedString> template <typename TAdaptedString>
const char* saveString(TAdaptedString str) { StringNode* saveString(TAdaptedString str) {
if (str.isNull()) if (str.isNull())
return 0; return 0;
auto node = findString(str); auto node = findString(str);
if (node) { if (node) {
node->references++; node->references++;
return node->data; return node;
} }
size_t n = str.size(); size_t n = str.size();
@ -126,7 +126,7 @@ class MemoryPool {
stringGetChars(str, node->data, n); stringGetChars(str, node->data, n);
node->data[n] = 0; // force NUL terminator node->data[n] = 0; // force NUL terminator
addStringToList(node); addStringToList(node);
return node->data; return node;
} }
void addStringToList(StringNode* node) { void addStringToList(StringNode* node) {

View File

@ -494,13 +494,13 @@ class MsgPackDeserializer {
ARDUINOJSON_ASSERT(object != 0); ARDUINOJSON_ASSERT(object != 0);
// Save key in memory pool. // Save key in memory pool.
key = stringStorage_.save(); auto savedKey = stringStorage_.save();
VariantSlot* slot = pool_->allocVariant(); VariantSlot* slot = pool_->allocVariant();
if (!slot) if (!slot)
return DeserializationError::NoMemory; return DeserializationError::NoMemory;
slot->setKey(key); slot->setKey(savedKey);
object->add(slot); object->add(slot);
member = slot->data(); member = slot->data();

View File

@ -25,7 +25,7 @@ class StringCopier {
node_ = pool_->allocString(initialCapacity); node_ = pool_->allocString(initialCapacity);
} }
JsonString save() { StringNode* save() {
ARDUINOJSON_ASSERT(node_ != nullptr); ARDUINOJSON_ASSERT(node_ != nullptr);
node_->data[size_] = 0; node_->data[size_] = 0;
StringNode* node = pool_->findString(adaptString(node_->data, size_)); StringNode* node = pool_->findString(adaptString(node_->data, size_));
@ -36,7 +36,7 @@ class StringCopier {
} else { } else {
node->references++; node->references++;
} }
return JsonString(node->data, node->length, JsonString::Copied); return node;
} }
void append(const char* s) { void append(const char* s) {

View File

@ -17,10 +17,9 @@ class StringMover {
startPtr_ = writePtr_; startPtr_ = writePtr_;
} }
FORCE_INLINE JsonString save() { FORCE_INLINE const char* save() {
JsonString s = str(); *writePtr_++ = 0; // terminator
writePtr_++; return startPtr_;
return s;
} }
void append(char c) { void append(char c) {

View File

@ -188,7 +188,7 @@ class MemoryPoolPrint : public Print {
copier_.startString(); copier_.startString();
} }
JsonString str() { StringNode* save() {
ARDUINOJSON_ASSERT(!overflowed()); ARDUINOJSON_ASSERT(!overflowed());
return copier_.save(); return copier_.save();
} }
@ -227,7 +227,7 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
data->setNull(); data->setNull();
return; return;
} }
data->setString(print.str()); data->setString(print.save());
} }
#endif #endif

View File

@ -157,10 +157,11 @@ class VariantData {
content_.asFloat = value; content_.asFloat = value;
} }
void setRawString(const char* data, size_t n) { void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(s);
setType(VALUE_IS_RAW_STRING); setType(VALUE_IS_RAW_STRING);
content_.asString.data = data; content_.asString.data = s->data;
content_.asString.size = n; content_.asString.size = s->length;
} }
template <typename T> template <typename T>
@ -179,14 +180,18 @@ class VariantData {
setType(VALUE_IS_NULL); setType(VALUE_IS_NULL);
} }
void setString(JsonString s) { void setString(StringNode* s) {
ARDUINOJSON_ASSERT(s); ARDUINOJSON_ASSERT(s);
if (s.isLinked()) setType(VALUE_IS_OWNED_STRING);
setType(VALUE_IS_LINKED_STRING); content_.asString.data = s->data;
else content_.asString.size = s->length;
setType(VALUE_IS_OWNED_STRING); }
content_.asString.data = s.c_str();
content_.asString.size = s.size(); void setString(const char* s) {
ARDUINOJSON_ASSERT(s);
setType(VALUE_IS_LINKED_STRING);
content_.asString.data = s;
content_.asString.size = strlen(s);
} }
CollectionData& toArray() { CollectionData& toArray() {

View File

@ -61,7 +61,7 @@ inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
auto dup = pool->saveString(str); auto dup = pool->saveString(str);
if (!dup) if (!dup)
return false; return false;
dst->setString(JsonString(dup, str.size(), JsonString::Copied)); dst->setString(dup);
return true; return true;
} }
case VALUE_IS_RAW_STRING: { case VALUE_IS_RAW_STRING: {
@ -69,7 +69,7 @@ inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
auto dup = pool->saveString(str); auto dup = pool->saveString(str);
if (!dup) if (!dup)
return false; return false;
dst->setRawString(dup, str.size()); dst->setRawString(dup);
return true; return true;
} }
default: default:
@ -121,13 +121,13 @@ inline void variantSetString(VariantData* var, TAdaptedString value,
} }
if (value.isLinked()) { if (value.isLinked()) {
var->setString(JsonString(value.data(), value.size(), JsonString::Linked)); var->setString(value.data());
return; return;
} }
auto dup = pool->saveString(value); auto dup = pool->saveString(value);
if (dup) if (dup)
var->setString(JsonString(dup, value.size(), JsonString::Copied)); var->setString(dup);
else else
var->setNull(); var->setNull();
} }
@ -138,9 +138,9 @@ inline void variantSetRawString(VariantData* var, SerializedValue<T> value,
if (!var) if (!var)
return; return;
variantRelease(var, pool); variantRelease(var, pool);
const char* dup = pool->saveString(adaptString(value.data(), value.size())); auto dup = pool->saveString(adaptString(value.data(), value.size()));
if (dup) if (dup)
var->setRawString(dup, value.size()); var->setRawString(dup);
else else
var->setNull(); var->setNull();
} }

View File

@ -137,4 +137,10 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
dst.set(src.template as<JsonVariantConst>()); dst.set(src.template as<JsonVariantConst>());
} }
inline void VariantSlot::setKey(StringNode* k) {
ARDUINOJSON_ASSERT(k);
flags_ |= OWNED_KEY_BIT;
key_ = k->data;
}
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@ -11,6 +11,8 @@
ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
struct StringNode;
typedef int_t<ARDUINOJSON_SLOT_OFFSET_SIZE * 8>::type VariantSlotDiff; typedef int_t<ARDUINOJSON_SLOT_OFFSET_SIZE * 8>::type VariantSlotDiff;
class VariantSlot { class VariantSlot {
@ -76,15 +78,14 @@ class VariantSlot {
next_ = VariantSlotDiff(slot - this); next_ = VariantSlotDiff(slot - this);
} }
void setKey(JsonString k) { void setKey(const char* k) {
ARDUINOJSON_ASSERT(k); ARDUINOJSON_ASSERT(k);
if (k.isLinked()) flags_ &= VALUE_MASK;
flags_ &= VALUE_MASK; key_ = k;
else
flags_ |= OWNED_KEY_BIT;
key_ = k.c_str();
} }
void setKey(StringNode* k);
const char* key() const { const char* key() const {
return key_; return key_;
} }