Removed the indirection via StringSlot

This commit is contained in:
Benoit Blanchon
2018-11-27 17:28:19 +01:00
parent 45f4e5ac20
commit a60162ba76
23 changed files with 105 additions and 164 deletions

View File

@ -7,8 +7,8 @@ project(ArduinoJson)
enable_testing()
add_definitions(-DARDUINOJSON_DEBUG)
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
add_definitions(-DARDUINOJSON_DEBUG)
add_compile_options(-g -O0)
endif()

View File

@ -1,4 +1,6 @@
Version,Date,JsonParserExample,JsonGeneratorExample
v6.6.0-beta-6-g8217012,2018-11-27,7204,7630
v6.6.0-beta-5-g13cc610,2018-11-27,7264,7850
v6.6.0-beta-2-g2bd280d,2018-11-16,7872,8446
v6.6.0-beta,2018-11-13,8380,8916
v6.5.0-beta,2018-10-13,7384,7874

1 Version Date JsonParserExample JsonGeneratorExample
2 v6.6.0-beta-6-g8217012 2018-11-27 7204 7630
3 v6.6.0-beta-5-g13cc610 2018-11-27 7264 7850
4 v6.6.0-beta-2-g2bd280d 2018-11-16 7872 8446
5 v6.6.0-beta 2018-11-13 8380 8916
6 v6.5.0-beta 2018-10-13 7384 7874

View File

