Fixed JsonVariant::isNull() not returning true after set((char*)0)

This commit is contained in:
Benoit Blanchon
2019-02-18 16:04:51 +01:00
parent e9b4c6289b
commit 56bf24e1ec
13 changed files with 127 additions and 47 deletions

View File

@ -13,6 +13,8 @@ HEAD
* Renamed `JsonArray::add()` (without arg) to `addElement()` * Renamed `JsonArray::add()` (without arg) to `addElement()`
* Renamed `JsonObject::get()` to `getMember()` * Renamed `JsonObject::get()` to `getMember()`
* Renamed `JsonObject::getOrCreate()` to `getOrAddMember()` * Renamed `JsonObject::getOrCreate()` to `getOrAddMember()`
* Fixed `JsonVariant::isNull()` not returning `true` after `set((char*)0)`
* Fixed segfault after `variant.set(serialized((char*)0))`
v6.8.0-beta (2019-01-30) v6.8.0-beta (2019-01-30)
----------- -----------

View File

@ -19,7 +19,6 @@ template <typename TReader, typename TStringStorage>
class JsonDeserializer { class JsonDeserializer {
typedef typename remove_reference<TStringStorage>::type::StringBuilder typedef typename remove_reference<TStringStorage>::type::StringBuilder
StringBuilder; StringBuilder;
typedef const char *StringType;
public: public:
JsonDeserializer(MemoryPool &pool, TReader reader, JsonDeserializer(MemoryPool &pool, TReader reader,
@ -124,10 +123,10 @@ class JsonDeserializer {
if (!slot) return DeserializationError::NoMemory; if (!slot) return DeserializationError::NoMemory;
// Parse key // Parse key
StringType key; const char *key;
err = parseKey(key); err = parseKey(key);
if (err) return err; if (err) return err;
slot->setOwnedKey(key); slot->setOwnedKey(make_not_null(key));
// Skip spaces // Skip spaces
err = skipSpacesAndComments(); err = skipSpacesAndComments();
@ -162,7 +161,7 @@ class JsonDeserializer {
} }
} }
DeserializationError parseKey(StringType &key) { DeserializationError parseKey(const char *&key) {
if (isQuote(current())) { if (isQuote(current())) {
return parseQuotedString(key); return parseQuotedString(key);
} else { } else {
@ -171,14 +170,14 @@ class JsonDeserializer {
} }
DeserializationError parseStringValue(VariantData &variant) { DeserializationError parseStringValue(VariantData &variant) {
StringType value; const char *value;
DeserializationError err = parseQuotedString(value); DeserializationError err = parseQuotedString(value);
if (err) return err; if (err) return err;
variant.setOwnedString(value); variant.setOwnedString(make_not_null(value));
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError parseQuotedString(StringType &result) { DeserializationError parseQuotedString(const char *&result) {
StringBuilder builder = _stringStorage.startString(); StringBuilder builder = _stringStorage.startString();
const char stopChar = current(); const char stopChar = current();
@ -219,7 +218,7 @@ class JsonDeserializer {
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError parseNonQuotedString(StringType &result) { DeserializationError parseNonQuotedString(const char *&result) {
StringBuilder builder = _stringStorage.startString(); StringBuilder builder = _stringStorage.startString();
char c = current(); char c = current();

View File

@ -17,7 +17,6 @@ template <typename TReader, typename TStringStorage>
class MsgPackDeserializer { class MsgPackDeserializer {
typedef typename remove_reference<TStringStorage>::type::StringBuilder typedef typename remove_reference<TStringStorage>::type::StringBuilder
StringBuilder; StringBuilder;
typedef const char *StringType;
public: public:
MsgPackDeserializer(MemoryPool &pool, TReader reader, MsgPackDeserializer(MemoryPool &pool, TReader reader,
@ -227,20 +226,20 @@ class MsgPackDeserializer {
} }
template <typename T> template <typename T>
DeserializationError readString(StringType &str) { DeserializationError readString(const char *&str) {
T size; T size;
if (!readInteger(size)) return DeserializationError::IncompleteInput; if (!readInteger(size)) return DeserializationError::IncompleteInput;
return readString(str, size); return readString(str, size);
} }
DeserializationError readString(VariantData &variant, size_t n) { DeserializationError readString(VariantData &variant, size_t n) {
StringType s; const char *s;
DeserializationError err = readString(s, n); DeserializationError err = readString(s, n);
if (!err) variant.setOwnedString(s); if (!err) variant.setOwnedString(make_not_null(s));
return err; return err;
} }
DeserializationError readString(StringType &result, size_t n) { DeserializationError readString(const char *&result, size_t n) {
StringBuilder builder = _stringStorage.startString(); StringBuilder builder = _stringStorage.startString();
for (; n; --n) { for (; n; --n) {
uint8_t c; uint8_t c;
@ -287,10 +286,10 @@ class MsgPackDeserializer {
VariantSlot *slot = object.addSlot(_pool); VariantSlot *slot = object.addSlot(_pool);
if (!slot) return DeserializationError::NoMemory; if (!slot) return DeserializationError::NoMemory;
StringType key; const char *key;
DeserializationError err = parseKey(key); DeserializationError err = parseKey(key);
if (err) return err; if (err) return err;
slot->setOwnedKey(key); slot->setOwnedKey(make_not_null(key));
err = parse(*slot->data()); err = parse(*slot->data());
if (err) return err; if (err) return err;
@ -299,7 +298,7 @@ class MsgPackDeserializer {
return DeserializationError::Ok; return DeserializationError::Ok;
} }
DeserializationError parseKey(StringType &key) { DeserializationError parseKey(const char *&key) {
uint8_t code; uint8_t code;
if (!readByte(code)) return DeserializationError::IncompleteInput; if (!readByte(code)) return DeserializationError::IncompleteInput;

View File

@ -0,0 +1,33 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2019
// MIT License
#pragma once
#include "../assert.hpp"
namespace ARDUINOJSON_NAMESPACE {
template <typename T>
class not_null {
public:
explicit not_null(T ptr) : _ptr(ptr) {
ARDUINOJSON_ASSERT(ptr != NULL);
}
T get() const {
ARDUINOJSON_ASSERT(_ptr != NULL);
return _ptr;
}
private:
T _ptr;
};
template <typename T>
not_null<T> make_not_null(T ptr) {
ARDUINOJSON_ASSERT(ptr != NULL);
return not_null<T>(ptr);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -29,6 +29,7 @@ class ConstRamStringAdapter {
} }
size_t size() const { size_t size() const {
if (!_str) return 0;
return strlen(_str); return strlen(_str);
} }

View File

@ -33,6 +33,7 @@ class FlashStringAdapter {
} }
size_t size() const { size_t size() const {
if (!_str) return 0;
return strlen_P(reinterpret_cast<const char*>(_str)); return strlen_P(reinterpret_cast<const char*>(_str));
} }

View File

@ -29,7 +29,7 @@ class SizedFlashStringAdapter {
} }
size_t size() const { size_t size() const {
return strlen_P(reinterpret_cast<const char*>(_str)); return _size;
} }
bool isStatic() const { bool isStatic() const {

View File

@ -30,7 +30,7 @@ class SizedRamStringAdapter {
} }
size_t size() const { size_t size() const {
return strlen(reinterpret_cast<const char*>(_str)); return _size;
} }
bool isStatic() const { bool isStatic() const {

View File

@ -15,11 +15,11 @@ template <typename TAdaptedString>
inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) { inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool) {
if (!var) return false; if (!var) return false;
if (key.isStatic()) { if (key.isStatic()) {
var->setLinkedKey(key.data()); var->setLinkedKey(make_not_null(key.data()));
} else { } else {
char* dup = key.save(pool); const char* dup = key.save(pool);
if (!dup) return false; if (!dup) return false;
var->setOwnedKey(dup); var->setOwnedKey(make_not_null(dup));
} }
return true; return true;
} }

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "../Misc/SerializedValue.hpp" #include "../Misc/SerializedValue.hpp"
#include "../Polyfills/gsl/not_null.hpp"
#include "VariantContent.hpp" #include "VariantContent.hpp"
namespace ARDUINOJSON_NAMESPACE { namespace ARDUINOJSON_NAMESPACE {
@ -179,9 +180,13 @@ class VariantData {
} }
void setLinkedRaw(SerializedValue<const char *> value) { void setLinkedRaw(SerializedValue<const char *> value) {
setType(VALUE_IS_LINKED_RAW); if (value.data()) {
_content.asRaw.data = value.data(); setType(VALUE_IS_LINKED_RAW);
_content.asRaw.size = value.size(); _content.asRaw.data = value.data();
_content.asRaw.size = value.size();
} else {
setType(VALUE_IS_NULL);
}
} }
template <typename T> template <typename T>
@ -220,25 +225,26 @@ class VariantData {
} }
void setLinkedString(const char *value) { void setLinkedString(const char *value) {
setType(VALUE_IS_LINKED_STRING); if (value) {
_content.asString = value; setType(VALUE_IS_LINKED_STRING);
_content.asString = value;
} else {
setType(VALUE_IS_NULL);
}
} }
void setNull() { void setNull() {
setType(VALUE_IS_NULL); setType(VALUE_IS_NULL);
} }
void setOwnedString(const char *s) { void setOwnedString(not_null<const char *> s) {
setType(VALUE_IS_OWNED_STRING); setType(VALUE_IS_OWNED_STRING);
_content.asString = s; _content.asString = s.get();
} }
template <typename T> bool setOwnedString(const char *s) {
bool setOwnedString(T value, MemoryPool *pool) { if (s) {
char *dup = value.save(pool); setOwnedString(make_not_null(s));
if (dup) {
setType(VALUE_IS_OWNED_STRING);
_content.asString = dup;
return true; return true;
} else { } else {
setType(VALUE_IS_NULL); setType(VALUE_IS_NULL);
@ -246,6 +252,11 @@ class VariantData {
} }
} }
template <typename T>
bool setOwnedString(T value, MemoryPool *pool) {
return setOwnedString(value.save(pool));
}
void setUnsignedInteger(UInt value) { void setUnsignedInteger(UInt value) {
setType(VALUE_IS_POSITIVE_INTEGER); setType(VALUE_IS_POSITIVE_INTEGER);
_content.asInteger = static_cast<UInt>(value); _content.asInteger = static_cast<UInt>(value);

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "../Polyfills/gsl/not_null.hpp"
#include "../Polyfills/type_traits.hpp" #include "../Polyfills/type_traits.hpp"
#include "../Variant/VariantContent.hpp" #include "../Variant/VariantContent.hpp"
@ -67,14 +68,14 @@ class VariantSlot {
_next = VariantSlotDiff(slot - this); _next = VariantSlotDiff(slot - this);
} }
void setOwnedKey(const char* k) { void setOwnedKey(not_null<const char*> k) {
_flags |= KEY_IS_OWNED; _flags |= KEY_IS_OWNED;
_key = k; _key = k.get();
} }
void setLinkedKey(const char* k) { void setLinkedKey(not_null<const char*> k) {
_flags &= VALUE_MASK; _flags &= VALUE_MASK;
_key = k; _key = k.get();
} }
const char* key() const { const char* key() const {

View File

@ -64,3 +64,11 @@ TEST_CASE("Invalid JSON string") {
REQUIRE(deserializeJson(doc, input) == DeserializationError::InvalidInput); REQUIRE(deserializeJson(doc, input) == DeserializationError::InvalidInput);
} }
} }
TEST_CASE("Not enough room to duplicate the string") {
DynamicJsonDocument doc(4);
REQUIRE(deserializeJson(doc, "\"hello world!\"") ==
DeserializationError::NoMemory);
REQUIRE(doc.isNull() == true);
}

View File

@ -35,15 +35,40 @@ TEST_CASE("JsonVariant::isNull()") {
REQUIRE(variant.isNull() == false); REQUIRE(variant.isNull() == false);
} }
/* SECTION("return true when InvalidArray") { SECTION("return true after set(JsonArray())") {
variant.set(JsonArray()); variant.set(JsonArray());
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
} }
*/
/* SECTION("return true when InvalidObject") { SECTION("return true after set(JsonObject())") {
variant.set(JsonObject()); variant.set(JsonObject());
REQUIRE(variant.isNull() == true); REQUIRE(variant.isNull() == true);
}*/ }
SECTION("return false after set('hello')") {
variant.set("hello");
REQUIRE(variant.isNull() == false);
}
SECTION("return true after set((char*)0)") {
variant.set(static_cast<char*>(0));
REQUIRE(variant.isNull() == true);
}
SECTION("return true after set((const char*)0)") {
variant.set(static_cast<const char*>(0));
REQUIRE(variant.isNull() == true);
}
SECTION("return true after set(serialized((char*)0))") {
variant.set(serialized(static_cast<char*>(0)));
REQUIRE(variant.isNull() == true);
}
SECTION("return true after set(serialized((const char*)0))") {
variant.set(serialized(static_cast<const char*>(0)));
REQUIRE(variant.isNull() == true);
}
SECTION("works with JsonVariantConst") { SECTION("works with JsonVariantConst") {
variant.set(42); variant.set(42);