Skip to content

Commit

Permalink
Added support of non standard JSON input (issue #44)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Jul 10, 2015
1 parent 78ae0b8 commit 92e6873
Show file tree
Hide file tree
Showing 30 changed files with 405 additions and 397 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/build
/bin
/lib
/sftp-config.json
6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ compiler:
- clang
before_install:
- sudo pip install cpp-coveralls
before_script:
- cmake -DCOVERAGE=true .
script:
- make && make test
- cmake -DCOVERAGE=true . && make && make test
after_success:
- coveralls --exclude test --exclude third-party --gcov-options '\-lp'
- if [ "$CC" = "gcc" ]; then coveralls --exclude third-party --gcov-options '\-lp'; fi
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ v5.0 (currently in beta)
----

* Added support of `String` class (issue #55, #56, #70, #77)
* Added support of non standard JSON input (issue #44)
* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators
* Switched to new the library layout (requires Arduino 1.0.6 or above)

Expand All @@ -20,6 +21,15 @@ The `String` class is **bad** because it uses dynamic memory allocation.
Compared to static allocation, it compiles to a bigger, slower program, and is less predictable.
You certainly don't want that in an embedded environment!

v4.5
----

* Fixed buffer overflow when input contains a backslash followed by a terminator (issue #81)

**Upgrading is recommended** since previous versions contain a potential security risk.

Special thanks to [Giancarlo Canales Barreto](https://github.com/gcanalesb) for finding this nasty bug.

v4.4
----

Expand Down
5 changes: 2 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ if(MSVC)
endif()

if(${COVERAGE})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage")
set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
endif()

add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(test)
39 changes: 39 additions & 0 deletions include/ArduinoJson/Internals/Encoding.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson

#pragma once

#include "../Arduino/Print.hpp"

namespace ArduinoJson {
namespace Internals {

class Encoding {
public:
// Optimized for code size on a 8-bit AVR
static char escapeChar(char c) {
const char *p = _escapeTable;
while (p[0] && p[1] != c) {
p += 2;
}
return p[0];
}

// Optimized for code size on a 8-bit AVR
static char unescapeChar(char c) {
const char *p = _escapeTable + 4;
for (;;) {
if (p[0] == '\0') return c;
if (p[0] == c) return p[1];
p += 2;
}
}

private:
static const char _escapeTable[];
};
}
}
12 changes: 7 additions & 5 deletions include/ArduinoJson/Internals/JsonParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@ namespace Internals {
class JsonParser {
public:
JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit)
: _buffer(buffer), _ptr(json), _nestingLimit(nestingLimit) {}
: _buffer(buffer),
_readPtr(json),
_writePtr(json),
_nestingLimit(nestingLimit) {}

JsonArray &parseArray();
JsonObject &parseObject();

private:
bool skip(char charToSkip);
bool skip(const char *wordToSkip);
void skipSpaces();

const char *parseString();
bool parseAnythingTo(JsonVariant *destination);
FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);

const char *parseString();

inline bool parseArrayTo(JsonVariant *destination);
inline bool parseBooleanTo(JsonVariant *destination);
inline bool parseNullTo(JsonVariant *destination);
Expand All @@ -41,7 +42,8 @@ class JsonParser {
inline bool parseStringTo(JsonVariant *destination);

JsonBuffer *_buffer;
char *_ptr;
const char *_readPtr;
char *_writePtr;
uint8_t _nestingLimit;
};
}
Expand Down
20 changes: 20 additions & 0 deletions include/ArduinoJson/Internals/JsonPrintable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#include "Prettyfier.hpp"
#include "StringBuilder.hpp"

#ifdef ARDUINOJSON_ENABLE_STD_STREAM
#include "StreamPrintAdapter.hpp"
#endif

namespace ArduinoJson {
namespace Internals {

Expand All @@ -28,6 +32,14 @@ class JsonPrintable {
return writer.bytesWritten();
}

#ifdef ARDUINOJSON_ENABLE_STD_STREAM
std::ostream& printTo(std::ostream &os) const {
StreamPrintAdapter adapter(os);
printTo(adapter);
return os;
}
#endif

size_t printTo(char *buffer, size_t bufferSize) const {
StringBuilder sb(buffer, bufferSize);
return printTo(sb);
Expand Down Expand Up @@ -61,5 +73,13 @@ class JsonPrintable {
private:
const T &downcast() const { return *static_cast<const T *>(this); }
};

#ifdef ARDUINOJSON_ENABLE_STD_STREAM
template<typename T>
inline std::ostream& operator<<(std::ostream& os, const JsonPrintable<T>& v) {
return v.printTo(os);
}
#endif

}
}
29 changes: 23 additions & 6 deletions include/ArduinoJson/Internals/JsonWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#pragma once

#include "../Arduino/Print.hpp"
#include "QuotedString.hpp"
#include "Encoding.hpp"

namespace ArduinoJson {
namespace Internals {
Expand All @@ -26,7 +26,7 @@ class JsonWriter {
// Returns the number of bytes sent to the Print implementation.
// This is very handy for implementations of printTo() that must return the
// number of bytes written.
size_t bytesWritten() { return _length; }
size_t bytesWritten() const { return _length; }

void beginArray() { write('['); }
void endArray() { write(']'); }
Expand All @@ -37,15 +37,32 @@ class JsonWriter {
void writeColon() { write(':'); }
void writeComma() { write(','); }

void writeBoolean(bool value) {
write(value ? "true" : "false");
}

void writeString(const char *value) {
_length += QuotedString::printTo(value, _sink);
if (!value) {
write("null");
} else {
write('\"');
while (*value) writeChar(*value++);
write('\"');
}
}

void writeChar(char c) {
char specialChar = Encoding::escapeChar(c);
if (specialChar) {
write('\\');
write(specialChar);
} else {
write(c);
}
}

void writeLong(long value) { _length += _sink.print(value); }

void writeBoolean(bool value) {
_length += _sink.print(value ? "true" : "false");
}
void writeDouble(double value, uint8_t decimals) {
_length += _sink.print(value, decimals);
}
Expand Down
29 changes: 0 additions & 29 deletions include/ArduinoJson/Internals/QuotedString.hpp

This file was deleted.

34 changes: 34 additions & 0 deletions include/ArduinoJson/Internals/StreamPrintAdapter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson

#pragma once

#ifdef ARDUINOJSON_ENABLE_STD_STREAM

#include "../Arduino/Print.hpp"

namespace ArduinoJson {
namespace Internals {

class StreamPrintAdapter : public Print {
public:
explicit StreamPrintAdapter(std::ostream& os) : _os(os) {}

virtual size_t write(uint8_t c) {
_os << static_cast<char>(c);
return 1;
}

private:
// cannot be assigned
StreamPrintAdapter& operator=(const StreamPrintAdapter&);

std::ostream& _os;
};
}
}

#endif // ARDUINOJSON_ENABLE_STD_STREAM
11 changes: 11 additions & 0 deletions include/ArduinoJson/JsonArraySubscript.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,19 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
return _array.is<T>(_index);
}

void writeTo(Internals::JsonWriter &writer) const {
_array.get(_index).writeTo(writer);
}

private:
JsonArray& _array;
const size_t _index;
};

#ifdef ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& os, const JsonArraySubscript& source) {
return source.printTo(os);
}
#endif

} // namespace ArduinoJson
10 changes: 10 additions & 0 deletions include/ArduinoJson/JsonObjectSubscript.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,18 @@ class JsonObjectSubscript : public JsonVariantBase<JsonObjectSubscript> {
return _object.is<T>(_key);
}

void writeTo(Internals::JsonWriter &writer) const {
_object.get(_key).writeTo(writer);
}

private:
JsonObject& _object;
JsonObjectKey _key;
};

#ifdef ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& os, const JsonObjectSubscript& source) {
return source.printTo(os);
}
#endif
}
3 changes: 1 addition & 2 deletions include/ArduinoJson/JsonVariant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ class JsonObject;
// - a char, short, int or a long (signed or unsigned)
// - a string (const char*)
// - a reference to a JsonArray or JsonObject
class JsonVariant : public Internals::JsonPrintable<JsonVariant>,
public JsonVariantBase<JsonVariant> {
class JsonVariant : public JsonVariantBase<JsonVariant> {
public:
// Creates an uninitialized JsonVariant
FORCE_INLINE JsonVariant() : _type(Internals::JSON_UNDEFINED) {}
Expand Down
7 changes: 7 additions & 0 deletions include/ArduinoJson/JsonVariant.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,11 @@ template <>
inline bool JsonVariant::is<unsigned short>() const {
return _type == Internals::JSON_LONG;
}

#ifdef ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& os, const JsonVariant& source) {
return source.printTo(os);
}
#endif

} // namespace ArduinoJson
5 changes: 4 additions & 1 deletion include/ArduinoJson/JsonVariantBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class JsonArraySubscript;
class JsonObjectSubscript;

template <typename TImpl>
class JsonVariantBase {
class JsonVariantBase : public Internals::JsonPrintable<TImpl> {
public:
// Gets the variant as a boolean value.
// Returns false if the variant is not a boolean value.
Expand Down Expand Up @@ -79,6 +79,9 @@ class JsonVariantBase {
FORCE_INLINE const JsonObjectSubscript operator[](const char *key) const;
FORCE_INLINE const JsonObjectSubscript 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); }
};
Expand Down
12 changes: 12 additions & 0 deletions src/Internals/Encoding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson

#include "../../include/ArduinoJson/Internals/Encoding.hpp"

// How to escape special chars:
// _escapeTable[2*i+1] => the special char
// _escapeTable[2*i] => the char to use instead
const char ArduinoJson::Internals::Encoding::_escapeTable[] = "\"\"\\\\b\bf\fn\nr\rt\t";
Loading

0 comments on commit 92e6873

Please sign in to comment.