Compare commits

..

8 Commits

16 changed files with 125 additions and 39 deletions

View File

@ -1,6 +1,16 @@
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
------

View File

@ -1,11 +1,11 @@
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.*
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.
@ -14,7 +14,7 @@ Features
* JSON decoding (comments are supported)
* JSON encoding (with optional indentation)
* Elegant API, very easy to use
* Elegant API, very easy to use
* Efficient (no malloc, nor copy)
* Portable (written in C++98)
* Self-contained (no external dependency)
@ -25,7 +25,7 @@ Quick start
-----------
#### Decoding / Parsing
char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
StaticJsonBuffer<200> jsonBuffer;
@ -38,7 +38,7 @@ Quick start
double longitude = root["data"][1];
#### Encoding / Generating
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
@ -57,13 +57,13 @@ Quick start
Documentation
-------------
The documentation is available online in the [Arduino JSON wiki](https://github.com/bblanchon/ArduinoJson/wiki)
The documentation is available online in the [Arduino JSON wiki](https://github.com/bblanchon/ArduinoJson/wiki)
Testimonials
------------
From Arduino's Forum user `jflaplante`:
> I tried aJson json-arduino before trying your library. I always ran into memory problem after a while.
> I tried aJson json-arduino before trying your library. I always ran into memory problem after a while.
> I have no such problem so far with your library. It is working perfectly with my web services.
From Arduino's Forum user `gbathree`:

View File

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

View File

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

View File

@ -59,9 +59,15 @@ class JsonBuffer {
// allocation fails.
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
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.
@ -76,30 +82,54 @@ class JsonBuffer {
// allocation fails.
JsonObject &parseObject(char *json, uint8_t nestingLimit = DEFAULT_LIMIT);
// Same as above with a String class
JsonObject &parseObject(const String &json, uint8_t nesting = DEFAULT_LIMIT) {
// Same with a const char*.
// 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);
}
// 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
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 char *, size_t);
// Allocates n bytes in the JsonBuffer.
// Return a pointer to the allocated memory or NULL if allocation fails.
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().
//
// 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.
// 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
// level deep.
// 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.
static const uint8_t DEFAULT_LIMIT = 10;
};

View File

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

View File

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

View File

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

View File

@ -17,6 +17,13 @@
#pragma GCC diagnostic ignored "-Wfloat-conversion"
#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 n = 0;
while (*s) {

View File

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

View File

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

View File

@ -10,6 +10,11 @@ include_directories(
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
${TESTS_FILES}
${INC_FILES}

View File

@ -22,9 +22,9 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, InitialSizeIsZero) {
TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) {
buffer.alloc(1);
ASSERT_EQ(1, buffer.size());
ASSERT_LE(1, buffer.size());
buffer.alloc(1);
ASSERT_EQ(2, buffer.size());
ASSERT_LE(2, buffer.size());
}
TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {
@ -32,3 +32,12 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {
void* p2 = buffer.alloc(2);
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 {
protected:
StaticJsonBuffer<42> buffer;
StaticJsonBuffer<64> buffer;
};
TEST_F(StaticJsonBuffer_Basic_Tests, CapacityMatchTemplateParameter) {
ASSERT_EQ(42, buffer.capacity());
ASSERT_EQ(64, buffer.capacity());
}
TEST_F(StaticJsonBuffer_Basic_Tests, InitialSizeIsZero) {
@ -26,34 +26,43 @@ TEST_F(StaticJsonBuffer_Basic_Tests, InitialSizeIsZero) {
TEST_F(StaticJsonBuffer_Basic_Tests, GrowsAfterAlloc) {
buffer.alloc(1);
ASSERT_EQ(1, buffer.size());
ASSERT_LE(1, buffer.size());
buffer.alloc(1);
ASSERT_EQ(2, buffer.size());
ASSERT_LE(2, buffer.size());
}
TEST_F(StaticJsonBuffer_Basic_Tests, DoesntGrowWhenFull) {
buffer.alloc(42);
buffer.alloc(64);
buffer.alloc(1);
ASSERT_EQ(42, buffer.size());
ASSERT_EQ(64, buffer.size());
}
TEST_F(StaticJsonBuffer_Basic_Tests, DoesntGrowWhenTooSmall) {
buffer.alloc(43);
buffer.alloc(65);
ASSERT_EQ(0, buffer.size());
}
TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNonNull) {
void *p = buffer.alloc(42);
void *p = buffer.alloc(64);
ASSERT_NE(static_cast<void *>(0), p);
}
TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNullWhenFull) {
buffer.alloc(42);
buffer.alloc(64);
void *p = buffer.alloc(1);
ASSERT_EQ(NULL, p);
}
TEST_F(StaticJsonBuffer_Basic_Tests, ReturnsNullWhenTooSmall) {
void *p = buffer.alloc(43);
void *p = buffer.alloc(65);
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

@ -69,4 +69,12 @@ TEST_F(StaticJsonBuffer_ParseArray_Tests,
with(bufferOfRightSize);
whenInputIs("[{}]");
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

@ -70,4 +70,12 @@ TEST_F(StaticJsonBuffer_ParseObject_Tests,
with(bufferOfRightSize);
whenInputIs("{\"a\":[]}");
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());
}