Skip to content

Commit

Permalink
Pass StringNode* to VariantData
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed May 2, 2023
1 parent 5c03389 commit 9f6afe0
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 60 deletions.
25 changes: 16 additions & 9 deletions extras/tests/MemoryPool/StringCopier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
str.startString();
str.append(s);
return str.save().c_str();
return str.save();
}

TEST_CASE("StringCopier::save() deduplicates strings") {
MemoryPool pool(4096);

SECTION("Basic") {
const char* s1 = addStringToPool(pool, "hello");
const char* s2 = addStringToPool(pool, "world");
const char* s3 = addStringToPool(pool, "hello");
auto s1 = addStringToPool(pool, "hello");
auto s2 = addStringToPool(pool, "world");
auto s3 = addStringToPool(pool, "hello");

REQUIRE(s1 == s3);
REQUIRE(s2 != s3);
REQUIRE(s1->references == 2);
REQUIRE(s2->references == 1);
REQUIRE(s3->references == 2);
REQUIRE(pool.size() == 2 * sizeofString(5));
}

SECTION("Requires terminator") {
const char* s1 = addStringToPool(pool, "hello world");
const char* s2 = addStringToPool(pool, "hello");
auto s1 = addStringToPool(pool, "hello world");
auto s2 = addStringToPool(pool, "hello");

REQUIRE(s2 != s1);
REQUIRE(s1->references == 1);
REQUIRE(s2->references == 1);
REQUIRE(pool.size() == sizeofString(11) + sizeofString(5));
}

SECTION("Don't overrun") {
const char* s1 = addStringToPool(pool, "hello world");
const char* s2 = addStringToPool(pool, "wor");
auto s1 = addStringToPool(pool, "hello world");
auto s2 = addStringToPool(pool, "wor");

REQUIRE(s2 != s1);
REQUIRE(s1->references == 1);
REQUIRE(s2->references == 1);
REQUIRE(pool.size() == sizeofString(11) + sizeofString(3));
}
}
36 changes: 24 additions & 12 deletions extras/tests/MemoryPool/saveString.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,59 @@

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));
}

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));
}

TEST_CASE("MemoryPool::saveString()") {
MemoryPool pool(32);

SECTION("Duplicates different strings") {
const char* a = saveString(pool, "hello");
const char* b = saveString(pool, "world");
REQUIRE(a != b);
auto a = saveString(pool, "hello");
auto b = saveString(pool, "world");
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));
}

SECTION("Deduplicates identical strings") {
const char* a = saveString(pool, "hello");
const char* b = saveString(pool, "hello");
auto a = saveString(pool, "hello");
auto b = saveString(pool, "hello");
REQUIRE(a == b);
REQUIRE(a->length == 5);
REQUIRE(a->references == 2);
REQUIRE(pool.size() == sizeofString(5));
}

SECTION("Deduplicates identical strings that contain NUL") {
const char* a = saveString(pool, "hello\0world", 11);
const char* b = saveString(pool, "hello\0world", 11);
auto a = saveString(pool, "hello\0world", 11);
auto b = saveString(pool, "hello\0world", 11);
REQUIRE(a == b);
REQUIRE(a->length == 11);
REQUIRE(a->references == 2);
REQUIRE(pool.size() == sizeofString(11));
}

SECTION("Don't stop on first NUL") {
const char* a = saveString(pool, "hello");
const char* b = saveString(pool, "hello\0world", 11);
auto a = saveString(pool, "hello");
auto b = saveString(pool, "hello\0world", 11);
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));
}

