初始化提交
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
class EscapeSequence {
|
||||
public:
|
||||
// Optimized for code size on a 8-bit AVR
|
||||
static char escapeChar(char c) {
|
||||
const char *p = escapeTable(true);
|
||||
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(false);
|
||||
for (;;) {
|
||||
if (p[0] == '\0')
|
||||
return 0;
|
||||
if (p[0] == c)
|
||||
return p[1];
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const char *escapeTable(bool excludeSolidus) {
|
||||
return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0];
|
||||
}
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,743 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Deserialization/deserialize.hpp>
|
||||
#include <ArduinoJson/Json/EscapeSequence.hpp>
|
||||
#include <ArduinoJson/Json/Latch.hpp>
|
||||
#include <ArduinoJson/Json/Utf16.hpp>
|
||||
#include <ArduinoJson/Json/Utf8.hpp>
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Numbers/parseNumber.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/type_traits.hpp>
|
||||
#include <ArduinoJson/Variant/VariantData.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TReader, typename TStringStorage>
|
||||
class JsonDeserializer {
|
||||
public:
|
||||
JsonDeserializer(MemoryPool &pool, TReader reader,
|
||||
TStringStorage stringStorage)
|
||||
: _stringStorage(stringStorage),
|
||||
_foundSomething(false),
|
||||
_latch(reader),
|
||||
_pool(&pool),
|
||||
_error(DeserializationError::Ok) {}
|
||||
|
||||
template <typename TFilter>
|
||||
DeserializationError parse(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
parseVariant(variant, filter, nestingLimit);
|
||||
|
||||
if (!_error && _latch.last() != 0 && !variant.isEnclosed()) {
|
||||
// We don't detect trailing characters earlier, so we need to check now
|
||||
return DeserializationError::InvalidInput;
|
||||
}
|
||||
|
||||
return _error;
|
||||
}
|
||||
|
||||
private:
|
||||
JsonDeserializer &operator=(const JsonDeserializer &); // non-copiable
|
||||
|
||||
char current() {
|
||||
return _latch.current();
|
||||
}
|
||||
|
||||
void move() {
|
||||
_latch.clear();
|
||||
}
|
||||
|
||||
bool eat(char charToSkip) {
|
||||
if (current() != charToSkip)
|
||||
return false;
|
||||
move();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool parseVariant(VariantData &variant, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
switch (current()) {
|
||||
case '[':
|
||||
if (filter.allowArray())
|
||||
return parseArray(variant.toArray(), filter, nestingLimit);
|
||||
else
|
||||
return skipArray(nestingLimit);
|
||||
|
||||
case '{':
|
||||
if (filter.allowObject())
|
||||
return parseObject(variant.toObject(), filter, nestingLimit);
|
||||
else
|
||||
return skipObject(nestingLimit);
|
||||
|
||||
case '\"':
|
||||
case '\'':
|
||||
if (filter.allowValue())
|
||||
return parseStringValue(variant);
|
||||
else
|
||||
return skipString();
|
||||
|
||||
default:
|
||||
if (filter.allowValue())
|
||||
return parseNumericValue(variant);
|
||||
else
|
||||
return skipNumericValue();
|
||||
}
|
||||
}
|
||||
|
||||
bool skipVariant(NestingLimit nestingLimit) {
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
switch (current()) {
|
||||
case '[':
|
||||
return skipArray(nestingLimit);
|
||||
|
||||
case '{':
|
||||
return skipObject(nestingLimit);
|
||||
|
||||
case '\"':
|
||||
case '\'':
|
||||
return skipString();
|
||||
|
||||
default:
|
||||
return skipNumericValue();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool parseArray(CollectionData &array, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening braket
|
||||
ARDUINOJSON_ASSERT(current() == '[');
|
||||
move();
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Empty array?
|
||||
if (eat(']'))
|
||||
return true;
|
||||
|
||||
TFilter memberFilter = filter[0UL];
|
||||
|
||||
// Read each value
|
||||
for (;;) {
|
||||
if (memberFilter.allow()) {
|
||||
// Allocate slot in array
|
||||
VariantData *value = array.addElement(_pool);
|
||||
if (!value) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1 - Parse value
|
||||
if (!parseVariant(*value, memberFilter, nestingLimit.decrement()))
|
||||
return false;
|
||||
} else {
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2 - Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// 3 - More values?
|
||||
if (eat(']'))
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool skipArray(NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening braket
|
||||
ARDUINOJSON_ASSERT(current() == '[');
|
||||
move();
|
||||
|
||||
// Read each value
|
||||
for (;;) {
|
||||
// 1 - Skip value
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
|
||||
// 2 - Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// 3 - More values?
|
||||
if (eat(']'))
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TFilter>
|
||||
bool parseObject(CollectionData &object, TFilter filter,
|
||||
NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening brace
|
||||
ARDUINOJSON_ASSERT(current() == '{');
|
||||
move();
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Empty object?
|
||||
if (eat('}'))
|
||||
return true;
|
||||
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
// Parse key
|
||||
if (!parseKey())
|
||||
return false;
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Colon
|
||||
if (!eat(':')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *key = _stringStorage.c_str();
|
||||
|
||||
TFilter memberFilter = filter[key];
|
||||
|
||||
if (memberFilter.allow()) {
|
||||
VariantData *variant = object.getMember(adaptString(key));
|
||||
if (!variant) {
|
||||
// Save key in memory pool.
|
||||
// This MUST be done before adding the slot.
|
||||
key = _stringStorage.save();
|
||||
|
||||
// Allocate slot in object
|
||||
VariantSlot *slot = object.addSlot(_pool);
|
||||
if (!slot) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
slot->setKey(key, typename TStringStorage::storage_policy());
|
||||
|
||||
variant = slot->data();
|
||||
}
|
||||
|
||||
// Parse value
|
||||
if (!parseVariant(*variant, memberFilter, nestingLimit.decrement()))
|
||||
return false;
|
||||
} else {
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// More keys/values?
|
||||
if (eat('}'))
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool skipObject(NestingLimit nestingLimit) {
|
||||
if (nestingLimit.reached()) {
|
||||
_error = DeserializationError::TooDeep;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip opening brace
|
||||
ARDUINOJSON_ASSERT(current() == '{');
|
||||
move();
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Empty object?
|
||||
if (eat('}'))
|
||||
return true;
|
||||
|
||||
// Read each key value pair
|
||||
for (;;) {
|
||||
// Skip key
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// Colon
|
||||
if (!eat(':')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip value
|
||||
if (!skipVariant(nestingLimit.decrement()))
|
||||
return false;
|
||||
|
||||
// Skip spaces
|
||||
if (!skipSpacesAndComments())
|
||||
return false;
|
||||
|
||||
// More keys/values?
|
||||
if (eat('}'))
|
||||
return true;
|
||||
if (!eat(',')) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool parseKey() {
|
||||
_stringStorage.startString();
|
||||
if (isQuote(current())) {
|
||||
return parseQuotedString();
|
||||
} else {
|
||||
return parseNonQuotedString();
|
||||
}
|
||||
}
|
||||
|
||||
bool parseStringValue(VariantData &variant) {
|
||||
_stringStorage.startString();
|
||||
if (!parseQuotedString())
|
||||
return false;
|
||||
const char *value = _stringStorage.save();
|
||||
variant.setStringPointer(value, typename TStringStorage::storage_policy());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseQuotedString() {
|
||||
#if ARDUINOJSON_DECODE_UNICODE
|
||||
Utf16::Codepoint codepoint;
|
||||
#endif
|
||||
const char stopChar = current();
|
||||
|
||||
move();
|
||||
for (;;) {
|
||||
char c = current();
|
||||
move();
|
||||
if (c == stopChar)
|
||||
break;
|
||||
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == '\\') {
|
||||
c = current();
|
||||
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == 'u') {
|
||||
#if ARDUINOJSON_DECODE_UNICODE
|
||||
move();
|
||||
uint16_t codeunit;
|
||||
if (!parseHex4(codeunit))
|
||||
return false;
|
||||
if (codepoint.append(codeunit))
|
||||
Utf8::encodeCodepoint(codepoint.value(), _stringStorage);
|
||||
continue;
|
||||
#else
|
||||
_error = DeserializationError::NotSupported;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// replace char
|
||||
c = EscapeSequence::unescapeChar(c);
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
move();
|
||||
}
|
||||
|
||||
_stringStorage.append(c);
|
||||
}
|
||||
|
||||
_stringStorage.append('\0');
|
||||
|
||||
if (!_stringStorage.isValid()) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseNonQuotedString() {
|
||||
char c = current();
|
||||
ARDUINOJSON_ASSERT(c);
|
||||
|
||||
if (canBeInNonQuotedString(c)) { // no quotes
|
||||
do {
|
||||
move();
|
||||
_stringStorage.append(c);
|
||||
c = current();
|
||||
} while (canBeInNonQuotedString(c));
|
||||
} else {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
_stringStorage.append('\0');
|
||||
|
||||
if (!_stringStorage.isValid()) {
|
||||
_error = DeserializationError::NoMemory;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skipString() {
|
||||
const char stopChar = current();
|
||||
|
||||
move();
|
||||
for (;;) {
|
||||
char c = current();
|
||||
move();
|
||||
if (c == stopChar)
|
||||
break;
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
if (c == '\\') {
|
||||
if (current() != '\0')
|
||||
move();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseNumericValue(VariantData &result) {
|
||||
uint8_t n = 0;
|
||||
|
||||
char c = current();
|
||||
while (canBeInNonQuotedString(c) && n < 63) {
|
||||
move();
|
||||
_buffer[n++] = c;
|
||||
c = current();
|
||||
}
|
||||
_buffer[n] = 0;
|
||||
|
||||
c = _buffer[0];
|
||||
if (c == 't') { // true
|
||||
result.setBoolean(true);
|
||||
if (n != 4) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == 'f') { // false
|
||||
result.setBoolean(false);
|
||||
if (n != 5) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (c == 'n') { // null
|
||||
// the variant is already null
|
||||
if (n != 4) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parseNumber(_buffer, result)) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skipNumericValue() {
|
||||
char c = current();
|
||||
while (canBeInNonQuotedString(c)) {
|
||||
move();
|
||||
c = current();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parseHex4(uint16_t &result) {
|
||||
result = 0;
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
char digit = current();
|
||||
if (!digit) {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
uint8_t value = decodeHex(digit);
|
||||
if (value > 0x0F) {
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
result = uint16_t((result << 4) | value);
|
||||
move();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool isBetween(char c, char min, char max) {
|
||||
return min <= c && c <= max;
|
||||
}
|
||||
|
||||
static inline bool canBeInNonQuotedString(char c) {
|
||||
return isBetween(c, '0', '9') || isBetween(c, '_', 'z') ||
|
||||
isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
|
||||
}
|
||||
|
||||
static inline bool isQuote(char c) {
|
||||
return c == '\'' || c == '\"';
|
||||
}
|
||||
|
||||
static inline uint8_t decodeHex(char c) {
|
||||
if (c < 'A')
|
||||
return uint8_t(c - '0');
|
||||
c = char(c & ~0x20); // uppercase
|
||||
return uint8_t(c - 'A' + 10);
|
||||
}
|
||||
|
||||
bool skipSpacesAndComments() {
|
||||
for (;;) {
|
||||
switch (current()) {
|
||||
// end of string
|
||||
case '\0':
|
||||
_error = _foundSomething ? DeserializationError::IncompleteInput
|
||||
: DeserializationError::EmptyInput;
|
||||
return false;
|
||||
|
||||
// spaces
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
move();
|
||||
continue;
|
||||
|
||||
#if ARDUINOJSON_ENABLE_COMMENTS
|
||||
// comments
|
||||
case '/':
|
||||
move(); // skip '/'
|
||||
switch (current()) {
|
||||
// block comment
|
||||
case '*': {
|
||||
move(); // skip '*'
|
||||
bool wasStar = false;
|
||||
for (;;) {
|
||||
char c = current();
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
if (c == '/' && wasStar) {
|
||||
move();
|
||||
break;
|
||||
}
|
||||
wasStar = c == '*';
|
||||
move();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// trailing comment
|
||||
case '/':
|
||||
// no need to skip "//"
|
||||
for (;;) {
|
||||
move();
|
||||
char c = current();
|
||||
if (c == '\0') {
|
||||
_error = DeserializationError::IncompleteInput;
|
||||
return false;
|
||||
}
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// not a comment, just a '/'
|
||||
default:
|
||||
_error = DeserializationError::InvalidInput;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
_foundSomething = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TStringStorage _stringStorage;
|
||||
bool _foundSomething;
|
||||
Latch<TReader> _latch;
|
||||
MemoryPool *_pool;
|
||||
char _buffer[64]; // using a member instead of a local variable because it
|
||||
// ended in the recursive path after compiler inlined the
|
||||
// code
|
||||
DeserializationError _error;
|
||||
};
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, const std::string&, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TString>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, const TString &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TString>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, const TString &input,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, std::istream&, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TStream &input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TStream>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TStream &input,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, char*, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TChar *input,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
|
||||
}
|
||||
|
||||
//
|
||||
// deserializeJson(JsonDocument&, char*, size_t, ...)
|
||||
//
|
||||
// ... = NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
AllowAllFilter());
|
||||
}
|
||||
// ... = Filter, NestingLimit
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(
|
||||
JsonDocument &doc, TChar *input, size_t inputSize, Filter filter,
|
||||
NestingLimit nestingLimit = NestingLimit()) {
|
||||
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
// ... = NestingLimit, Filter
|
||||
template <typename TChar>
|
||||
DeserializationError deserializeJson(JsonDocument &doc, TChar *input,
|
||||
size_t inputSize,
|
||||
NestingLimit nestingLimit, Filter filter) {
|
||||
return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
|
||||
filter);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,135 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Json/TextFormatter.hpp>
|
||||
#include <ArduinoJson/Misc/Visitable.hpp>
|
||||
#include <ArduinoJson/Serialization/measure.hpp>
|
||||
#include <ArduinoJson/Serialization/serialize.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class JsonSerializer : public Visitor<size_t> {
|
||||
public:
|
||||
JsonSerializer(TWriter writer) : _formatter(writer) {}
|
||||
|
||||
FORCE_INLINE size_t visitArray(const CollectionData &array) {
|
||||
write('[');
|
||||
|
||||
VariantSlot *slot = array.head();
|
||||
|
||||
while (slot != 0) {
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
if (slot == 0)
|
||||
break;
|
||||
|
||||
write(',');
|
||||
}
|
||||
|
||||
write(']');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(const CollectionData &object) {
|
||||
write('{');
|
||||
|
||||
VariantSlot *slot = object.head();
|
||||
|
||||
while (slot != 0) {
|
||||
_formatter.writeString(slot->key());
|
||||
write(':');
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
if (slot == 0)
|
||||
break;
|
||||
|
||||
write(',');
|
||||
}
|
||||
|
||||
write('}');
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitFloat(Float value) {
|
||||
_formatter.writeFloat(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitString(const char *value) {
|
||||
_formatter.writeString(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitRawJson(const char *data, size_t n) {
|
||||
_formatter.writeRaw(data, n);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitNegativeInteger(UInt value) {
|
||||
_formatter.writeNegativeInteger(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitPositiveInteger(UInt value) {
|
||||
_formatter.writePositiveInteger(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitBoolean(bool value) {
|
||||
_formatter.writeBoolean(value);
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitNull() {
|
||||
_formatter.writeRaw("null");
|
||||
return bytesWritten();
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t bytesWritten() const {
|
||||
return _formatter.bytesWritten();
|
||||
}
|
||||
|
||||
void write(char c) {
|
||||
_formatter.writeRaw(c);
|
||||
}
|
||||
|
||||
void write(const char *s) {
|
||||
_formatter.writeRaw(s);
|
||||
}
|
||||
|
||||
private:
|
||||
TextFormatter<TWriter> _formatter;
|
||||
};
|
||||
|
||||
template <typename TSource, typename TDestination>
|
||||
size_t serializeJson(const TSource &source, TDestination &destination) {
|
||||
return serialize<JsonSerializer>(source, destination);
|
||||
}
|
||||
|
||||
template <typename TSource>
|
||||
size_t serializeJson(const TSource &source, void *buffer, size_t bufferSize) {
|
||||
return serialize<JsonSerializer>(source, buffer, bufferSize);
|
||||
}
|
||||
|
||||
template <typename TSource>
|
||||
size_t measureJson(const TSource &source) {
|
||||
return measure<JsonSerializer>(source);
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_ENABLE_STD_STREAM
|
||||
template <typename T>
|
||||
inline typename enable_if<IsVisitable<T>::value, std::ostream &>::type
|
||||
operator<<(std::ostream &os, const T &source) {
|
||||
serializeJson(source, os);
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,55 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TReader>
|
||||
class Latch {
|
||||
public:
|
||||
Latch(TReader reader) : _reader(reader), _loaded(false) {
|
||||
#if ARDUINOJSON_DEBUG
|
||||
_ended = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_loaded = false;
|
||||
}
|
||||
|
||||
int last() const {
|
||||
return _current;
|
||||
}
|
||||
|
||||
FORCE_INLINE char current() {
|
||||
if (!_loaded) {
|
||||
load();
|
||||
}
|
||||
return _current;
|
||||
}
|
||||
|
||||
private:
|
||||
void load() {
|
||||
ARDUINOJSON_ASSERT(!_ended);
|
||||
int c = _reader.read();
|
||||
#if ARDUINOJSON_DEBUG
|
||||
if (c <= 0)
|
||||
_ended = true;
|
||||
#endif
|
||||
_current = static_cast<char>(c > 0 ? c : 0);
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
TReader _reader;
|
||||
char _current;
|
||||
bool _loaded;
|
||||
#if ARDUINOJSON_DEBUG
|
||||
bool _ended;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,89 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Configuration.hpp>
|
||||
#include <ArduinoJson/Json/JsonSerializer.hpp>
|
||||
#include <ArduinoJson/Serialization/measure.hpp>
|
||||
#include <ArduinoJson/Serialization/serialize.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class PrettyJsonSerializer : public JsonSerializer<TWriter> {
|
||||
typedef JsonSerializer<TWriter> base;
|
||||
|
||||
public:
|
||||
PrettyJsonSerializer(TWriter &writer) : base(writer), _nesting(0) {}
|
||||
|
||||
size_t visitArray(const CollectionData &array) {
|
||||
VariantSlot *slot = array.head();
|
||||
if (slot) {
|
||||
base::write("[\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
indent();
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::write("]");
|
||||
} else {
|
||||
base::write("[]");
|
||||
}
|
||||
return this->bytesWritten();
|
||||
}
|
||||
|
||||
size_t visitObject(const CollectionData &object) {
|
||||
VariantSlot *slot = object.head();
|
||||
if (slot) {
|
||||
base::write("{\r\n");
|
||||
_nesting++;
|
||||
while (slot != 0) {
|
||||
indent();
|
||||
base::visitString(slot->key());
|
||||
base::write(": ");
|
||||
slot->data()->accept(*this);
|
||||
|
||||
slot = slot->next();
|
||||
base::write(slot ? ",\r\n" : "\r\n");
|
||||
}
|
||||
_nesting--;
|
||||
indent();
|
||||
base::write("}");
|
||||
} else {
|
||||
base::write("{}");
|
||||
}
|
||||
return this->bytesWritten();
|
||||
}
|
||||
|
||||
private:
|
||||
void indent() {
|
||||
for (uint8_t i = 0; i < _nesting; i++) base::write(ARDUINOJSON_TAB);
|
||||
}
|
||||
|
||||
uint8_t _nesting;
|
||||
};
|
||||
|
||||
template <typename TSource, typename TDestination>
|
||||
size_t serializeJsonPretty(const TSource &source, TDestination &destination) {
|
||||
return serialize<PrettyJsonSerializer>(source, destination);
|
||||
}
|
||||
|
||||
template <typename TSource>
|
||||
size_t serializeJsonPretty(const TSource &source, void *buffer,
|
||||
size_t bufferSize) {
|
||||
return serialize<PrettyJsonSerializer>(source, buffer, bufferSize);
|
||||
}
|
||||
|
||||
template <typename TSource>
|
||||
size_t measureJsonPretty(const TSource &source) {
|
||||
return measure<PrettyJsonSerializer>(source);
|
||||
}
|
||||
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,159 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h> // for strlen
|
||||
|
||||
#include <ArduinoJson/Json/EscapeSequence.hpp>
|
||||
#include <ArduinoJson/Numbers/FloatParts.hpp>
|
||||
#include <ArduinoJson/Numbers/Integer.hpp>
|
||||
#include <ArduinoJson/Polyfills/assert.hpp>
|
||||
#include <ArduinoJson/Polyfills/attributes.hpp>
|
||||
#include <ArduinoJson/Serialization/CountingDecorator.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
template <typename TWriter>
|
||||
class TextFormatter {
|
||||
public:
|
||||
explicit TextFormatter(TWriter writer) : _writer(writer) {}
|
||||
|
||||
// Returns the number of bytes sent to the TWriter implementation.
|
||||
size_t bytesWritten() const {
|
||||
return _writer.count();
|
||||
}
|
||||
|
||||
void writeBoolean(bool value) {
|
||||
if (value)
|
||||
writeRaw("true");
|
||||
else
|
||||
writeRaw("false");
|
||||
}
|
||||
|
||||
void writeString(const char *value) {
|
||||
ARDUINOJSON_ASSERT(value != NULL);
|
||||
writeRaw('\"');
|
||||
while (*value) writeChar(*value++);
|
||||
writeRaw('\"');
|
||||
}
|
||||
|
||||
void writeChar(char c) {
|
||||
char specialChar = EscapeSequence::escapeChar(c);
|
||||
if (specialChar) {
|
||||
writeRaw('\\');
|
||||
writeRaw(specialChar);
|
||||
} else {
|
||||
writeRaw(c);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writeFloat(T value) {
|
||||
if (isnan(value))
|
||||
return writeRaw(ARDUINOJSON_ENABLE_NAN ? "NaN" : "null");
|
||||
|
||||
#if ARDUINOJSON_ENABLE_INFINITY
|
||||
if (value < 0.0) {
|
||||
writeRaw('-');
|
||||
value = -value;
|
||||
}
|
||||
|
||||
if (isinf(value))
|
||||
return writeRaw("Infinity");
|
||||
#else
|
||||
if (isinf(value))
|
||||
return writeRaw("null");
|
||||
|
||||
if (value < 0.0) {
|
||||
writeRaw('-');
|
||||
value = -value;
|
||||
}
|
||||
#endif
|
||||
|
||||
FloatParts<T> parts(value);
|
||||
|
||||
writePositiveInteger(parts.integral);
|
||||
if (parts.decimalPlaces)
|
||||
writeDecimals(parts.decimal, parts.decimalPlaces);
|
||||
|
||||
if (parts.exponent < 0) {
|
||||
writeRaw("e-");
|
||||
writePositiveInteger(-parts.exponent);
|
||||
}
|
||||
|
||||
if (parts.exponent > 0) {
|
||||
writeRaw('e');
|
||||
writePositiveInteger(parts.exponent);
|
||||
}
|
||||
}
|
||||
|
||||
void writeNegativeInteger(UInt value) {
|
||||
writeRaw('-');
|
||||
writePositiveInteger(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void writePositiveInteger(T value) {
|
||||
char buffer[22];
|
||||
char *end = buffer + sizeof(buffer);
|
||||
char *begin = end;
|
||||
|
||||
// write the string in reverse order
|
||||
do {
|
||||
*--begin = char(value % 10 + '0');
|
||||
value = T(value / 10);
|
||||
} while (value);
|
||||
|
||||
// and dump it in the right order
|
||||
writeRaw(begin, end);
|
||||
}
|
||||
|
||||
void writeDecimals(uint32_t value, int8_t width) {
|
||||
// buffer should be big enough for all digits and the dot
|
||||
char buffer[16];
|
||||
char *end = buffer + sizeof(buffer);
|
||||
char *begin = end;
|
||||
|
||||
// write the string in reverse order
|
||||
while (width--) {
|
||||
*--begin = char(value % 10 + '0');
|
||||
value /= 10;
|
||||
}
|
||||
*--begin = '.';
|
||||
|
||||
// and dump it in the right order
|
||||
writeRaw(begin, end);
|
||||
}
|
||||
|
||||
void writeRaw(const char *s) {
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(s), strlen(s));
|
||||
}
|
||||
|
||||
void writeRaw(const char *s, size_t n) {
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(s), n);
|
||||
}
|
||||
|
||||
void writeRaw(const char *begin, const char *end) {
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(begin),
|
||||
static_cast<size_t>(end - begin));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void writeRaw(const char (&s)[N]) {
|
||||
_writer.write(reinterpret_cast<const uint8_t *>(s), N - 1);
|
||||
}
|
||||
void writeRaw(char c) {
|
||||
_writer.write(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
protected:
|
||||
CountingDecorator<TWriter> _writer;
|
||||
size_t _length;
|
||||
|
||||
private:
|
||||
TextFormatter &operator=(const TextFormatter &); // cannot be assigned
|
||||
};
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
@@ -0,0 +1,67 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
#include <stdint.h> // uint16_t, uint32_t
|
||||
|
||||
// The high surrogate may be uninitialized if the pair is invalid,
|
||||
// we choose to ignore the problem to reduce the size of the code
|
||||
// Garbage in => Garbage out
|
||||
#if defined(__GNUC__)
|
||||
#if __GNUC__ >= 7
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
namespace Utf16 {
|
||||
inline bool isHighSurrogate(uint16_t codeunit) {
|
||||
return codeunit >= 0xD800 && codeunit < 0xDC00;
|
||||
}
|
||||
|
||||
inline bool isLowSurrogate(uint16_t codeunit) {
|
||||
return codeunit >= 0xDC00 && codeunit < 0xE000;
|
||||
}
|
||||
|
||||
class Codepoint {
|
||||
public:
|
||||
Codepoint() : _highSurrogate(0) {}
|
||||
|
||||
bool append(uint16_t codeunit) {
|
||||
if (isHighSurrogate(codeunit)) {
|
||||
_highSurrogate = codeunit & 0x3FF;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isLowSurrogate(codeunit)) {
|
||||
_codepoint =
|
||||
uint32_t(0x10000 + ((_highSurrogate << 10) | (codeunit & 0x3FF)));
|
||||
return true;
|
||||
}
|
||||
|
||||
_codepoint = codeunit;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t value() const {
|
||||
return _codepoint;
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t _highSurrogate;
|
||||
uint32_t _codepoint;
|
||||
};
|
||||
} // namespace Utf16
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if __GNUC__ >= 8
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Namespace.hpp>
|
||||
|
||||
namespace ARDUINOJSON_NAMESPACE {
|
||||
|
||||
namespace Utf8 {
|
||||
template <typename TStringBuilder>
|
||||
inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
|
||||
// this function was optimize for code size on AVR
|
||||
|
||||
// a buffer to store the string in reverse
|
||||
char buf[5];
|
||||
char* p = buf;
|
||||
|
||||
*(p++) = 0;
|
||||
if (codepoint32 < 0x80) {
|
||||
*(p++) = char((codepoint32));
|
||||
} else {
|
||||
*(p++) = char((codepoint32 | 0x80) & 0xBF);
|
||||
uint16_t codepoint16 = uint16_t(codepoint32 >> 6);
|
||||
if (codepoint16 < 0x20) { // 0x800
|
||||
*(p++) = char(codepoint16 | 0xC0);
|
||||
} else {
|
||||
*(p++) = char((codepoint16 | 0x80) & 0xBF);
|
||||
codepoint16 = uint16_t(codepoint16 >> 6);
|
||||
if (codepoint16 < 0x10) { // 0x10000
|
||||
*(p++) = char(codepoint16 | 0xE0);
|
||||
} else {
|
||||
*(p++) = char((codepoint16 | 0x80) & 0xBF);
|
||||
codepoint16 = uint16_t(codepoint16 >> 6);
|
||||
*(p++) = char(codepoint16 | 0xF0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (*(--p)) {
|
||||
str.append(*p);
|
||||
}
|
||||
}
|
||||
} // namespace Utf8
|
||||
} // namespace ARDUINOJSON_NAMESPACE
|
||||
Reference in New Issue
Block a user