@ -48,8 +48,6 @@ union JsonVariantContent {
JsonArrayData asArray;
JsonObjectData asObject;
const char *asString;
struct StringSlot *asOwnedString;
struct StringSlot *asOwnedRaw;
struct {
const char *data;
size_t size;

View File

@ -103,9 +103,9 @@ inline bool objectCopy(JsonObjectData* dst, const JsonObjectData* src,
for (VariantSlot* s = src->head; s; s = s->next) {
JsonVariantData* var;
if (s->value.keyIsOwned)
var = objectAdd(dst, ZeroTerminatedRamString(s->ownedKey->value), pool);
var = objectAdd(dst, ZeroTerminatedRamString(s->key), pool);
else
var = objectAdd(dst, ZeroTerminatedRamStringConst(s->linkedKey), pool);
var = objectAdd(dst, ZeroTerminatedRamStringConst(s->key), pool);
if (!variantCopy(var, &s->value, pool)) return false;
}
return true;

View File

@ -13,28 +13,28 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TKey>
inline bool slotSetKey(VariantSlot* var, TKey key, MemoryPool* pool) {
StringSlot* slot = key.save(pool);
if (!slot) return false;
var->ownedKey = slot;
char* dup = key.save(pool);
if (!dup) return false;
var->key = dup;
var->value.keyIsOwned = true;
return true;
}
inline bool slotSetKey(VariantSlot* var, ZeroTerminatedRamStringConst key,
MemoryPool*) {
var->linkedKey = key.c_str();
var->key = key.c_str();
var->value.keyIsOwned = false;
return true;
}
inline bool slotSetKey(VariantSlot* var, StringInMemoryPool key, MemoryPool*) {
var->ownedKey = key.slot();
var->key = key.c_str();
var->value.keyIsOwned = true;
return true;
}
inline const char* slotGetKey(const VariantSlot* var) {
return var->value.keyIsOwned ? var->ownedKey->value : var->linkedKey;
return var->key;
}
inline const VariantSlot* slotAdvance(const VariantSlot* var, size_t distance) {

View File

@ -22,9 +22,8 @@ inline T variantAsIntegral(const JsonVariantData* var) {
case JSON_NEGATIVE_INTEGER:
return T(~var->content.asInteger + 1);
case JSON_LINKED_STRING:
return parseInteger<T>(var->content.asString);
case JSON_OWNED_STRING:
return parseInteger<T>(var->content.asOwnedString->value);
return parseInteger<T>(var->content.asString);
case JSON_FLOAT:
return T(var->content.asFloat);
default:
@ -47,9 +46,8 @@ inline T variantAsFloat(const JsonVariantData* var) {
case JSON_NEGATIVE_INTEGER:
return -static_cast<T>(var->content.asInteger);
case JSON_LINKED_STRING:
return parseFloat<T>(var->content.asString);
case JSON_OWNED_STRING:
return parseFloat<T>(var->content.asOwnedString->value);
return parseFloat<T>(var->content.asString);
case JSON_FLOAT:
return static_cast<T>(var->content.asFloat);
default:
@ -61,9 +59,8 @@ inline const char* variantAsString(const JsonVariantData* var) {
if (!var) return 0;
switch (var->type) {
case JSON_LINKED_STRING:
return var->content.asString;
case JSON_OWNED_STRING:
return var->content.asOwnedString->value;
return var->content.asString;
default:
return 0;
}
@ -144,10 +141,11 @@ template <typename T>
inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value,
MemoryPool* pool) {
if (!var) return false;
StringSlot* slot = makeString(value.data(), value.size()).save(pool);
if (slot) {
char* dup = makeString(value.data(), value.size()).save(pool);
if (dup) {
var->type = JSON_OWNED_RAW;
var->content.asOwnedRaw = slot;
var->content.asRaw.data = dup;
var->content.asRaw.size = value.size();
return true;
} else {
var->type = JSON_NULL;
@ -158,10 +156,10 @@ inline bool variantSetOwnedRaw(JsonVariantData* var, SerializedValue<T> value,
template <typename T>
inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) {
if (!var) return false;
StringSlot* slot = value.save(pool);
if (slot) {
char* dup = value.save(pool);
if (dup) {
var->type = JSON_OWNED_STRING;
var->content.asOwnedString = slot;
var->content.asString = dup;
return true;
} else {
var->type = JSON_NULL;
@ -169,10 +167,10 @@ inline bool variantSetString(JsonVariantData* var, T value, MemoryPool* pool) {
}
}
inline bool variantSetOwnedString(JsonVariantData* var, StringSlot* slot) {
inline bool variantSetOwnedString(JsonVariantData* var, char* s) {
if (!var) return false;
var->type = JSON_OWNED_STRING;
var->content.asOwnedString = slot;
var->content.asString = s;
return true;
}
@ -218,11 +216,10 @@ inline bool variantCopy(JsonVariantData* dst, const JsonVariantData* src,
return objectCopy(variantToObject(dst), &src->content.asObject, pool);
case JSON_OWNED_STRING:
return variantSetString(
dst, makeString(src->content.asOwnedString->value), pool);
dst, ZeroTerminatedRamString(src->content.asString), pool);
case JSON_OWNED_RAW:
return variantSetOwnedRaw(dst,
serialized(src->content.asOwnedRaw->value,
src->content.asOwnedRaw->size),
return variantSetOwnedRaw(
dst, serialized(src->content.asRaw.data, src->content.asRaw.size),
pool);
default:
// caution: don't override keyIsOwned
@ -266,16 +263,15 @@ inline bool variantEquals(const JsonVariantData* a, const JsonVariantData* b) {
if (a->type != b->type) return false;
switch (a->type) {
case JSON_LINKED_RAW:
case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return !strcmp(a->content.asString, b->content.asString);
case JSON_LINKED_RAW:
case JSON_OWNED_RAW:
case JSON_OWNED_STRING:
return a->content.asOwnedString->size == b->content.asOwnedString->size &&
!memcmp(a->content.asOwnedString->value,
b->content.asOwnedString->value,
a->content.asOwnedString->size);
return a->content.asRaw.size == b->content.asRaw.size &&
!memcmp(a->content.asRaw.data, b->content.asRaw.data,
a->content.asRaw.size);
case JSON_BOOLEAN:
case JSON_POSITIVE_INTEGER:

View File

@ -19,7 +19,7 @@ class JsonKey {
}
bool isNull() const {
return _slot == 0 || _slot->linkedKey == 0;
return _slot == 0 || _slot->key == 0;
}
friend bool operator==(JsonKey lhs, const char* rhs) {

View File

@ -206,7 +206,8 @@ class JsonVariant : public JsonVariantProxy<JsonVariantData>,
// for internal use only
FORCE_INLINE bool set(StringInMemoryPool value) const {
return variantSetOwnedString(_data, value.slot());
return variantSetOwnedString(_data,
value.save(_memoryPool)); // TODO: remove?
}
FORCE_INLINE bool set(ZeroTerminatedRamStringConst value) const {
return variantSetString(_data, value.c_str());

View File

@ -89,15 +89,10 @@ inline void JsonVariantConst::accept(Visitor& visitor) const {
return visitor.visitObject(JsonObjectConst(&_data->content.asObject));
case JSON_LINKED_STRING:
case JSON_OWNED_STRING:
return visitor.visitString(_data->content.asString);
case JSON_OWNED_STRING:
return visitor.visitString(_data->content.asOwnedString->value);
case JSON_OWNED_RAW:
return visitor.visitRawJson(_data->content.asOwnedRaw->value,
_data->content.asOwnedRaw->size);
case JSON_LINKED_RAW:
return visitor.visitRawJson(_data->content.asRaw.data,
_data->content.asRaw.size);

View File

@ -23,21 +23,6 @@ namespace ARDUINOJSON_NAMESPACE {
// _left _right
class MemoryPool {
class UpdateStringSlotAddress {
public:
UpdateStringSlotAddress(const char* address, size_t offset)
: _address(address), _offset(offset) {}
void operator()(StringSlot* slot) const {
ARDUINOJSON_ASSERT(slot != NULL);
if (slot->value > _address) slot->value -= _offset;
}
private:
const char* _address;
size_t _offset;
};
public:
MemoryPool(char* buf, size_t capa)
: _begin(buf),
@ -66,34 +51,26 @@ class MemoryPool {
return allocRight<VariantSlot>();
}
StringSlot* allocFrozenString(size_t n) {
StringSlot* s = allocStringSlot();
if (!s) return 0;
char* allocFrozenString(size_t n) {
if (!canAlloc(n)) return 0;
s->value = _left;
s->size = n;
char* s = _left;
_left += n;
checkInvariants();
return s;
}
StringSlot* allocExpandableString() {
StringSlot* s = allocStringSlot();
if (!s) return 0;
s->value = _left;
s->size = size_t(_right - _left);
StringSlot allocExpandableString() {
StringSlot s;
s.value = _left;
s.size = size_t(_right - _left);
_left = _right;
checkInvariants();
return s;
}
void freezeString(StringSlot* slot, size_t newSize) {
_left -= (slot->size - newSize);
slot->size = newSize;
void freezeString(StringSlot& s, size_t newSize) {
_left -= (s.size - newSize);
s.size = newSize;
checkInvariants();
}

View File

@ -26,28 +26,28 @@ class StringBuilder {
}
void append(char c) {
if (!_slot) return;
if (!_slot.value) return;
if (_size >= _slot->size) {
_slot = 0;
if (_size >= _slot.size) {
_slot.value = 0;
return;
}
_slot->value[_size++] = c;
_slot.value[_size++] = c;
}
StringType complete() {
append('\0');
if (_slot) {
if (_slot.value) {
_parent->freezeString(_slot, _size);
}
return _slot;
return _slot.value;
}
private:
MemoryPool* _parent;
size_t _size;
StringSlot* _slot;
StringSlot _slot;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -7,14 +7,12 @@
#include <stddef.h> // for size_t
#include "../Configuration.hpp"
#define JSON_STRING_SIZE(SIZE) \
(sizeof(ARDUINOJSON_NAMESPACE::StringSlot) + (SIZE))
#define JSON_STRING_SIZE(SIZE) (SIZE)
namespace ARDUINOJSON_NAMESPACE {
struct StringSlot {
char *value;
size_t size;
struct StringSlot *next;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -12,10 +12,7 @@ struct VariantSlot {
JsonVariantData value;
struct VariantSlot* next;
struct VariantSlot* prev;
union {
const char* linkedKey;
StringSlot* ownedKey;
};
const char* key;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -12,13 +12,12 @@ class ArduinoString {
public:
ArduinoString(const ::String& str) : _str(&str) {}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool* memoryPool) const {
char* save(MemoryPool* memoryPool) const {
if (isNull()) return NULL;
size_t n = _str->length() + 1;
StringSlot* slot = memoryPool->allocFrozenString(n);
if (slot) memcpy(slot->value, _str->c_str(), n);
return slot;
char* dup = memoryPool->allocFrozenString(n);
if (dup) memcpy(dup, _str->c_str(), n);
return dup;
}
bool isNull() const {

View File

@ -21,12 +21,11 @@ class FixedSizeFlashString {
return !_str;
}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool* memoryPool) const {
char* save(MemoryPool* memoryPool) const {
if (!_str) return NULL;
StringSlot* slot = memoryPool->allocFrozenString(_size);
if (!slot) memcpy_P(slot->value, (const char*)_str, _size);
return slot;
char* dup = memoryPool->allocFrozenString(_size);
if (!dup) memcpy_P(dup, (const char*)_str, _size);
return dup;
}
size_t size() const {

View File

@ -23,11 +23,11 @@ class FixedSizeRamString {
}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool* memoryPool) const {
char* save(TMemoryPool* memoryPool) const {
if (!_str) return NULL;
StringSlot* slot = memoryPool->allocFrozenString(_size);
if (slot) memcpy(slot->value, _str, _size);
return slot;
char* dup = memoryPool->allocFrozenString(_size);
if (dup) memcpy(dup, _str, _size);
return dup;
}
size_t size() const {

View File

@ -12,12 +12,11 @@ class StlString {
public:
StlString(const std::string& str) : _str(&str) {}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool* memoryPool) const {
char* save(MemoryPool* memoryPool) const {
size_t n = _str->length() + 1;
StringSlot* slot = memoryPool->allocFrozenString(n);
if (slot) memcpy(slot->value, _str->c_str(), n);
return slot;
char* dup = memoryPool->allocFrozenString(n);
if (dup) memcpy(dup, _str->c_str(), n);
return dup;
}
bool isNull() const {

View File

@ -5,44 +5,35 @@
#pragma once
#include <string.h>
#include "../Memory/StringSlot.hpp"
#include "../Memory/MemoryPool.hpp"
namespace ARDUINOJSON_NAMESPACE {
class StringInMemoryPool {
public:
StringInMemoryPool(StringSlot* s = 0) : _slot(s) {}
StringInMemoryPool(char* s = 0) : _value(s) {}
bool equals(const char* expected) const {
if (!_slot) return expected == 0;
const char* actual = _slot->value;
if (!_value) return expected == 0;
const char* actual = _value;
if (actual == expected) return true;
return strcmp(actual, expected) == 0;
}
char* save(void*) {
return _value;
}
bool isNull() const {
return !_slot;
}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool*) const {
return _slot;
}
size_t size() const {
return _slot->size;
}
StringSlot* slot() const {
return _slot;
return !_value;
}
const char* c_str() const {
return _slot->value;
return _value;
}
protected:
StringSlot* _slot;
char* _value;
};
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -20,13 +20,12 @@ class ZeroTerminatedFlashString {
return !_str;
}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool* memoryPool) const {
char* save(MemoryPool* memoryPool) const {
if (!_str) return NULL;
size_t n = size() + 1; // copy the terminator
StringSlot* slot = memoryPool->allocFrozenString(n);
if (slot) memcpy_P(slot->value, reinterpret_cast<const char*>(_str), n);
return slot;
char* dup = memoryPool->allocFrozenString(n);
if (dup) memcpy_P(dup, reinterpret_cast<const char*>(_str), n);
return dup;
}
size_t size() const {

View File

@ -14,12 +14,12 @@ class ZeroTerminatedRamString : public ZeroTerminatedRamStringConst {
: ZeroTerminatedRamStringConst(str) {}
template <typename TMemoryPool>
StringSlot* save(TMemoryPool* memoryPool) const {
char* save(TMemoryPool* memoryPool) const {
if (!_str) return NULL;
size_t n = size() + 1;
StringSlot* slot = memoryPool->allocFrozenString(n);
if (slot) memcpy(slot->value, _str, n);
return slot;
char* dup = memoryPool->allocFrozenString(n);
if (dup) memcpy(dup, _str, n);
return dup;
}
};

View File

@ -79,12 +79,12 @@ TEST_CASE("JsonVariant with not enough memory") {
JsonVariant v = doc.to<JsonVariant>();
SECTION("std::string") {
v.set(std::string("hello"));
v.set(std::string("hello world!!"));
REQUIRE(v.isNull());
}
SECTION("Serialized<std::string>") {
v.set(serialized(std::string("hello")));
v.set(serialized(std::string("hello world!!")));
REQUIRE(v.isNull());
}
}

View File

@ -9,26 +9,22 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemoryPool::allocFrozenString()") {
const size_t poolCapacity = 64;
const size_t longestString = poolCapacity - sizeof(StringSlot);
const size_t longestString = poolCapacity;
char buffer[poolCapacity];
MemoryPool pool(buffer, poolCapacity);
SECTION("Returns different addresses") {
StringSlot *a = pool.allocFrozenString(1);
StringSlot *b = pool.allocFrozenString(1);
char *a = pool.allocFrozenString(1);
char *b = pool.allocFrozenString(1);
REQUIRE(a != b);
REQUIRE(a->value != b->value);
}
SECTION("Returns a StringSlot of the right size") {
StringSlot *s = pool.allocFrozenString(12);
REQUIRE(s->size == 12);
}
SECTION("Returns NULL when full") {
pool.allocFrozenString(longestString);
void *p = pool.allocFrozenString(1);
REQUIRE(0 == p);
void *p1 = pool.allocFrozenString(longestString);
REQUIRE(p1 != 0);
void *p2 = pool.allocFrozenString(1);
REQUIRE(p2 == 0);
}
SECTION("Returns NULL when pool is too small") {
@ -46,22 +42,16 @@ TEST_CASE("MemoryPool::allocFrozenString()") {
REQUIRE(0 == pool2.allocFrozenString(2));
}
SECTION("Returns aligned pointers") {
REQUIRE(isAligned(pool.allocFrozenString(1)));
REQUIRE(isAligned(pool.allocFrozenString(1)));
}
SECTION("Returns same address after clear()") {
StringSlot *a = pool.allocFrozenString(1);
void *a = pool.allocFrozenString(1);
pool.clear();
StringSlot *b = pool.allocFrozenString(1);
void *b = pool.allocFrozenString(1);
REQUIRE(a == b);
REQUIRE(a->value == b->value);
}
SECTION("Can use full capacity when fresh") {
StringSlot *a = pool.allocFrozenString(longestString);
void *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
@ -70,7 +60,7 @@ TEST_CASE("MemoryPool::allocFrozenString()") {
pool.allocFrozenString(longestString);
pool.clear();
StringSlot *a = pool.allocFrozenString(longestString);
void *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}

View File

@ -28,11 +28,11 @@ TEST_CASE("MemoryPool::size()") {
}
SECTION("Decreases after freezeString()") {
StringSlot* a = memoryPool.allocExpandableString();
StringSlot a = memoryPool.allocExpandableString();
memoryPool.freezeString(a, 1);
REQUIRE(memoryPool.size() == JSON_STRING_SIZE(1));
StringSlot* b = memoryPool.allocExpandableString();
StringSlot b = memoryPool.allocExpandableString();
memoryPool.freezeString(b, 1);
REQUIRE(memoryPool.size() == 2 * JSON_STRING_SIZE(1));
}