Reduced memory consumption by not duplicating spaces and comments

This commit is contained in:
Benoit Blanchon
2016-12-29 17:26:16 +01:00
parent 8032a4b564
commit 3f96e070ce
20 changed files with 596 additions and 287 deletions

View File

@ -5,6 +5,7 @@ HEAD
----
* Added operator `==` to compare `JsonVariant` and strings (issue #402)
* Reduced memory consumption by not duplicating spaces and comments
v5.7.3
------

View File

@ -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

View File

@ -30,6 +30,17 @@ template <typename TString>
struct StringFuncs<TString&> : StringFuncs<TString> {};
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) {
return strcmp(str, expected) == 0;
}
@ -71,6 +82,10 @@ struct StdStringFuncs {
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) {
return str == expected;
}
@ -99,6 +114,18 @@ struct StringFuncs<std::string> : StdStringFuncs<std::string> {};
#if ARDUINOJSON_ENABLE_PROGMEM
template <>
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) {
return strcmp_P(expected, (PGM_P)str) == 0;
}

View File

@ -12,51 +12,51 @@ namespace Internals {
template <typename TInput>
void skipSpacesAndComments(TInput& input) {
for (;;) {
switch (input.peek()) {
switch (input.current()) {
// spaces
case ' ':
case '\t':
case '\r':
case '\n':
input.skip();
input.move();
continue;
// comments
case '/':
switch (input.peekNext()) {
switch (input.next()) {
// C-style block comment
case '*':
input.skip(); // skip '/'
input.skip(); // skip '*'
input.move(); // skip '/'
input.move(); // skip '*'
for (;;) {
switch (input.peek()) {
switch (input.current()) {
case '\0':
return;
case '*':
input.skip(); // skip '*'
if (input.peek() == '/') {
input.skip(); // skip '/'
input.move(); // skip '*'
if (input.current() == '/') {
input.move(); // skip '/'
return;
}
break;
default:
input.skip();
input.move();
}
}
break;
// C++-style line comment
case '/':
input.skip(); // skip '/'
input.move(); // skip '/'
for (;;) {
switch (input.peek()) {
switch (input.current()) {
case '\0':
return;
case '\n':
input.skip();
input.move();
return;
default:
input.skip();
input.move();
}
}
return;

View File

@ -18,12 +18,14 @@ 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()
template <typename TReader, typename TWriter>
class JsonParser {
public:
JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit)
JsonParser(JsonBuffer *buffer, TReader reader, TWriter writer,
uint8_t nestingLimit)
: _buffer(buffer),
_reader(json),
_writer(json),
_reader(reader),
_writer(writer),
_nestingLimit(nestingLimit) {}
JsonArray &parseArray();
@ -36,7 +38,9 @@ class JsonParser {
}
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) {
return eat(_reader, charToSkip);
}
@ -63,9 +67,42 @@ class JsonParser {
}
JsonBuffer *_buffer;
StringReader _reader;
StringWriter _writer;
TReader _reader;
TWriter _writer;
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);
}
}
}

View File

@ -10,16 +10,19 @@
#include "Comments.hpp"
#include "JsonParser.hpp"
inline bool ArduinoJson::Internals::JsonParser::eat(StringReader &reader,
char charToSkip) {
template <typename TReader, typename TWriter>
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::eat(
TReader &reader, char charToSkip) {
skipSpacesAndComments(reader);
if (reader.peek() != charToSkip) return false;
reader.skip();
if (reader.current() != charToSkip) return false;
reader.move();
skipSpacesAndComments(reader);
return true;
}
inline bool ArduinoJson::Internals::JsonParser::parseAnythingTo(
template <typename TReader, typename TWriter>
inline bool
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingTo(
JsonVariant *destination) {
if (_nestingLimit == 0) return false;
_nestingLimit--;
@ -28,11 +31,13 @@ inline bool ArduinoJson::Internals::JsonParser::parseAnythingTo(
return success;
}
inline bool ArduinoJson::Internals::JsonParser::parseAnythingToUnsafe(
template <typename TReader, typename TWriter>
inline bool
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingToUnsafe(
JsonVariant *destination) {
skipSpacesAndComments(_reader);
switch (_reader.peek()) {
switch (_reader.current()) {
case '[':
return parseArrayTo(destination);
@ -44,8 +49,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseAnythingToUnsafe(
}
}
template <typename TReader, typename TWriter>
inline ArduinoJson::JsonArray &
ArduinoJson::Internals::JsonParser::parseArray() {
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArray() {
// Create an empty array
JsonArray &array = _buffer->createArray();
@ -76,7 +82,8 @@ ERROR_NO_MEMORY:
return JsonArray::invalid();
}
inline bool ArduinoJson::Internals::JsonParser::parseArrayTo(
template <typename TReader, typename TWriter>
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArrayTo(
JsonVariant *destination) {
JsonArray &array = parseArray();
if (!array.success()) return false;
@ -85,8 +92,9 @@ inline bool ArduinoJson::Internals::JsonParser::parseArrayTo(
return true;
}
template <typename TReader, typename TWriter>
inline ArduinoJson::JsonObject &
ArduinoJson::Internals::JsonParser::parseObject() {
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObject() {
// Create an empty object
JsonObject &object = _buffer->createObject();
@ -124,7 +132,8 @@ ERROR_NO_MEMORY:
return JsonObject::invalid();
}
inline bool ArduinoJson::Internals::JsonParser::parseObjectTo(
template <typename TReader, typename TWriter>
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObjectTo(
JsonVariant *destination) {
JsonObject &object = parseObject();
if (!object.success()) return false;
@ -133,46 +142,49 @@ inline bool ArduinoJson::Internals::JsonParser::parseObjectTo(
return true;
}
inline const char *ArduinoJson::Internals::JsonParser::parseString() {
const char *str = _writer.startString();
template <typename TReader, typename TWriter>
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
_reader.skip();
_reader.move();
char stopChar = c;
for (;;) {
c = _reader.peek();
c = _reader.current();
if (c == '\0') break;
_reader.skip();
_reader.move();
if (c == stopChar) break;
if (c == '\\') {
// replace char
c = Encoding::unescapeChar(_reader.peek());
c = Encoding::unescapeChar(_reader.current());
if (c == '\0') break;
_reader.skip();
_reader.move();
}
_writer.append(c);
str.append(c);
}
} else { // no quotes
for (;;) {
if (!isLetterOrNumber(c)) break;
_reader.skip();
_writer.append(c);
c = _reader.peek();
_reader.move();
str.append(c);
c = _reader.current();
}
}
_writer.stopString();
return str;
return str.c_str();
}
inline bool ArduinoJson::Internals::JsonParser::parseStringTo(
template <typename TReader, typename TWriter>
inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseStringTo(
JsonVariant *destination) {
bool hasQuotes = isQuote(_reader.peek());
bool hasQuotes = isQuote(_reader.current());
const char *value = parseString();
if (value == NULL) return false;
if (hasQuotes) {

View File

@ -13,24 +13,29 @@ 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()
template <typename TIterator>
class StringReader {
TIterator _input;
char _current, _next;
public:
StringReader(const char *input) : _ptr(input ? input : "") {}
void skip() {
_ptr++;
StringReader(const TIterator& input) : _input(input) {
_current = _input.next();
_next = _input.next();
}
char peek() const {
return _ptr[0];
void move() {
_current = _next;
_next = _input.next();
}
char peekNext() const {
return _ptr[1];
char current() const {
return _current;
}
private:
const char *_ptr;
char next() const {
return _next;
}
};
}
}

View File

@ -10,23 +10,30 @@
namespace ArduinoJson {
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 {
public:
StringWriter(char *buffer) : _ptr(buffer) {}
const char *startString() {
return _ptr;
}
void stopString() {
*_ptr++ = 0;
}
class String {
public:
String(char** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
void append(char c) {
*_ptr++ = c;
*(*_writePtr)++ = c;
}
const char* c_str() const {
*(*_writePtr)++ = 0;
return _startPtr;
}
private:
char** _writePtr;
char* _startPtr;
};
StringWriter(char* buffer) : _ptr(buffer) {}
String startString() {
return String(&_ptr);
}
private:

View File

@ -7,12 +7,153 @@
#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 {
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.
// You are strongly encouraged to consider using StaticJsonBuffer which is much
// more suitable for embedded systems.
typedef Internals::BlockJsonBuffer<Internals::DefaultAllocator>
DynamicJsonBuffer;
typedef DynamicJsonBufferBase<DefaultAllocator> DynamicJsonBuffer;
}

View File

@ -51,58 +51,6 @@ class JsonBuffer {
// allocation fails.
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
template <typename TString>
char *strdup(const TString &src) {
@ -114,7 +62,7 @@ class JsonBuffer {
virtual void *alloc(size_t size) = 0;
protected:
// Preserve aligment if nessary
// Preserve aligment if necessary
static FORCE_INLINE size_t round_size_up(size_t bytes) {
#if ARDUINOJSON_ENABLE_ALIGNMENT
const size_t x = sizeof(void *) - 1;

View 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

View File

@ -18,21 +18,3 @@ inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::createObject() {
JsonObject *ptr = new (this) JsonObject(this);
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();
}

View File

@ -7,7 +7,7 @@
#pragma once
#include "Internals/StringFuncs.hpp"
#include "Data/StringFuncs.hpp"
#include "JsonVariantBase.hpp"
#include "TypeTraits/EnableIf.hpp"

View File

@ -7,7 +7,7 @@
#pragma once
#include "JsonBuffer.hpp"
#include "JsonBufferBase.hpp"
#if defined(__clang__)
#pragma clang diagnostic push
@ -21,32 +21,87 @@
namespace ArduinoJson {
// 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 JsonBuffer {
class StaticJsonBufferBase : public JsonBufferBase<StaticJsonBufferBase> {
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 {
return CAPACITY;
return _capacity;
}
size_t size() const {
return _size;
}
virtual void* alloc(size_t bytes) {
if (_size + bytes > CAPACITY) return NULL;
void* p = &_buffer[_size];
_size += round_size_up(bytes);
return p;
alignNextAlloc();
if (!canAlloc(bytes)) return NULL;
return doAlloc(bytes);
}
String startString() {
return String(this);
}
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;
};
// 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__)

View File

@ -30,13 +30,19 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {
ASSERT_NE(p1, p2);
}
TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) {
size_t mask = sizeof(void*) - 1;
for (size_t size = 1; size <= sizeof(void*); size++) {
size_t addr = reinterpret_cast<size_t>(buffer.alloc(1));
ASSERT_EQ(0, addr & mask);
static bool isAligned(void* ptr) {
const size_t mask = sizeof(void*) - 1;
size_t addr = reinterpret_cast<size_t>(ptr);
return (addr & mask) == 0;
}
TEST_F(DynamicJsonBuffer_Basic_Tests, Alignment) {
// make room for tow but not three
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) {

View File

@ -5,18 +5,20 @@
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#include <gtest/gtest.h>
#include <ArduinoJson.h>
#include <gtest/gtest.h>
class NoMemoryAllocator {
public:
void* allocate(size_t) { return NULL; }
void* allocate(size_t) {
return NULL;
}
void deallocate(void*) {}
};
class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test {
protected:
Internals::BlockJsonBuffer<NoMemoryAllocator> _jsonBuffer;
DynamicJsonBufferBase<NoMemoryAllocator> _jsonBuffer;
};
TEST_F(DynamicJsonBuffer_NoMemory_Tests, FixCodeCoverage) {

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

View File

@ -5,12 +5,12 @@
// https://github.com/bblanchon/ArduinoJson
// If you like this project, please add a star!
#include <gtest/gtest.h>
#include <ArduinoJson.h>
#include <gtest/gtest.h>
class StaticJsonBuffer_ParseArray_Tests : public testing::Test {
protected:
void with(JsonBuffer& jsonBuffer) {
void with(StaticJsonBufferBase& jsonBuffer) {
_jsonBuffer = &jsonBuffer;
}
@ -27,7 +27,7 @@ class StaticJsonBuffer_ParseArray_Tests : public testing::Test {
}
private:
JsonBuffer* _jsonBuffer;
StaticJsonBufferBase* _jsonBuffer;
char _jsonString[256];
};
@ -86,3 +86,11 @@ TEST_F(StaticJsonBuffer_ParseArray_Tests, ConstCharPtrNull) {
.parseArray(static_cast<const char*>(0))
.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
}

View File

@ -10,7 +10,7 @@
class StaticJsonBuffer_ParseObject_Tests : public testing::Test {
protected:
void with(JsonBuffer& jsonBuffer) {
void with(StaticJsonBufferBase& jsonBuffer) {
_jsonBuffer = &jsonBuffer;
}
@ -27,7 +27,7 @@ class StaticJsonBuffer_ParseObject_Tests : public testing::Test {
}
private:
JsonBuffer* _jsonBuffer;
StaticJsonBufferBase* _jsonBuffer;
char _jsonString[256];
};

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