// ArduinoJson - arduinojson.org // Copyright Benoit Blanchon 2014-2018 // MIT License #pragma once #include "JsonBufferBase.hpp" #include #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnon-virtual-dtor" #elif defined(__GNUC__) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) #pragma GCC diagnostic push #endif #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif namespace ArduinoJson { namespace Internals { class DefaultAllocator { public: void* allocate(size_t size) { return malloc(size); } void deallocate(void* pointer) { free(pointer); } }; template class DynamicJsonBufferBase : public JsonBufferBase > { struct Block; struct EmptyBlock { Block* next; size_t capacity; size_t size; }; struct Block : EmptyBlock { uint8_t data[1]; }; public: enum { EmptyBlockSize = sizeof(EmptyBlock) }; DynamicJsonBufferBase(size_t initialSize = 256) : _head(NULL), _nextBlockCapacity(initialSize) {} ~DynamicJsonBufferBase() { clear(); } // Gets the number of bytes occupied in the buffer size_t size() const { size_t total = 0; for (const Block* b = _head; b; b = b->next) total += b->size; return total; } // Allocates the specified amount of bytes in the buffer virtual void* alloc(size_t bytes) { alignNextAlloc(); return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); } // Resets the buffer. // USE WITH CAUTION: this invalidates all previously allocated data void clear() { Block* currentBlock = _head; while (currentBlock != NULL) { _nextBlockCapacity = currentBlock->capacity; Block* nextBlock = currentBlock->next; _allocator.deallocate(currentBlock); currentBlock = nextBlock; } _head = 0; } class String { public: String(DynamicJsonBufferBase* parent) : _parent(parent), _start(NULL), _length(0) {} void append(char c) { if (_parent->canAllocInHead(1)) { char* end = static_cast(_parent->allocInHead(1)); *end = c; if (_length == 0) _start = end; } else { char* newStart = static_cast(_parent->allocInNewBlock(_length + 1)); if (_start && newStart) memcpy(newStart, _start, _length); if (newStart) newStart[_length] = c; _start = newStart; } _length++; } const char* c_str() { append(0); return _start; } private: DynamicJsonBufferBase* _parent; char* _start; size_t _length; }; String startString() { return String(this); } private: void alignNextAlloc() { if (_head) _head->size = this->round_size_up(_head->size); } bool canAllocInHead(size_t bytes) const { return _head != NULL && _head->size + bytes <= _head->capacity; } void* allocInHead(size_t bytes) { void* p = _head->data + _head->size; _head->size += bytes; return p; } void* allocInNewBlock(size_t bytes) { size_t capacity = _nextBlockCapacity; if (bytes > capacity) capacity = bytes; if (!addNewBlock(capacity)) return NULL; _nextBlockCapacity *= 2; return allocInHead(bytes); } bool addNewBlock(size_t capacity) { size_t bytes = EmptyBlockSize + capacity; Block* block = static_cast(_allocator.allocate(bytes)); if (block == NULL) return false; block->capacity = capacity; block->size = 0; block->next = _head; _head = block; return true; } TAllocator _allocator; Block* _head; size_t _nextBlockCapacity; }; } #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) #pragma GCC diagnostic pop #endif #endif // Implements a JsonBuffer with dynamic memory allocation. // You are strongly encouraged to consider using StaticJsonBuffer which is much // more suitable for embedded systems. typedef Internals::DynamicJsonBufferBase DynamicJsonBuffer; }