// ArduinoJson - https://arduinojson.org // Copyright © 2014-2022, Benoit BLANCHON // MIT License #pragma once #include namespace ARDUINOJSON_NAMESPACE { // Helper to implement the "base-from-member" idiom // (we need to store the allocator before constructing JsonDocument) template class AllocatorOwner { public: AllocatorOwner() {} AllocatorOwner(TAllocator a) : _allocator(a) {} void* allocate(size_t size) { return _allocator.allocate(size); } void deallocate(void* ptr) { if (ptr) _allocator.deallocate(ptr); } void* reallocate(void* ptr, size_t new_size) { return _allocator.reallocate(ptr, new_size); } TAllocator& allocator() { return _allocator; } private: TAllocator _allocator; }; // A JsonDocument that uses the provided allocator to allocate its memory pool. // https://arduinojson.org/v6/api/basicjsondocument/ template class BasicJsonDocument : AllocatorOwner, public JsonDocument { public: explicit BasicJsonDocument(size_t capa, TAllocator alloc = TAllocator()) : AllocatorOwner(alloc), JsonDocument(allocPool(capa)) {} // Copy-constructor BasicJsonDocument(const BasicJsonDocument& src) : AllocatorOwner(src), JsonDocument() { copyAssignFrom(src); } // Move-constructor BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner(src) { moveAssignFrom(src); } BasicJsonDocument(const JsonDocument& src) { copyAssignFrom(src); } // Construct from variant, array, or object template BasicJsonDocument( const T& src, typename enable_if< is_same::value || is_same::value || is_same::value || is_same::value || is_same::value || is_same::value>::type* = 0) : JsonDocument(allocPool(src.memoryUsage())) { set(src); } // disambiguate BasicJsonDocument(JsonVariant src) : JsonDocument(allocPool(src.memoryUsage())) { set(src); } ~BasicJsonDocument() { freePool(); } BasicJsonDocument& operator=(const BasicJsonDocument& src) { copyAssignFrom(src); return *this; } BasicJsonDocument& operator=(BasicJsonDocument&& src) { moveAssignFrom(src); return *this; } template BasicJsonDocument& operator=(const T& src) { size_t requiredSize = src.memoryUsage(); if (requiredSize > capacity()) reallocPool(requiredSize); set(src); return *this; } // Reduces the capacity of the memory pool to match the current usage. // https://arduinojson.org/v6/api/basicjsondocument/shrinktofit/ 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(new_ptr) - static_cast(old_ptr); _pool.movePointers(ptr_offset); _data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed); } // Reclaims the memory leaked when removing and replacing values. // https://arduinojson.org/v6/api/jsondocument/garbagecollect/ bool garbageCollect() { // make a temporary clone and move assign BasicJsonDocument tmp(*this); if (!tmp.capacity()) return false; tmp.set(*this); moveAssignFrom(tmp); return true; } using AllocatorOwner::allocator; private: MemoryPool allocPool(size_t requiredSize) { size_t capa = addPadding(requiredSize); return MemoryPool(reinterpret_cast(this->allocate(capa)), capa); } void reallocPool(size_t requiredSize) { size_t capa = addPadding(requiredSize); if (capa == _pool.capacity()) return; freePool(); replacePool(allocPool(addPadding(requiredSize))); } void freePool() { this->deallocate(getPool()->buffer()); } void copyAssignFrom(const JsonDocument& src) { reallocPool(src.capacity()); set(src); } void moveAssignFrom(BasicJsonDocument& src) { freePool(); _data = src._data; _pool = src._pool; src._data.setNull(); src._pool = MemoryPool(0, 0); } }; } // namespace ARDUINOJSON_NAMESPACE