Store static strings in a dedicated pool

Because a slot id is smaller than a pointer, this change will ultimately allow reducing the slot size.
This commit is contained in:
Benoit Blanchon
2025-02-24 15:35:09 +01:00
parent 509807d3c2
commit cc077c1b63
36 changed files with 282 additions and 91 deletions

View File

@@ -34,6 +34,11 @@ class Slot {
return ptr_;
}
T& operator*() const {
ARDUINOJSON_ASSERT(ptr_ != nullptr);
return *ptr_;
}
T* operator->() const {
ARDUINOJSON_ASSERT(ptr_ != nullptr);
return ptr_;
@@ -76,6 +81,14 @@ class MemoryPool {
return slots_ + id;
}
SlotId find(const T& value) const {
for (SlotId i = 0; i < usage_; i++) {
if (slots_[i] == value)
return i;
}
return NULL_SLOT;
}
void clear() {
usage_ = 0;
}

View File

@@ -114,6 +114,15 @@ class MemoryPoolList {
return pools_[poolIndex].getSlot(indexInPool);
}
SlotId find(const T& value) const {
for (PoolCount i = 0; i < count_; i++) {
SlotId id = pools_[i].find(value);
if (id != NULL_SLOT)
return SlotId(i * ARDUINOJSON_POOL_CAPACITY + id);
}
return NULL_SLOT;
}
void clear(Allocator* allocator) {
for (PoolCount i = 0; i < count_; i++)
pools_[i].destroy(allocator);

View File

@@ -34,6 +34,7 @@ class ResourceManager {
~ResourceManager() {
stringPool_.clear(allocator_);
variantPools_.clear(allocator_);
staticStringsPools_.clear(allocator_);
}
ResourceManager(const ResourceManager&) = delete;
@@ -42,6 +43,7 @@ class ResourceManager {
friend void swap(ResourceManager& a, ResourceManager& b) {
swap(a.stringPool_, b.stringPool_);
swap(a.variantPools_, b.variantPools_);
swap(a.staticStringsPools_, b.staticStringsPools_);
swap_(a.allocator_, b.allocator_);
swap_(a.overflowed_, b.overflowed_);
}
@@ -111,14 +113,34 @@ class ResourceManager {
stringPool_.dereference(s, allocator_);
}
SlotId saveStaticString(const char* s) {
auto existingSlotId = staticStringsPools_.find(s);
if (existingSlotId != NULL_SLOT)
return existingSlotId;
auto slot = staticStringsPools_.allocSlot(allocator_);
if (slot)
*slot = s;
else
overflowed_ = true;
return slot.id();
}
const char* getStaticString(SlotId id) const {
return *staticStringsPools_.getSlot(id);
}
void clear() {
variantPools_.clear(allocator_);
overflowed_ = false;
variantPools_.clear(allocator_);
stringPool_.clear(allocator_);
staticStringsPools_.clear(allocator_);
}
void shrinkToFit() {
variantPools_.shrinkToFit(allocator_);
staticStringsPools_.shrinkToFit(allocator_);
}
private:
@@ -126,6 +148,7 @@ class ResourceManager {
bool overflowed_;
StringPool stringPool_;
MemoryPoolList<SlotData> variantPools_;
MemoryPoolList<const char*> staticStringsPools_;
};
ARDUINOJSON_END_PRIVATE_NAMESPACE

View File

@@ -18,7 +18,7 @@ class JsonPair {
JsonPair(detail::ObjectData::iterator iterator,
detail::ResourceManager* resources) {
if (!iterator.done()) {
key_ = iterator->asString();
key_ = iterator->asString(resources);
iterator.next(resources);
value_ = JsonVariant(iterator.data(), resources);
}
@@ -46,7 +46,7 @@ class JsonPairConst {
JsonPairConst(detail::ObjectData::iterator iterator,
const detail::ResourceManager* resources) {
if (!iterator.done()) {
key_ = iterator->asString();
key_ = iterator->asString(resources);
iterator.next(resources);
value_ = JsonVariantConst(iterator.data(), resources);
}

View File

@@ -36,7 +36,7 @@ inline ObjectData::iterator ObjectData::findKey(
return iterator();
bool isKey = true;
for (auto it = createIterator(resources); !it.done(); it.next(resources)) {
if (isKey && stringEquals(key, adaptString(it->asString())))
if (isKey && stringEquals(key, adaptString(it->asString(resources))))
return it;
isKey = !isKey;
}

View File

@@ -90,8 +90,9 @@ template <size_t N>
struct StringAdapter<const char (&)[N]> {
using AdaptedString = RamString;
static AdaptedString adapt(const char (&p)[N]) {
return RamString(p, N - 1, true);
static AdaptedString adapt(const char* p) {
ARDUINOJSON_ASSERT(p);
return RamString(p, ::strlen(p), true);
}
};

View File

@@ -28,8 +28,7 @@ class JsonString {
detail::enable_if_t<detail::is_integral<TSize>::value &&
!detail::is_same<TSize, bool>::value,
int> = 0>
JsonString(const char* data, TSize sz, bool isStatic = false)
: str_(data, size_t(sz), isStatic) {}
JsonString(const char* data, TSize sz) : str_(data, size_t(sz), false) {}
// Returns a pointer to the characters.
const char* c_str() const {

View File

@@ -160,7 +160,7 @@ struct Converter<const char*> : private detail::VariantAttorney {
static const char* fromJson(JsonVariantConst src) {
auto data = getData(src);
return data ? data->asString().c_str() : 0;
return data ? data->asString(getResourceManager(src)).c_str() : 0;
}
static bool checkJson(JsonVariantConst src) {
@@ -178,7 +178,7 @@ struct Converter<JsonString> : private detail::VariantAttorney {
static JsonString fromJson(JsonVariantConst src) {
auto data = getData(src);
return data ? data->asString() : JsonString();
return data ? data->asString(getResourceManager(src)) : JsonString();
}
static bool checkJson(JsonVariantConst src) {

View File

@@ -56,13 +56,10 @@ union VariantContent {
bool asBoolean;
uint32_t asUint32;
int32_t asInt32;
#if ARDUINOJSON_USE_EXTENSIONS
SlotId asSlotId;
#endif
ArrayData asArray;
ObjectData asObject;
CollectionData asCollection;
const char* asLinkedString;
struct StringNode* asOwnedString;
char asTinyString[tinyStringMaxLength + 1];
};

View File

@@ -77,7 +77,7 @@ class VariantData {
return visit.visit(JsonString(content_.asTinyString));
case VariantType::LinkedString:
return visit.visit(JsonString(content_.asLinkedString, true));
return visit.visit(JsonString(asLinkedString(resources), true));
case VariantType::OwnedString:
return visit.visit(JsonString(content_.asOwnedString->data,
@@ -216,7 +216,7 @@ class VariantData {
str = content_.asTinyString;
break;
case VariantType::LinkedString:
str = content_.asLinkedString;
str = asLinkedString(resources);
break;
case VariantType::OwnedString:
str = content_.asOwnedString->data;
@@ -261,7 +261,7 @@ class VariantData {
str = content_.asTinyString;
break;
case VariantType::LinkedString:
str = content_.asLinkedString;
str = asLinkedString(resources);
break;
case VariantType::OwnedString:
str = content_.asOwnedString->data;
@@ -298,12 +298,14 @@ class VariantData {
}
}
JsonString asString() const {
const char* asLinkedString(const ResourceManager* resources) const;
JsonString asString(const ResourceManager* resources) const {
switch (type_) {
case VariantType::TinyString:
return JsonString(content_.asTinyString);
case VariantType::LinkedString:
return JsonString(content_.asLinkedString, true);
return JsonString(asLinkedString(resources), true);
case VariantType::OwnedString:
return JsonString(content_.asOwnedString->data,
content_.asOwnedString->length);
@@ -519,12 +521,7 @@ class VariantData {
var->setString(value, resources);
}
void setLinkedString(const char* s) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
type_ = VariantType::LinkedString;
content_.asLinkedString = s;
}
bool setLinkedString(const char* s, ResourceManager* resources);
template <typename TAdaptedString>
void setTinyString(const TAdaptedString& s) {

View File

@@ -18,6 +18,20 @@ inline void VariantData::setRawString(SerializedValue<T> value,
setRawString(dup);
}
inline bool VariantData::setLinkedString(const char* s,
ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VariantType::Null); // must call clear() first
ARDUINOJSON_ASSERT(s);
auto slotId = resources->saveStaticString(s);
if (slotId == NULL_SLOT)
return false;
type_ = VariantType::LinkedString;
content_.asSlotId = slotId;
return true;
}
template <typename TAdaptedString>
inline bool VariantData::setString(TAdaptedString value,
ResourceManager* resources) {
@@ -26,10 +40,8 @@ inline bool VariantData::setString(TAdaptedString value,
if (value.isNull())
return false;
if (value.isStatic()) {
setLinkedString(value.data());
return true;
}
if (value.isStatic())
return setLinkedString(value.data(), resources);
if (isTinyString(value, value.size())) {
setTinyString(value);
@@ -70,6 +82,12 @@ inline const VariantExtension* VariantData::getExtension(
}
#endif
inline const char* VariantData::asLinkedString(
const ResourceManager* resources) const {
ARDUINOJSON_ASSERT(type_ == VariantType::LinkedString);
return resources->getStaticString(content_.asSlotId);
}
template <typename T>
enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
T value, ResourceManager* resources) {