Support NUL inside string values (issue #1646)

This commit is contained in:
Benoit Blanchon
2021-11-21 15:07:56 +01:00
parent a27398e445
commit be70f6ddd7
22 changed files with 144 additions and 59 deletions

View File

@ -14,6 +14,7 @@ HEAD
* Fix `JsonVariant::memoryUsage()` for raw strings * Fix `JsonVariant::memoryUsage()` for raw strings
* Fix `call of overloaded 'swap(BasicJsonDocument&, BasicJsonDocument&)' is ambiguous` (issue #1678) * Fix `call of overloaded 'swap(BasicJsonDocument&, BasicJsonDocument&)' is ambiguous` (issue #1678)
* Fix inconsistent pool size in `BasicJsonDocument`'s copy constructor * Fix inconsistent pool size in `BasicJsonDocument`'s copy constructor
* Support NUL in string values (issue #1646)
v6.18.5 (2021-09-28) v6.18.5 (2021-09-28)
------- -------

View File

@ -8,7 +8,7 @@
#endif #endif
TEST_CASE("string_view") { TEST_CASE("string_view") {
StaticJsonDocument<128> doc; StaticJsonDocument<256> doc;
JsonVariant variant = doc.to<JsonVariant>(); JsonVariant variant = doc.to<JsonVariant>();
SECTION("deserializeJson()") { SECTION("deserializeJson()") {
@ -57,6 +57,12 @@ TEST_CASE("string_view") {
doc.add(std::string_view("example two", 7)); doc.add(std::string_view("example two", 7));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8); REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
doc.add(std::string_view("example\0tree", 12));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(3) + 21);
doc.add(std::string_view("example\0tree and a half", 12));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(4) + 21);
} }
SECTION("as<std::string_view>()") { SECTION("as<std::string_view>()") {
@ -72,6 +78,12 @@ TEST_CASE("string_view") {
REQUIRE(doc["s"].is<std::string_view>() == true); REQUIRE(doc["s"].is<std::string_view>() == true);
REQUIRE(doc["i"].is<std::string_view>() == false); REQUIRE(doc["i"].is<std::string_view>() == false);
} }
SECTION("String containing NUL") {
doc.set(std::string("hello\0world", 11));
REQUIRE(doc.as<std::string_view>().size() == 11);
REQUIRE(doc.as<std::string_view>() == std::string_view("hello\0world", 11));
}
} }
using ARDUINOJSON_NAMESPACE::adaptString; using ARDUINOJSON_NAMESPACE::adaptString;

View File

@ -60,9 +60,8 @@ TEST_CASE("\\u0000") {
CHECK(result[4] == 'z'); CHECK(result[4] == 'z');
CHECK(result[5] == 0); CHECK(result[5] == 0);
// ArduinoJson strings doesn't store string length, so the following returns 2 CHECK(doc.as<JsonString>().size() == 5);
// instead of 5 (issue #1646) CHECK(doc.as<std::string>().size() == 5);
CHECK(doc.as<std::string>().size() == 2);
} }
TEST_CASE("Truncated JSON string") { TEST_CASE("Truncated JSON string") {

View File

@ -63,6 +63,10 @@ TEST_CASE("serializeJson(JsonVariant)") {
SECTION("Escape tab") { SECTION("Escape tab") {
check(std::string("hello\tworld"), "\"hello\\tworld\""); check(std::string("hello\tworld"), "\"hello\\tworld\"");
} }
SECTION("NUL char") {
check(std::string("hello\0world", 11), "\"hello\\u0000world\"");
}
} }
SECTION("SerializedValue<const char*>") { SECTION("SerializedValue<const char*>") {

View File

@ -45,3 +45,13 @@ TEST_CASE("serialize JsonObject to std::string") {
REQUIRE("{\r\n \"key\": \"value\"\r\n}" == json); REQUIRE("{\r\n \"key\": \"value\"\r\n}" == json);
} }
} }
TEST_CASE("serialize an std::string containing a NUL") {
StaticJsonDocument<256> doc;
doc.set(std::string("hello\0world", 11));
CHECK(doc.memoryUsage() == 12);
std::string json;
serializeJson(doc, json);
CHECK("\"hello\\u0000world\"" == json);
}

View File

@ -12,6 +12,10 @@ static const char *saveString(MemoryPool &pool, const char *s) {
return pool.saveString(adaptString(const_cast<char *>(s))); return pool.saveString(adaptString(const_cast<char *>(s)));
} }
static const char *saveString(MemoryPool &pool, const char *s, size_t n) {
return pool.saveString(adaptString(s, n));
}
TEST_CASE("MemoryPool::saveString()") { TEST_CASE("MemoryPool::saveString()") {
char buffer[32]; char buffer[32];
MemoryPool pool(buffer, 32); MemoryPool pool(buffer, 32);
@ -30,6 +34,27 @@ TEST_CASE("MemoryPool::saveString()") {
REQUIRE(pool.size() == 6); REQUIRE(pool.size() == 6);
} }
SECTION("Deduplicates identical strings that contain NUL") {
const char *a = saveString(pool, "hello\0world", 11);
const char *b = saveString(pool, "hello\0world", 11);
REQUIRE(a == b);
REQUIRE(pool.size() == 12);
}
SECTION("Reuse part of a string if it ends with NUL") {
const char *a = saveString(pool, "hello\0world", 11);
const char *b = saveString(pool, "hello");
REQUIRE(a == b);
REQUIRE(pool.size() == 12);
}
SECTION("Don't stop on first NUL") {
const char *a = saveString(pool, "hello");
const char *b = saveString(pool, "hello\0world", 11);
REQUIRE(a != b);
REQUIRE(pool.size() == 18);
}
SECTION("Returns NULL when full") { SECTION("Returns NULL when full") {
REQUIRE(pool.capacity() == 32); REQUIRE(pool.capacity() == 32);

View File

@ -68,6 +68,11 @@ class JsonSerializer : public Visitor<size_t> {
return bytesWritten(); return bytesWritten();
} }
size_t visitString(const char *value, size_t n) {
_formatter.writeString(value, n);
return bytesWritten();
}
size_t visitRawJson(const char *data, size_t n) { size_t visitRawJson(const char *data, size_t n) {
_formatter.writeRaw(data, n); _formatter.writeRaw(data, n);
return bytesWritten(); return bytesWritten();

View File

@ -41,13 +41,22 @@ class TextFormatter {
writeRaw('\"'); writeRaw('\"');
} }
void writeString(const char *value, size_t n) {
ARDUINOJSON_ASSERT(value != NULL);
writeRaw('\"');
while (n--) writeChar(*value++);
writeRaw('\"');
}
void writeChar(char c) { void writeChar(char c) {
char specialChar = EscapeSequence::escapeChar(c); char specialChar = EscapeSequence::escapeChar(c);
if (specialChar) { if (specialChar) {
writeRaw('\\'); writeRaw('\\');
writeRaw(specialChar); writeRaw(specialChar);
} else { } else if (c) {
writeRaw(c); writeRaw(c);
} else {
writeRaw("\\u0000");
} }
} }

View File

@ -62,12 +62,12 @@ class MemoryPool {
template <typename TAdaptedString> template <typename TAdaptedString>
const char* saveString(const TAdaptedString& str) { const char* saveString(const TAdaptedString& str) {
if (str.isNull()) if (str.isNull())
return 0; return CopiedString();
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
const char* existingCopy = findString(str); const char* existingCopy = findString(str);
if (existingCopy) if (existingCopy)
return existingCopy; return CopiedString(existingCopy, str.size());
#endif #endif
size_t n = str.size(); size_t n = str.size();
@ -77,7 +77,7 @@ class MemoryPool {
str.copyTo(newCopy, n); str.copyTo(newCopy, n);
newCopy[n] = 0; // force null-terminator newCopy[n] = 0; // force null-terminator
} }
return newCopy; return CopiedString(newCopy, n);
} }
void getFreeZone(char** zoneStart, size_t* zoneSize) const { void getFreeZone(char** zoneStart, size_t* zoneSize) const {
@ -89,14 +89,14 @@ class MemoryPool {
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
const char* dup = findString(adaptString(_left, len)); const char* dup = findString(adaptString(_left, len));
if (dup) if (dup)
return dup; return CopiedString(dup, len);
#endif #endif
const char* str = _left; const char* str = _left;
_left += len; _left += len;
*_left++ = 0; *_left++ = 0;
checkInvariants(); checkInvariants();
return str; return CopiedString(str, len);
} }
void markAsOverflowed() { void markAsOverflowed() {

View File

@ -78,9 +78,11 @@ class MsgPackSerializer : public Visitor<size_t> {
} }
size_t visitString(const char* value) { size_t visitString(const char* value) {
ARDUINOJSON_ASSERT(value != NULL); return visitString(value, strlen(value));
}
size_t n = strlen(value); size_t visitString(const char* value, size_t n) {
ARDUINOJSON_ASSERT(value != NULL);
if (n < 0x20) { if (n < 0x20) {
writeByte(uint8_t(0xA0 + n)); writeByte(uint8_t(0xA0 + n));

View File

@ -24,7 +24,7 @@ class StringCopier {
string_type save() { string_type save() {
ARDUINOJSON_ASSERT(_ptr); ARDUINOJSON_ASSERT(_ptr);
ARDUINOJSON_ASSERT(_size < _capacity); // needs room for the terminator ARDUINOJSON_ASSERT(_size < _capacity); // needs room for the terminator
return _pool->saveStringFromFreeZone(_size); return string_type(_pool->saveStringFromFreeZone(_size), _size);
} }
void append(const char* s) { void append(const char* s) {
@ -54,7 +54,7 @@ class StringCopier {
ARDUINOJSON_ASSERT(_ptr); ARDUINOJSON_ASSERT(_ptr);
ARDUINOJSON_ASSERT(_size < _capacity); ARDUINOJSON_ASSERT(_size < _capacity);
_ptr[_size] = 0; _ptr[_size] = 0;
return _ptr; return string_type(_ptr, _size);
} }
private: private:

View File

@ -35,7 +35,7 @@ class StringMover {
} }
string_type str() const { string_type str() const {
return string_type(_startPtr); return string_type(_startPtr, size());
} }
size_t size() const { size_t size() const {

View File

@ -12,7 +12,8 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TStoragePolicy> template <typename TStoragePolicy>
class StoredString { class StoredString {
public: public:
StoredString(const char* p) : _data(p) {} StoredString() : _data(0), _size(0) {}
StoredString(const char* p, size_t n) : _data(p), _size(n) {}
operator const char*() const { operator const char*() const {
return _data; return _data;
@ -22,8 +23,13 @@ class StoredString {
return _data; return _data;
} }
size_t size() const {
return _size;
}
private: private:
const char* _data; const char* _data;
size_t _size;
}; };
typedef StoredString<storage_policies::store_by_address> LinkedString; typedef StoredString<storage_policies::store_by_address> LinkedString;

View File

@ -10,9 +10,15 @@ namespace ARDUINOJSON_NAMESPACE {
class String : public SafeBoolIdom<String> { class String : public SafeBoolIdom<String> {
public: public:
String() : _data(0), _isStatic(true) {} String() : _data(0), _size(0), _isStatic(true) {}
String(const char* data, bool isStaticData = true) String(const char* data, bool isStaticData = true)
: _data(data), _isStatic(isStaticData) {} : _data(data),
_size(data ? ::strlen(data) : 0),
_isStatic(isStaticData) {}
String(const char* data, size_t sz, bool isStaticData = true)
: _data(data), _size(sz), _isStatic(isStaticData) {}
const char* c_str() const { const char* c_str() const {
return _data; return _data;
@ -26,6 +32,10 @@ class String : public SafeBoolIdom<String> {
return _isStatic; return _isStatic;
} }
size_t size() const {
return _size;
}
// safe bool idiom // safe bool idiom
operator bool_type() const { operator bool_type() const {
return _data ? safe_true() : safe_false(); return _data ? safe_true() : safe_false();
@ -53,6 +63,7 @@ class String : public SafeBoolIdom<String> {
private: private:
const char* _data; const char* _data;
size_t _size;
bool _isStatic; bool _isStatic;
}; };

View File

@ -205,7 +205,7 @@ class MemoryPoolPrint : public Print {
CopiedString str() { CopiedString str() {
ARDUINOJSON_ASSERT(_size < _capacity); ARDUINOJSON_ASSERT(_size < _capacity);
return _pool->saveStringFromFreeZone(_size); return CopiedString(_pool->saveStringFromFreeZone(_size), _size);
} }
size_t write(uint8_t c) { size_t write(uint8_t c) {
@ -257,8 +257,7 @@ inline void convertToJson(const ::Printable& src, VariantRef dst) {
#if ARDUINOJSON_ENABLE_ARDUINO_STRING #if ARDUINOJSON_ENABLE_ARDUINO_STRING
inline void convertFromJson(VariantConstRef src, ::String& dst) { inline void convertFromJson(VariantConstRef src, ::String& dst) {
const VariantData* data = getData(src); String str = src.as<String>();
String str = data != 0 ? data->asString() : String();
if (str) if (str)
dst = str.c_str(); dst = str.c_str();
else else
@ -266,8 +265,7 @@ inline void convertFromJson(VariantConstRef src, ::String& dst) {
} }
inline bool canConvertFromJson(VariantConstRef src, const ::String&) { inline bool canConvertFromJson(VariantConstRef src, const ::String&) {
const VariantData* data = getData(src); return src.is<String>();
return data && data->isString();
} }
#endif #endif
@ -275,17 +273,15 @@ inline bool canConvertFromJson(VariantConstRef src, const ::String&) {
#if ARDUINOJSON_ENABLE_STD_STRING #if ARDUINOJSON_ENABLE_STD_STRING
inline void convertFromJson(VariantConstRef src, std::string& dst) { inline void convertFromJson(VariantConstRef src, std::string& dst) {
const VariantData* data = getData(src); String str = src.as<String>();
String str = data != 0 ? data->asString() : String();
if (str) if (str)
dst.assign(str.c_str()); dst.assign(str.c_str(), str.size());
else else
serializeJson(src, dst); serializeJson(src, dst);
} }
inline bool canConvertFromJson(VariantConstRef src, const std::string&) { inline bool canConvertFromJson(VariantConstRef src, const std::string&) {
const VariantData* data = getData(src); return src.is<String>();
return data && data->isString();
} }
#endif #endif
@ -293,13 +289,13 @@ inline bool canConvertFromJson(VariantConstRef src, const std::string&) {
#if ARDUINOJSON_ENABLE_STRING_VIEW #if ARDUINOJSON_ENABLE_STRING_VIEW
inline void convertFromJson(VariantConstRef src, std::string_view& dst) { inline void convertFromJson(VariantConstRef src, std::string_view& dst) {
const char* str = src.as<const char*>(); String str = src.as<String>();
if (str) // the standard doesn't allow passing null to the constructor if (str) // the standard doesn't allow passing null to the constructor
dst = std::string_view(str); dst = std::string_view(str.c_str(), str.size());
} }
inline bool canConvertFromJson(VariantConstRef src, const std::string_view&) { inline bool canConvertFromJson(VariantConstRef src, const std::string_view&) {
return src.is<const char*>(); return src.is<String>();
} }
#endif #endif

View File

@ -30,14 +30,14 @@ template <typename TAdaptedString>
inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool*, inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool*,
storage_policies::store_by_address) { storage_policies::store_by_address) {
ARDUINOJSON_ASSERT(var); ARDUINOJSON_ASSERT(var);
var->setKey(LinkedString(key.data())); var->setKey(LinkedString(key.data(), key.size()));
return true; return true;
} }
template <typename TAdaptedString> template <typename TAdaptedString>
inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool, inline bool slotSetKey(VariantSlot* var, TAdaptedString key, MemoryPool* pool,
storage_policies::store_by_copy) { storage_policies::store_by_copy) {
CopiedString dup = pool->saveString(key); CopiedString dup(pool->saveString(key), key.size());
if (!dup) if (!dup)
return false; return false;
ARDUINOJSON_ASSERT(var); ARDUINOJSON_ASSERT(var);

View File

@ -27,7 +27,7 @@ struct Comparer<T, typename enable_if<IsString<T>::value>::type>
explicit Comparer(T value) : rhs(value) {} explicit Comparer(T value) : rhs(value) {}
CompareResult visitString(const char *lhs) { CompareResult visitString(const char *lhs, size_t) {
int i = adaptString(rhs).compare(lhs); int i = adaptString(rhs).compare(lhs);
if (i < 0) if (i < 0)
return COMPARE_RESULT_GREATER; return COMPARE_RESULT_GREATER;
@ -150,7 +150,7 @@ struct Comparer<T, typename enable_if<IsVisitable<T>::value>::type>
return accept(comparer); return accept(comparer);
} }
CompareResult visitString(const char *lhs) { CompareResult visitString(const char *lhs, size_t) {
Comparer<const char *> comparer(lhs); Comparer<const char *> comparer(lhs);
return accept(comparer); return accept(comparer);
} }

View File

@ -49,10 +49,9 @@ union VariantContent {
UInt asUnsignedInteger; UInt asUnsignedInteger;
Integer asSignedInteger; Integer asSignedInteger;
CollectionData asCollection; CollectionData asCollection;
const char *asString;
struct { struct {
const char *data; const char *data;
size_t size; size_t size;
} asRaw; } asString;
}; };
} // namespace ARDUINOJSON_NAMESPACE } // namespace ARDUINOJSON_NAMESPACE

View File

@ -51,11 +51,13 @@ class VariantData {
case VALUE_IS_LINKED_STRING: case VALUE_IS_LINKED_STRING:
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return visitor.visitString(_content.asString); return visitor.visitString(_content.asString.data,
_content.asString.size);
case VALUE_IS_OWNED_RAW: case VALUE_IS_OWNED_RAW:
case VALUE_IS_LINKED_RAW: case VALUE_IS_LINKED_RAW:
return visitor.visitRawJson(_content.asRaw.data, _content.asRaw.size); return visitor.visitRawJson(_content.asString.data,
_content.asString.size);
case VALUE_IS_SIGNED_INTEGER: case VALUE_IS_SIGNED_INTEGER:
return visitor.visitSignedInteger(_content.asSignedInteger); return visitor.visitSignedInteger(_content.asSignedInteger);
@ -105,10 +107,13 @@ class VariantData {
return toObject().copyFrom(src._content.asCollection, pool); return toObject().copyFrom(src._content.asCollection, pool);
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return storeString( return storeString(
adaptString(const_cast<char *>(src._content.asString)), pool); adaptString(const_cast<char *>(src._content.asString.data),
src._content.asString.size),
pool);
case VALUE_IS_OWNED_RAW: case VALUE_IS_OWNED_RAW:
return storeOwnedRaw( return storeOwnedRaw(
serialized(src._content.asRaw.data, src._content.asRaw.size), pool); serialized(src._content.asString.data, src._content.asString.size),
pool);
default: default:
setType(src.type()); setType(src.type());
_content = src._content; _content = src._content;
@ -186,8 +191,8 @@ class VariantData {
void setLinkedRaw(SerializedValue<const char *> value) { void setLinkedRaw(SerializedValue<const char *> value) {
if (value.data()) { if (value.data()) {
setType(VALUE_IS_LINKED_RAW); setType(VALUE_IS_LINKED_RAW);
_content.asRaw.data = value.data(); _content.asString.data = value.data();
_content.asRaw.size = value.size(); _content.asString.size = value.size();
} else { } else {
setType(VALUE_IS_NULL); setType(VALUE_IS_NULL);
} }
@ -198,8 +203,8 @@ class VariantData {
const char *dup = pool->saveString(adaptString(value.data(), value.size())); const char *dup = pool->saveString(adaptString(value.data(), value.size()));
if (dup) { if (dup) {
setType(VALUE_IS_OWNED_RAW); setType(VALUE_IS_OWNED_RAW);
_content.asRaw.data = dup; _content.asString.data = dup;
_content.asRaw.size = value.size(); _content.asString.size = value.size();
return true; return true;
} else { } else {
setType(VALUE_IS_NULL); setType(VALUE_IS_NULL);
@ -226,13 +231,15 @@ class VariantData {
void setString(CopiedString s) { void setString(CopiedString s) {
ARDUINOJSON_ASSERT(s); ARDUINOJSON_ASSERT(s);
setType(VALUE_IS_OWNED_STRING); setType(VALUE_IS_OWNED_STRING);
_content.asString = s.c_str(); _content.asString.data = s.c_str();
_content.asString.size = s.size();
} }
void setString(LinkedString s) { void setString(LinkedString s) {
ARDUINOJSON_ASSERT(s); ARDUINOJSON_ASSERT(s);
setType(VALUE_IS_LINKED_STRING); setType(VALUE_IS_LINKED_STRING);
_content.asString = s.c_str(); _content.asString.data = s.c_str();
_content.asString.size = s.size();
} }
template <typename TAdaptedString> template <typename TAdaptedString>
@ -255,11 +262,10 @@ class VariantData {
size_t memoryUsage() const { size_t memoryUsage() const {
switch (type()) { switch (type()) {
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return strlen(_content.asString) + 1;
case VALUE_IS_OWNED_RAW: case VALUE_IS_OWNED_RAW:
// We always add a zero at the end: the deduplication function uses it // We always add a zero at the end: the deduplication function uses it
// to detect the beginning of the next string. // to detect the beginning of the next string.
return _content.asRaw.size + 1; return _content.asString.size + 1;
case VALUE_IS_OBJECT: case VALUE_IS_OBJECT:
case VALUE_IS_ARRAY: case VALUE_IS_ARRAY:
return _content.asCollection.memoryUsage(); return _content.asCollection.memoryUsage();
@ -312,7 +318,7 @@ class VariantData {
void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) { void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) {
if (_flags & OWNED_VALUE_BIT) if (_flags & OWNED_VALUE_BIT)
_content.asString += stringDistance; _content.asString.data += stringDistance;
if (_flags & COLLECTION_MASK) if (_flags & COLLECTION_MASK)
_content.asCollection.movePointers(stringDistance, variantDistance); _content.asCollection.movePointers(stringDistance, variantDistance);
} }
@ -342,7 +348,7 @@ class VariantData {
if (value.isNull()) if (value.isNull())
setNull(); setNull();
else else
setString(LinkedString(value.data())); setString(LinkedString(value.data(), value.size()));
return true; return true;
} }
@ -358,7 +364,7 @@ class VariantData {
setNull(); setNull();
return false; return false;
} }
setString(CopiedString(copy)); setString(CopiedString(copy, value.size()));
return true; return true;
} }
}; };

View File

@ -26,7 +26,7 @@ inline T VariantData::asIntegral() const {
return convertNumber<T>(_content.asSignedInteger); return convertNumber<T>(_content.asSignedInteger);
case VALUE_IS_LINKED_STRING: case VALUE_IS_LINKED_STRING:
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return parseNumber<T>(_content.asString); return parseNumber<T>(_content.asString.data);
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
return convertNumber<T>(_content.asFloat); return convertNumber<T>(_content.asFloat);
default: default:
@ -62,7 +62,7 @@ inline T VariantData::asFloat() const {
return static_cast<T>(_content.asSignedInteger); return static_cast<T>(_content.asSignedInteger);
case VALUE_IS_LINKED_STRING: case VALUE_IS_LINKED_STRING:
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return parseNumber<T>(_content.asString); return parseNumber<T>(_content.asString.data);
case VALUE_IS_FLOAT: case VALUE_IS_FLOAT:
return static_cast<T>(_content.asFloat); return static_cast<T>(_content.asFloat);
default: default:
@ -73,11 +73,11 @@ inline T VariantData::asFloat() const {
inline String VariantData::asString() const { inline String VariantData::asString() const {
switch (type()) { switch (type()) {
case VALUE_IS_LINKED_STRING: case VALUE_IS_LINKED_STRING:
return String(_content.asString, true); return String(_content.asString.data, _content.asString.size, true);
case VALUE_IS_OWNED_STRING: case VALUE_IS_OWNED_STRING:
return String(_content.asString, false); return String(_content.asString.data, _content.asString.size, false);
default: default:
return 0; return String();
} }
} }

View File

@ -108,7 +108,7 @@ class VariantSlot {
if (_flags & OWNED_KEY_BIT) if (_flags & OWNED_KEY_BIT)
_key += stringDistance; _key += stringDistance;
if (_flags & OWNED_VALUE_BIT) if (_flags & OWNED_VALUE_BIT)
_content.asString += stringDistance; _content.asString.data += stringDistance;
if (_flags & COLLECTION_MASK) if (_flags & COLLECTION_MASK)
_content.asCollection.movePointers(stringDistance, variantDistance); _content.asCollection.movePointers(stringDistance, variantDistance);
} }

View File

@ -46,7 +46,7 @@ struct Visitor {
return TResult(); return TResult();
} }
TResult visitString(const char *) { TResult visitString(const char *, size_t) {
return TResult(); return TResult();
} }
}; };