Reduced stack usage when compiled with -Og (issue #1210)

This saves 112 bytes on ESP8266
This commit is contained in:
Benoit Blanchon
2020-07-26 12:58:12 +02:00
parent 824b7a25ca
commit 51b177ce47

View File

@ -22,19 +22,22 @@ class JsonDeserializer {
public: public:
JsonDeserializer(MemoryPool &pool, TReader reader, JsonDeserializer(MemoryPool &pool, TReader reader,
TStringStorage stringStorage) TStringStorage stringStorage)
: _stringStorage(stringStorage), _latch(reader), _pool(&pool) {} : _stringStorage(stringStorage),
_latch(reader),
_pool(&pool),
_error(DeserializationError::Ok) {}
template <typename TFilter> template <typename TFilter>
DeserializationError parse(VariantData &variant, TFilter filter, DeserializationError parse(VariantData &variant, TFilter filter,
NestingLimit nestingLimit) { NestingLimit nestingLimit) {
DeserializationError err = parseVariant(variant, filter, nestingLimit); parseVariant(variant, filter, nestingLimit);
if (!err && _latch.last() != 0 && !variant.isEnclosed()) { if (!_error && _latch.last() != 0 && !variant.isEnclosed()) {
// We don't detect trailing characters earlier, so we need to check now // We don't detect trailing characters earlier, so we need to check now
err = DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
} }
return err; return _error;
} }
private: private:
@ -56,11 +59,10 @@ class JsonDeserializer {
} }
template <typename TFilter> template <typename TFilter>
DeserializationError parseVariant(VariantData &variant, TFilter filter, bool parseVariant(VariantData &variant, TFilter filter,
NestingLimit nestingLimit) { NestingLimit nestingLimit) {
DeserializationError err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
switch (current()) { switch (current()) {
case '[': case '[':
@ -90,10 +92,9 @@ class JsonDeserializer {
} }
} }
DeserializationError skipVariant(NestingLimit nestingLimit) { bool skipVariant(NestingLimit nestingLimit) {
DeserializationError err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
switch (current()) { switch (current()) {
case '[': case '[':
@ -112,23 +113,24 @@ class JsonDeserializer {
} }
template <typename TFilter> template <typename TFilter>
DeserializationError parseArray(CollectionData &array, TFilter filter, bool parseArray(CollectionData &array, TFilter filter,
NestingLimit nestingLimit) { NestingLimit nestingLimit) {
if (nestingLimit.reached()) if (nestingLimit.reached()) {
return DeserializationError::TooDeep; _error = DeserializationError::TooDeep;
return false;
}
// Skip opening braket // Skip opening braket
ARDUINOJSON_ASSERT(current() == '['); ARDUINOJSON_ASSERT(current() == '[');
move(); move();
// Skip spaces // Skip spaces
DeserializationError err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// Empty array? // Empty array?
if (eat(']')) if (eat(']'))
return DeserializationError::Ok; return true;
TFilter memberFilter = filter[0UL]; TFilter memberFilter = filter[0UL];
@ -137,35 +139,38 @@ class JsonDeserializer {
if (memberFilter.allow()) { if (memberFilter.allow()) {
// Allocate slot in array // Allocate slot in array
VariantData *value = array.addElement(_pool); VariantData *value = array.addElement(_pool);
if (!value) if (!value) {
return DeserializationError::NoMemory; _error = DeserializationError::NoMemory;
return false;
}
// 1 - Parse value // 1 - Parse value
err = parseVariant(*value, memberFilter, nestingLimit.decrement()); if (!parseVariant(*value, memberFilter, nestingLimit.decrement()))
if (err) return false;
return err;
} else { } else {
err = skipVariant(nestingLimit.decrement()); if (!skipVariant(nestingLimit.decrement()))
if (err) return false;
return err;
} }
// 2 - Skip spaces // 2 - Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// 3 - More values? // 3 - More values?
if (eat(']')) if (eat(']'))
return DeserializationError::Ok; return true;
if (!eat(',')) if (!eat(',')) {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
} }
} }
DeserializationError skipArray(NestingLimit nestingLimit) { bool skipArray(NestingLimit nestingLimit) {
if (nestingLimit.reached()) if (nestingLimit.reached()) {
return DeserializationError::TooDeep; _error = DeserializationError::TooDeep;
return false;
}
// Skip opening braket // Skip opening braket
ARDUINOJSON_ASSERT(current() == '['); ARDUINOJSON_ASSERT(current() == '[');
@ -174,55 +179,60 @@ class JsonDeserializer {
// Read each value // Read each value
for (;;) { for (;;) {
// 1 - Skip value // 1 - Skip value
DeserializationError err = skipVariant(nestingLimit.decrement()); if (!skipVariant(nestingLimit.decrement()))
if (err) return false;
return err;
// 2 - Skip spaces // 2 - Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// 3 - More values? // 3 - More values?
if (eat(']')) if (eat(']'))
return DeserializationError::Ok; return true;
if (!eat(',')) if (!eat(',')) {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
} }
} }
template <typename TFilter> template <typename TFilter>
DeserializationError parseObject(CollectionData &object, TFilter filter, bool parseObject(CollectionData &object, TFilter filter,
NestingLimit nestingLimit) { NestingLimit nestingLimit) {
if (nestingLimit.reached()) if (nestingLimit.reached()) {
return DeserializationError::TooDeep; _error = DeserializationError::TooDeep;
return false;
}
// Skip opening brace // Skip opening brace
ARDUINOJSON_ASSERT(current() == '{'); ARDUINOJSON_ASSERT(current() == '{');
move(); move();
// Skip spaces // Skip spaces
DeserializationError err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// Empty object? // Empty object?
if (eat('}')) if (eat('}')) {
return DeserializationError::Ok; _error = DeserializationError::Ok;
return false;
}
// Read each key value pair // Read each key value pair
for (;;) { for (;;) {
// Parse key // Parse key
err = parseKey(); if (!parseKey())
if (err) return false;
return err;
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err; // Colon
if (!eat(':')) // Colon
return DeserializationError::InvalidInput; if (!eat(':')) {
_error = DeserializationError::InvalidInput;
return false;
}
const char *key = _stringStorage.c_str(); const char *key = _stringStorage.c_str();
@ -237,8 +247,10 @@ class JsonDeserializer {
// Allocate slot in object // Allocate slot in object
VariantSlot *slot = object.addSlot(_pool); VariantSlot *slot = object.addSlot(_pool);
if (!slot) if (!slot) {
return DeserializationError::NoMemory; _error = DeserializationError::NoMemory;
return false;
}
slot->setKey(key, typename TStringStorage::storage_policy()); slot->setKey(key, typename TStringStorage::storage_policy());
@ -246,85 +258,84 @@ class JsonDeserializer {
} }
// Parse value // Parse value
err = parseVariant(*variant, memberFilter, nestingLimit.decrement()); if (!parseVariant(*variant, memberFilter, nestingLimit.decrement()))
if (err) return false;
return err;
} else { } else {
err = skipVariant(nestingLimit.decrement()); if (!skipVariant(nestingLimit.decrement()))
if (err) return false;
return err;
} }
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// More keys/values? // More keys/values?
if (eat('}')) if (eat('}'))
return DeserializationError::Ok; return true;
if (!eat(',')) if (!eat(',')) {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
} }
} }
DeserializationError skipObject(NestingLimit nestingLimit) { bool skipObject(NestingLimit nestingLimit) {
if (nestingLimit.reached()) if (nestingLimit.reached()) {
return DeserializationError::TooDeep; _error = DeserializationError::TooDeep;
return false;
}
// Skip opening brace // Skip opening brace
ARDUINOJSON_ASSERT(current() == '{'); ARDUINOJSON_ASSERT(current() == '{');
move(); move();
// Skip spaces // Skip spaces
DeserializationError err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// Empty object? // Empty object?
if (eat('}')) if (eat('}'))
return DeserializationError::Ok; return true;
// Read each key value pair // Read each key value pair
for (;;) { for (;;) {
// Skip key // Skip key
err = skipVariant(nestingLimit.decrement()); if (!skipVariant(nestingLimit.decrement()))
if (err) return false;
return err;
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// Colon // Colon
if (!eat(':')) if (!eat(':')) {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
// Skip value // Skip value
err = skipVariant(nestingLimit.decrement()); if (!skipVariant(nestingLimit.decrement()))
if (err) return false;
return err;
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); if (!skipSpacesAndComments())
if (err) return false;
return err;
// More keys/values? // More keys/values?
if (eat('}')) if (eat('}'))
return DeserializationError::Ok; return true;
if (!eat(',')) if (!eat(',')) {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
} }
} }
DeserializationError parseKey() { bool parseKey() {
_stringStorage.startString(_pool); _stringStorage.startString(_pool);
if (isQuote(current())) { if (isQuote(current())) {
return parseQuotedString(); return parseQuotedString();
@ -333,18 +344,17 @@ class JsonDeserializer {
} }
} }
DeserializationError parseStringValue(VariantData &variant) { bool parseStringValue(VariantData &variant) {
_stringStorage.startString(_pool); _stringStorage.startString(_pool);
DeserializationError err = parseQuotedString(); if (!parseQuotedString())
if (err) return false;
return err;
const char *value = _stringStorage.save(_pool); const char *value = _stringStorage.save(_pool);
variant.setString(make_not_null(value), variant.setString(make_not_null(value),
typename TStringStorage::storage_policy()); typename TStringStorage::storage_policy());
return DeserializationError::Ok; return true;
} }
DeserializationError parseQuotedString() { bool parseQuotedString() {
#if ARDUINOJSON_DECODE_UNICODE #if ARDUINOJSON_DECODE_UNICODE
Utf16::Codepoint codepoint; Utf16::Codepoint codepoint;
#endif #endif
@ -357,31 +367,40 @@ class JsonDeserializer {
if (c == stopChar) if (c == stopChar)
break; break;
if (c == '\0') if (c == '\0') {
return DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
if (c == '\\') { if (c == '\\') {
c = current(); c = current();
if (c == '\0')
return DeserializationError::IncompleteInput; if (c == '\0') {
_error = DeserializationError::IncompleteInput;
return false;
}
if (c == 'u') { if (c == 'u') {
#if ARDUINOJSON_DECODE_UNICODE #if ARDUINOJSON_DECODE_UNICODE
move(); move();
uint16_t codeunit; uint16_t codeunit;
DeserializationError err = parseHex4(codeunit); if (!parseHex4(codeunit))
if (err) return false;
return err;
if (codepoint.append(codeunit)) if (codepoint.append(codeunit))
Utf8::encodeCodepoint(codepoint.value(), _stringStorage); Utf8::encodeCodepoint(codepoint.value(), _stringStorage);
continue; continue;
#else #else
return DeserializationError::NotSupported; _error = DeserializationError::NotSupported;
return false;
#endif #endif
} }
// replace char // replace char
c = EscapeSequence::unescapeChar(c); c = EscapeSequence::unescapeChar(c);
if (c == '\0') if (c == '\0') {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
move(); move();
} }
@ -390,13 +409,15 @@ class JsonDeserializer {
_stringStorage.append('\0'); _stringStorage.append('\0');
if (!_stringStorage.isValid()) if (!_stringStorage.isValid()) {
return DeserializationError::NoMemory; _error = DeserializationError::NoMemory;
return false;
}
return DeserializationError::Ok; return true;
} }
DeserializationError parseNonQuotedString() { bool parseNonQuotedString() {
char c = current(); char c = current();
ARDUINOJSON_ASSERT(c); ARDUINOJSON_ASSERT(c);
@ -407,18 +428,21 @@ class JsonDeserializer {
c = current(); c = current();
} while (canBeInNonQuotedString(c)); } while (canBeInNonQuotedString(c));
} else { } else {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
} }
_stringStorage.append('\0'); _stringStorage.append('\0');
if (!_stringStorage.isValid()) if (!_stringStorage.isValid()) {
return DeserializationError::NoMemory; _error = DeserializationError::NoMemory;
return false;
}
return DeserializationError::Ok; return true;
} }
DeserializationError skipString() { bool skipString() {
const char stopChar = current(); const char stopChar = current();
move(); move();
@ -427,18 +451,20 @@ class JsonDeserializer {
move(); move();
if (c == stopChar) if (c == stopChar)
break; break;
if (c == '\0') if (c == '\0') {
return DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
if (c == '\\') { if (c == '\\') {
if (current() != '\0') if (current() != '\0')
move(); move();
} }
} }
return DeserializationError::Ok; return true;
} }
DeserializationError parseNumericValue(VariantData &result) { bool parseNumericValue(VariantData &result) {
uint8_t n = 0; uint8_t n = 0;
char c = current(); char c = current();
@ -452,18 +478,27 @@ class JsonDeserializer {
c = _buffer[0]; c = _buffer[0];
if (c == 't') { // true if (c == 't') { // true
result.setBoolean(true); result.setBoolean(true);
return n == 4 ? DeserializationError::Ok if (n != 4) {
: DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
return true;
} }
if (c == 'f') { // false if (c == 'f') { // false
result.setBoolean(false); result.setBoolean(false);
return n == 5 ? DeserializationError::Ok if (n != 5) {
: DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
return true;
} }
if (c == 'n') { // null if (c == 'n') { // null
// the variant is already null // the variant is already null
return n == 4 ? DeserializationError::Ok if (n != 4) {
: DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
return true;
} }
ParsedNumber<Float, UInt> num; ParsedNumber<Float, UInt> num;
@ -472,42 +507,48 @@ class JsonDeserializer {
switch (num.type()) { switch (num.type()) {
case VALUE_IS_NEGATIVE_INTEGER: case VALUE_IS_NEGATIVE_INTEGER:
result.setNegativeInteger(num.uintValue); result.setNegativeInteger(num.uintValue);
return DeserializationError::Ok; return true;
case VALUE_IS_POSITIVE_INTEGER: case VALUE_IS_POSITIVE_INTEGER:
result.setPositiveInteger(num.uintValue); result.setPositiveInteger(num.uintValue);
return DeserializationError::Ok; return true;
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
result.setFloat(num.floatValue); result.setFloat(num.floatValue);
return DeserializationError::Ok; return true;
}
return DeserializationError::InvalidInput; default:
_error = DeserializationError::InvalidInput;
return false;
}
} }
DeserializationError skipNumericValue() { bool skipNumericValue() {
char c = current(); char c = current();
while (canBeInNonQuotedString(c)) { while (canBeInNonQuotedString(c)) {
move(); move();
c = current(); c = current();
} }
return DeserializationError::Ok; return true;
} }
DeserializationError parseHex4(uint16_t &result) { bool parseHex4(uint16_t &result) {
result = 0; result = 0;
for (uint8_t i = 0; i < 4; ++i) { for (uint8_t i = 0; i < 4; ++i) {
char digit = current(); char digit = current();
if (!digit) if (!digit) {
return DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
uint8_t value = decodeHex(digit); uint8_t value = decodeHex(digit);
if (value > 0x0F) if (value > 0x0F) {
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
}
result = uint16_t((result << 4) | value); result = uint16_t((result << 4) | value);
move(); move();
} }
return DeserializationError::Ok; return true;
} }
static inline bool isBetween(char c, char min, char max) { static inline bool isBetween(char c, char min, char max) {
@ -530,12 +571,13 @@ class JsonDeserializer {
return uint8_t(c - 'A' + 10); return uint8_t(c - 'A' + 10);
} }
DeserializationError skipSpacesAndComments() { bool skipSpacesAndComments() {
for (;;) { for (;;) {
switch (current()) { switch (current()) {
// end of string // end of string
case '\0': case '\0':
return DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
// spaces // spaces
case ' ': case ' ':
@ -556,8 +598,10 @@ class JsonDeserializer {
bool wasStar = false; bool wasStar = false;
for (;;) { for (;;) {
char c = current(); char c = current();
if (c == '\0') if (c == '\0') {
return DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
if (c == '/' && wasStar) { if (c == '/' && wasStar) {
move(); move();
break; break;
@ -574,8 +618,10 @@ class JsonDeserializer {
for (;;) { for (;;) {
move(); move();
char c = current(); char c = current();
if (c == '\0') if (c == '\0') {
return DeserializationError::IncompleteInput; _error = DeserializationError::IncompleteInput;
return false;
}
if (c == '\n') if (c == '\n')
break; break;
} }
@ -583,13 +629,14 @@ class JsonDeserializer {
// not a comment, just a '/' // not a comment, just a '/'
default: default:
return DeserializationError::InvalidInput; _error = DeserializationError::InvalidInput;
return false;
} }
break; break;
#endif #endif
default: default:
return DeserializationError::Ok; return true;
} }
} }
} }
@ -600,6 +647,7 @@ class JsonDeserializer {
char _buffer[64]; // using a member instead of a local variable because it char _buffer[64]; // using a member instead of a local variable because it
// ended in the recursive path after compiler inlined the // ended in the recursive path after compiler inlined the
// code // code
DeserializationError _error;
}; };
// deserializeJson(JsonDocument&, const std::string&, ...) // deserializeJson(JsonDocument&, const std::string&, ...)