Compare commits

..

13 Commits

24 changed files with 211 additions and 44 deletions

View File

@ -1,6 +1,22 @@
ArduinoJson: change log
=======================
v5.0.3
------
* Fixed `printTo(String)` which wrote numbers instead of strings (issue #120)
* Fixed return type of `JsonArray::is<T>()` and some others (issue #121)
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`:
@ -78,6 +78,14 @@ From GitHub user `zacsketches`:
> I've been watching you consistently develop this library over the past six months, and I used it today for a publish and subscribe architecture designed to help hobbyists move into more advanced robotics. Your library allowed me to implement remote subscription in order to facilitate multi-processor robots.
> ArduinoJson saved me a week's worth of time!!
[From Reddit user `erm_what_`](https://www.reddit.com/r/arduino/comments/3jj6ep/announcing_arduinojson_50/cusjk8c):
> This is a great library and I wouldn't be able to do the project I'm doing without it. I completely recommend it.
[From Reddit user `makerhacks`](https://www.reddit.com/r/arduino/comments/3jj6ep/announcing_arduinojson_50/cusqg7b):
> I am just starting an ESP8266 clock project and now I can output JSON from my server script and interpret it painlessly.
---
Found this library useful? Please star this project or [help me back with a donation!](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=donate%40benoitblanchon%2efr&lc=GB&item_name=Benoit%20Blanchon&item_number=Arduino%20JSON&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) :smile:

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

@ -18,7 +18,8 @@ class DynamicStringBuilder : public Print {
DynamicStringBuilder(String &str) : _str(str) {}
virtual size_t write(uint8_t c) {
_str += c;
// Need to cast to char, otherwise String will print a number (issue #120)
_str += static_cast<char>(c);
return 1;
}

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

@ -92,7 +92,7 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
// Check the type of the value at specified index.
template <typename T>
FORCE_INLINE T is(size_t index) const;
FORCE_INLINE bool is(size_t index) const;
// Creates a JsonArray and adds a reference at the end of the array.
// It's a shortcut for JsonBuffer::createArray() and JsonArray::add()

View File

@ -182,7 +182,7 @@ inline T JsonArray::get(size_t index) const {
}
template <typename T>
inline T JsonArray::is(size_t index) const {
inline bool JsonArray::is(size_t index) const {
node_type *node = getNodeAt(index);
return node ? node->content.is<T>() : false;
}

View File

@ -26,7 +26,7 @@ class JsonArraySubscript : public JsonSubscriptBase<JsonArraySubscript> {
}
template <typename T>
FORCE_INLINE T is() const {
FORCE_INLINE bool is() const {
return _array.is<T>(_index);
}

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

@ -29,7 +29,7 @@ class JsonObjectSubscript
}
template <typename TValue>
FORCE_INLINE TValue is() const {
FORCE_INLINE bool is() const {
return _object.is<TValue>(_key);
}

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.3
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=An efficient and elegant JSON library for Arduino.

View File

@ -0,0 +1,52 @@
#!/bin/bash
TAG=$(git describe)
OUTPUT="ArduinoJson-$TAG-old-layout.zip"
cd $(dirname $0)/..
cat > ArduinoJson.h <<END
// WARNING:
// --------
// This file is a workaround for old version of the Arduino IDE.
// If you are using Arduino IDE 1.0.6 or above, then you installed the wrong
// package of ArduinoJson.
// In that case, just delete the current installation and install the package.
END
cp ArduinoJson.h ArduinoJson.cpp
cat "include/ArduinoJson.h" | sed 's!include "!include "include/!g' >> ArduinoJson.h
find src -name "*.cpp" |
while read FILE; do
echo >> ArduinoJson.cpp
echo "// $FILE" >> ArduinoJson.cpp
echo "//" >> ArduinoJson.cpp
cat "$FILE" | sed 's!\.\./!!g' >> ArduinoJson.cpp
done
unix2dos ArduinoJson.cpp
unix2dos ArduinoJson.h
pushd ..
# remove existing file
rm -f $OUTPUT
# create zipman dos2
7z a $OUTPUT \
ArduinoJson/CHANGELOG.md \
ArduinoJson/examples \
ArduinoJson/include/ArduinoJson \
ArduinoJson/keywords.txt \
ArduinoJson/LICENSE.md \
ArduinoJson/README.md \
ArduinoJson/ArduinoJson.h \
ArduinoJson/ArduinoJson.cpp \
-x!ArduinoJson/src/CMakeLists.txt
popd
rm ArduinoJson.h
rm ArduinoJson.cpp

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

@ -39,11 +39,13 @@ class JsonArray_Container_Tests : public ::testing::Test {
private:
template <typename T>
void itemMustEqual(int index, T expected) {
EXPECT_TRUE(_array[index].is<T>());
EXPECT_EQ(expected, _array[index].as<T>());
}
template <typename T>
void itemMustReference(int index, const T& expected) {
EXPECT_TRUE(_array[index].is<T&>());
EXPECT_EQ(&expected, &_array[index].as<T&>());
}
};

View File

@ -61,6 +61,8 @@ TEST_F(JsonObject_Container_Tests, CanStoreIntegers) {
_object["hello"] = 123;
_object.set("world", 456);
EXPECT_TRUE(_object["hello"].is<int>());
EXPECT_FALSE(_object["hello"].is<double>());
EXPECT_EQ(123, _object["hello"].as<int>());
EXPECT_EQ(456, _object["world"].as<int>());
}
@ -69,6 +71,8 @@ TEST_F(JsonObject_Container_Tests, CanStoreDoubles) {
_object["hello"] = 123.45;
_object.set("world", 456.78);
EXPECT_TRUE(_object["hello"].is<double>());
EXPECT_FALSE(_object["hello"].is<long>());
EXPECT_EQ(123.45, _object["hello"].as<double>());
EXPECT_EQ(456.78, _object["world"].as<double>());
}
@ -77,6 +81,8 @@ TEST_F(JsonObject_Container_Tests, CanStoreBooleans) {
_object["hello"] = true;
_object.set("world", false);
EXPECT_TRUE(_object["hello"].is<bool>());
EXPECT_FALSE(_object["hello"].is<long>());
EXPECT_TRUE(_object["hello"].as<bool>());
EXPECT_FALSE(_object["world"].as<bool>());
}
@ -85,6 +91,8 @@ TEST_F(JsonObject_Container_Tests, CanStoreStrings) {
_object["hello"] = "h3110";
_object.set("world", "w0r1d");
EXPECT_TRUE(_object["hello"].is<const char*>());
EXPECT_FALSE(_object["hello"].is<long>());
EXPECT_STREQ("h3110", _object["hello"].as<const char*>());
EXPECT_STREQ("w0r1d", _object["world"].as<const char*>());
}
@ -96,6 +104,8 @@ TEST_F(JsonObject_Container_Tests, CanStoreArrays) {
_object["hello"] = array1;
_object.set("world", array2);
EXPECT_TRUE(_object["hello"].is<JsonArray&>());
EXPECT_FALSE(_object["hello"].is<JsonObject&>());
EXPECT_EQ(&array1, &_object["hello"].asArray());
EXPECT_EQ(&array2, &_object["world"].asArray());
}
@ -107,6 +117,8 @@ TEST_F(JsonObject_Container_Tests, CanStoreObjects) {
_object["hello"] = object1;
_object.set("world", object2);
EXPECT_TRUE(_object["hello"].is<JsonObject&>());
EXPECT_FALSE(_object["hello"].is<JsonArray&>());
EXPECT_EQ(&object1, &_object["hello"].asObject());
EXPECT_EQ(&object2, &_object["world"].asObject());
}

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());
}