mirror of
https://github.com/bblanchon/ArduinoJson.git
synced 2025-07-27 01:07:30 +02:00
Compare commits
2 Commits
v5.0-beta-
...
v5.0-beta-
Author | SHA1 | Date | |
---|---|---|---|
92e687303d | |||
78ae0b8aee |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
/build
|
||||
/bin
|
||||
/lib
|
||||
/sftp-config.json
|
||||
|
@ -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
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,12 +1,13 @@
|
||||
ArduinoJson: change log
|
||||
=======================
|
||||
|
||||
v5.0 (currently under development)
|
||||
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 library layout (requires Arduino 1.0.6 or above)
|
||||
* Switched to new the library layout (requires Arduino 1.0.6 or above)
|
||||
|
||||
**BREAKING CHANGES**:
|
||||
- `JsonObject::add()` was renamed to `set()`
|
||||
@ -17,9 +18,18 @@ v5.0 (currently under development)
|
||||
Support of the `String` class has been added to the library because many people use it in their programs.
|
||||
However, you should not see this as an invitation to use the `String` class.
|
||||
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 predicate.
|
||||
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
|
||||
----
|
||||
|
||||
|
@ -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
include/ArduinoJson/Internals/Encoding.hpp
Normal file
39
include/ArduinoJson/Internals/Encoding.hpp
Normal file
@ -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[];
|
||||
};
|
||||
}
|
||||
}
|
@ -18,7 +18,10 @@ 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();
|
||||
@ -26,12 +29,10 @@ class JsonParser {
|
||||
private:
|
||||
bool skip(char charToSkip);
|
||||
bool skip(const char *wordToSkip);
|
||||
void skipSpaces();
|
||||
|
||||
bool parseAnythingTo(JsonVariant *destination);
|
||||
FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);
|
||||
|
||||
const char *parseString();
|
||||
bool parseAnythingTo(JsonVariant *destination);
|
||||
FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);
|
||||
|
||||
inline bool parseArrayTo(JsonVariant *destination);
|
||||
inline bool parseBooleanTo(JsonVariant *destination);
|
||||
@ -41,7 +42,8 @@ class JsonParser {
|
||||
inline bool parseStringTo(JsonVariant *destination);
|
||||
|
||||
JsonBuffer *_buffer;
|
||||
char *_ptr;
|
||||
const char *_readPtr;
|
||||
char *_writePtr;
|
||||
uint8_t _nestingLimit;
|
||||
};
|
||||
}
|
||||
|
@ -12,6 +12,10 @@
|
||||
#include "Prettyfier.hpp"
|
||||
#include "StringBuilder.hpp"
|
||||
|
||||
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include "StreamPrintAdapter.hpp"
|
||||
#endif
|
||||
|
||||
namespace ArduinoJson {
|
||||
namespace Internals {
|
||||
|
||||
@ -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);
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Arduino/Print.hpp"
|
||||
#include "QuotedString.hpp"
|
||||
#include "Encoding.hpp"
|
||||
|
||||
namespace ArduinoJson {
|
||||
namespace Internals {
|
||||
@ -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(']'); }
|
||||
@ -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);
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
// 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 {
|
||||
|
||||
// An helper class to print and extract doubly-quoted strings
|
||||
class QuotedString {
|
||||
public:
|
||||
// Writes a doubly-quote string to a Print implementation.
|
||||
// It adds the double quotes (") at the beginning and the end of the string.
|
||||
// It escapes the special characters as required by the JSON specifications.
|
||||
static size_t printTo(const char *, Print &);
|
||||
|
||||
// Reads a doubly-quoted string from a buffer.
|
||||
// It removes the double quotes (").
|
||||
// It unescapes the special character as required by the JSON specification,
|
||||
// with the exception of the Unicode characters (\u0000).
|
||||
static char *extractFrom(char *input, char **end);
|
||||
};
|
||||
}
|
||||
}
|
34
include/ArduinoJson/Internals/StreamPrintAdapter.hpp
Normal file
34
include/ArduinoJson/Internals/StreamPrintAdapter.hpp
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
@ -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); }
|
||||
};
|
||||
|
12
src/Internals/Encoding.cpp
Normal file
12
src/Internals/Encoding.cpp
Normal file
@ -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";
|
@ -9,7 +9,7 @@
|
||||
#include <stdlib.h> // for strtol, strtod
|
||||
#include <ctype.h>
|
||||
|
||||
#include "../../include/ArduinoJson/Internals/QuotedString.hpp"
|
||||
#include "../../include/ArduinoJson/Internals/Encoding.hpp"
|
||||
#include "../../include/ArduinoJson/JsonArray.hpp"
|
||||
#include "../../include/ArduinoJson/JsonBuffer.hpp"
|
||||
#include "../../include/ArduinoJson/JsonObject.hpp"
|
||||
@ -17,25 +17,28 @@
|
||||
using namespace ArduinoJson;
|
||||
using namespace ArduinoJson::Internals;
|
||||
|
||||
void JsonParser::skipSpaces() {
|
||||
while (isspace(*_ptr)) _ptr++;
|
||||
static const char *skipSpaces(const char *ptr) {
|
||||
while (isspace(*ptr)) ptr++;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool JsonParser::skip(char charToSkip) {
|
||||
skipSpaces();
|
||||
if (*_ptr != charToSkip) return false;
|
||||
_ptr++;
|
||||
skipSpaces();
|
||||
register const char *ptr = skipSpaces(_readPtr);
|
||||
if (*ptr != charToSkip) return false;
|
||||
ptr++;
|
||||
_readPtr = skipSpaces(ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonParser::skip(const char *wordToSkip) {
|
||||
const char *charToSkip = wordToSkip;
|
||||
while (*charToSkip && *_ptr == *charToSkip) {
|
||||
charToSkip++;
|
||||
_ptr++;
|
||||
register const char *ptr = _readPtr;
|
||||
while (*wordToSkip && *ptr == *wordToSkip) {
|
||||
wordToSkip++;
|
||||
ptr++;
|
||||
}
|
||||
return *charToSkip == '\0';
|
||||
if (*wordToSkip != '\0') return false;
|
||||
_readPtr = ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonParser::parseAnythingTo(JsonVariant *destination) {
|
||||
@ -47,9 +50,9 @@ bool JsonParser::parseAnythingTo(JsonVariant *destination) {
|
||||
}
|
||||
|
||||
inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) {
|
||||
skipSpaces();
|
||||
_readPtr = skipSpaces(_readPtr);
|
||||
|
||||
switch (*_ptr) {
|
||||
switch (*_readPtr) {
|
||||
case '[':
|
||||
return parseArrayTo(destination);
|
||||
|
||||
@ -181,7 +184,7 @@ bool JsonParser::parseBooleanTo(JsonVariant *destination) {
|
||||
|
||||
bool JsonParser::parseNumberTo(JsonVariant *destination) {
|
||||
char *endOfLong;
|
||||
long longValue = strtol(_ptr, &endOfLong, 10);
|
||||
long longValue = strtol(_readPtr, &endOfLong, 10);
|
||||
char stopChar = *endOfLong;
|
||||
|
||||
// Could it be a floating point value?
|
||||
@ -189,14 +192,14 @@ bool JsonParser::parseNumberTo(JsonVariant *destination) {
|
||||
|
||||
if (couldBeFloat) {
|
||||
// Yes => parse it as a double
|
||||
double doubleValue = strtod(_ptr, &_ptr);
|
||||
double doubleValue = strtod(_readPtr, const_cast<char **>(&_readPtr));
|
||||
// Count the decimal digits
|
||||
uint8_t decimals = static_cast<uint8_t>(_ptr - endOfLong - 1);
|
||||
uint8_t decimals = static_cast<uint8_t>(_readPtr - endOfLong - 1);
|
||||
// Set the variant as a double
|
||||
*destination = JsonVariant(doubleValue, decimals);
|
||||
} else {
|
||||
// No => set the variant as a long
|
||||
_ptr = endOfLong;
|
||||
_readPtr = endOfLong;
|
||||
*destination = longValue;
|
||||
}
|
||||
return true;
|
||||
@ -209,8 +212,53 @@ bool JsonParser::parseNullTo(JsonVariant *destination) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isStopChar(char c) {
|
||||
return c == '\0' || c == ':' || c == '}' || c == ']' || c == ',';
|
||||
}
|
||||
|
||||
const char *JsonParser::parseString() {
|
||||
return QuotedString::extractFrom(_ptr, &_ptr);
|
||||
const char *readPtr = _readPtr;
|
||||
char *writePtr = _writePtr;
|
||||
|
||||
char c = *readPtr;
|
||||
|
||||
if (c == '\'' || c == '\"') {
|
||||
char stopChar = c;
|
||||
for (;;) {
|
||||
c = *++readPtr;
|
||||
if (c == '\0') break;
|
||||
|
||||
if (c == stopChar) {
|
||||
readPtr++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
// replace char
|
||||
c = Encoding::unescapeChar(*++readPtr);
|
||||
if (c == '\0') break;
|
||||
}
|
||||
|
||||
*writePtr++ = c;
|
||||
}
|
||||
} else {
|
||||
for (;;) {
|
||||
if (isStopChar(c)) break;
|
||||
*writePtr++ = c;
|
||||
c = *++readPtr;
|
||||
}
|
||||
}
|
||||
// end the string here
|
||||
*writePtr++ = '\0';
|
||||
|
||||
const char *startPtr = _writePtr;
|
||||
|
||||
// update end ptr
|
||||
_readPtr = readPtr;
|
||||
_writePtr = writePtr;
|
||||
|
||||
// return pointer to unquoted string
|
||||
return startPtr;
|
||||
}
|
||||
|
||||
bool JsonParser::parseStringTo(JsonVariant *destination) {
|
||||
|
@ -1,103 +0,0 @@
|
||||
// Copyright Benoit Blanchon 2014-2015
|
||||
// MIT License
|
||||
//
|
||||
// Arduino JSON library
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include "../../include/ArduinoJson/Internals/QuotedString.hpp"
|
||||
|
||||
using namespace ArduinoJson::Internals;
|
||||
|
||||
// How to escape special chars:
|
||||
// specialChars[2*i+1] => the special char
|
||||
// specialChars[2*i] => the char to use instead
|
||||
static const char specialChars[] = "\"\"\\\\b\bf\fn\nr\rt\t";
|
||||
|
||||
static inline char getSpecialChar(char c) {
|
||||
// Optimized for code size on a 8-bit AVR
|
||||
|
||||
const char *p = specialChars;
|
||||
|
||||
while (p[0] && p[1] != c) {
|
||||
p += 2;
|
||||
}
|
||||
|
||||
return p[0];
|
||||
}
|
||||
|
||||
static inline size_t printCharTo(char c, Print &p) {
|
||||
char specialChar = getSpecialChar(c);
|
||||
|
||||
return specialChar ? p.write('\\') + p.write(specialChar) : p.write(c);
|
||||
}
|
||||
|
||||
size_t QuotedString::printTo(const char *s, Print &p) {
|
||||
if (!s) return p.print("null");
|
||||
|
||||
size_t n = p.write('\"');
|
||||
|
||||
while (*s) {
|
||||
n += printCharTo(*s++, p);
|
||||
}
|
||||
|
||||
return n + p.write('\"');
|
||||
}
|
||||
|
||||
static char unescapeChar(char c) {
|
||||
// Optimized for code size on a 8-bit AVR
|
||||
|
||||
const char *p = specialChars + 4;
|
||||
|
||||
for (;;) {
|
||||
if (p[0] == '\0') return c;
|
||||
if (p[0] == c) return p[1];
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool isQuote(char c) { return c == '\"' || c == '\''; }
|
||||
|
||||
char *QuotedString::extractFrom(char *input, char **endPtr) {
|
||||
char firstChar = *input;
|
||||
|
||||
if (!isQuote(firstChar)) {
|
||||
// must start with a quote
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char stopChar = firstChar; // closing quote is the same as opening quote
|
||||
|
||||
char *startPtr = input + 1; // skip the quote
|
||||
char *readPtr = startPtr;
|
||||
char *writePtr = startPtr;
|
||||
char c;
|
||||
|
||||
for (;;) {
|
||||
c = *readPtr++;
|
||||
|
||||
if (c == '\0') {
|
||||
// premature ending
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (c == stopChar) {
|
||||
// closing quote
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
// replace char
|
||||
c = unescapeChar(*readPtr++);
|
||||
}
|
||||
|
||||
*writePtr++ = c;
|
||||
}
|
||||
|
||||
// end the string here
|
||||
*writePtr = '\0';
|
||||
|
||||
// update end ptr
|
||||
*endPtr = readPtr;
|
||||
|
||||
return startPtr;
|
||||
}
|
@ -18,4 +18,4 @@ add_executable(ArduinoJsonTests
|
||||
|
||||
target_link_libraries(ArduinoJsonTests ArduinoJson)
|
||||
|
||||
add_test(ArduinoJsonTests ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ArduinoJsonTests)
|
||||
add_test(ArduinoJsonTests ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ArduinoJsonTests)
|
||||
|
@ -5,8 +5,8 @@
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include <ArduinoJson.h>
|
||||
#include "Printers.hpp"
|
||||
|
||||
class GbathreeBug : public testing::Test {
|
||||
public:
|
||||
|
@ -5,8 +5,8 @@
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include <ArduinoJson.h>
|
||||
#include "Printers.hpp"
|
||||
|
||||
class JsonObject_Iterator_Test : public testing::Test {
|
||||
public:
|
||||
|
@ -9,14 +9,19 @@
|
||||
|
||||
class JsonParser_Array_Tests : public testing::Test {
|
||||
protected:
|
||||
void whenInputIs(const char *json) {
|
||||
strcpy(_jsonString, json);
|
||||
_array = &_jsonBuffer.parseArray(_jsonString);
|
||||
void whenInputIs(const char *json) { strcpy(_jsonString, json); }
|
||||
|
||||
void whenInputIs(const char *json, size_t len) {
|
||||
memcpy(_jsonString, json, len);
|
||||
}
|
||||
|
||||
void parseMustSucceed() { EXPECT_TRUE(_array->success()); }
|
||||
void parseMustSucceed() {
|
||||
_array = &_jsonBuffer.parseArray(_jsonString);
|
||||
EXPECT_TRUE(_array->success());
|
||||
}
|
||||
|
||||
void parseMustFail() {
|
||||
_array = &_jsonBuffer.parseArray(_jsonString);
|
||||
EXPECT_FALSE(_array->success());
|
||||
EXPECT_EQ(0, _array->size());
|
||||
}
|
||||
@ -154,6 +159,11 @@ TEST_F(JsonParser_Array_Tests, IncompleteFalse) {
|
||||
parseMustFail();
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, MixedTrueFalse) {
|
||||
whenInputIs("[trufalse]");
|
||||
parseMustFail();
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, TwoStrings) {
|
||||
whenInputIs("[\"hello\",\"world\"]");
|
||||
|
||||
@ -162,3 +172,55 @@ TEST_F(JsonParser_Array_Tests, TwoStrings) {
|
||||
firstElementMustBe("hello");
|
||||
secondElementMustBe("world");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, EmptyStringsDoubleQuotes) {
|
||||
whenInputIs("[\"\",\"\"]");
|
||||
|
||||
parseMustSucceed();
|
||||
sizeMustBe(2);
|
||||
firstElementMustBe("");
|
||||
secondElementMustBe("");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, EmptyStringSingleQuotes) {
|
||||
whenInputIs("[\'\',\'\']");
|
||||
|
||||
parseMustSucceed();
|
||||
sizeMustBe(2);
|
||||
firstElementMustBe("");
|
||||
secondElementMustBe("");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, EmptyStringNoQuotes) {
|
||||
whenInputIs("[,]");
|
||||
|
||||
parseMustSucceed();
|
||||
sizeMustBe(2);
|
||||
firstElementMustBe("");
|
||||
secondElementMustBe("");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, ClosingDoubleQuoteMissing) {
|
||||
whenInputIs("[\"]");
|
||||
|
||||
parseMustFail();
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, ClosingSignleQuoteMissing) {
|
||||
whenInputIs("[\']");
|
||||
|
||||
parseMustFail();
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, StringWithEscapedChars) {
|
||||
whenInputIs("[\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"]");
|
||||
|
||||
parseMustSucceed();
|
||||
sizeMustBe(1);
|
||||
firstElementMustBe("1\"2\\3/4\b5\f6\n7\r8\t9");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Array_Tests, StringWithUnterminatedEscapeSequence) {
|
||||
whenInputIs("\"\\\0\"", 4);
|
||||
parseMustFail();
|
||||
}
|
||||
|
@ -75,6 +75,13 @@ TEST_F(JsonParser_Object_Test, OneStringSingleQuotes) {
|
||||
keyMustHaveValue("key", "value");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Object_Test, OneStringNoQuotes) {
|
||||
whenInputIs("{key:value}");
|
||||
parseMustSucceed();
|
||||
sizeMustBe(1);
|
||||
keyMustHaveValue("key", "value");
|
||||
}
|
||||
|
||||
TEST_F(JsonParser_Object_Test, OneStringSpaceBeforeKey) {
|
||||
whenInputIs("{ \"key\":\"value\"}");
|
||||
parseMustSucceed();
|
||||
|
@ -5,8 +5,8 @@
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include <ArduinoJson/JsonVariant.hpp>
|
||||
#include "Printers.hpp"
|
||||
|
||||
using namespace ArduinoJson;
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include <ArduinoJson.h>
|
||||
#include "Printers.hpp"
|
||||
|
||||
class JsonVariant_Undefined_Tests : public ::testing::Test {
|
||||
protected:
|
||||
|
@ -6,16 +6,18 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <ArduinoJson/Internals/QuotedString.hpp>
|
||||
#include <ArduinoJson/Internals/JsonWriter.hpp>
|
||||
#include <ArduinoJson/Internals/StringBuilder.hpp>
|
||||
|
||||
using namespace ArduinoJson::Internals;
|
||||
|
||||
class QuotedString_PrintTo_Tests : public testing::Test {
|
||||
class JsonWriter_WriteString_Tests : public testing::Test {
|
||||
protected:
|
||||
void whenInputIs(const char *input) {
|
||||
StringBuilder sb(buffer, sizeof(buffer));
|
||||
returnValue = QuotedString::printTo(input, sb);
|
||||
JsonWriter writer(sb);
|
||||
writer.writeString(input);
|
||||
returnValue = writer.bytesWritten();
|
||||
}
|
||||
|
||||
void outputMustBe(const char *expected) {
|
||||
@ -28,52 +30,52 @@ class QuotedString_PrintTo_Tests : public testing::Test {
|
||||
size_t returnValue;
|
||||
};
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, Null) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, Null) {
|
||||
whenInputIs(0);
|
||||
outputMustBe("null");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, EmptyString) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, EmptyString) {
|
||||
whenInputIs("");
|
||||
outputMustBe("\"\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, QuotationMark) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, QuotationMark) {
|
||||
whenInputIs("\"");
|
||||
outputMustBe("\"\\\"\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, ReverseSolidus) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, ReverseSolidus) {
|
||||
whenInputIs("\\");
|
||||
outputMustBe("\"\\\\\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, Solidus) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, Solidus) {
|
||||
whenInputIs("/");
|
||||
outputMustBe("\"/\""); // but the JSON format allows \/
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, Backspace) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, Backspace) {
|
||||
whenInputIs("\b");
|
||||
outputMustBe("\"\\b\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, Formfeed) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, Formfeed) {
|
||||
whenInputIs("\f");
|
||||
outputMustBe("\"\\f\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, Newline) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, Newline) {
|
||||
whenInputIs("\n");
|
||||
outputMustBe("\"\\n\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, CarriageReturn) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, CarriageReturn) {
|
||||
whenInputIs("\r");
|
||||
outputMustBe("\"\\r\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_PrintTo_Tests, HorizontalTab) {
|
||||
TEST_F(JsonWriter_WriteString_Tests, HorizontalTab) {
|
||||
whenInputIs("\t");
|
||||
outputMustBe("\"\\t\"");
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
// Copyright Benoit Blanchon 2014-2015
|
||||
// MIT License
|
||||
//
|
||||
// Arduino JSON library
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include "Printers.hpp"
|
||||
#include <ArduinoJson/JsonArray.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
std::ostream& ArduinoJson::operator<<(std::ostream& os,
|
||||
const ArduinoJson::JsonVariant& v) {
|
||||
StreamPrintAdapter adapter(os);
|
||||
v.printTo(adapter);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& ArduinoJson::operator<<(std::ostream& os,
|
||||
const ArduinoJson::JsonArray& v) {
|
||||
StreamPrintAdapter adapter(os);
|
||||
v.printTo(adapter);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& ArduinoJson::operator<<(
|
||||
std::ostream& os, const ArduinoJson::JsonObjectSubscript& v) {
|
||||
JsonVariant value = v;
|
||||
return os << value;
|
||||
}
|
||||
|
||||
std::ostream& ArduinoJson::operator<<(
|
||||
std::ostream& os, const ArduinoJson::JsonArraySubscript& v) {
|
||||
JsonVariant value = v;
|
||||
return os << value;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
// Copyright Benoit Blanchon 2014-2015
|
||||
// MIT License
|
||||
//
|
||||
// Arduino JSON library
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ostream>
|
||||
|
||||
namespace ArduinoJson {
|
||||
std::ostream& operator<<(std::ostream& os, const JsonVariant& v);
|
||||
std::ostream& operator<<(std::ostream& os, const JsonArray& v);
|
||||
std::ostream& operator<<(std::ostream& os, const JsonObjectSubscript& v);
|
||||
std::ostream& operator<<(std::ostream& os, const JsonArraySubscript& v);
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
// Copyright Benoit Blanchon 2014-2015
|
||||
// MIT License
|
||||
//
|
||||
// Arduino JSON library
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <ArduinoJson/Internals/QuotedString.hpp>
|
||||
|
||||
using namespace ArduinoJson::Internals;
|
||||
|
||||
class QuotedString_ExtractFrom_Tests : public testing::Test {
|
||||
protected:
|
||||
void whenInputIs(const char *json) {
|
||||
strcpy(_jsonString, json);
|
||||
_result = QuotedString::extractFrom(_jsonString, &_trailing);
|
||||
}
|
||||
|
||||
void resultMustBe(const char *expected) { EXPECT_STREQ(expected, _result); }
|
||||
|
||||
void trailingMustBe(const char *expected) {
|
||||
EXPECT_STREQ(expected, _trailing);
|
||||
}
|
||||
|
||||
private:
|
||||
char _jsonString[256];
|
||||
char *_result;
|
||||
char *_trailing;
|
||||
};
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EmptyDoubleQuotedString) {
|
||||
whenInputIs("\"\"");
|
||||
|
||||
resultMustBe("");
|
||||
trailingMustBe("");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, NoQuotes) {
|
||||
whenInputIs("hello world");
|
||||
|
||||
resultMustBe(0);
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, MissingClosingQuote) {
|
||||
whenInputIs("\"hello world");
|
||||
|
||||
resultMustBe(0);
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EmptySingleQuotedString) {
|
||||
whenInputIs("''");
|
||||
|
||||
resultMustBe("");
|
||||
trailingMustBe("");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, SimpleDoubleQuotedString) {
|
||||
whenInputIs("\"hello world\"");
|
||||
|
||||
resultMustBe("hello world");
|
||||
trailingMustBe("");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, DoubleQuotedStringWithTrailing) {
|
||||
whenInputIs("\"hello\" world");
|
||||
|
||||
resultMustBe("hello");
|
||||
trailingMustBe(" world");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, SingleQuotedStringWithTrailing) {
|
||||
whenInputIs("'hello' world");
|
||||
|
||||
resultMustBe("hello");
|
||||
trailingMustBe(" world");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, CurlyBraces) {
|
||||
whenInputIs("\"{hello:world}\"");
|
||||
resultMustBe("{hello:world}");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, SquareBraquets) {
|
||||
whenInputIs("\"[hello,world]\"");
|
||||
resultMustBe("[hello,world]");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedDoubleQuote) {
|
||||
whenInputIs("\"hello \\\"world\\\"\"");
|
||||
resultMustBe("hello \"world\"");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedSingleQuote) {
|
||||
whenInputIs("\"hello \\\'world\\\'\"");
|
||||
resultMustBe("hello 'world'");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedSolidus) {
|
||||
whenInputIs("\"hello \\/world\\/\"");
|
||||
resultMustBe("hello /world/");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedReverseSolidus) {
|
||||
whenInputIs("\"hello \\\\world\\\\\"");
|
||||
resultMustBe("hello \\world\\");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedBackspace) {
|
||||
whenInputIs("\"hello \\bworld\\b\"");
|
||||
resultMustBe("hello \bworld\b");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedFormfeed) {
|
||||
whenInputIs("\"hello \\fworld\\f\"");
|
||||
resultMustBe("hello \fworld\f");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedNewline) {
|
||||
whenInputIs("\"hello \\nworld\\n\"");
|
||||
resultMustBe("hello \nworld\n");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedCarriageReturn) {
|
||||
whenInputIs("\"hello \\rworld\\r\"");
|
||||
resultMustBe("hello \rworld\r");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, EscapedTab) {
|
||||
whenInputIs("\"hello \\tworld\\t\"");
|
||||
resultMustBe("hello \tworld\t");
|
||||
}
|
||||
|
||||
TEST_F(QuotedString_ExtractFrom_Tests, AllEscapedCharsTogether) {
|
||||
whenInputIs("\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"");
|
||||
resultMustBe("1\"2\\3/4\b5\f6\n7\r8\t9");
|
||||
}
|
62
test/StdStream.cpp
Normal file
62
test/StdStream.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright Benoit Blanchon 2014-2015
|
||||
// MIT License
|
||||
//
|
||||
// Arduino JSON library
|
||||
// https://github.com/bblanchon/ArduinoJson
|
||||
|
||||
#include <sstream>
|
||||
#include <gtest/gtest.h>
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
TEST(StdStream, JsonVariantFalse) {
|
||||
std::ostringstream os;
|
||||
JsonVariant variant = false;
|
||||
os << variant;
|
||||
ASSERT_EQ("false", os.str());
|
||||
}
|
||||
|
||||
TEST(StdStream, JsonVariantString) {
|
||||
std::ostringstream os;
|
||||
JsonVariant variant = "coucou";
|
||||
os << variant;
|
||||
ASSERT_EQ("\"coucou\"", os.str());
|
||||
}
|
||||
|
||||
TEST(StdStream, JsonObject) {
|
||||
std::ostringstream os;
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& object = jsonBuffer.createObject();
|
||||
object["key"] = "value";
|
||||
os << object;
|
||||
ASSERT_EQ("{\"key\":\"value\"}", os.str());
|
||||
}
|
||||
|
||||
TEST(StdStream, JsonObjectSubscript) {
|
||||
std::ostringstream os;
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonObject& object = jsonBuffer.createObject();
|
||||
object["key"] = "value";
|
||||
os << object["key"];
|
||||
ASSERT_EQ("\"value\"", os.str());
|
||||
}
|
||||
|
||||
TEST(StdStream, JsonArray) {
|
||||
std::ostringstream os;
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonArray& array = jsonBuffer.createArray();
|
||||
array.add("value");
|
||||
os << array;
|
||||
ASSERT_EQ("[\"value\"]", os.str());
|
||||
}
|
||||
|
||||
TEST(StdStream, JsonArraySubscript) {
|
||||
std::ostringstream os;
|
||||
DynamicJsonBuffer jsonBuffer;
|
||||
JsonArray& array = jsonBuffer.createArray();
|
||||
array.add("value");
|
||||
os << array[0];
|
||||
ASSERT_EQ("\"value\"", os.str());
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user