Changed the rules of string duplication (fixes #658)

This commit is contained in:
Benoit Blanchon
2018-01-14 13:46:28 +01:00
parent 5c33fd4b94
commit e92612b511
22 changed files with 416 additions and 224 deletions

View File

@ -1,6 +1,22 @@
ArduinoJson: change log
=======================
HEAD
----
* Changed the rules of string duplication (issue #658)
> ### New rules for string duplication
>
> | type | duplication |
> |:-------------|:------------|
> | const char* | no |
> | char* | ~~no~~ yes |
> | String | yes |
> | std::string | yes |
>
> These new rules make `JsonBuffer::strdup()` useless.
v5.12.0
-------

View File

@ -0,0 +1,34 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../JsonBuffer.hpp"
#include "../JsonVariant.hpp"
#include "../StringTraits/StringTraits.hpp"
#include "../TypeTraits/EnableIf.hpp"
namespace ArduinoJson {
namespace Internals {
template <typename Source, typename Enable = void>
struct ValueSaver {
template <typename Destination>
static bool save(JsonBuffer*, Destination& destination, Source source) {
destination = source;
return true;
}
};
template <typename Source>
struct ValueSaver<Source, typename TypeTraits::EnableIf<
TypeTraits::IsString<Source>::value>::type> {
template <typename Destination>
static bool save(JsonBuffer* buffer, Destination& destination,
Source source) {
return StringTraits<Source>::save(source, destination, buffer);
}
};
}
}

View File

@ -1,48 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#pragma once
#include "../JsonBuffer.hpp"
#include "../JsonVariant.hpp"
#include "../StringTraits/StringTraits.hpp"
#include "../TypeTraits/EnableIf.hpp"
namespace ArduinoJson {
namespace Internals {
template <typename TSourceRef, typename Enable = void>
struct ValueSetter {
template <typename TDestination>
static bool set(JsonBuffer*, TDestination& destination, TSourceRef source) {
destination = source;
return true;
}
};
template <typename TSourceRef>
struct ValueSetter<TSourceRef, typename TypeTraits::EnableIf<StringTraits<
TSourceRef>::should_duplicate>::type> {
template <typename TDestination>
static bool set(JsonBuffer* buffer, TDestination& destination,
TSourceRef source) {
const char* copy = buffer->strdup(source);
if (!copy) return false;
destination = copy;
return true;
}
};
template <typename TSourceRef>
struct ValueSetter<TSourceRef, typename TypeTraits::EnableIf<!StringTraits<
TSourceRef>::should_duplicate>::type> {
template <typename TDestination>
static bool set(JsonBuffer*, TDestination& destination, TSourceRef source) {
// unsigned char* -> char*
destination = reinterpret_cast<const char*>(source);
return true;
}
};
}
}

View File

@ -7,7 +7,7 @@
#include "Data/JsonBufferAllocated.hpp"
#include "Data/List.hpp"
#include "Data/ReferenceType.hpp"
#include "Data/ValueSetter.hpp"
#include "Data/ValueSaver.hpp"
#include "JsonVariant.hpp"
#include "Serialization/JsonPrintable.hpp"
#include "StringTraits/StringTraits.hpp"
@ -56,19 +56,17 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
//
// bool add(TValue);
// TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename T>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<T>::value, bool>::type add(
const T &value) {
bool add(const T &value) {
return add_impl<const T &>(value);
}
//
// bool add(TValue);
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename T>
bool add(const T *value) {
return add_impl<const T *>(value);
bool add(T *value) {
return add_impl<T *>(value);
}
//
// bool add(TValue value, uint8_t decimals);
@ -81,21 +79,19 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
// Sets the value at specified index.
//
// bool add(size_t index, TValue);
// bool add(size_t index, const TValue&);
// TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename T>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<T>::value, bool>::type set(
size_t index, const T &value) {
bool set(size_t index, const T &value) {
return set_impl<const T &>(index, value);
}
//
// bool add(size_t index, TValue);
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename T>
bool set(size_t index, const T *value) {
return set_impl<const T *>(index, value);
bool set(size_t index, T *value) {
return set_impl<T *>(index, value);
}
//
// bool set(size_t index, TValue value, uint8_t decimals);
@ -208,14 +204,14 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
bool set_impl(size_t index, TValueRef value) {
iterator it = begin() += index;
if (it == end()) return false;
return Internals::ValueSetter<TValueRef>::set(_buffer, *it, value);
return Internals::ValueSaver<TValueRef>::save(_buffer, *it, value);
}
template <typename TValueRef>
bool add_impl(TValueRef value) {
iterator it = Internals::List<JsonVariant>::add();
if (it == end()) return false;
return Internals::ValueSetter<TValueRef>::set(_buffer, *it, value);
return Internals::ValueSaver<TValueRef>::save(_buffer, *it, value);
}
};

View File

@ -25,10 +25,9 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
// Replaces the value
//
// operator=(TValue)
// operator=(const TValue&)
// TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename T>
FORCE_INLINE JsonArraySubscript& operator=(const T& src) {
_array.set(_index, src);
@ -36,9 +35,9 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
}
//
// operator=(TValue)
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename T>
FORCE_INLINE JsonArraySubscript& operator=(const T* src) {
FORCE_INLINE JsonArraySubscript& operator=(T* src) {
_array.set(_index, src);
return *this;
}
@ -59,19 +58,18 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
// Replaces the value
//
// bool set(TValue)
// bool set(const TValue&)
// TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue>
FORCE_INLINE bool set(const TValue& value) {
return _array.set(_index, value);
}
//
// bool set(TValue)
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename TValue>
FORCE_INLINE bool set(const TValue* value) {
FORCE_INLINE bool set(TValue* value) {
return _array.set(_index, value);
}
//

View File

@ -7,7 +7,7 @@
#include "Data/JsonBufferAllocated.hpp"
#include "Data/List.hpp"
#include "Data/ReferenceType.hpp"
#include "Data/ValueSetter.hpp"
#include "Data/ValueSaver.hpp"
#include "JsonPair.hpp"
#include "Serialization/JsonPrintable.hpp"
#include "StringTraits/StringTraits.hpp"
@ -50,17 +50,15 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// JsonObjectSubscript operator[](TKey)
// TKey = const std::string&, const String&
template <typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
JsonObjectSubscript<const TString&> >::type
operator[](const TString& key) {
JsonObjectSubscript<const TString&> operator[](const TString& key) {
return JsonObjectSubscript<const TString&>(*this, key);
}
//
// JsonObjectSubscript operator[](TKey)
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, char[], const char[N], const FlashStringHelper*
template <typename TString>
JsonObjectSubscript<const TString*> operator[](const TString* key) {
return JsonObjectSubscript<const TString*>(*this, key);
JsonObjectSubscript<TString*> operator[](TString* key) {
return JsonObjectSubscript<TString*>(*this, key);
}
// Gets the value associated with the specified key.
@ -68,10 +66,8 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// const JsonObjectSubscript operator[](TKey) const;
// TKey = const std::string&, const String&
template <typename TString>
typename TypeTraits::EnableIf<
!TypeTraits::IsArray<TString>::value,
const JsonObjectSubscript<const TString&> >::type
operator[](const TString& key) const {
const JsonObjectSubscript<const TString&> operator[](
const TString& key) const {
return JsonObjectSubscript<const TString&>(*const_cast<JsonObject*>(this),
key);
}
@ -79,10 +75,8 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// const JsonObjectSubscript operator[](TKey) const;
// TKey = const char*, const char[N], const FlashStringHelper*
template <typename TString>
const JsonObjectSubscript<const TString*> operator[](
const TString* key) const {
return JsonObjectSubscript<const TString*>(*const_cast<JsonObject*>(this),
key);
const JsonObjectSubscript<TString*> operator[](TString* key) const {
return JsonObjectSubscript<TString*>(*const_cast<JsonObject*>(this), key);
}
// Sets the specified key with the specified value.
@ -90,43 +84,35 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// bool set(TKey, TValue);
// TKey = const std::string&, const String&
// TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value &&
!TypeTraits::IsArray<TValue>::value,
bool>::type
set(const TString& key, const TValue& value) {
bool set(const TString& key, const TValue& value) {
return set_impl<const TString&, const TValue&>(key, value);
}
//
// bool set(TKey, TValue);
// TKey = const std::string&, const String&
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename TValue, typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
bool>::type
set(const TString& key, const TValue* value) {
return set_impl<const TString&, const TValue*>(key, value);
bool set(const TString& key, TValue* value) {
return set_impl<const TString&, TValue*>(key, value);
}
//
// bool set(TKey, TValue);
// TKey = const char*, const char[N], const FlashStringHelper*
// bool set(TKey, const TValue&);
// TKey = char*, const char*, const FlashStringHelper*
// TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TValue>::value, bool>::type
set(const TString* key, const TValue& value) {
return set_impl<const TString*, const TValue&>(key, value);
bool set(TString* key, const TValue& value) {
return set_impl<TString*, const TValue&>(key, value);
}
//
// bool set(TKey, TValue);
// TKey = const char*, const char[N], const FlashStringHelper*
// TValue = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename TValue, typename TString>
bool set(const TString* key, const TValue* value) {
return set_impl<const TString*, const TValue*>(key, value);
bool set(TString* key, TValue* value) {
return set_impl<TString*, TValue*>(key, value);
}
//
// bool set(TKey, TValue, uint8_t decimals);
@ -134,8 +120,7 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// TValue = float, double
template <typename TValue, typename TString>
DEPRECATED("Second argument is not supported anymore")
typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<TValue>::value &&
!TypeTraits::IsArray<TString>::value,
typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<TValue>::value,
bool>::type
set(const TString& key, TValue value, uint8_t) {
return set_impl<const TString&, const JsonVariant&>(key,
@ -143,41 +128,35 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
}
//
// bool set(TKey, TValue, uint8_t decimals);
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, const FlashStringHelper*
// TValue = float, double
template <typename TValue, typename TString>
DEPRECATED("Second argument is not supported anymore")
typename TypeTraits::EnableIf<TypeTraits::IsFloatingPoint<TValue>::value,
bool>::type
set(const TString* key, TValue value, uint8_t) {
return set_impl<const TString*, const JsonVariant&>(key,
JsonVariant(value));
set(TString* key, TValue value, uint8_t) {
return set_impl<TString*, const JsonVariant&>(key, JsonVariant(value));
}
// Gets the value associated with the specified key.
//
// TValue get<TValue>(TKey);
// TValue get<TValue>(TKey) const;
// TKey = const std::string&, const String&
// TValue = bool, char, long, int, short, float, double,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename TypeTraits::EnableIf<
!TypeTraits::IsArray<TString>::value,
typename Internals::JsonVariantAs<TValue>::type>::type
get(const TString& key) const {
typename Internals::JsonVariantAs<TValue>::type get(
const TString& key) const {
return get_impl<const TString&, TValue>(key);
}
//
// TValue get<TValue>(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
// TValue get<TValue>(TKey) const;
// TKey = char*, const char*, const FlashStringHelper*
// TValue = bool, char, long, int, short, float, double,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename Internals::JsonVariantAs<TValue>::type get(
const TString* key) const {
return get_impl<const TString*, TValue>(key);
typename Internals::JsonVariantAs<TValue>::type get(TString* key) const {
return get_impl<TString*, TValue>(key);
}
// Checks the type of the value associated with the specified key.
@ -186,23 +165,19 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// bool is<TValue>(TKey) const;
// TKey = const std::string&, const String&
// TValue = bool, char, long, int, short, float, double,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
bool>::type
is(const TString& key) const {
bool is(const TString& key) const {
return is_impl<const TString&, TValue>(key);
}
//
// bool is<TValue>(TKey) const;
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, const FlashStringHelper*
// TValue = bool, char, long, int, short, float, double,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue, typename TString>
bool is(const TString* key) const {
return is_impl<const TString*, TValue>(key);
bool is(TString* key) const {
return is_impl<TString*, TValue>(key);
}
// Creates and adds a JsonArray.
@ -210,16 +185,14 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// JsonArray& createNestedArray(TKey);
// TKey = const std::string&, const String&
template <typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
JsonArray&>::type
createNestedArray(const TString& key) {
JsonArray& createNestedArray(const TString& key) {
return createNestedArray_impl<const TString&>(key);
}
// JsonArray& createNestedArray(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
JsonArray& createNestedArray(const TString* key) {
return createNestedArray_impl<const TString*>(key);
JsonArray& createNestedArray(TString* key) {
return createNestedArray_impl<TString*>(key);
}
// Creates and adds a JsonObject.
@ -227,17 +200,15 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// JsonObject& createNestedObject(TKey);
// TKey = const std::string&, const String&
template <typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
JsonObject&>::type
createNestedObject(const TString& key) {
JsonObject& createNestedObject(const TString& key) {
return createNestedObject_impl<const TString&>(key);
}
//
// JsonObject& createNestedObject(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
JsonObject& createNestedObject(const TString* key) {
return createNestedObject_impl<const TString*>(key);
JsonObject& createNestedObject(TString* key) {
return createNestedObject_impl<TString*>(key);
}
// Tells weither the specified key is present and associated with a value.
@ -245,17 +216,15 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// bool containsKey(TKey);
// TKey = const std::string&, const String&
template <typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
bool>::type
containsKey(const TString& key) const {
bool containsKey(const TString& key) const {
return findKey<const TString&>(key) != end();
}
//
// bool containsKey(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
bool containsKey(const TString* key) const {
return findKey<const TString*>(key) != end();
bool containsKey(TString* key) const {
return findKey<TString*>(key) != end();
}
// Removes the specified key and the associated value.
@ -263,17 +232,15 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
// void remove(TKey);
// TKey = const std::string&, const String&
template <typename TString>
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TString>::value,
void>::type
remove(const TString& key) {
void remove(const TString& key) {
remove(findKey<const TString&>(key));
}
//
// void remove(TKey);
// TKey = const char*, const char[N], const FlashStringHelper*
// TKey = char*, const char*, char[], const char[], const FlashStringHelper*
template <typename TString>
void remove(const TString* key) {
remove(findKey<const TString*>(key));
void remove(TString* key) {
remove(findKey<TString*>(key));
}
//
// void remove(iterator)
@ -318,10 +285,10 @@ class JsonObject : public Internals::JsonPrintable<JsonObject>,
if (it == end()) return false;
bool key_ok =
Internals::ValueSetter<TStringRef>::set(_buffer, it->key, key);
Internals::ValueSaver<TStringRef>::save(_buffer, it->key, key);
if (!key_ok) return false;
}
return Internals::ValueSetter<TValueRef>::set(_buffer, it->value, value);
return Internals::ValueSaver<TValueRef>::save(_buffer, it->value, value);
}
template <typename TStringRef, typename TValue>

View File

@ -31,10 +31,9 @@ class JsonObjectSubscript
// Set the specified value
//
// operator=(TValue);
// operator=(const TValue&);
// TValue = bool, char, long, int, short, float, double,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue>
FORCE_INLINE
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TValue>::value,
@ -45,7 +44,7 @@ class JsonObjectSubscript
}
//
// operator=(TValue);
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char*, const FlashStringHelper*
template <typename TValue>
FORCE_INLINE this_type& operator=(const TValue* src) {
_object.set(_key, src);
@ -68,10 +67,9 @@ class JsonObjectSubscript
// Sets the specified value.
//
// bool set(TValue);
// bool set(const TValue&);
// TValue = bool, char, long, int, short, float, double, RawJson, JsonVariant,
// const std::string&, const String&,
// const JsonArray&, const JsonObject&
// std::string, String, JsonArray, JsonObject
template <typename TValue>
FORCE_INLINE
typename TypeTraits::EnableIf<!TypeTraits::IsArray<TValue>::value,
@ -81,7 +79,7 @@ class JsonObjectSubscript
}
//
// bool set(TValue);
// TValue = const char*, const char[N], const FlashStringHelper*
// TValue = char*, const char, const FlashStringHelper*
template <typename TValue>
FORCE_INLINE bool set(const TValue* value) {
return _object.set(_key, value);

View File

@ -6,7 +6,7 @@
#ifdef _MSC_VER // Visual Studio
#define FORCE_INLINE __forceinline
#define FORCE_INLINE // __forceinline causes C4714 when returning std::string
#define NO_INLINE __declspec(noinline)
#define DEPRECATED(msg) __declspec(deprecated(msg))

View File

@ -33,6 +33,7 @@ struct CharPointerTraits {
return strcmp(reinterpret_cast<const char*>(str), expected) == 0;
}
// TODO: remove
template <typename Buffer>
static char* duplicate(const TChar* str, Buffer* buffer) {
if (!str) return NULL;
@ -44,12 +45,46 @@ struct CharPointerTraits {
static const bool has_append = false;
static const bool has_equals = true;
static const bool should_duplicate = false;
};
// const char*, const unsigned char*, const signed char*
template <typename TChar>
struct StringTraits<TChar*, typename TypeTraits::EnableIf<
TypeTraits::IsChar<TChar>::value>::type>
: CharPointerTraits<TChar> {};
TypeTraits::IsChar<TChar>::value &&
TypeTraits::IsConst<TChar>::value>::type>
: CharPointerTraits<TChar> {
// Just save the pointer
template <typename Buffer, typename Destination>
static typename TypeTraits::EnableIf<TypeTraits::IsConst<TChar>::value,
bool>::type
save(const TChar* source, Destination& dest, Buffer*) {
dest = reinterpret_cast<const char*>(source);
return true;
}
};
// char*, unsigned char*, signed char*
template <typename TChar>
struct StringTraits<TChar*, typename TypeTraits::EnableIf<
TypeTraits::IsChar<TChar>::value &&
!TypeTraits::IsConst<TChar>::value>::type>
: CharPointerTraits<TChar> {
// Make a copy of the string
template <typename Buffer, typename Destination>
static typename TypeTraits::EnableIf<!TypeTraits::IsConst<TChar>::value,
bool>::type
save(const TChar* source, Destination& dest, Buffer* buffer) {
if (source) {
size_t size = strlen(reinterpret_cast<const char*>(source)) + 1;
void* dup = buffer->alloc(size);
if (!dup) return false;
memcpy(dup, source, size);
dest = reinterpret_cast<const char*>(dup);
} else {
dest = reinterpret_cast<const char*>(source);
}
return true;
}
};
}
}

View File

@ -34,6 +34,7 @@ struct StringTraits<const __FlashStringHelper*, void> {
return strcmp_P(expected, (const char*)str) == 0;
}
// TODO: remove
template <typename Buffer>
static char* duplicate(const __FlashStringHelper* str, Buffer* buffer) {
if (!str) return NULL;
@ -43,9 +44,22 @@ struct StringTraits<const __FlashStringHelper*, void> {
return static_cast<char*>(dup);
}
template <typename Buffer, typename Destination>
static bool save(const __FlashStringHelper* source, Destination& dest,
Buffer* buffer) {
if (source) {
size_t size = strlen_P((const char*)source) + 1;
void* dup = buffer->alloc(size);
if (dup != NULL) memcpy_P(dup, (const char*)source, size);
dest = reinterpret_cast<const char*>(dup);
} else {
dest = reinterpret_cast<const char*>(source);
}
return true;
}
static const bool has_append = false;
static const bool has_equals = true;
static const bool should_duplicate = true;
};
}
}

View File

@ -19,6 +19,7 @@ namespace Internals {
template <typename TString>
struct StdStringTraits {
// TODO: remove
template <typename Buffer>
static char* duplicate(const TString& str, Buffer* buffer) {
if (!str.c_str()) return NULL; // <- Arduino string can return NULL
@ -28,6 +29,21 @@ struct StdStringTraits {
return static_cast<char*>(dup);
}
template <typename Buffer, typename Destination>
static bool save(const TString& str, Destination& dest, Buffer* buffer) {
// Arduino's String::c_str() can return NULL
if (str.c_str()) {
size_t size = str.length() + 1;
void* dup = buffer->alloc(size);
if (!dup) return false;
memcpy(dup, str.c_str(), size);
dest = reinterpret_cast<const char*>(dup);
} else {
dest = str.c_str();
}
return true;
}
struct Reader : CharPointerTraits<char>::Reader {
Reader(const TString& str) : CharPointerTraits<char>::Reader(str.c_str()) {}
};
@ -46,7 +62,6 @@ struct StdStringTraits {
static const bool has_append = true;
static const bool has_equals = true;
static const bool should_duplicate = true;
};
#if ARDUINOJSON_ENABLE_ARDUINO_STRING

View File

@ -9,6 +9,7 @@
#include "../TypeTraits/EnableIf.hpp"
#include "../TypeTraits/IsBaseOf.hpp"
#include "../TypeTraits/IsChar.hpp"
#include "../TypeTraits/IsConst.hpp"
#include "../TypeTraits/RemoveReference.hpp"
namespace ArduinoJson {

View File

@ -13,6 +13,7 @@ add_executable(JsonArrayTests
printTo.cpp
remove.cpp
set.cpp
size.cpp
subscript.cpp
)

View File

@ -9,11 +9,6 @@ TEST_CASE("JsonArray::add()") {
DynamicJsonBuffer _jsonBuffer;
JsonArray& _array = _jsonBuffer.createArray();
SECTION("SizeIncreased_WhenValuesAreAdded") {
_array.add("hello");
REQUIRE(1U == _array.size());
}
SECTION("int") {
_array.add(123);
REQUIRE(123 == _array[0].as<int>());
@ -38,7 +33,7 @@ TEST_CASE("JsonArray::add()") {
SECTION("const char*") {
const char* str = "hello";
_array.add(str);
REQUIRE(str == _array[0].as<const char*>());
REQUIRE(str == _array[0].as<std::string>());
REQUIRE(_array[0].is<const char*>());
REQUIRE_FALSE(_array[0].is<int>());
}
@ -82,4 +77,22 @@ TEST_CASE("JsonArray::add()") {
REQUIRE(str == _array[0]);
}
SECTION("should not duplicate const char*") {
_array.add("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate char*") {
_array.add(const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate std::string") {
_array.add(std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
}

View File

@ -12,11 +12,6 @@ TEST_CASE("JsonArray::set()") {
JsonArray& _array = _jsonBuffer.createArray();
_array.add(0);
SECTION("SizeIsUnchanged") {
_array.set(0, "hello");
REQUIRE(1U == _array.size());
}
SECTION("int") {
_array.set(0, 123);
REQUIRE(123 == _array[0].as<int>());
@ -82,4 +77,22 @@ TEST_CASE("JsonArray::set()") {
REQUIRE_THAT(_array[0].as<char*>(), Equals("hello"));
}
SECTION("should not duplicate const char*") {
_array.set(0, "world");
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate char*") {
_array.set(0, const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate std::string") {
_array.set(0, std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
}

35
test/JsonArray/size.cpp Normal file
View File

@ -0,0 +1,35 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::size()") {
DynamicJsonBuffer _jsonBuffer;
JsonArray& _array = _jsonBuffer.createArray();
SECTION("increases after add()") {
_array.add("hello");
REQUIRE(1U == _array.size());
_array.add("world");
REQUIRE(2U == _array.size());
}
SECTION("remains the same after set()") {
_array.add("hello");
REQUIRE(1U == _array.size());
_array.set(0, "hello");
REQUIRE(1U == _array.size());
}
SECTION("remains the same after assigment") {
_array.add("hello");
REQUIRE(1U == _array.size());
_array[0] = "hello";
REQUIRE(1U == _array.size());
}
}

View File

@ -11,11 +11,6 @@ TEST_CASE("JsonArray::operator[]") {
JsonArray& _array = _jsonBuffer.createArray();
_array.add(0);
SECTION("SizeIsUnchanged") {
_array[0] = "hello";
REQUIRE(1U == _array.size());
}
SECTION("int") {
_array[0] = 123;
REQUIRE(123 == _array[0].as<int>());
@ -103,4 +98,22 @@ TEST_CASE("JsonArray::operator[]") {
REQUIRE(str == _array[0]);
}
SECTION("should not duplicate const char*") {
_array[0] = "world";
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate char*") {
_array[0] = const_cast<char*>("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate std::string") {
_array[0] = std::string("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
}

View File

@ -12,6 +12,7 @@ add_executable(JsonObjectTests
printTo.cpp
remove.cpp
set.cpp
size.cpp
subscript.cpp
)

View File

@ -10,17 +10,6 @@ TEST_CASE("JsonObject::set()") {
DynamicJsonBuffer jb;
JsonObject& _object = jb.createObject();
SECTION("SizeIncreased_WhenValuesAreAdded") {
_object.set("hello", 42);
REQUIRE(1 == _object.size());
}
SECTION("SizeUntouched_WhenSameValueIsAdded") {
_object["hello"] = 1;
_object["hello"] = 2;
REQUIRE(1 == _object.size());
}
SECTION("int") {
_object.set("hello", 123);
@ -91,17 +80,59 @@ TEST_CASE("JsonObject::set()") {
REQUIRE(42 == _object["a"]);
}
SECTION("ShouldReturnTrue_WhenAllocationSucceeds") {
SECTION("returns true when allocation succeeds") {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 15> jsonBuffer;
JsonObject& obj = jsonBuffer.createObject();
REQUIRE(true == obj.set(std::string("hello"), std::string("world")));
}
SECTION("ShouldReturnFalse_WhenAllocationFails") {
SECTION("returns false when allocation fails") {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 10> jsonBuffer;
JsonObject& obj = jsonBuffer.createObject();
REQUIRE(false == obj.set(std::string("hello"), std::string("world")));
}
SECTION("should not duplicate const char*") {
_object.set("hello", "world");
const size_t expectedSize = JSON_OBJECT_SIZE(1);
REQUIRE(expectedSize == jb.size());
}
SECTION("should duplicate char* value") {
_object.set("hello", const_cast<char*>("world"));
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == jb.size());
}
SECTION("should duplicate char* key") {
_object.set(const_cast<char*>("hello"), "world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == jb.size());
}
SECTION("should duplicate char* key&value") {
_object.set(const_cast<char*>("hello"), const_cast<char*>("world"));
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
REQUIRE(expectedSize <= jb.size());
}
SECTION("should duplicate std::string value") {
_object.set("hello", std::string("world"));
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == jb.size());
}
SECTION("should duplicate std::string key") {
_object.set(std::string("hello"), "world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == jb.size());
}
SECTION("should duplicate std::string key&value") {
_object.set(std::string("hello"), std::string("world"));
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
REQUIRE(expectedSize <= jb.size());
}
}

23
test/JsonObject/size.cpp Normal file
View File

@ -0,0 +1,23 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
TEST_CASE("JsonObject::size()") {
DynamicJsonBuffer jb;
JsonObject& _object = jb.createObject();
SECTION("increases when values are added") {
_object.set("hello", 42);
REQUIRE(1 == _object.size());
}
SECTION("doesn't increase when the smae key is added twice") {
_object["hello"] = 1;
_object["hello"] = 2;
REQUIRE(1 == _object.size());
}
}

View File

@ -9,17 +9,6 @@ TEST_CASE("JsonObject::operator[]") {
DynamicJsonBuffer _jsonBuffer;
JsonObject& _object = _jsonBuffer.createObject();
SECTION("SizeIncreased_WhenValuesAreAdded") {
_object["hello"] = 1;
REQUIRE(1 == _object.size());
}
SECTION("SizeUntouched_WhenSameValueIsAdded") {
_object["hello"] = 1;
_object["hello"] = 2;
REQUIRE(1 == _object.size());
}
SECTION("int") {
_object["hello"] = 123;
@ -113,9 +102,51 @@ TEST_CASE("JsonObject::operator[]") {
REQUIRE(42 == _object["a"]);
}
SECTION("KeyAsCharArray") { // issue #423
SECTION("char key[]") { // issue #423
char key[] = "hello";
_object[key] = 42;
REQUIRE(42 == _object[key]);
}
SECTION("should not duplicate const char*") {
_object["hello"] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1);
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate char* value") {
_object["hello"] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate char* key") {
_object[const_cast<char*>("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate char* key&value") {
_object[const_cast<char*>("hello")] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
REQUIRE(expectedSize <= _jsonBuffer.size());
}
SECTION("should duplicate std::string value") {
_object["hello"] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate std::string key") {
_object[std::string("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
REQUIRE(expectedSize == _jsonBuffer.size());
}
SECTION("should duplicate std::string key&value") {
_object[std::string("hello")] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
REQUIRE(expectedSize <= _jsonBuffer.size());
}
}

View File

@ -36,4 +36,9 @@ TEST_CASE("TypeTraits") {
REQUIRE((IsString<std::string>::value));
REQUIRE_FALSE((IsString<double>::value));
}
SECTION("IsConst") {
REQUIRE_FALSE((IsConst<char>::value));
REQUIRE((IsConst<const char>::value));
}
}