Added BasicJsonDocument::shrinkToFit()

This commit is contained in:
Benoit Blanchon
2019-11-07 15:40:20 +01:00
parent 1b8107094f
commit 062c1c13b5
13 changed files with 270 additions and 19 deletions

View File

@ -5,6 +5,7 @@
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <stddef.h> // size_t
@ -63,6 +64,8 @@ class CollectionData {
size_t nesting() const;
size_t size() const;
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance);
private:
VariantSlot *getSlot(size_t index) const;

View File

@ -160,4 +160,20 @@ inline size_t CollectionData::size() const {
return slotSize(_head);
}
template <typename T>
inline void movePointer(T*& p, ptrdiff_t offset) {
if (!p) return;
p = reinterpret_cast<T*>(
reinterpret_cast<void*>(reinterpret_cast<char*>(p) + offset));
ARDUINOJSON_ASSERT(isAligned(p));
}
inline void CollectionData::movePointers(ptrdiff_t stringDistance,
ptrdiff_t variantDistance) {
movePointer(_head, variantDistance);
movePointer(_tail, variantDistance);
for (VariantSlot* slot = _head; slot; slot = slot->next())
slot->movePointers(stringDistance, variantDistance);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -8,6 +8,8 @@
namespace ARDUINOJSON_NAMESPACE {
// Helper to implement the "base-from-member" idiom
// (we need to store the allocator before constructing JsonDocument)
template <typename TAllocator>
class AllocatorOwner {
protected:
@ -15,12 +17,16 @@ class AllocatorOwner {
AllocatorOwner(const AllocatorOwner& src) : _allocator(src._allocator) {}
AllocatorOwner(TAllocator allocator) : _allocator(allocator) {}
void* allocate(size_t n) {
return _allocator.allocate(n);
void* allocate(size_t size) {
return _allocator.allocate(size);
}
void deallocate(void* p) {
_allocator.deallocate(p);
void deallocate(void* ptr) {
_allocator.deallocate(ptr);
}
void* reallocate(void* ptr, size_t new_size) {
return _allocator.reallocate(ptr, new_size);
}
private:
@ -69,6 +75,20 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
return *this;
}
void shrinkToFit() {
ptrdiff_t bytes_reclaimed = _pool.squash();
if (bytes_reclaimed == 0) return;
void* old_ptr = _pool.buffer();
void* new_ptr = this->reallocate(old_ptr, _pool.capacity());
ptrdiff_t ptr_offset =
static_cast<char*>(new_ptr) - static_cast<char*>(old_ptr);
_pool.movePointers(ptr_offset);
_data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed);
}
private:
MemoryPool allocPool(size_t requiredSize) {
size_t capa = addPadding(requiredSize);

View File

@ -11,12 +11,16 @@
namespace ARDUINOJSON_NAMESPACE {
struct DefaultAllocator {
void* allocate(size_t n) {
return malloc(n);
void* allocate(size_t size) {
return malloc(size);
}
void deallocate(void* p) {
free(p);
void deallocate(void* ptr) {
free(ptr);
}
void* reallocate(void* ptr, size_t new_size) {
return realloc(ptr, new_size);
}
};

View File

@ -301,7 +301,6 @@ class JsonDocument : public Visitable {
_pool = pool;
}
private:
VariantRef getVariant() {
return VariantRef(&_pool, &_data);
}

View File

@ -21,6 +21,12 @@ inline size_t addPadding(size_t bytes) {
return (bytes + mask) & ~mask;
}
template <typename T>
inline T *addPadding(T *p) {
size_t address = addPadding(reinterpret_cast<size_t>(p));
return reinterpret_cast<T *>(address);
}
template <size_t bytes>
struct AddPadding {
static const size_t mask = sizeof(void *) - 1;

View File

@ -10,13 +10,15 @@
#include <ArduinoJson/Polyfills/mpl/max.hpp>
#include <ArduinoJson/Variant/VariantSlot.hpp>
#include <string.h> // memmove
namespace ARDUINOJSON_NAMESPACE {
// _begin _end
// v v
// +-------------+--------------+-----------+
// | strings... | (free) | ...slots |
// +-------------+--------------+-----------+
// _begin _end
// v v
// +-------------+--------------+--------------+
// | strings... | (free) | ...variants |
// +-------------+--------------+--------------+
// ^ ^
// _left _right
@ -101,6 +103,39 @@ class MemoryPool {
return p;
}
// Squash the free space between strings and variants
//
// _begin _end
// v v
// +-------------+--------------+
// | strings... | ...variants |
// +-------------+--------------+
// ^
// _left _right
//
// This funcion is called before a realloc.
ptrdiff_t squash() {
char* new_right = addPadding(_left);
if (new_right >= _right) return 0;
size_t right_size = static_cast<size_t>(_end - _right);
memmove(new_right, _right, right_size);
ptrdiff_t bytes_reclaimed = _right - new_right;
_right = new_right;
_end = new_right + right_size;
return bytes_reclaimed;
}
// Move all pointers together
// This funcion is called after a realloc.
void movePointers(ptrdiff_t offset) {
_begin += offset;
_left += offset;
_right += offset;
_end += offset;
}
private:
StringSlot* allocStringSlot() {
return allocRight<StringSlot>();

View File

@ -16,14 +16,14 @@ namespace ARDUINOJSON_NAMESPACE {
enum {
VALUE_MASK = 0x7F,
OWNERSHIP_BIT = 0x01,
VALUE_IS_OWNED = 0x01,
VALUE_IS_NULL = 0,
VALUE_IS_LINKED_RAW = 0x02,
VALUE_IS_OWNED_RAW = 0x03,
VALUE_IS_LINKED_STRING = 0x04,
VALUE_IS_OWNED_STRING = 0x05,
// CAUTION: no OWNERSHIP_BIT below
// CAUTION: no VALUE_IS_OWNED below
VALUE_IS_BOOLEAN = 0x06,
VALUE_IS_POSITIVE_INTEGER = 0x08,
VALUE_IS_NEGATIVE_INTEGER = 0x0A,

View File

@ -103,7 +103,7 @@ class VariantData {
bool equals(const VariantData &other) const {
// Check that variant have the same type, but ignore string ownership
if ((type() | OWNERSHIP_BIT) != (other.type() | OWNERSHIP_BIT))
if ((type() | VALUE_IS_OWNED) != (other.type() | VALUE_IS_OWNED))
return false;
switch (type()) {
@ -352,6 +352,12 @@ class VariantData {
return _content.asCollection.add(key, pool);
}
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) {
if (_flags & VALUE_IS_OWNED) _content.asString += stringDistance;
if (_flags & COLLECTION_MASK)
_content.asCollection.movePointers(stringDistance, variantDistance);
}
private:
uint8_t type() const {
return _flags & VALUE_MASK;

View File

@ -14,8 +14,6 @@ namespace ARDUINOJSON_NAMESPACE {
typedef conditional<sizeof(void*) <= 2, int8_t, int16_t>::type VariantSlotDiff;
class VairantData;
class VariantSlot {
// CAUTION: same layout as VariantData
// we cannot use composition because it adds padding
@ -93,6 +91,13 @@ class VariantSlot {
_flags = 0;
_key = 0;
}
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) {
if (_flags & KEY_IS_OWNED) _key += stringDistance;
if (_flags & VALUE_IS_OWNED) _content.asString += stringDistance;
if (_flags & COLLECTION_MASK)
_content.asCollection.movePointers(stringDistance, variantDistance);
}
};
} // namespace ARDUINOJSON_NAMESPACE