mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 16:14:11 +01:00 
			
		
		
		
	Fixed issue #65
This commit is contained in:
		| @@ -9,6 +9,7 @@ | ||||
| // This file is here to help the Arduino IDE find the other files. | ||||
|  | ||||
| #include "src/Arduino/Print.cpp" | ||||
| #include "src/DynamicJsonBuffer.cpp" | ||||
| #include "src/Internals/IndentedPrint.cpp" | ||||
| #include "src/Internals/JsonParser.cpp" | ||||
| #include "src/Internals/List.cpp" | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| Arduino JSON: change log | ||||
| ======================== | ||||
|  | ||||
| HEAD | ||||
| ---- | ||||
|  | ||||
| * Fixed stack-overflow in `DynamicJsonBuffer` when parsing huge JSON files (issue #65) | ||||
|  | ||||
| v4.2 | ||||
| ---- | ||||
|  | ||||
|   | ||||
| @@ -8,60 +8,39 @@ | ||||
|  | ||||
| #include "JsonBuffer.hpp" | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| namespace ArduinoJson { | ||||
|  | ||||
| // Forward declaration | ||||
| namespace Internals { | ||||
| struct DynamicJsonBufferBlock; | ||||
| } | ||||
|  | ||||
| // Implements a JsonBuffer with dynamic memory allocation. | ||||
| // You are strongly encouraged to consider using StaticJsonBuffer which is much | ||||
| // more suitable for embedded systems. | ||||
| class DynamicJsonBuffer : public JsonBuffer { | ||||
|  public: | ||||
|   DynamicJsonBuffer() : _next(NULL), _size(0) {} | ||||
|   DynamicJsonBuffer(); | ||||
|   ~DynamicJsonBuffer(); | ||||
|  | ||||
|   ~DynamicJsonBuffer() { delete _next; } | ||||
|  | ||||
|   size_t size() const { return _size + (_next ? _next->size() : 0); } | ||||
|  | ||||
|   size_t blockCount() const { return 1 + (_next ? _next->blockCount() : 0); } | ||||
|  | ||||
|   static const size_t BLOCK_CAPACITY = 32; | ||||
|   size_t size() const; | ||||
|  | ||||
|  protected: | ||||
|   virtual void* alloc(size_t bytes) { | ||||
|     if (canAllocInThisBlock(bytes)) | ||||
|       return allocInThisBlock(bytes); | ||||
|     else if (canAllocInOtherBlocks(bytes)) | ||||
|       return allocInOtherBlocks(bytes); | ||||
|     else | ||||
|       return NULL; | ||||
|   } | ||||
|   virtual void* alloc(size_t bytes); | ||||
|  | ||||
|  private: | ||||
|   bool canAllocInThisBlock(size_t bytes) const { | ||||
|     return _size + bytes <= BLOCK_CAPACITY; | ||||
|   } | ||||
|   typedef Internals::DynamicJsonBufferBlock Block; | ||||
|  | ||||
|   void* allocInThisBlock(size_t bytes) { | ||||
|     void* p = _buffer + _size; | ||||
|     _size += bytes; | ||||
|     return p; | ||||
|   } | ||||
|   static const size_t FIRST_BLOCK_CAPACITY = 32; | ||||
|  | ||||
|   bool canAllocInOtherBlocks(size_t bytes) const { | ||||
|     // by design a DynamicJsonBuffer can't alloc a block bigger than | ||||
|     // BLOCK_CAPACITY | ||||
|     return bytes <= BLOCK_CAPACITY; | ||||
|   } | ||||
|   static Block* createBlock(size_t capacity); | ||||
|  | ||||
|   void* allocInOtherBlocks(size_t bytes) { | ||||
|     if (!_next) { | ||||
|       _next = new DynamicJsonBuffer(); | ||||
|       if (!_next) return NULL; | ||||
|     } | ||||
|     return _next->alloc(bytes); | ||||
|   } | ||||
|   inline bool canAllocInHead(size_t bytes) const; | ||||
|   inline void* allocInHead(size_t bytes); | ||||
|   inline void addNewBlock(); | ||||
|  | ||||
|   DynamicJsonBuffer* _next; | ||||
|   size_t _size; | ||||
|   uint8_t _buffer[BLOCK_CAPACITY]; | ||||
|   Block* _head; | ||||
| }; | ||||
| } | ||||
|   | ||||
							
								
								
									
										79
									
								
								src/DynamicJsonBuffer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/DynamicJsonBuffer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // Copyright Benoit Blanchon 2014-2015 | ||||
