mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-18 04:52:22 +02:00
Pass StringNode*
to VariantData
This commit is contained in:
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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_;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user