SECTION("Returns NULL when allocation fails") {
MemoryPool pool2(32, FailingAllocator::instance());
REQUIRE(0 == saveString(pool2, "a"));
REQUIRE(saveString(pool2, "a") == nullptr);
}
}
4 changes: 2 additions & 2 deletions src/ArduinoJson/Collection/CollectionFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ inline VariantData* collectionAddMember(CollectionData* obj, TAdaptedString key,
if (!slot)
return nullptr;
if (key.isLinked())
slot->setKey(JsonString(key.data(), key.size(), JsonString::Linked));
slot->setKey(key.data());
else {
auto storedKey = pool->saveString(key);
if (!storedKey)
return nullptr;
slot->setKey(JsonString(storedKey, key.size(), JsonString::Copied));
slot->setKey(storedKey);
}
obj->add(slot);
return slot->data();
Expand Down
4 changes: 2 additions & 2 deletions src/ArduinoJson/Json/JsonDeserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,14 +277,14 @@ class JsonDeserializer {
VariantSlot* slot = object.get(adaptString(key.c_str()));
if (!slot) {
// Save key in memory pool.
key = stringStorage_.save();
auto savedKey = stringStorage_.save();

// Allocate slot in object
slot = pool_->allocVariant();
if (!slot)
return DeserializationError::NoMemory;

slot->setKey(key);
slot->setKey(savedKey);
object.add(slot);
}

Expand Down
6 changes: 3 additions & 3 deletions src/ArduinoJson/Memory/MemoryPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ class MemoryPool {
}

template <typename TAdaptedString>
const char* saveString(TAdaptedString str) {
StringNode* saveString(TAdaptedString str) {
if (str.isNull())
return 0;

auto node = findString(str);
if (node) {
node->references++;
return node->data;
return node;
}

size_t n = str.size();
Expand All @@ -126,7 +126,7 @@ class MemoryPool {
stringGetChars(str, node->data, n);
node->data[n] = 0; // force NUL terminator
addStringToList(node);
return node->data;
return node;
}

void addStringToList(StringNode* node) {
Expand Down
4 changes: 2 additions & 2 deletions src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,13 +494,13 @@ class MsgPackDeserializer {
ARDUINOJSON_ASSERT(object != 0);

// Save key in memory pool.
key = stringStorage_.save();
auto savedKey = stringStorage_.save();

VariantSlot* slot = pool_->allocVariant();
if (!slot)
return DeserializationError::NoMemory;

slot->setKey(key);
slot->setKey(savedKey);
object->add(slot);

member = slot->data();
Expand Down
4 changes: 2 additions & 2 deletions src/ArduinoJson/StringStorage/StringCopier.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class StringCopier {
node_ = pool_->allocString(initialCapacity);
}

JsonString save() {
StringNode* save() {
ARDUINOJSON_ASSERT(node_ != nullptr);
node_->data[size_] = 0;
StringNode* node = pool_->findString(adaptString(node_->data, size_));
Expand All @@ -36,7 +36,7 @@ class StringCopier {
} else {
node->references++;
}
return JsonString(node->data, node->length, JsonString::Copied);
return node;
}

void append(const char* s) {
Expand Down
7 changes: 3 additions & 4 deletions src/ArduinoJson/StringStorage/StringMover.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ class StringMover {
startPtr_ = writePtr_;
}

FORCE_INLINE JsonString save() {
JsonString s = str();
writePtr_++;
return s;
FORCE_INLINE const char* save() {
*writePtr_++ = 0; // terminator
return startPtr_;
}

void append(char c) {
Expand Down
4 changes: 2 additions & 2 deletions src/ArduinoJson/Variant/ConverterImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class MemoryPoolPrint : public Print {
copier_.startString();
}

JsonString str() {
StringNode* save() {
ARDUINOJSON_ASSERT(!overflowed());
return copier_.save();
}
Expand Down Expand Up @@ -227,7 +227,7 @@ inline void convertToJson(const ::Printable& src, JsonVariant dst) {
data->setNull();
return;
}
data->setString(print.str());
data->setString(print.save());
}

#endif
Expand Down
25 changes: 15 additions & 10 deletions src/ArduinoJson/Variant/VariantData.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,11 @@ class VariantData {
content_.asFloat = value;
}

void setRawString(const char* data, size_t n) {
void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(s);
setType(VALUE_IS_RAW_STRING);
content_.asString.data = data;
content_.asString.size = n;
content_.asString.data = s->data;
content_.asString.size = s->length;
}

template <typename T>
Expand All @@ -179,14 +180,18 @@ class VariantData {
setType(VALUE_IS_NULL);
}

void setString(JsonString s) {
void setString(StringNode* s) {
ARDUINOJSON_ASSERT(s);
if (s.isLinked())
setType(VALUE_IS_LINKED_STRING);
else
setType(VALUE_IS_OWNED_STRING);
content_.asString.data = s.c_str();
content_.asString.size = s.size();
setType(VALUE_IS_OWNED_STRING);
content_.asString.data = s->data;
content_.asString.size = s->length;
}

void setString(const char* s) {
ARDUINOJSON_ASSERT(s);
setType(VALUE_IS_LINKED_STRING);
content_.asString.data = s;
content_.asString.size = strlen(s);
}

CollectionData& toArray() {
Expand Down
12 changes: 6 additions & 6 deletions src/ArduinoJson/Variant/VariantFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ inline bool variantCopyFrom(VariantData* dst, const VariantData* src,
auto dup = pool->saveString(str);
if (!dup)
return false;
dst->setString(JsonString(dup, str.size(), JsonString::Copied));
dst->setString(dup);
return true;
}
case VALUE_IS_RAW_STRING: {
auto str = adaptString(src->asRawString());
auto dup = pool->saveString(str);
if (!dup)
return false;
dst->setRawString(dup, str.size());
dst->setRawString(dup);
return true;
}
default:
Expand Down Expand Up @@ -121,13 +121,13 @@ inline void variantSetString(VariantData* var, TAdaptedString value,
}

if (value.isLinked()) {
var->setString(JsonString(value.data(), value.size(), JsonString::Linked));
var->setString(value.data());
return;
}

auto dup = pool->saveString(value);
if (dup)
var->setString(JsonString(dup, value.size(), JsonString::Copied));
var->setString(dup);
else
var->setNull();
}
Expand All @@ -138,9 +138,9 @@ inline void variantSetRawString(VariantData* var, SerializedValue<T> value,
if (!var)
return;
variantRelease(var, pool);
const char* dup = pool->saveString(adaptString(value.data(), value.size()));
auto dup = pool->saveString(adaptString(value.data(), value.size()));
if (dup)
var->setRawString(dup, value.size());
var->setRawString(dup);
else
var->setNull();
}
Expand Down
6 changes: 6 additions & 0 deletions src/ArduinoJson/Variant/VariantImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,10 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
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
13 changes: 7 additions & 6 deletions src/ArduinoJson/Variant/VariantSlot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE

class StringNode;

typedef int_t<ARDUINOJSON_SLOT_OFFSET_SIZE * 8>::type VariantSlotDiff;

class VariantSlot {
Expand Down Expand Up @@ -76,15 +78,14 @@ class VariantSlot {
next_ = VariantSlotDiff(slot - this);
}

void setKey(JsonString k) {
void setKey(const char* k) {
ARDUINOJSON_ASSERT(k);
if (k.isLinked())
flags_ &= VALUE_MASK;
else
flags_ |= OWNED_KEY_BIT;
key_ = k.c_str();
flags_ &= VALUE_MASK;
key_ = k;
}

void setKey(StringNode* k);

const char* key() const {
return key_;
}
Expand Down

0 comments on commit 9f6afe0

Please sign in to comment.