Compare commits

..

5 Commits

22 changed files with 258 additions and 139 deletions

View File

@ -1,6 +1,14 @@
ArduinoJson: change log
=======================
v5.6.7
------
* Fixed `array[idx].as<JsonVariant>()` and `object[key].as<JsonVariant>()`
* Fixed return value of `JsonObject::set()` (issue #350)
* Fixed undefined behavior in `Prettyfier` and `Print` (issue #354)
* Fixed parser that incorrectly rejected floats containing a `+` (issue #349)
v5.6.6
------

View File

@ -1,4 +1,4 @@
version: 5.6.6.{build}
version: 5.6.7.{build}
environment:
matrix:
- CMAKE_GENERATOR: Visual Studio 14 2015

View File

@ -13,6 +13,7 @@
#include "ArduinoJson/StaticJsonBuffer.hpp"
#include "ArduinoJson/Internals/JsonParser.ipp"
#include "ArduinoJson/Internals/JsonSerializer.ipp"
#include "ArduinoJson/JsonArray.ipp"
#include "ArduinoJson/JsonBuffer.ipp"
#include "ArduinoJson/JsonObject.ipp"

View File

@ -50,7 +50,7 @@ class JsonParser {
static inline bool isLetterOrNumber(char c) {
return isInRange(c, '0', '9') || isInRange(c, 'a', 'z') ||
isInRange(c, 'A', 'Z') || c == '-' || c == '.';
isInRange(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
}
static inline bool isQuote(char c) {

View File

@ -9,11 +9,12 @@
#include "../Configuration.hpp"
#include "DummyPrint.hpp"
#include "DynamicStringBuilder.hpp"
#include "IndentedPrint.hpp"
#include "JsonSerializer.hpp"
#include "JsonWriter.hpp"
#include "Prettyfier.hpp"
#include "StaticStringBuilder.hpp"
#include "DynamicStringBuilder.hpp"
#if ARDUINOJSON_ENABLE_STD_STREAM
#include "StreamPrintAdapter.hpp"
@ -31,7 +32,7 @@ class JsonPrintable {
public:
size_t printTo(Print &print) const {
JsonWriter writer(print);
downcast().writeTo(writer);
JsonSerializer::serialize(downcast(), writer);
return writer.bytesWritten();
}
@ -84,7 +85,9 @@ class JsonPrintable {
}
private:
const T &downcast() const { return *static_cast<const T *>(this); }
const T &downcast() const {
return *static_cast<const T *>(this);
}
};
#if ARDUINOJSON_ENABLE_STD_STREAM

View File

@ -0,0 +1,33 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "JsonWriter.hpp"
namespace ArduinoJson {
class JsonArray;
class JsonArraySubscript;
class JsonObject;
template <typename TKey>
class JsonObjectSubscript;
class JsonVariant;
namespace Internals {
class JsonSerializer {
public:
static void serialize(const JsonArray &, JsonWriter &);
static void serialize(const JsonArraySubscript &, JsonWriter &);
static void serialize(const JsonObject &, JsonWriter &);
template <typename TKey>
static void serialize(const JsonObjectSubscript<TKey> &, JsonWriter &);
static void serialize(const JsonVariant &, JsonWriter &);
};
}
}

View File

@ -0,0 +1,101 @@
// Copyright Benoit Blanchon 2014-2016
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#pragma once
#include "../JsonArray.hpp"
#include "../JsonArraySubscript.hpp"
#include "../JsonObject.hpp"
#include "../JsonObjectSubscript.hpp"
#include "../JsonVariant.hpp"
#include "JsonSerializer.hpp"
inline void ArduinoJson::Internals::JsonSerializer::serialize(
const JsonArray& array, JsonWriter& writer) {
writer.beginArray();
JsonArray::const_iterator it = array.begin();
while (it != array.end()) {
serialize(*it, writer);
++it;
if (it == array.end()) break;
writer.writeComma();
}
writer.endArray();
}
inline void ArduinoJson::Internals::JsonSerializer::serialize(
const JsonArraySubscript& arraySubscript, JsonWriter& writer) {
serialize(arraySubscript.as<JsonVariant>(), writer);
}
inline void ArduinoJson::Internals::JsonSerializer::serialize(
const JsonObject& object, JsonWriter& writer) {
writer.beginObject();
JsonObject::const_iterator it = object.begin();
while (it != object.end()) {
writer.writeString(it->key);
writer.writeColon();
serialize(it->value, writer);
++it;
if (it == object.end()) break;
writer.writeComma();
}
writer.endObject();
}
template <typename TKey>
inline void ArduinoJson::Internals::JsonSerializer::serialize(
const JsonObjectSubscript<TKey>& objectSubscript, JsonWriter& writer) {
serialize(objectSubscript.template as<JsonVariant>(), writer);
}
inline void ArduinoJson::Internals::JsonSerializer::serialize(
const JsonVariant& variant, JsonWriter& writer) {
switch (variant._type) {
case JSON_UNDEFINED:
return;
case JSON_ARRAY:
serialize(*variant._content.asArray, writer);
return;
case JSON_OBJECT:
serialize(*variant._content.asObject, writer);
return;
case JSON_STRING:
writer.writeString(variant._content.asString);
return;
case JSON_UNPARSED:
writer.writeRaw(variant._content.asString);
return;
case JSON_NEGATIVE_INTEGER:
writer.writeRaw('-');
case JSON_POSITIVE_INTEGER:
writer.writeInteger(variant._content.asInteger);
return;
case JSON_BOOLEAN:
writer.writeBoolean(variant._content.asInteger != 0);
return;
default:
uint8_t decimals =
static_cast<uint8_t>(variant._type - JSON_FLOAT_0_DECIMALS);
writer.writeFloat(variant._content.asFloat, decimals);
}
}

View File

@ -66,28 +66,46 @@ class Prettyfier : public Print {
}
size_t writeBlockClose(uint8_t c) {
return unindentIfNeeded() + _sink.write(c);
size_t n = 0;
n += unindentIfNeeded();
n += _sink.write(c);
return n;
}
size_t writeBlockOpen(uint8_t c) {
return indentIfNeeded() + _sink.write(c);
size_t n = 0;
n += indentIfNeeded();
n += _sink.write(c);
return n;
}
size_t writeColon() {
return _sink.write(':') + _sink.write(' ');
size_t n = 0;
n += _sink.write(':');
n += _sink.write(' ');
return n;
}
size_t writeComma() {
return _sink.write(',') + _sink.println();
size_t n = 0;
n += _sink.write(',');
n += _sink.println();
return n;
}
size_t writeQuoteOpen() {
_inString = true;
return indentIfNeeded() + _sink.write('"');
size_t n = 0;
n += indentIfNeeded();
n += _sink.write('"');
return n;
}
size_t writeNormalChar(uint8_t c) {
return indentIfNeeded() + _sink.write(c);
size_t n = 0;
n += indentIfNeeded();
n += _sink.write(c);
return n;
}
size_t indentIfNeeded() {

View File

@ -170,23 +170,6 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
return instance;
}
// Serialize the array to the specified JsonWriter.
void writeTo(Internals::JsonWriter &writer) const {
writer.beginArray();
const node_type *child = _firstNode;
while (child) {
child->content.writeTo(writer);
child = child->next;
if (!child) break;
writer.writeComma();
}
writer.endArray();
}
// Imports a 1D array
template <typename T, size_t N>
bool copyFrom(T(&array)[N]) {

View File

@ -60,10 +60,6 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
return _array.is<T>(_index);
}
void writeTo(Internals::JsonWriter& writer) const {
_array.get(_index).writeTo(writer);
}
template <typename TValue>
void set(TValue value) {
_array.set(_index, value);

View File

@ -144,25 +144,6 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
return instance;
}
// Serialize the object to the specified JsonWriter
void writeTo(Internals::JsonWriter& writer) const {
writer.beginObject();
const node_type* node = _firstNode;
while (node) {
writer.writeString(node->content.key);
writer.writeColon();
node->content.value.writeTo(writer);
node = node->next;
if (!node) break;
writer.writeComma();
}
writer.endObject();
}
private:
// Returns the list node that matches the specified key.
node_type* getNodeAt(const char* key) const {

View File

@ -15,14 +15,16 @@ namespace ArduinoJson {
template <>
inline bool JsonObject::setNodeValue(node_type *node, String &value) {
node->content.value = _buffer->strdup(value);
return node->content.value;
const char *dup = _buffer->strdup(value);
node->content.value = dup;
return dup != NULL;
}
template <>
inline bool JsonObject::setNodeValue(node_type *node, const String &value) {
node->content.value = _buffer->strdup(value);
return node->content.value;
const char *dup = _buffer->strdup(value);
node->content.value = dup;
return dup != NULL;
}
template <>

View File

@ -77,10 +77,6 @@ class JsonObjectSubscript : public JsonVariantBase<JsonObjectSubscript<TKey> > {
return _object.get(_key);
}
void writeTo(Internals::JsonWriter& writer) const {
_object.get(_key).writeTo(writer);
}
private:
JsonObject& _object;
TKey _key;

View File

@ -36,6 +36,9 @@ class JsonObject;
// - a string (const char*)
// - a reference to a JsonArray or JsonObject
class JsonVariant : public JsonVariantBase<JsonVariant> {
friend void Internals::JsonSerializer::serialize(const JsonVariant &,
JsonWriter &);
public:
template <typename T>
struct IsConstructibleFrom;
@ -211,6 +214,14 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
as() const {
return asObject();
}
//
// JsonVariant as<JsonVariant> const;
template <typename T>
typename TypeTraits::EnableIf<TypeTraits::IsSame<T, JsonVariant>::value,
T>::type
as() const {
return *this;
}
// Tells weither the variant has the specified type.
// Returns true if the variant has type type T, false otherwise.
@ -266,9 +277,9 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
return isArray();
}
//
// JsonObject& as<JsonObject> const;
// JsonObject& as<JsonObject&> const;
// JsonObject& as<const JsonObject&> const;
// bool is<JsonObject> const;
// bool is<JsonObject&> const;
// bool is<const JsonObject&> const;
template <typename T>
typename TypeTraits::EnableIf<
TypeTraits::IsSame<
@ -285,9 +296,6 @@ class JsonVariant : public JsonVariantBase<JsonVariant> {
return _type != Internals::JSON_UNDEFINED;
}
// Serialize the variant to a JsonWriter
void writeTo(Internals::JsonWriter &writer) const;
// Value returned if the variant has an incompatible type
template <typename T>
static typename Internals::JsonVariantAs<T>::type defaultValue() {

View File

@ -133,44 +133,6 @@ inline bool JsonVariant::isFloat() const {
return *end == '\0' && errno == 0 && !is<long>();
}
inline void JsonVariant::writeTo(Internals::JsonWriter &writer) const {
using namespace Internals;
switch (_type) {
case JSON_UNDEFINED:
return;
case JSON_ARRAY:
_content.asArray->writeTo(writer);
return;
case JSON_OBJECT:
_content.asObject->writeTo(writer);
return;
case JSON_STRING:
writer.writeString(_content.asString);
return;
case JSON_UNPARSED:
writer.writeRaw(_content.asString);
return;
case JSON_NEGATIVE_INTEGER:
writer.writeRaw('-');
case JSON_POSITIVE_INTEGER:
writer.writeInteger(_content.asInteger);
return;
case JSON_BOOLEAN:
writer.writeBoolean(_content.asInteger != 0);
return;
default:
uint8_t decimals = static_cast<uint8_t>(_type - JSON_FLOAT_0_DECIMALS);
writer.writeFloat(_content.asFloat, decimals);
}
}
#if ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream &operator<<(std::ostream &os, const JsonVariant &source) {
return source.printTo(os);

View File

@ -82,9 +82,6 @@ class JsonVariantBase : public Internals::JsonPrintable<TImpl> {
FORCE_INLINE const JsonObjectSubscript<const String &> operator[](
const String &key) const;
// Serialize the variant to a JsonWriter
void writeTo(Internals::JsonWriter &writer) const;
private:
const TImpl *impl() const {
return static_cast<const TImpl *>(this);

View File

@ -23,12 +23,17 @@ class Print {
size_t print(const char* s) {
size_t n = 0;
while (*s) {
n += write(*s++);
n += write(static_cast<uint8_t>(*s++));
}
return n;
}
size_t println() { return write('\r') + write('\n'); }
size_t println() {
size_t n = 0;
n += write('\r');
n += write('\n');
return n;
}
};
}

View File

@ -6,7 +6,7 @@
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "5.6.6",
"version": "5.6.7",
"authors": {
"name": "Benoit Blanchon",
"url": "http://blog.benoitblanchon.fr"

View File

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

View File

@ -26,9 +26,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
-Winit-self
-Wmissing-include-dirs
-Wparentheses
-Wno-sign-conversion
-Wno-unused
-Wno-variadic-macros
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
@ -63,7 +60,10 @@ endif()
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_options(-W4)
add_compile_options(
/W4 # Set warning level
/WX # Treats all compiler warnings as errors.
)
endif()
add_executable(ArduinoJsonTests ${TESTS_FILES})

View File

@ -107,3 +107,21 @@ TEST_(StoreObjectSubscript) {
EXPECT_EQ(42, _object["a"]);
}
TEST_(ShouldReturnTrue_WhenAllocationSucceeds) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 15> jsonBuffer;
JsonObject& obj = jsonBuffer.createObject();
bool result = obj.set(String("hello"), String("world"));
ASSERT_TRUE(result);
}
TEST_(ShouldReturnFalse_WhenAllocationFails) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 10> jsonBuffer;
JsonObject& obj = jsonBuffer.createObject();
bool result = obj.set(String("hello"), String("world"));
ASSERT_FALSE(result);
}

View File

@ -24,13 +24,29 @@ class JsonParser_Variant_Test : public testing::Test {
EXPECT_STREQ(expected, _result.as<char*>());
}
void resultMustEqual(double expected) {
EXPECT_DOUBLE_EQ(expected, _result.as<double>());
}
template <typename T>
void resultTypeMustBe() {
EXPECT_TRUE(_result.is<T>());
}
void resultMustBeInvalid() { EXPECT_FALSE(_result.success()); }
void resultMustBeValid() { EXPECT_TRUE(_result.success()); }
void resultMustBeInvalid() {
EXPECT_FALSE(_result.success());
}
void resultMustBeValid() {
EXPECT_TRUE(_result.success());
}
template <typename T>
void verify(const char* input, T expected) {
whenInputIs(input);
resultMustBeValid();
resultTypeMustBe<T>();
resultMustEqual(expected);
}
private:
DynamicJsonBuffer _jsonBuffer;
@ -51,38 +67,29 @@ TEST_F(JsonParser_Variant_Test, EmptyArray) {
}
TEST_F(JsonParser_Variant_Test, Integer) {
whenInputIs("42");
resultMustBeValid();
resultTypeMustBe<int>();
resultMustEqual(42);
verify("42", 42);
verify("-42", -42);
}
TEST_F(JsonParser_Variant_Test, Double) {
whenInputIs("3.14");
resultMustBeValid();
resultTypeMustBe<double>();
resultMustEqual(3.14);
verify("3.14", 3.14);
verify("3.14", 3.14);
verify("1E+10", 1E+10);
verify("-1E+10", -1E+10);
verify("1.234E+10", 1.234E+10);
verify("1.79769e+308", 1.79769e+308);
verify("-1.79769e+308", -1.79769e+308);
verify("1.7976931348623157e+308", 1.7976931348623157e+308);
verify("0.017976931348623157e+310", 0.017976931348623157e+310);
}
TEST_F(JsonParser_Variant_Test, String) {
whenInputIs("\"hello world\"");
resultMustBeValid();
resultTypeMustBe<char*>();
resultMustEqual("hello world");
verify("\"hello world\"", "hello world");
}
TEST_F(JsonParser_Variant_Test, True) {
whenInputIs("true");
resultMustBeValid();
resultTypeMustBe<bool>();
resultMustEqual(true);
}
TEST_F(JsonParser_Variant_Test, False) {
whenInputIs("false");
resultMustBeValid();
resultTypeMustBe<bool>();
resultMustEqual(false);
verify("true", true);
verify("false", false);
}
TEST_F(JsonParser_Variant_Test, Invalid) {