forked from bblanchon/ArduinoJson
Reduced memory consumption by not duplicating spaces and comments
This commit is contained in:
@ -5,6 +5,7 @@ HEAD
|
|||||||
----
|
----
|
||||||
|
|
||||||
* Added operator `==` to compare `JsonVariant` and strings (issue #402)
|
* Added operator `==` to compare `JsonVariant` and strings (issue #402)
|
||||||
|
* Reduced memory consumption by not duplicating spaces and comments
|
||||||
|
|
||||||
v5.7.3
|
v5.7.3
|
||||||
------
|
------
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
// 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 "../JsonBuffer.hpp"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#endif
|
|
||||||
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ArduinoJson {
|
|
||||||
namespace Internals {
|
|
||||||
class DefaultAllocator {
|
|
||||||
public:
|
|
||||||
void* allocate(size_t size) {
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
void deallocate(void* pointer) {
|
|
||||||
free(pointer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TAllocator>
|
|
||||||
class BlockJsonBuffer : public JsonBuffer {
|
|
||||||
struct Block;
|
|
||||||
struct EmptyBlock {
|
|
||||||
Block* next;
|
|
||||||
size_t capacity;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
struct Block : EmptyBlock {
|
|
||||||
uint8_t data[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
BlockJsonBuffer(size_t initialSize = 256)
|
|
||||||
: _head(NULL), _nextBlockSize(initialSize) {}
|
|
||||||
|
|
||||||
~BlockJsonBuffer() {
|
|
||||||
Block* currentBlock = _head;
|
|
||||||
|
|
||||||
while (currentBlock != NULL) {
|
|
||||||
Block* nextBlock = currentBlock->next;
|
|
||||||
_allocator.deallocate(currentBlock);
|
|
||||||
currentBlock = nextBlock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const {
|
|
||||||
size_t total = 0;
|
|
||||||
for (const Block* b = _head; b; b = b->next) total += b->size;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void* alloc(size_t bytes) {
|
|
||||||
return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool canAllocInHead(size_t bytes) const {
|
|
||||||
return _head != NULL && _head->size + bytes <= _head->capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* allocInHead(size_t bytes) {
|
|
||||||
void* p = _head->data + _head->size;
|
|
||||||
_head->size += round_size_up(bytes);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* allocInNewBlock(size_t bytes) {
|
|
||||||
size_t capacity = _nextBlockSize;
|
|
||||||
if (bytes > capacity) capacity = bytes;
|
|
||||||
if (!addNewBlock(capacity)) return NULL;
|
|
||||||
_nextBlockSize *= 2;
|
|
||||||
return allocInHead(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool addNewBlock(size_t capacity) {
|
|
||||||
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;
|
|
||||||
block->next = _head;
|
|
||||||
_head = block;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAllocator _allocator;
|
|
||||||
Block* _head;
|
|
||||||
size_t _nextBlockSize;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
#endif
|
|
@ -30,6 +30,17 @@ template <typename TString>
|
|||||||
struct StringFuncs<TString&> : StringFuncs<TString> {};
|
struct StringFuncs<TString&> : StringFuncs<TString> {};
|
||||||
|
|
||||||
struct CharPtrFuncs {
|
struct CharPtrFuncs {
|
||||||
|
class Iterator {
|
||||||
|
const char* _ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Iterator(const char* ptr) : _ptr(ptr ? ptr : "") {}
|
||||||
|
|
||||||
|
char next() {
|
||||||
|
return *_ptr++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static bool equals(const char* str, const char* expected) {
|
static bool equals(const char* str, const char* expected) {
|
||||||
return strcmp(str, expected) == 0;
|
return strcmp(str, expected) == 0;
|
||||||
}
|
}
|
||||||
@ -71,6 +82,10 @@ struct StdStringFuncs {
|
|||||||
return static_cast<char*>(dup);
|
return static_cast<char*>(dup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Iterator : CharPtrFuncs::Iterator {
|
||||||
|
Iterator(const TString& str) : CharPtrFuncs::Iterator(str.c_str()) {}
|
||||||
|
};
|
||||||
|
|
||||||
static bool equals(const TString& str, const char* expected) {
|
static bool equals(const TString& str, const char* expected) {
|
||||||
return str == expected;
|
return str == expected;
|
||||||
}
|
}
|
||||||
@ -99,6 +114,18 @@ struct StringFuncs<std::string> : StdStringFuncs<std::string> {};
|
|||||||
#if ARDUINOJSON_ENABLE_PROGMEM
|
#if ARDUINOJSON_ENABLE_PROGMEM
|
||||||
template <>
|
template <>
|
||||||
struct StringFuncs<const __FlashStringHelper*> {
|
struct StringFuncs<const __FlashStringHelper*> {
|
||||||
|
class Iterator {
|
||||||
|
const char* _ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Iterator(const __FlashStringHelper* ptr)
|
||||||
|
: _ptr(reinterpret_cast<const char*>(ptr)) {}
|
||||||
|
|
||||||
|
char next() {
|
||||||
|
return pgm_read_byte_near(_ptr++);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static bool equals(const __FlashStringHelper* str, const char* expected) {
|
static bool equals(const __FlashStringHelper* str, const char* expected) {
|
||||||
return strcmp_P(expected, (PGM_P)str) == 0;
|
return strcmp_P(expected, (PGM_P)str) == 0;
|
||||||
}
|
}
|
||||||
|
@ -12,51 +12,51 @@ namespace Internals {
|
|||||||
template <typename TInput>
|
template <typename TInput>
|
||||||
void skipSpacesAndComments(TInput& input) {
|
void skipSpacesAndComments(TInput& input) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (input.peek()) {
|
switch (input.current()) {
|
||||||
// spaces
|
// spaces
|
||||||
case ' ':
|
case ' ':
|
||||||
case '\t':
|
case '\t':
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\n':
|
case '\n':
|
||||||
input.skip();
|
input.move();
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// comments
|
// comments
|
||||||
case '/':
|
case '/':
|
||||||
switch (input.peekNext()) {
|
switch (input.next()) {
|
||||||
// C-style block comment
|
// C-style block comment
|
||||||
case '*':
|
case '*':
|
||||||
input.skip(); // skip '/'
|
input.move(); // skip '/'
|
||||||
input.skip(); // skip '*'
|
input.move(); // skip '*'
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (input.peek()) {
|
switch (input.current()) {
|
||||||
case '\0':
|
case '\0':
|
||||||
return;
|
return;
|
||||||
case '*':
|
case '*':
|
||||||
input.skip(); // skip '*'
|
input.move(); // skip '*'
|
||||||
if (input.peek() == '/') {
|
if (input.current() == '/') {
|
||||||
input.skip(); // skip '/'
|
input.move(); // skip '/'
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
input.skip();
|
input.move();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// C++-style line comment
|
// C++-style line comment
|
||||||
case '/':
|
case '/':
|
||||||
input.skip(); // skip '/'
|
input.move(); // skip '/'
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (input.peek()) {
|
switch (input.current()) {
|
||||||
case '\0':
|
case '\0':
|
||||||
return;
|
return;
|
||||||
case '\n':
|
case '\n':
|
||||||
input.skip();
|
input.move();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
input.skip();
|
input.move();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -18,12 +18,14 @@ namespace Internals {
|
|||||||
// Parse JSON string to create JsonArrays and JsonObjects
|
// Parse JSON string to create JsonArrays and JsonObjects
|
||||||
// This internal class is not indended to be used directly.
|
// This internal class is not indended to be used directly.
|
||||||
// Instead, use JsonBuffer.parseArray() or .parseObject()
|
// Instead, use JsonBuffer.parseArray() or .parseObject()
|
||||||
|
template <typename TReader, typename TWriter>
|
||||||
class JsonParser {
|
class JsonParser {
|
||||||
public:
|
public:
|
||||||
JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit)
|
JsonParser(JsonBuffer *buffer, TReader reader, TWriter writer,
|
||||||
|
uint8_t nestingLimit)
|
||||||
: _buffer(buffer),
|
: _buffer(buffer),
|
||||||
_reader(json),
|
_reader(reader),
|
||||||
_writer(json),
|
_writer(writer),
|
||||||
_nestingLimit(nestingLimit) {}
|
_nestingLimit(nestingLimit) {}
|
||||||
|
|
||||||
JsonArray &parseArray();
|
JsonArray &parseArray();
|
||||||
@ -36,7 +38,9 @@ class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool eat(StringReader &, char charToSkip);
|
JsonParser &operator=(const JsonParser &); // non-copiable
|
||||||
|
|
||||||
|
static bool eat(TReader &, char charToSkip);
|
||||||
FORCE_INLINE bool eat(char charToSkip) {
|
FORCE_INLINE bool eat(char charToSkip) {
|
||||||
return eat(_reader, charToSkip);
|
return eat(_reader, charToSkip);
|
||||||
}
|
}
|
||||||
@ -63,9 +67,42 @@ class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JsonBuffer *_buffer;
|
JsonBuffer *_buffer;
|
||||||
StringReader _reader;
|
TReader _reader;
|
||||||
StringWriter _writer;
|
TWriter _writer;
|
||||||
uint8_t _nestingLimit;
|
uint8_t _nestingLimit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename TJsonBuffer, typename TString>
|
||||||
|
struct JsonParserBuilder {
|
||||||
|
typedef typename Internals::StringFuncs<TString>::Iterator InputIterator;
|
||||||
|
typedef JsonParser<StringReader<InputIterator>, TJsonBuffer &> TParser;
|
||||||
|
|
||||||
|
static TParser makeParser(TJsonBuffer *buffer, const TString &json,
|
||||||
|
uint8_t nestingLimit) {
|
||||||
|
return TParser(buffer, InputIterator(json), *buffer, nestingLimit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TJsonBuffer>
|
||||||
|
struct JsonParserBuilder<TJsonBuffer, char *> {
|
||||||
|
typedef typename Internals::StringFuncs<char *>::Iterator InputIterator;
|
||||||
|
typedef JsonParser<StringReader<InputIterator>, StringWriter> TParser;
|
||||||
|
|
||||||
|
static TParser makeParser(TJsonBuffer *buffer, char *json,
|
||||||
|
uint8_t nestingLimit) {
|
||||||
|
return TParser(buffer, InputIterator(json), json, nestingLimit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TJsonBuffer, typename TChar, size_t N>
|
||||||
|
struct JsonParserBuilder<TJsonBuffer, TChar[N]>
|
||||||
|
: JsonParserBuilder<TJsonBuffer, TChar *> {};
|
||||||
|
|
||||||
|
template <typename TJsonBuffer, typename TString>
|
||||||
|
inline typename JsonParserBuilder<TJsonBuffer, TString>::TParser makeParser(
|
||||||
|
TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) {
|
||||||
|
return JsonParserBuilder<TJsonBuffer, TString>::makeParser(buffer, json,
|
||||||
|
nestingLimit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,19 @@
|
|||||||
#include "Comments.hpp"
|
#include "Comments.hpp"
|
||||||
#include "JsonParser.hpp"
|
#include "JsonParser.hpp"
|
||||||
|
|
||||||
inline bool ArduinoJson::Internals::JsonParser::eat(StringReader &reader,
|
template <typename TReader, typename TWriter>
|
||||||
char charToSkip) {
|
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::eat(
|
||||||
|
TReader &reader, char charToSkip) {
|
||||||
skipSpacesAndComments(reader);
|
skipSpacesAndComments(reader);
|
||||||
if (reader.peek() != charToSkip) return false;
|
if (reader.current() != charToSkip) return false;
|
||||||
reader.skip();
|
reader.move();
|
||||||
skipSpacesAndComments(reader);
|
skipSpacesAndComments(reader);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool ArduinoJson::Internals::JsonParser::parseAnythingTo(
|
template <typename TReader, typename TWriter>
|
||||||
|
inline bool
|
||||||
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingTo(
|
||||||
JsonVariant *destination) {
|
JsonVariant *destination) {
|
||||||
if (_nestingLimit == 0) return false;
|
if (_nestingLimit == 0) return false;
|
||||||
_nestingLimit--;
|
_nestingLimit--;
|
||||||
@ -28,11 +31,13 @@ inline bool ArduinoJson::Internals::JsonParser::parseAnythingTo(
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool ArduinoJson::Internals::JsonParser::parseAnythingToUnsafe(
|
template <typename TReader, typename TWriter>
|
||||||
|
inline bool
|
||||||
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingToUnsafe(
|
||||||
JsonVariant *destination) {
|
JsonVariant *destination) {
|
||||||
skipSpacesAndComments(_reader);
|
skipSpacesAndComments(_reader);
|
||||||
|
|
||||||
switch (_reader.peek()) {
|
switch (_reader.current()) {
|
||||||
case '[':
|
case '[':
|
||||||
return parseArrayTo(destination);
|
return parseArrayTo(destination);
|
||||||
|
|
||||||
@ -44,8 +49,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseAnythingToUnsafe(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TReader, typename TWriter>
|
||||||
inline ArduinoJson::JsonArray &
|
inline ArduinoJson::JsonArray &
|
||||||
ArduinoJson::Internals::JsonParser::parseArray() {
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArray() {
|
||||||
// Create an empty array
|
// Create an empty array
|
||||||
JsonArray &array = _buffer->createArray();
|
JsonArray &array = _buffer->createArray();
|
||||||
|
|
||||||
@ -76,7 +82,8 @@ ERROR_NO_MEMORY:
|
|||||||
return JsonArray::invalid();
|
return JsonArray::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool ArduinoJson::Internals::JsonParser::parseArrayTo(
|
template <typename TReader, typename TWriter>
|
||||||
|
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArrayTo(
|
||||||
JsonVariant *destination) {
|
JsonVariant *destination) {
|
||||||
JsonArray &array = parseArray();
|
JsonArray &array = parseArray();
|
||||||
if (!array.success()) return false;
|
if (!array.success()) return false;
|
||||||
@ -85,8 +92,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseArrayTo(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TReader, typename TWriter>
|
||||||
inline ArduinoJson::JsonObject &
|
inline ArduinoJson::JsonObject &
|
||||||
ArduinoJson::Internals::JsonParser::parseObject() {
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObject() {
|
||||||
// Create an empty object
|
// Create an empty object
|
||||||
JsonObject &object = _buffer->createObject();
|
JsonObject &object = _buffer->createObject();
|
||||||
|
|
||||||
@ -124,7 +132,8 @@ ERROR_NO_MEMORY:
|
|||||||
return JsonObject::invalid();
|
return JsonObject::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool ArduinoJson::Internals::JsonParser::parseObjectTo(
|
template <typename TReader, typename TWriter>
|
||||||
|
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObjectTo(
|
||||||
JsonVariant *destination) {
|
JsonVariant *destination) {
|
||||||
JsonObject &object = parseObject();
|
JsonObject &object = parseObject();
|
||||||
if (!object.success()) return false;
|
if (!object.success()) return false;
|
||||||
@ -133,46 +142,49 @@ inline bool ArduinoJson::Internals::JsonParser::parseObjectTo(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char *ArduinoJson::Internals::JsonParser::parseString() {
|
template <typename TReader, typename TWriter>
|
||||||
const char *str = _writer.startString();
|
inline const char *
|
||||||
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
|
||||||
|
typename TypeTraits::RemoveReference<TWriter>::type::String str =
|
||||||
|
_writer.startString();
|
||||||
|
|
||||||
char c = _reader.peek();
|
char c = _reader.current();
|
||||||
|
|
||||||
if (isQuote(c)) { // quotes
|
if (isQuote(c)) { // quotes
|
||||||
_reader.skip();
|
_reader.move();
|
||||||
char stopChar = c;
|
char stopChar = c;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = _reader.peek();
|
c = _reader.current();
|
||||||
if (c == '\0') break;
|
if (c == '\0') break;
|
||||||
_reader.skip();
|
_reader.move();
|
||||||
|
|
||||||
if (c == stopChar) break;
|
if (c == stopChar) break;
|
||||||
|
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
// replace char
|
// replace char
|
||||||
c = Encoding::unescapeChar(_reader.peek());
|
c = Encoding::unescapeChar(_reader.current());
|
||||||
if (c == '\0') break;
|
if (c == '\0') break;
|
||||||
_reader.skip();
|
_reader.move();
|
||||||
}
|
}
|
||||||
|
|
||||||
_writer.append(c);
|
str.append(c);
|
||||||
}
|
}
|
||||||
} else { // no quotes
|
} else { // no quotes
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!isLetterOrNumber(c)) break;
|
if (!isLetterOrNumber(c)) break;
|
||||||
_reader.skip();
|
_reader.move();
|
||||||
_writer.append(c);
|
str.append(c);
|
||||||
c = _reader.peek();
|
c = _reader.current();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_writer.stopString();
|
return str.c_str();
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool ArduinoJson::Internals::JsonParser::parseStringTo(
|
template <typename TReader, typename TWriter>
|
||||||
|
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseStringTo(
|
||||||
JsonVariant *destination) {
|
JsonVariant *destination) {
|
||||||
bool hasQuotes = isQuote(_reader.peek());
|
bool hasQuotes = isQuote(_reader.current());
|
||||||
const char *value = parseString();
|
const char *value = parseString();
|
||||||
if (value == NULL) return false;
|
if (value == NULL) return false;
|
||||||
if (hasQuotes) {
|
if (hasQuotes) {
|
||||||
|
@ -13,24 +13,29 @@ namespace Internals {
|
|||||||
// Parse JSON string to create JsonArrays and JsonObjects
|
// Parse JSON string to create JsonArrays and JsonObjects
|
||||||
// This internal class is not indended to be used directly.
|
// This internal class is not indended to be used directly.
|
||||||
// Instead, use JsonBuffer.parseArray() or .parseObject()
|
// Instead, use JsonBuffer.parseArray() or .parseObject()
|
||||||
|
template <typename TIterator>
|
||||||
class StringReader {
|
class StringReader {
|
||||||
|
TIterator _input;
|
||||||
|
char _current, _next;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StringReader(const char *input) : _ptr(input ? input : "") {}
|
StringReader(const TIterator& input) : _input(input) {
|
||||||
|
_current = _input.next();
|
||||||
void skip() {
|
_next = _input.next();
|
||||||
_ptr++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char peek() const {
|
void move() {
|
||||||
return _ptr[0];
|
_current = _next;
|
||||||
|
_next = _input.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
char peekNext() const {
|
char current() const {
|
||||||
return _ptr[1];
|
return _current;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
char next() const {
|
||||||
const char *_ptr;
|
return _next;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,27 +10,34 @@
|
|||||||
namespace ArduinoJson {
|
namespace ArduinoJson {
|
||||||
namespace Internals {
|
namespace Internals {
|
||||||
|
|
||||||
// Parse JSON string to create JsonArrays and JsonObjects
|
|
||||||
// This internal class is not indended to be used directly.
|
|
||||||
// Instead, use JsonBuffer.parseArray() or .parseObject()
|
|
||||||
class StringWriter {
|
class StringWriter {
|
||||||
public:
|
public:
|
||||||
StringWriter(char *buffer) : _ptr(buffer) {}
|
class String {
|
||||||
|
public:
|
||||||
const char *startString() {
|
String(char** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
|
||||||
return _ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stopString() {
|
|
||||||
*_ptr++ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void append(char c) {
|
void append(char c) {
|
||||||
*_ptr++ = c;
|
*(*_writePtr)++ = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* c_str() const {
|
||||||
|
*(*_writePtr)++ = 0;
|
||||||
|
return _startPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char *_ptr;
|
char** _writePtr;
|
||||||
|
char* _startPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringWriter(char* buffer) : _ptr(buffer) {}
|
||||||
|
|
||||||
|
String startString() {
|
||||||
|
return String(&_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* _ptr;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,153 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Data/BlockJsonBuffer.hpp"
|
#include "JsonBufferBase.hpp"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#endif
|
||||||
|
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ArduinoJson {
|
namespace ArduinoJson {
|
||||||
|
class DefaultAllocator {
|
||||||
|
public:
|
||||||
|
void* allocate(size_t size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
void deallocate(void* pointer) {
|
||||||
|
free(pointer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TAllocator>
|
||||||
|
class DynamicJsonBufferBase
|
||||||
|
: public JsonBufferBase<DynamicJsonBufferBase<TAllocator> > {
|
||||||
|
struct Block;
|
||||||
|
struct EmptyBlock {
|
||||||
|
Block* next;
|
||||||
|
size_t capacity;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
struct Block : EmptyBlock {
|
||||||
|
uint8_t data[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
DynamicJsonBufferBase(size_t initialSize = 256)
|
||||||
|
: _head(NULL), _nextBlockCapacity(initialSize) {}
|
||||||
|
|
||||||
|
~DynamicJsonBufferBase() {
|
||||||
|
Block* currentBlock = _head;
|
||||||
|
|
||||||
|
while (currentBlock != NULL) {
|
||||||
|
Block* nextBlock = currentBlock->next;
|
||||||
|
_allocator.deallocate(currentBlock);
|
||||||
|
currentBlock = nextBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
size_t total = 0;
|
||||||
|
for (const Block* b = _head; b; b = b->next) total += b->size;
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void* alloc(size_t bytes) {
|
||||||
|
alignNextAlloc();
|
||||||
|
return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
class String {
|
||||||
|
public:
|
||||||
|
String(DynamicJsonBufferBase* parent)
|
||||||
|
: _parent(parent), _start(NULL), _length(0) {}
|
||||||
|
|
||||||
|
void append(char c) {
|
||||||
|
if (_parent->canAllocInHead(1)) {
|
||||||
|
char* end = static_cast<char*>(_parent->allocInHead(1));
|
||||||
|
*end = c;
|
||||||
|
if (_length == 0) _start = end;
|
||||||
|
} else {
|
||||||
|
char* newStart =
|
||||||
|
static_cast<char*>(_parent->allocInNewBlock(_length + 1));
|
||||||
|
if (_start && newStart) memcpy(newStart, _start, _length);
|
||||||
|
newStart[_length] = c;
|
||||||
|
_start = newStart;
|
||||||
|
}
|
||||||
|
_length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* c_str() {
|
||||||
|
append(0);
|
||||||
|
return _start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
DynamicJsonBufferBase* _parent;
|
||||||
|
char* _start;
|
||||||
|
int _length;
|
||||||
|
};
|
||||||
|
|
||||||
|
String startString() {
|
||||||
|
return String(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void alignNextAlloc() {
|
||||||
|
if (_head) _head->size = this->round_size_up(_head->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canAllocInHead(size_t bytes) const {
|
||||||
|
return _head != NULL && _head->size + bytes <= _head->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* allocInHead(size_t bytes) {
|
||||||
|
void* p = _head->data + _head->size;
|
||||||
|
_head->size += bytes;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* allocInNewBlock(size_t bytes) {
|
||||||
|
size_t capacity = _nextBlockCapacity;
|
||||||
|
if (bytes > capacity) capacity = bytes;
|
||||||
|
if (!addNewBlock(capacity)) return NULL;
|
||||||
|
_nextBlockCapacity *= 2;
|
||||||
|
return allocInHead(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addNewBlock(size_t capacity) {
|
||||||
|
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;
|
||||||
|
block->next = _head;
|
||||||
|
_head = block;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TAllocator _allocator;
|
||||||
|
Block* _head;
|
||||||
|
size_t _nextBlockCapacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// Implements a JsonBuffer with dynamic memory allocation.
|
// Implements a JsonBuffer with dynamic memory allocation.
|
||||||
// You are strongly encouraged to consider using StaticJsonBuffer which is much
|
// You are strongly encouraged to consider using StaticJsonBuffer which is much
|
||||||
// more suitable for embedded systems.
|
// more suitable for embedded systems.
|
||||||
typedef Internals::BlockJsonBuffer<Internals::DefaultAllocator>
|
typedef DynamicJsonBufferBase<DefaultAllocator> DynamicJsonBuffer;
|
||||||
DynamicJsonBuffer;
|
|
||||||
}
|
}
|
||||||
|
@ -51,58 +51,6 @@ class JsonBuffer {
|
|||||||
// allocation fails.
|
// allocation fails.
|
||||||
JsonObject &createObject();
|
JsonObject &createObject();
|
||||||
|
|
||||||
// Allocates and populate a JsonArray from a JSON string.
|
|
||||||
//
|
|
||||||
// The First argument is a pointer to the JSON string, the memory must be
|
|
||||||
// writable
|
|
||||||
// because the parser will insert null-terminators and replace escaped chars.
|
|
||||||
//
|
|
||||||
// The second argument set the nesting limit
|
|
||||||
//
|
|
||||||
// Returns a reference to the new JsonObject or JsonObject::invalid() if the
|
|
||||||
// allocation fails.
|
|
||||||
JsonArray &parseArray(
|
|
||||||
char *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT);
|
|
||||||
|
|
||||||
// With this overload, the JsonBuffer will make a copy of the string
|
|
||||||
template <typename TString>
|
|
||||||
JsonArray &parseArray(const TString &json,
|
|
||||||
uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
|
||||||
return parseArray(strdup(json), nesting);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocates and populate a JsonObject from a JSON string.
|
|
||||||
//
|
|
||||||
// The First argument is a pointer to the JSON string, the memory must be
|
|
||||||
// writable
|
|
||||||
// because the parser will insert null-terminators and replace escaped chars.
|
|
||||||
//
|
|
||||||
// The second argument set the nesting limit
|
|
||||||
//
|
|
||||||
// Returns a reference to the new JsonObject or JsonObject::invalid() if the
|
|
||||||
// allocation fails.
|
|
||||||
JsonObject &parseObject(
|
|
||||||
char *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT);
|
|
||||||
|
|
||||||
// With this overload, the JsonBuffer will make a copy of the string
|
|
||||||
template <typename TString>
|
|
||||||
JsonObject &parseObject(const TString &json,
|
|
||||||
uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
|
||||||
return parseObject(strdup(json), nesting);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generalized version of parseArray() and parseObject(), also works for
|
|
||||||
// integral types.
|
|
||||||
JsonVariant parse(char *json,
|
|
||||||
uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT);
|
|
||||||
|
|
||||||
// With this overload, the JsonBuffer will make a copy of the string
|
|
||||||
template <typename TString>
|
|
||||||
JsonVariant parse(const TString &json,
|
|
||||||
uint8_t nesting = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
|
||||||
return parse(strdup(json), nesting);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duplicate a string
|
// Duplicate a string
|
||||||
template <typename TString>
|
template <typename TString>
|
||||||
char *strdup(const TString &src) {
|
char *strdup(const TString &src) {
|
||||||
@ -114,7 +62,7 @@ class JsonBuffer {
|
|||||||
virtual void *alloc(size_t size) = 0;
|
virtual void *alloc(size_t size) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Preserve aligment if nessary
|
// Preserve aligment if necessary
|
||||||
static FORCE_INLINE size_t round_size_up(size_t bytes) {
|
static FORCE_INLINE size_t round_size_up(size_t bytes) {
|
||||||
#if ARDUINOJSON_ENABLE_ALIGNMENT
|
#if ARDUINOJSON_ENABLE_ALIGNMENT
|
||||||
const size_t x = sizeof(void *) - 1;
|
const size_t x = sizeof(void *) - 1;
|
||||||
|
97
include/ArduinoJson/JsonBufferBase.hpp
Normal file
97
include/ArduinoJson/JsonBufferBase.hpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// 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 "Deserialization/JsonParser.hpp"
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#endif
|
||||||
|
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ArduinoJson {
|
||||||
|
template <typename TDerived>
|
||||||
|
class JsonBufferBase : public JsonBuffer {
|
||||||
|
public:
|
||||||
|
// Allocates and populate a JsonArray from a JSON string.
|
||||||
|
//
|
||||||
|
// The First argument is a pointer to the JSON string, the memory must be
|
||||||
|
// writable
|
||||||
|
// because the parser will insert null-terminators and replace escaped chars.
|
||||||
|
//
|
||||||
|
// The second argument set the nesting limit
|
||||||
|
//
|
||||||
|
// Returns a reference to the new JsonObject or JsonObject::invalid() if the
|
||||||
|
// allocation fails.
|
||||||
|
// With this overload, the JsonBuffer will make a copy of the string
|
||||||
|
template <typename TString>
|
||||||
|
JsonArray &parseArray(
|
||||||
|
const TString &json,
|
||||||
|
uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
||||||
|
return Internals::makeParser(that(), json, nestingLimit).parseArray();
|
||||||
|
}
|
||||||
|
template <typename TString>
|
||||||
|
JsonArray &parseArray(
|
||||||
|
TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
||||||
|
return Internals::makeParser(that(), json, nestingLimit).parseArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates and populate a JsonObject from a JSON string.
|
||||||
|
//
|
||||||
|
// The First argument is a pointer to the JSON string, the memory must be
|
||||||
|
// writable
|
||||||
|
// because the parser will insert null-terminators and replace escaped chars.
|
||||||
|
//
|
||||||
|
// The second argument set the nesting limit
|
||||||
|
//
|
||||||
|
// Returns a reference to the new JsonObject or JsonObject::invalid() if the
|
||||||
|
// allocation fails.
|
||||||
|
template <typename TString>
|
||||||
|
JsonObject &parseObject(
|
||||||
|
const TString &json,
|
||||||
|
uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
||||||
|
return Internals::makeParser(that(), json, nestingLimit).parseObject();
|
||||||
|
}
|
||||||
|
template <typename TString>
|
||||||
|
JsonObject &parseObject(
|
||||||
|
TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
||||||
|
return Internals::makeParser(that(), json, nestingLimit).parseObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generalized version of parseArray() and parseObject(), also works for
|
||||||
|
// integral types.
|
||||||
|
template <typename TString>
|
||||||
|
JsonVariant parse(const TString &json,
|
||||||
|
uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
||||||
|
return Internals::makeParser(that(), json, nestingLimit).parseVariant();
|
||||||
|
}
|
||||||
|
template <typename TString>
|
||||||
|
JsonVariant parse(TString &json,
|
||||||
|
uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
|
||||||
|
return Internals::makeParser(that(), json, nestingLimit).parseVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TDerived *that() {
|
||||||
|
return static_cast<TDerived *>(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
#endif
|
@ -18,21 +18,3 @@ inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::createObject() {
|
|||||||
JsonObject *ptr = new (this) JsonObject(this);
|
JsonObject *ptr = new (this) JsonObject(this);
|
||||||
return ptr ? *ptr : JsonObject::invalid();
|
return ptr ? *ptr : JsonObject::invalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ArduinoJson::JsonArray &ArduinoJson::JsonBuffer::parseArray(
|
|
||||||
char *json, uint8_t nestingLimit) {
|
|
||||||
Internals::JsonParser parser(this, json, nestingLimit);
|
|
||||||
return parser.parseArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::parseObject(
|
|
||||||
char *json, uint8_t nestingLimit) {
|
|
||||||
Internals::JsonParser parser(this, json, nestingLimit);
|
|
||||||
return parser.parseObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ArduinoJson::JsonVariant ArduinoJson::JsonBuffer::parse(
|
|
||||||
char *json, uint8_t nestingLimit) {
|
|
||||||
Internals::JsonParser parser(this, json, nestingLimit);
|
|
||||||
return parser.parseVariant();
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Internals/StringFuncs.hpp"
|
#include "Data/StringFuncs.hpp"
|
||||||
#include "JsonVariantBase.hpp"
|
#include "JsonVariantBase.hpp"
|
||||||
#include "TypeTraits/EnableIf.hpp"
|
#include "TypeTraits/EnableIf.hpp"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "JsonBuffer.hpp"
|
#include "JsonBufferBase.hpp"
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
@ -21,32 +21,87 @@
|
|||||||
|
|
||||||
namespace ArduinoJson {
|
namespace ArduinoJson {
|
||||||
|
|
||||||
// Implements a JsonBuffer with fixed memory allocation.
|
class StaticJsonBufferBase : public JsonBufferBase<StaticJsonBufferBase> {
|
||||||
// The template paramenter CAPACITY specifies the capacity of the buffer in
|
|
||||||
// bytes.
|
|
||||||
template <size_t CAPACITY>
|
|
||||||
class StaticJsonBuffer : public JsonBuffer {
|
|
||||||
public:
|
public:
|
||||||
explicit StaticJsonBuffer() : _size(0) {}
|
class String {
|
||||||
|
public:
|
||||||
|
String(StaticJsonBufferBase* parent) : _parent(parent) {
|
||||||
|
_start = parent->_buffer + parent->_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(char c) {
|
||||||
|
if (_parent->canAlloc(1)) {
|
||||||
|
char* last = static_cast<char*>(_parent->doAlloc(1));
|
||||||
|
*last = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* c_str() const {
|
||||||
|
if (_parent->canAlloc(1)) {
|
||||||
|
char* last = static_cast<char*>(_parent->doAlloc(1));
|
||||||
|
*last = '\0';
|
||||||
|
return _start;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
StaticJsonBufferBase* _parent;
|
||||||
|
char* _start;
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticJsonBufferBase(char* buffer, size_t capa)
|
||||||
|
: _buffer(buffer), _capacity(capa), _size(0) {}
|
||||||
|
|
||||||
size_t capacity() const {
|
size_t capacity() const {
|
||||||
return CAPACITY;
|
return _capacity;
|
||||||
}
|
}
|
||||||
size_t size() const {
|
size_t size() const {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void* alloc(size_t bytes) {
|
virtual void* alloc(size_t bytes) {
|
||||||
if (_size + bytes > CAPACITY) return NULL;
|
alignNextAlloc();
|
||||||
void* p = &_buffer[_size];
|
if (!canAlloc(bytes)) return NULL;
|
||||||
_size += round_size_up(bytes);
|
return doAlloc(bytes);
|
||||||
return p;
|
}
|
||||||
|
|
||||||
|
String startString() {
|
||||||
|
return String(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _buffer[CAPACITY];
|
void alignNextAlloc() {
|
||||||
|
_size = round_size_up(_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canAlloc(size_t bytes) const {
|
||||||
|
return _size + bytes <= _capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* doAlloc(size_t bytes) {
|
||||||
|
void* p = &_buffer[_size];
|
||||||
|
_size += bytes;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* _buffer;
|
||||||
|
size_t _capacity;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Implements a JsonBuffer with fixed memory allocation.
|
||||||
|
// The template paramenter CAPACITY specifies the capacity of the buffer in
|
||||||
|
// bytes.
|
||||||
|
template <size_t CAPACITY>
|
||||||
|
class StaticJsonBuffer : public StaticJsonBufferBase {
|
||||||
|
public:
|
||||||
|
explicit StaticJsonBuffer() : StaticJsonBufferBase(_buffer, CAPACITY) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
char _buffer[CAPACITY];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
|
@ -30,13 +30,19 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {
|
|||||||
ASSERT_NE(p1, p2);
|
ASSERT_NE(p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) {
|
static bool isAligned(void* ptr) {
|
||||||
size_t mask = sizeof(void*) - 1;
|
const size_t mask = sizeof(void*) - 1;
|
||||||
|
size_t addr = reinterpret_cast<size_t>(ptr);
|
||||||
|
return (addr & mask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t size = 1; size <= sizeof(void*); size++) {
|
TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) {
|
||||||
size_t addr = reinterpret_cast<size_t>(buffer.alloc(1));
|
// make room for tow but not three
|
||||||
ASSERT_EQ(0, addr & mask);
|
buffer = DynamicJsonBuffer(2 * sizeof(void*) + 1);
|
||||||
}
|
|
||||||
|
ASSERT_TRUE(isAligned(buffer.alloc(1))); // this on is aligned by design
|
||||||
|
ASSERT_TRUE(isAligned(buffer.alloc(1))); // this one fits in the first block
|
||||||
|
ASSERT_TRUE(isAligned(buffer.alloc(1))); // this one requires a new block
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DynamicJsonBuffer_Basic_Tests, strdup) {
|
TEST_F(DynamicJsonBuffer_Basic_Tests, strdup) {
|
||||||
|
@ -5,18 +5,20 @@
|
|||||||
// https://github.com/bblanchon/ArduinoJson
|
// https://github.com/bblanchon/ArduinoJson
|
||||||
// If you like this project, please add a star!
|
// If you like this project, please add a star!
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
class NoMemoryAllocator {
|
class NoMemoryAllocator {
|
||||||
public:
|
public:
|
||||||
void* allocate(size_t) { return NULL; }
|
void* allocate(size_t) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
void deallocate(void*) {}
|
void deallocate(void*) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test {
|
class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
Internals::BlockJsonBuffer<NoMemoryAllocator> _jsonBuffer;
|
DynamicJsonBufferBase<NoMemoryAllocator> _jsonBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(DynamicJsonBuffer_NoMemory_Tests, FixCodeCoverage) {
|
TEST_F(DynamicJsonBuffer_NoMemory_Tests, FixCodeCoverage) {
|
||||||
|
48
test/DynamicJsonBuffer_String_Tests.cpp
Normal file
48
test/DynamicJsonBuffer_String_Tests.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright Benoit Blanchon 2014-2016
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Arduino JSON library
|
||||||
|
// https://github.com/bblanchon/ArduinoJson
|
||||||
|
// If you like this project, please add a star!
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(DynamicJsonBuffer_String_Tests, WorksWhenBufferIsBigEnough) {
|
||||||
|
DynamicJsonBuffer jsonBuffer(6);
|
||||||
|
|
||||||
|
DynamicJsonBuffer::String str = jsonBuffer.startString();
|
||||||
|
str.append('h');
|
||||||
|
str.append('e');
|
||||||
|
str.append('l');
|
||||||
|
str.append('l');
|
||||||
|
str.append('o');
|
||||||
|
|
||||||
|
ASSERT_STREQ("hello", str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DynamicJsonBuffer_String_Tests, GrowsWhenBufferIsTooSmall) {
|
||||||
|
DynamicJsonBuffer jsonBuffer(5);
|
||||||
|
|
||||||
|
DynamicJsonBuffer::String str = jsonBuffer.startString();
|
||||||
|
str.append('h');
|
||||||
|
str.append('e');
|
||||||
|
str.append('l');
|
||||||
|
str.append('l');
|
||||||
|
str.append('o');
|
||||||
|
|
||||||
|
ASSERT_STREQ("hello", str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DynamicJsonBuffer_String_Tests, SizeIncreases) {
|
||||||
|
DynamicJsonBuffer jsonBuffer(5);
|
||||||
|
|
||||||
|
DynamicJsonBuffer::String str = jsonBuffer.startString();
|
||||||
|
ASSERT_EQ(0, jsonBuffer.size());
|
||||||
|
|
||||||
|
str.append('h');
|
||||||
|
ASSERT_EQ(1, jsonBuffer.size());
|
||||||
|
|
||||||
|
str.c_str();
|
||||||
|
ASSERT_EQ(2, jsonBuffer.size());
|
||||||
|
}
|
@ -5,12 +5,12 @@
|
|||||||
// https://github.com/bblanchon/ArduinoJson
|
// https://github.com/bblanchon/ArduinoJson
|
||||||
// If you like this project, please add a star!
|
// If you like this project, please add a star!
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
class StaticJsonBuffer_ParseArray_Tests : public testing::Test {
|
class StaticJsonBuffer_ParseArray_Tests : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void with(JsonBuffer& jsonBuffer) {
|
void with(StaticJsonBufferBase& jsonBuffer) {
|
||||||
_jsonBuffer = &jsonBuffer;
|
_jsonBuffer = &jsonBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class StaticJsonBuffer_ParseArray_Tests : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JsonBuffer* _jsonBuffer;
|
StaticJsonBufferBase* _jsonBuffer;
|
||||||
char _jsonString[256];
|
char _jsonString[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,3 +86,11 @@ TEST_F(StaticJsonBuffer_ParseArray_Tests, ConstCharPtrNull) {
|
|||||||
.parseArray(static_cast<const char*>(0))
|
.parseArray(static_cast<const char*>(0))
|
||||||
.success());
|
.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(StaticJsonBuffer_ParseArray_Tests, CopyStringNotSpaces) {
|
||||||
|
StaticJsonBuffer<100> jsonBuffer;
|
||||||
|
jsonBuffer.parseArray(" [ \"1234567\" ] ");
|
||||||
|
ASSERT_EQ(JSON_ARRAY_SIZE(1) + sizeof("1234567"), jsonBuffer.size());
|
||||||
|
// note we use a string of 8 bytes to be sure that the StaticJsonBuffer
|
||||||
|
// will not insert bytes to enforce alignement
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
class StaticJsonBuffer_ParseObject_Tests : public testing::Test {
|
class StaticJsonBuffer_ParseObject_Tests : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
void with(JsonBuffer& jsonBuffer) {
|
void with(StaticJsonBufferBase& jsonBuffer) {
|
||||||
_jsonBuffer = &jsonBuffer;
|
_jsonBuffer = &jsonBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class StaticJsonBuffer_ParseObject_Tests : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JsonBuffer* _jsonBuffer;
|
StaticJsonBufferBase* _jsonBuffer;
|
||||||
char _jsonString[256];
|
char _jsonString[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
48
test/StaticJsonBuffer_String_Tests.cpp
Normal file
48
test/StaticJsonBuffer_String_Tests.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright Benoit Blanchon 2014-2016
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Arduino JSON library
|
||||||
|
// https://github.com/bblanchon/ArduinoJson
|
||||||
|
// If you like this project, please add a star!
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(StaticJsonBuffer_String_Tests, WorksWhenBufferIsBigEnough) {
|
||||||
|
StaticJsonBuffer<6> jsonBuffer;
|
||||||
|
|
||||||
|
StaticJsonBufferBase::String str = jsonBuffer.startString();
|
||||||
|
str.append('h');
|
||||||
|
str.append('e');
|
||||||
|
str.append('l');
|
||||||
|
str.append('l');
|
||||||
|
str.append('o');
|
||||||
|
|
||||||
|
ASSERT_STREQ("hello", str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StaticJsonBuffer_String_Tests, ReturnsNullWhenTooSmall) {
|
||||||
|
StaticJsonBuffer<5> jsonBuffer;
|
||||||
|
|
||||||
|
StaticJsonBufferBase::String str = jsonBuffer.startString();
|
||||||
|
str.append('h');
|
||||||
|
str.append('e');
|
||||||
|
str.append('l');
|
||||||
|
str.append('l');
|
||||||
|
str.append('o');
|
||||||
|
|
||||||
|
ASSERT_EQ(NULL, str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StaticJsonBuffer_String_Tests, SizeIncreases) {
|
||||||
|
StaticJsonBuffer<5> jsonBuffer;
|
||||||
|
|
||||||
|
StaticJsonBufferBase::String str = jsonBuffer.startString();
|
||||||
|
ASSERT_EQ(0, jsonBuffer.size());
|
||||||
|
|
||||||
|
str.append('h');
|
||||||
|
ASSERT_EQ(1, jsonBuffer.size());
|
||||||
|
|
||||||
|
str.c_str();
|
||||||
|
ASSERT_EQ(2, jsonBuffer.size());
|
||||||
|
}
|
Reference in New Issue
Block a user