| // MIT License | ||||
| // | ||||
| // Arduino JSON library | ||||
| // https://github.com/bblanchon/ArduinoJson | ||||
|  | ||||
| #include "../include/ArduinoJson/DynamicJsonBuffer.hpp" | ||||
|  | ||||
| namespace ArduinoJson { | ||||
| namespace Internals { | ||||
| struct DynamicJsonBufferBlockWithoutData { | ||||
|   DynamicJsonBufferBlock* next; | ||||
|   size_t capacity; | ||||
|   size_t size; | ||||
| }; | ||||
|  | ||||
|  | ||||
| struct DynamicJsonBufferBlock : DynamicJsonBufferBlockWithoutData { | ||||
|   uint8_t data[1]; | ||||
| }; | ||||
| } | ||||
| } | ||||
|  | ||||
| using namespace ArduinoJson; | ||||
| using namespace ArduinoJson::Internals; | ||||
|  | ||||
| DynamicJsonBuffer::DynamicJsonBuffer() { | ||||
|   _head = createBlock(FIRST_BLOCK_CAPACITY); | ||||
| } | ||||
|  | ||||
| DynamicJsonBuffer::~DynamicJsonBuffer() { | ||||
|   Block* currentBlock = _head; | ||||
|  | ||||
|   while (currentBlock != NULL) { | ||||
|     Block* nextBlock = currentBlock->next; | ||||
|     free(currentBlock); | ||||
|     currentBlock = nextBlock; | ||||
|   } | ||||
| } | ||||
|  | ||||
| size_t DynamicJsonBuffer::size() const { | ||||
|   size_t total = 0; | ||||
|  | ||||
|   for (const Block* b = _head; b != NULL; b = b->next) { | ||||
|     total += b->size; | ||||
|   } | ||||
|  | ||||
|   return total; | ||||
| } | ||||
|  | ||||
| void* DynamicJsonBuffer::alloc(size_t bytes) { | ||||
|   if (!canAllocInHead(bytes)) addNewBlock(); | ||||
|   return allocInHead(bytes); | ||||
| } | ||||
|  | ||||
| bool DynamicJsonBuffer::canAllocInHead(size_t bytes) const { | ||||
|   return _head->size + bytes <= _head->capacity; | ||||
| } | ||||
|  | ||||
| void* DynamicJsonBuffer::allocInHead(size_t bytes) { | ||||
|   void* p = _head->data + _head->size; | ||||
|   _head->size += bytes; | ||||
|   return p; | ||||
| } | ||||
|  | ||||
| void DynamicJsonBuffer::addNewBlock() { | ||||
|   Block* block = createBlock(_head->capacity * 2); | ||||
|   block->next = _head; | ||||
|   _head = block; | ||||
| } | ||||
|  | ||||
| DynamicJsonBuffer::Block* DynamicJsonBuffer::createBlock(size_t capacity) { | ||||
|   size_t blkSize = sizeof(DynamicJsonBufferBlockWithoutData) + capacity; | ||||
|   Block* block = static_cast<Block*>(malloc(blkSize)); | ||||
|   block->capacity = capacity; | ||||
|   block->size = 0; | ||||
|   block->next = NULL; | ||||
|   return block; | ||||
| } | ||||
| @@ -20,40 +20,11 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, InitialSizeIsZero) { | ||||
|   ASSERT_EQ(0, buffer.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, InitialBlockCountIsOne) { | ||||
|   ASSERT_EQ(1, buffer.blockCount()); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) { | ||||
|   buffer.alloc(1); | ||||
|   ASSERT_EQ(1, buffer.size()); | ||||
|   buffer.alloc(1); | ||||
|   ASSERT_EQ(2, buffer.size()); | ||||
|   buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY); | ||||
|   ASSERT_EQ(2 + DynamicJsonBuffer::BLOCK_CAPACITY, buffer.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, BlockCountDoesntChangeWhenNotFull) { | ||||
|   buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY); | ||||
|   ASSERT_EQ(1, buffer.blockCount()); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, BlockCountChangesWhenFull) { | ||||
|   buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY); | ||||
|   buffer.alloc(1); | ||||
|   ASSERT_EQ(2, buffer.blockCount()); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, CanAllocLessThanBlockCapacity) { | ||||
|   void* p1 = buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY); | ||||
|   ASSERT_FALSE(p1 == NULL); | ||||
|   void* p2 = buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY); | ||||
|   ASSERT_FALSE(p2 == NULL); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, CantAllocMoreThanBlockCapacity) { | ||||
|   void* p = buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY + 1); | ||||
|   ASSERT_TRUE(p == NULL); | ||||
| } | ||||
|  | ||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user