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. | // This file is here to help the Arduino IDE find the other files. | ||||||
|  |  | ||||||
| #include "src/Arduino/Print.cpp" | #include "src/Arduino/Print.cpp" | ||||||
|  | #include "src/DynamicJsonBuffer.cpp" | ||||||
| #include "src/Internals/IndentedPrint.cpp" | #include "src/Internals/IndentedPrint.cpp" | ||||||
| #include "src/Internals/JsonParser.cpp" | #include "src/Internals/JsonParser.cpp" | ||||||
| #include "src/Internals/List.cpp" | #include "src/Internals/List.cpp" | ||||||
|   | |||||||
| @@ -1,6 +1,11 @@ | |||||||
| Arduino JSON: change log | Arduino JSON: change log | ||||||
| ======================== | ======================== | ||||||
|  |  | ||||||
|  | HEAD | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | * Fixed stack-overflow in `DynamicJsonBuffer` when parsing huge JSON files (issue #65) | ||||||
|  |  | ||||||
| v4.2 | v4.2 | ||||||
| ---- | ---- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,60 +8,39 @@ | |||||||
|  |  | ||||||
| #include "JsonBuffer.hpp" | #include "JsonBuffer.hpp" | ||||||
|  |  | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
| namespace ArduinoJson { | namespace ArduinoJson { | ||||||
|  |  | ||||||
|  | // Forward declaration | ||||||
|  | namespace Internals { | ||||||
|  | struct DynamicJsonBufferBlock; | ||||||
|  | } | ||||||
|  |  | ||||||
| // Implements a JsonBuffer with dynamic memory allocation. | // Implements a JsonBuffer with dynamic memory allocation. | ||||||
| // You are strongly encouraged to consider using StaticJsonBuffer which is much | // You are strongly encouraged to consider using StaticJsonBuffer which is much | ||||||
| // more suitable for embedded systems. | // more suitable for embedded systems. | ||||||
| class DynamicJsonBuffer : public JsonBuffer { | class DynamicJsonBuffer : public JsonBuffer { | ||||||
|  public: |  public: | ||||||
|   DynamicJsonBuffer() : _next(NULL), _size(0) {} |   DynamicJsonBuffer(); | ||||||
|  |   ~DynamicJsonBuffer(); | ||||||
|  |  | ||||||
|   ~DynamicJsonBuffer() { delete _next; } |   size_t size() const; | ||||||
|  |  | ||||||
|   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; |  | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   virtual void* alloc(size_t bytes) { |   virtual void* alloc(size_t bytes); | ||||||
|     if (canAllocInThisBlock(bytes)) |  | ||||||
|       return allocInThisBlock(bytes); |  | ||||||
|     else if (canAllocInOtherBlocks(bytes)) |  | ||||||
|       return allocInOtherBlocks(bytes); |  | ||||||
|     else |  | ||||||
|       return NULL; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   bool canAllocInThisBlock(size_t bytes) const { |   typedef Internals::DynamicJsonBufferBlock Block; | ||||||
|     return _size + bytes <= BLOCK_CAPACITY; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void* allocInThisBlock(size_t bytes) { |   static const size_t FIRST_BLOCK_CAPACITY = 32; | ||||||
|     void* p = _buffer + _size; |  | ||||||
|     _size += bytes; |  | ||||||
|     return p; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   bool canAllocInOtherBlocks(size_t bytes) const { |   static Block* createBlock(size_t capacity); | ||||||
|     // by design a DynamicJsonBuffer can't alloc a block bigger than |  | ||||||
|     // BLOCK_CAPACITY |  | ||||||
|     return bytes <= BLOCK_CAPACITY; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void* allocInOtherBlocks(size_t bytes) { |   inline bool canAllocInHead(size_t bytes) const; | ||||||
|     if (!_next) { |   inline void* allocInHead(size_t bytes); | ||||||
|       _next = new DynamicJsonBuffer(); |   inline void addNewBlock(); | ||||||
|       if (!_next) return NULL; |  | ||||||
|     } |  | ||||||
|     return _next->alloc(bytes); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   DynamicJsonBuffer* _next; |   Block* _head; | ||||||
|   size_t _size; |  | ||||||
|   uint8_t _buffer[BLOCK_CAPACITY]; |  | ||||||
| }; | }; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										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()); |   ASSERT_EQ(0, buffer.size()); | ||||||
| } | } | ||||||
|  |  | ||||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, InitialBlockCountIsOne) { |  | ||||||
|   ASSERT_EQ(1, buffer.blockCount()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) { | TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) { | ||||||
|   buffer.alloc(1); |   buffer.alloc(1); | ||||||
|   ASSERT_EQ(1, buffer.size()); |   ASSERT_EQ(1, buffer.size()); | ||||||
|   buffer.alloc(1); |   buffer.alloc(1); | ||||||
|   ASSERT_EQ(2, buffer.size()); |   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) { | TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user