diff --git a/CHANGELOG.md b/CHANGELOG.md index 193afe46..22ae6974 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ HEAD * Made `JsonBuffer` non-copyable (PR #524 by @luisrayas3) * Added `StaticJsonBuffer::clear()` +* Added `DynamicJsonBuffer::clear()` v5.10.1 ------- diff --git a/src/ArduinoJson/DynamicJsonBuffer.hpp b/src/ArduinoJson/DynamicJsonBuffer.hpp index bda4a58e..77da72cc 100644 --- a/src/ArduinoJson/DynamicJsonBuffer.hpp +++ b/src/ArduinoJson/DynamicJsonBuffer.hpp @@ -46,30 +46,35 @@ class DynamicJsonBufferBase }; public: + enum { EmptyBlockSize = sizeof(EmptyBlock) }; + DynamicJsonBufferBase(size_t initialSize = 256) : _head(NULL), _nextBlockCapacity(initialSize) {} ~DynamicJsonBufferBase() { - Block* currentBlock = _head; - - while (currentBlock != NULL) { - Block* nextBlock = currentBlock->next; - _allocator.deallocate(currentBlock); - currentBlock = nextBlock; - } + freeAllBlocks(); } + // 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() { + freeAllBlocks(); + _head = 0; + } + class String { public: String(DynamicJsonBufferBase* parent) @@ -129,7 +134,7 @@ class DynamicJsonBufferBase } bool addNewBlock(size_t capacity) { - size_t bytes = sizeof(EmptyBlock) + capacity; + size_t bytes = EmptyBlockSize + capacity; Block* block = static_cast(_allocator.allocate(bytes)); if (block == NULL) return false; block->capacity = capacity; @@ -139,6 +144,16 @@ class DynamicJsonBufferBase return true; } + void freeAllBlocks() { + Block* currentBlock = _head; + + while (currentBlock != NULL) { + Block* nextBlock = currentBlock->next; + _allocator.deallocate(currentBlock); + currentBlock = nextBlock; + } + } + TAllocator _allocator; Block* _head; size_t _nextBlockCapacity; diff --git a/src/ArduinoJson/StaticJsonBuffer.hpp b/src/ArduinoJson/StaticJsonBuffer.hpp index 0c31aad4..7d848524 100644 --- a/src/ArduinoJson/StaticJsonBuffer.hpp +++ b/src/ArduinoJson/StaticJsonBuffer.hpp @@ -71,7 +71,7 @@ class StaticJsonBufferBase : public JsonBufferBase { return doAlloc(bytes); } - // Resets the size to zero. + // Resets the buffer. // USE WITH CAUTION: this invalidates all previously allocated data void clear() { _size = 0; diff --git a/test/DynamicJsonBuffer/CMakeLists.txt b/test/DynamicJsonBuffer/CMakeLists.txt index fa270e97..665fc82b 100644 --- a/test/DynamicJsonBuffer/CMakeLists.txt +++ b/test/DynamicJsonBuffer/CMakeLists.txt @@ -8,10 +8,11 @@ add_executable(DynamicJsonBufferTests alloc.cpp createArray.cpp - no_memory.cpp createObject.cpp - strdup.cpp + no_memory.cpp + size.cpp startString.cpp + strdup.cpp ) target_link_libraries(DynamicJsonBufferTests catch) diff --git a/test/DynamicJsonBuffer/alloc.cpp b/test/DynamicJsonBuffer/alloc.cpp index 27871751..a821e386 100644 --- a/test/DynamicJsonBuffer/alloc.cpp +++ b/test/DynamicJsonBuffer/alloc.cpp @@ -7,6 +7,7 @@ #include #include +#include static bool isAligned(void* ptr) { const size_t mask = sizeof(void*) - 1; @@ -14,26 +15,58 @@ static bool isAligned(void* ptr) { return (addr & mask) == 0; } +std::stringstream allocatorLog; + +struct SpyingAllocator : DefaultAllocator { + void* allocate(size_t n) { + allocatorLog << "A" << (n - DynamicJsonBuffer::EmptyBlockSize); + return DefaultAllocator::allocate(n); + } + void deallocate(void* p) { + allocatorLog << "F"; + return DefaultAllocator::deallocate(p); + } +}; + TEST_CASE("DynamicJsonBuffer::alloc()") { - DynamicJsonBuffer buffer; - - SECTION("InitialSizeIsZero") { - REQUIRE(0 == buffer.size()); - } - - SECTION("SizeIncreasesAfterAlloc") { - buffer.alloc(1); - REQUIRE(1U <= buffer.size()); - buffer.alloc(1); - REQUIRE(2U <= buffer.size()); - } - - SECTION("ReturnDifferentPointer") { + SECTION("Returns different pointers") { + DynamicJsonBuffer buffer; void* p1 = buffer.alloc(1); void* p2 = buffer.alloc(2); REQUIRE(p1 != p2); } + SECTION("Doubles allocation size when full") { + allocatorLog.str(""); + { + DynamicJsonBufferBase buffer(1); + buffer.alloc(1); + buffer.alloc(1); + } + REQUIRE(allocatorLog.str() == "A1A2FF"); + } + + SECTION("Keeps increasing allocation size after clear") { + allocatorLog.str(""); + { + DynamicJsonBufferBase buffer(1); + buffer.alloc(1); + buffer.alloc(1); + buffer.clear(); + buffer.alloc(1); + } + REQUIRE(allocatorLog.str() == "A1A2FFA4F"); + } + + SECTION("Makes a big allocation when needed") { + allocatorLog.str(""); + { + DynamicJsonBufferBase buffer(1); + buffer.alloc(42); + } + REQUIRE(allocatorLog.str() == "A42F"); + } + SECTION("Alignment") { // make room for two but not three DynamicJsonBuffer tinyBuf(2 * sizeof(void*) + 1); diff --git a/test/DynamicJsonBuffer/size.cpp b/test/DynamicJsonBuffer/size.cpp new file mode 100644 index 00000000..f9952f51 --- /dev/null +++ b/test/DynamicJsonBuffer/size.cpp @@ -0,0 +1,30 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://bblanchon.github.io/ArduinoJson/ +// If you like this project, please add a star! + +#include +#include + +TEST_CASE("DynamicJsonBuffer::size()") { + DynamicJsonBuffer buffer; + + SECTION("Initial size is 0") { + REQUIRE(0 == buffer.size()); + } + + SECTION("Increases after alloc()") { + buffer.alloc(1); + REQUIRE(1U <= buffer.size()); + buffer.alloc(1); + REQUIRE(2U <= buffer.size()); + } + + SECTION("Goes back to 0 after clear()") { + buffer.alloc(1); + buffer.clear(); + REQUIRE(0 == buffer.size()); + } +}