diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..0f553e88 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html + +BasedOnStyle: Google \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..526c8a38 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1246742b..deba5c1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Arduino JSON: change log ======================== +v4.6 +---- + +* Fixed segmentation fault in `DynamicJsonBuffer` when memory allocation fails (issue #92) + v4.5 ---- diff --git a/include/ArduinoJson/DynamicJsonBuffer.hpp b/include/ArduinoJson/DynamicJsonBuffer.hpp index 667c52d9..a57f8dff 100644 --- a/include/ArduinoJson/DynamicJsonBuffer.hpp +++ b/include/ArduinoJson/DynamicJsonBuffer.hpp @@ -6,41 +6,12 @@ #pragma once -#include "JsonBuffer.hpp" - -#include +#include "Internals/BlockJsonBuffer.hpp" 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(); - ~DynamicJsonBuffer(); - - size_t size() const; - - protected: - virtual void* alloc(size_t bytes); - - private: - typedef Internals::DynamicJsonBufferBlock Block; - - static const size_t FIRST_BLOCK_CAPACITY = 32; - - static Block* createBlock(size_t capacity); - - inline bool canAllocInHead(size_t bytes) const; - inline void* allocInHead(size_t bytes); - inline void addNewBlock(); - - Block* _head; -}; +typedef Internals::BlockJsonBuffer + DynamicJsonBuffer; } diff --git a/include/ArduinoJson/Internals/BlockJsonBuffer.hpp b/include/ArduinoJson/Internals/BlockJsonBuffer.hpp new file mode 100644 index 00000000..a156f659 --- /dev/null +++ b/include/ArduinoJson/Internals/BlockJsonBuffer.hpp @@ -0,0 +1,93 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#pragma once + +#include "../JsonBuffer.hpp" + +#include + +namespace ArduinoJson { +namespace Internals { +class DefaultAllocator { + public: + void* allocate(size_t size) { return malloc(size); } + void deallocate(void* pointer) { free(pointer); } +}; + +template +class BlockJsonBuffer : public JsonBuffer { + struct Block; + struct EmptyBlock { + Block* next; + size_t capacity; + size_t size; + }; + struct Block : EmptyBlock { + uint8_t data[1]; + }; + + public: + BlockJsonBuffer() : _head(NULL) {} + + ~BlockJsonBuffer() { + Block* currentBlock = _head; + + while (currentBlock != NULL) { + Block* nextBlock = currentBlock->next; + _allocator.deallocate(currentBlock); + currentBlock = nextBlock; + } + } + + size_t size() const { + size_t total = 0; + for (const Block* b = _head; b; b = b->next) total += b->size; + return total; + } + + protected: + virtual void* alloc(size_t bytes) { + return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes); + } + + private: + static const size_t FIRST_BLOCK_CAPACITY = 32; + + 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 = FIRST_BLOCK_CAPACITY; + if (_head != NULL) capacity = _head->capacity * 2; + if (bytes > capacity) capacity = bytes; + if (!addNewBlock(capacity)) return NULL; + return allocInHead(bytes); + } + + bool addNewBlock(size_t capacity) { + size_t size = sizeof(EmptyBlock) + capacity; + Block* block = static_cast(_allocator.allocate(size)); + if (block == NULL) return false; + block->capacity = capacity; + block->size = 0; + block->next = _head; + _head = block; + return true; + } + + Block* _head; + TAllocator _allocator; +}; +} +} diff --git a/scripts/build-arduino-package.sh b/scripts/build-arduino-package.sh index f91481a4..dd05fb1c 100755 --- a/scripts/build-arduino-package.sh +++ b/scripts/build-arduino-package.sh @@ -1,23 +1,22 @@ -#!/bin/bash - -ZIP="C:\Program Files\7-Zip\7z.exe" -TAG=$(git describe) -OUTPUT="ArduinoJson-$TAG.zip" - -cd ../.. - -# remove existing file -rm -f $OUTPUT - -# create zip -"$ZIP" a $OUTPUT \ - ArduinoJson/CHANGELOG.md \ - ArduinoJson/examples \ - ArduinoJson/include \ - ArduinoJson/keywords.txt \ - ArduinoJson/LICENSE.md \ - ArduinoJson/README.md \ - ArduinoJson/src \ - ArduinoJson/ArduinoJson.h \ - ArduinoJson/ArduinoJson.cpp \ +#!/bin/bash + +TAG=$(git describe) +OUTPUT="ArduinoJson-$TAG.zip" + +cd ../.. + +# remove existing file +rm -f $OUTPUT + +# create zip +7z a $OUTPUT \ + ArduinoJson/CHANGELOG.md \ + ArduinoJson/examples \ + ArduinoJson/include \ + ArduinoJson/keywords.txt \ + ArduinoJson/LICENSE.md \ + ArduinoJson/README.md \ + ArduinoJson/src \ + ArduinoJson/ArduinoJson.h \ + ArduinoJson/ArduinoJson.cpp \ -x!ArduinoJson/src/CMakeLists.txt \ No newline at end of file diff --git a/src/DynamicJsonBuffer.cpp b/src/DynamicJsonBuffer.cpp deleted file mode 100644 index f0fb9393..00000000 --- a/src/DynamicJsonBuffer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// 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(malloc(blkSize)); - block->capacity = capacity; - block->size = 0; - block->next = NULL; - return block; -} diff --git a/test/DynamicJsonBuffer_NoMemory_Tests.cpp b/test/DynamicJsonBuffer_NoMemory_Tests.cpp new file mode 100644 index 00000000..cecfc5a5 --- /dev/null +++ b/test/DynamicJsonBuffer_NoMemory_Tests.cpp @@ -0,0 +1,37 @@ +// Copyright Benoit Blanchon 2014-2015 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson + +#include +#include + +class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test { + class NoMemoryAllocator { + public: + void* allocate(size_t) { return NULL; } + void deallocate(void*) {} + }; + + protected: + Internals::BlockJsonBuffer _jsonBuffer; +}; + +TEST_F(DynamicJsonBuffer_NoMemory_Tests, CreateArray) { + ASSERT_FALSE(_jsonBuffer.createArray().success()); +} + +TEST_F(DynamicJsonBuffer_NoMemory_Tests, CreateObject) { + ASSERT_FALSE(_jsonBuffer.createObject().success()); +} + +TEST_F(DynamicJsonBuffer_NoMemory_Tests, ParseArray) { + char json[] = "[]"; + ASSERT_FALSE(_jsonBuffer.parseArray(json).success()); +} + +TEST_F(DynamicJsonBuffer_NoMemory_Tests, ParseObject) { + char json[] = "{}"; + ASSERT_FALSE(_jsonBuffer.parseObject(json).success()); +}