Compare commits

...

8 Commits

16 changed files with 125 additions and 39 deletions

View File

@ -1,6 +1,16 @@
ArduinoJson: change log ArduinoJson: change log
======================= =======================
v5.0.2
------
* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the
`StaticJsonBuffer` is too small to hold a copy of the string
* Fixed Clang warning "register specifier is deprecated" (issue #102)
* Fixed GCC warning "declaration shadows a member" (issue #103)
* Fixed memory alignment, which made ESP8266 crash (issue #104)
* Fixed compilation on Visual Studio 2010 and 2012 (issue #107)
v5.0.1 v5.0.1
------ ------

View File

@ -1,11 +1,11 @@
Arduino JSON library Arduino JSON library
==================== ====================
[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master) [![Build status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/master?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/master) [![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master)
*An elegant and efficient JSON library for embedded systems.* *An elegant and efficient JSON library for embedded systems.*
It's design to have the most intuitive API, the smallest footprint and works without any allocation on the heap (no malloc). It's designed to have the most intuitive API, the smallest footprint and works without any allocation on the heap (no malloc).
It has been written with Arduino in mind, but it isn't linked to Arduino libraries so you can use this library in any other C++ project. It has been written with Arduino in mind, but it isn't linked to Arduino libraries so you can use this library in any other C++ project.

View File

@ -63,7 +63,7 @@ class BlockJsonBuffer : public JsonBuffer {
void* allocInHead(size_t bytes) { void* allocInHead(size_t bytes) {
void* p = _head->data + _head->size; void* p = _head->data + _head->size;
_head->size += bytes; _head->size += round_size_up(bytes);
return p; return p;
} }
@ -76,8 +76,8 @@ class BlockJsonBuffer : public JsonBuffer {
} }
bool addNewBlock(size_t capacity) { bool addNewBlock(size_t capacity) {
size_t size = sizeof(EmptyBlock) + capacity; size_t bytes = sizeof(EmptyBlock) + capacity;
Block* block = static_cast<Block*>(_allocator.allocate(size)); Block* block = static_cast<Block*>(_allocator.allocate(bytes));
if (block == NULL) return false; if (block == NULL) return false;
block->capacity = capacity; block->capacity = capacity;
block->size = 0; block->size = 0;

View File

@ -19,7 +19,7 @@ class JsonParser {
public: public:
JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit) JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit)
: _buffer(buffer), : _buffer(buffer),
_readPtr(json), _readPtr(json ? json : ""),
_writePtr(json), _writePtr(json),
_nestingLimit(nestingLimit) {} _nestingLimit(nestingLimit) {}

View File

@ -59,9 +59,15 @@ class JsonBuffer {
// allocation fails. // allocation fails.
JsonArray &parseArray(char *json, uint8_t nestingLimit = DEFAULT_LIMIT); JsonArray &parseArray(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
// Same with a const char*.
// With this overload, the JsonBuffer will make a copy of the string
JsonArray &parseArray(const char *json, uint8_t nesting = DEFAULT_LIMIT) {
return parseArray(strdup(json), nesting);
}
// Same as above with a String class // Same as above with a String class
JsonArray &parseArray(const String &json, uint8_t nesting = DEFAULT_LIMIT) { JsonArray &parseArray(const String &json, uint8_t nesting = DEFAULT_LIMIT) {
return parseArray(strdup(json), nesting); return parseArray(json.c_str(), nesting);
} }
// Allocates and populate a JsonObject from a JSON string. // Allocates and populate a JsonObject from a JSON string.
@ -76,30 +82,54 @@ class JsonBuffer {
// allocation fails. // allocation fails.
JsonObject &parseObject(char *json, uint8_t nestingLimit = DEFAULT_LIMIT); JsonObject &parseObject(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
// Same as above with a String class // Same with a const char*.
JsonObject &parseObject(const String &json, uint8_t nesting = DEFAULT_LIMIT) { // With this overload, the JsonBuffer will make a copy of the string
JsonObject &parseObject(const char *json, uint8_t nesting = DEFAULT_LIMIT) {
return parseObject(strdup(json), nesting); return parseObject(strdup(json), nesting);
} }
// Same as above with a String class
JsonObject &parseObject(const String &json, uint8_t nesting = DEFAULT_LIMIT) {
return parseObject(json.c_str(), nesting);
}
// Duplicate a string // Duplicate a string
char *strdup(const char *src) { return strdup(src, strlen(src)); } char *strdup(const char *src) {
return src ? strdup(src, strlen(src)) : NULL;
}
char *strdup(const String &src) { return strdup(src.c_str(), src.length()); } char *strdup(const String &src) { return strdup(src.c_str(), src.length()); }
char *strdup(const char *, size_t);
// Allocates n bytes in the JsonBuffer. // Allocates n bytes in the JsonBuffer.
// Return a pointer to the allocated memory or NULL if allocation fails. // Return a pointer to the allocated memory or NULL if allocation fails.
virtual void *alloc(size_t size) = 0; virtual void *alloc(size_t size) = 0;
protected:
// Preserve aligment if nessary
static FORCE_INLINE size_t round_size_up(size_t bytes) {
#if defined ARDUINO_ARCH_AVR
// alignment isn't needed for 8-bit AVR
return bytes;
#else
const size_t x = sizeof(void *) - 1;
return (bytes + x) & ~x;
#endif
}
private:
char *strdup(const char *, size_t);
// Default value of nesting limit of parseArray() and parseObject(). // Default value of nesting limit of parseArray() and parseObject().
// //
// The nesting limit is a contain on the level of nesting allowed in the JSON // The nesting limit is a contain on the level of nesting allowed in the
// JSON
// string. // string.
// If set to 0, only a flat array or objects can be parsed. // If set to 0, only a flat array or objects can be parsed.
// If set to 1, the object can contain nested arrays or objects but only 1 // If set to 1, the object can contain nested arrays or objects but only 1
// level deep. // level deep.
// And bigger values will allow more level of nesting. // And bigger values will allow more level of nesting.
// //
// The purpose of this feature is to prevent stack overflow that could lead to // The purpose of this feature is to prevent stack overflow that could
// lead to
// a security risk. // a security risk.
static const uint8_t DEFAULT_LIMIT = 10; static const uint8_t DEFAULT_LIMIT = 10;
}; };

View File

@ -74,9 +74,9 @@ class JsonSubscriptBase : public JsonVariantBase<TImpl> {
private: private:
template <typename TValue> template <typename TValue>
FORCE_INLINE TImpl& assign(TValue value) { FORCE_INLINE TImpl& assign(TValue value) {
TImpl* impl = static_cast<TImpl*>(this); TImpl* that = static_cast<TImpl*>(this);
impl->template set<TValue>(value); that->template set<TValue>(value);
return *impl; return *that;
} }
}; };
} }

View File

@ -21,11 +21,10 @@ class StaticJsonBuffer : public JsonBuffer {
size_t capacity() const { return CAPACITY; } size_t capacity() const { return CAPACITY; }
size_t size() const { return _size; } size_t size() const { return _size; }
protected:
virtual void* alloc(size_t bytes) { virtual void* alloc(size_t bytes) {
if (_size + bytes > CAPACITY) return NULL; if (_size + bytes > CAPACITY) return NULL;
void* p = &_buffer[_size]; void* p = &_buffer[_size];
_size += bytes; _size += round_size_up(bytes);
return p; return p;
} }

View File

@ -1,5 +1,5 @@
name=ArduinoJson name=ArduinoJson
version=5.0.1 version=5.0.2
author=Benoit Blanchon <blog.benoitblanchon.fr> author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr> maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=An efficient and elegant JSON library for Arduino. sentence=An efficient and elegant JSON library for Arduino.

View File

@ -17,6 +17,13 @@
#pragma GCC diagnostic ignored "-Wfloat-conversion" #pragma GCC diagnostic ignored "-Wfloat-conversion"
#endif #endif
// Visual Studo 2012 didn't have isnan, nor isinf
#if defined(_MSC_VER) && _MSC_VER <= 1700
#include <float.h>
#define isnan(x) _isnan(x)
#define isinf(x) (!_finite(x))
#endif
size_t Print::print(const char s[]) { size_t Print::print(const char s[]) {
size_t n = 0; size_t n = 0;
while (*s) { while (*s) {

View File

@ -43,6 +43,7 @@ endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_definitions( add_definitions(
-Wc++11-compat -Wc++11-compat
-Wdeprecated-register
) )
endif() endif()

View File

@ -16,7 +16,7 @@ using namespace ArduinoJson;
using namespace ArduinoJson::Internals; using namespace ArduinoJson::Internals;
bool JsonParser::skip(char charToSkip) { bool JsonParser::skip(char charToSkip) {
register const char *ptr = skipSpacesAndComments(_readPtr); const char *ptr = skipSpacesAndComments(_readPtr);
if (*ptr != charToSkip) return false; if (*ptr != charToSkip) return false;
ptr++; ptr++;
_readPtr = skipSpacesAndComments(ptr); _readPtr = skipSpacesAndComments(ptr);

View File

@ -10,6 +10,11 @@ include_directories(
add_definitions(-DGTEST_HAS_PTHREAD=0) add_definitions(-DGTEST_HAS_PTHREAD=0)
# Workaround for Visual Studio 2012
if (MSVC AND MSVC_VERSION EQUAL 1700)
add_definitions(-D_VARIADIC_MAX=10)
endif()
add_executable(ArduinoJsonTests add_executable(ArduinoJsonTests
${TESTS_FILES} ${TESTS_FILES}
${INC_FILES} ${INC_FILES}

View File

@ -22,9 +22,9 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, InitialSizeIsZero) {
TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) { TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) {
buffer.alloc(1); buffer.alloc(1);
ASSERT_EQ(1, buffer.size()); ASSERT_LE(1, buffer.size());
buffer.alloc(1); buffer.alloc(1);
ASSERT_EQ(2, buffer.size()); ASSERT_LE(2, buffer.size());
} }
TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) { TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {
@ -32,3 +32,12 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {
void* p2 = buffer.alloc(2); void* p2 = buffer.alloc(2);
ASSERT_NE(p1, p2); ASSERT_NE(p1, p2);
} }
TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) {
size_t mask = sizeof(void*) - 1;
for (size_t size = 1; size <= sizeof(void*); size++) {
size_t addr = reinterpret_cast<size_t>(buffer.alloc(1));
ASSERT_EQ(0, addr & mask);
}
}

View File

@ -13,11 +13,11 @@ using namespace ArduinoJson;
class StaticJsonBuffer_Basic_Tests : public testing::Test { class StaticJsonBuffer_Basic_Tests : public testing::Test {
protected: protected:
StaticJsonBuffer<42> buffer; StaticJsonBuffer<64> buffer;
}; };
TEST_F(StaticJsonBuffer_Basic_Tests, CapacityMatchTemplateParameter) { TEST_F(StaticJsonBuffer_Basic_Tests, CapacityMatchTemplateParameter) {
ASSERT_EQ(42, buffer.capacity()); ASSERT_EQ(64, buffer.capacity());
} }
TEST_F(StaticJsonBuffer_Basic_Tests, InitialSizeIsZero) { TEST_F(StaticJsonBuffer_Basic_Tests, InitialSizeIsZero) {
@ -26,34 +26,43 @@ TEST_F(StaticJsonBuffer_Basic_Tests, InitialSizeIsZero) {
TEST_F(StaticJsonBuffer_Basic_Tests, GrowsAfterAlloc) { TEST_F(StaticJsonBuffer_Basic_Tests, GrowsAfterAlloc) {
buffer.alloc(1); buffer.alloc(1);
ASSERT_EQ(1, buffer.size()); ASSERT_LE(1, buffer.size());
buffer.alloc(1); buffer.alloc(1);
ASSERT_EQ(2, buffer.size()); ASSERT_LE(2, buffer.size());
} }
TEST_F(StaticJsonBuffer_Basic_Tests, DoesntGrowWhenFull) { TEST_F(StaticJsonBuffer_Basic_Tests, DoesntGrowWhenFull) {
buffer.alloc(42); buffer.alloc(64);
buffer.alloc(1); buffer.alloc(1);
ASSERT_EQ(42, buffer.size()); ASSERT_EQ(64, buffer.size());
} }
TEST_F(StaticJsonBuffer_Basic_Tests, DoesntGrowWhenTooSmall) { TEST_F(StaticJsonBuffer_Basic_Tests, DoesntGrowWhenTooSmall) {
buffer.alloc(43); buffer.alloc(65);
ASSERT_EQ(0, buffer.size()); ASSERT_EQ(0, buffer.size());
} }
TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNonNull) { TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNonNull) {
void *p = buffer.alloc(42); void *p = buffer.alloc(64);
ASSERT_NE(static_cast<void *>(0), p); ASSERT_NE(static_cast<void *>(0), p);
} }
TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNullWhenFull) { TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNullWhenFull) {
buffer.alloc(42); buffer.alloc(64);
void *p = buffer.alloc(1); void *p = buffer.alloc(1);
ASSERT_EQ(NULL, p); ASSERT_EQ(NULL, p);
} }
TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNullWhenTooSmall) { TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNullWhenTooSmall) {
void *p = buffer.alloc(43); void *p = buffer.alloc(65);
ASSERT_EQ(NULL, p); ASSERT_EQ(NULL, p);
} }
TEST_F(StaticJsonBuffer_Basic_Tests, Alignment) {
size_t mask = sizeof(void *) - 1;
for (size_t size = 1; size <= sizeof(void *); size++) {
size_t addr = reinterpret_cast<size_t>(buffer.alloc(1));
ASSERT_EQ(0, addr & mask);
}
}

View File

@ -70,3 +70,11 @@ TEST_F(StaticJsonBuffer_ParseArray_Tests,
whenInputIs("[{}]"); whenInputIs("[{}]");
parseMustSucceed(); parseMustSucceed();
} }
TEST_F(StaticJsonBuffer_ParseArray_Tests, CharPtrNull) {
ASSERT_FALSE(StaticJsonBuffer<100>().parseArray((char*)0).success());
}
TEST_F(StaticJsonBuffer_ParseArray_Tests, ConstCharPtrNull) {
ASSERT_FALSE(StaticJsonBuffer<100>().parseArray((const char*)0).success());
}

View File

@ -71,3 +71,11 @@ TEST_F(StaticJsonBuffer_ParseObject_Tests,
whenInputIs("{\"a\":[]}"); whenInputIs("{\"a\":[]}");
parseMustSucceed(); parseMustSucceed();
} }
TEST_F(StaticJsonBuffer_ParseObject_Tests, CharPtrNull) {
ASSERT_FALSE(StaticJsonBuffer<100>().parseObject((char*)0).success());
}
TEST_F(StaticJsonBuffer_ParseObject_Tests, ConstCharPtrNull) {
ASSERT_FALSE(StaticJsonBuffer<100>().parseObject((const char*)0).success());
}