Fixed result of JsonVariant::set((char*)0) (fixes #1368)

This commit is contained in:
Benoit Blanchon
2020-09-05 17:33:47 +02:00
parent 05fc136915
commit d04669d0cc
7 changed files with 92 additions and 92 deletions

View File

@ -6,6 +6,7 @@ HEAD
* Added a build failure when nullptr is defined as a macro (issue #1355)
* Added `JsonDocument::overflowed()` which tells if the memory pool was too small (issue #1358)
* Fixed `JsonVariant::set((char*)0)` which returned false instead of true (issue #1368)
v6.16.1 (2020-08-04)
-------

View File

@ -7,115 +7,150 @@
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
TEST_CASE("JsonVariant and strings") {
TEST_CASE("JsonVariant::set() when there is enough memory") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("stores const char* by reference") {
SECTION("const char*") {
char str[16];
strcpy(str, "hello");
variant.set(static_cast<const char *>(str));
bool result = variant.set(static_cast<const char *>(str));
strcpy(str, "world");
REQUIRE(variant == "world");
REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer
}
SECTION("stores char* by copy") {
char str[16];
SECTION("(const char*)0") {
bool result = variant.set(static_cast<const char *>(0));
strcpy(str, "hello");
variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant.isNull());
}
SECTION("stores unsigned char* by copy") {
SECTION("char*") {
char str[16];
strcpy(str, "hello");
variant.set(reinterpret_cast<unsigned char *>(str));
bool result = variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("stores signed char* by copy") {
SECTION("(char*)0") {
bool result = variant.set(static_cast<char *>(0));
REQUIRE(result == true);
REQUIRE(variant.isNull());
}
SECTION("unsigned char*") {
char str[16];
strcpy(str, "hello");
variant.set(reinterpret_cast<signed char *>(str));
bool result = variant.set(reinterpret_cast<unsigned char *>(str));
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("signed char*") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(reinterpret_cast<signed char *>(str));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("stores VLA by copy") {
SECTION("VLA") {
int n = 16;
char str[n];
strcpy(str, "hello");
variant.set(str);
bool result = variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
#endif
SECTION("stores std::string by copy") {
SECTION("std::string") {
std::string str;
str = "hello";
variant.set(str);
bool result = variant.set(str);
str.replace(0, 5, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("stores static JsonString by reference") {
SECTION("static JsonString") {
char str[16];
strcpy(str, "hello");
variant.set(JsonString(str, true));
bool result = variant.set(JsonString(str, true));
strcpy(str, "world");
REQUIRE(variant == "world");
REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer
}
SECTION("stores non-static JsonString by copy") {
SECTION("non-static JsonString") {
char str[16];
strcpy(str, "hello");
variant.set(JsonString(str, false));
bool result = variant.set(JsonString(str, false));
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("stores an enum as an integer") {
SECTION("enum") {
ErrorCode code = ERROR_10;
variant.set(code);
bool result = variant.set(code);
REQUIRE(result == true);
REQUIRE(variant.is<int>() == true);
REQUIRE(variant.as<int>() == 10);
}
}
TEST_CASE("JsonVariant with not enough memory") {
TEST_CASE("JsonVariant::set() with not enough memory") {
StaticJsonDocument<1> doc;
JsonVariant v = doc.to<JsonVariant>();
SECTION("std::string") {
v.set(std::string("hello world!!"));
bool result = v.set(std::string("hello world!!"));
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("Serialized<std::string>") {
v.set(serialized(std::string("hello world!!")));
bool result = v.set(serialized(std::string("hello world!!")));
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("char*") {
char s[] = "hello world!!";
bool result = v.set(s);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
}

View File

@ -347,8 +347,7 @@ class JsonDeserializer {
if (!parseQuotedString())
return false;
const char *value = _stringStorage.save();
variant.setString(make_not_null(value),
typename TStringStorage::storage_policy());
variant.setStringPointer(value, typename TStringStorage::storage_policy());
return true;
}

View File

@ -233,8 +233,7 @@ class MsgPackDeserializer {
const char *s = 0; // <- mute "maybe-uninitialized" (+4 bytes on AVR)
if (!readString(s, n))
return false;
variant.setString(make_not_null(s),
typename TStringStorage::storage_policy());
variant.setStringPointer(s, typename TStringStorage::storage_policy());
return true;
}

View File

@ -1,34 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/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

@ -7,7 +7,6 @@
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Misc/SerializedValue.hpp>
#include <ArduinoJson/Numbers/convertNumber.hpp>
#include <ArduinoJson/Polyfills/gsl/not_null.hpp>
#include <ArduinoJson/Strings/RamStringAdapter.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>
@ -245,25 +244,14 @@ class VariantData {
setType(VALUE_IS_NULL);
}
void setString(not_null<const char *> s, storage_policies::store_by_copy) {
void setStringPointer(const char *s, storage_policies::store_by_copy) {
setType(VALUE_IS_OWNED_STRING);
_content.asString = s.get();
_content.asString = s;
}
void setString(not_null<const char *> s, storage_policies::store_by_address) {
void setStringPointer(const char *s, storage_policies::store_by_address) {
setType(VALUE_IS_LINKED_STRING);
_content.asString = s.get();
}
template <typename TStoragePolicy>
bool setString(const char *s, TStoragePolicy storage_policy) {
if (s) {
setString(make_not_null(s), storage_policy);
return true;
} else {
setType(VALUE_IS_NULL);
return false;
}
_content.asString = s;
}
template <typename TAdaptedString>
@ -283,14 +271,27 @@ class VariantData {
template <typename TAdaptedString>
inline bool setString(TAdaptedString value, MemoryPool *,
storage_policies::store_by_address) {
return setString(value.data(), storage_policies::store_by_address());
if (value.isNull())
setNull();
else
setStringPointer(value.data(), storage_policies::store_by_address());
return true;
}
template <typename TAdaptedString>
inline bool setString(TAdaptedString value, MemoryPool *pool,
storage_policies::store_by_copy) {
return setString(pool->saveString(value),
storage_policies::store_by_copy());
if (value.isNull()) {
setNull();
return true;
}
const char *copy = pool->saveString(value);
if (!copy) {
setNull();
return false;
}
setStringPointer(copy, storage_policies::store_by_copy());
return true;
}
CollectionData &toArray() {

View File

@ -6,7 +6,6 @@
#include <stdint.h> // int8_t, int16_t
#include <ArduinoJson/Polyfills/gsl/not_null.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#include <ArduinoJson/Strings/StoragePolicy.hpp>
#include <ArduinoJson/Variant/VariantContent.hpp>