forked from bblanchon/ArduinoJson
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