mirror of
https://github.com/open-source-parsers/jsoncpp.git
synced 2024-12-27 11:21:02 +08:00
Merge pull request #197 from cdunn2001/allow-zero
allow zeroes in strings
This commit is contained in:
commit
9e49e3d84a
@ -99,19 +99,24 @@ private:
|
|||||||
* The type of the held value is represented by a #ValueType and
|
* The type of the held value is represented by a #ValueType and
|
||||||
* can be obtained using type().
|
* can be obtained using type().
|
||||||
*
|
*
|
||||||
* values of an #objectValue or #arrayValue can be accessed using operator[]()
|
* Values of an #objectValue or #arrayValue can be accessed using operator[]()
|
||||||
*methods.
|
* methods.
|
||||||
* Non const methods will automatically create the a #nullValue element
|
* Non-const methods will automatically create the a #nullValue element
|
||||||
* if it does not exist.
|
* if it does not exist.
|
||||||
* The sequence of an #arrayValue will be automatically resize and initialized
|
* The sequence of an #arrayValue will be automatically resized and initialized
|
||||||
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
|
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
|
||||||
*
|
*
|
||||||
* The get() methods can be used to obtanis default value in the case the
|
* The get() methods can be used to obtain default value in the case the
|
||||||
*required element
|
* required element does not exist.
|
||||||
* does not exist.
|
|
||||||
*
|
*
|
||||||
* It is possible to iterate over the list of a #objectValue values using
|
* It is possible to iterate over the list of a #objectValue values using
|
||||||
* the getMemberNames() method.
|
* the getMemberNames() method.
|
||||||
|
*
|
||||||
|
* \note #Value string-length fit in size_t, but keys must be < 2^30.
|
||||||
|
* (The reason is an implementation detail.) A #CharReader will raise an
|
||||||
|
* exception if a bound is exceeded to avoid security holes in your app,
|
||||||
|
* but the Value API does *not* check bounds. That is the responsibility
|
||||||
|
* of the caller.
|
||||||
*/
|
*/
|
||||||
class JSON_API Value {
|
class JSON_API Value {
|
||||||
friend class ValueIteratorBase;
|
friend class ValueIteratorBase;
|
||||||
@ -164,24 +169,27 @@ private:
|
|||||||
duplicateOnCopy
|
duplicateOnCopy
|
||||||
};
|
};
|
||||||
CZString(ArrayIndex index);
|
CZString(ArrayIndex index);
|
||||||
CZString(char const* cstr, unsigned length, DuplicationPolicy allocate);
|
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
|
||||||
CZString(const CZString& other);
|
CZString(CZString const& other);
|
||||||
~CZString();
|
~CZString();
|
||||||
CZString& operator=(CZString other);
|
CZString& operator=(CZString other);
|
||||||
bool operator<(const CZString& other) const;
|
bool operator<(CZString const& other) const;
|
||||||
bool operator==(const CZString& other) const;
|
bool operator==(CZString const& other) const;
|
||||||
ArrayIndex index() const;
|
ArrayIndex index() const;
|
||||||
const char* c_str() const;
|
//const char* c_str() const; ///< \deprecated
|
||||||
|
char const* data() const;
|
||||||
|
unsigned length() const;
|
||||||
bool isStaticString() const;
|
bool isStaticString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void swap(CZString& other);
|
void swap(CZString& other);
|
||||||
|
|
||||||
struct StringStorage {
|
struct StringStorage {
|
||||||
DuplicationPolicy policy_: 2;
|
DuplicationPolicy policy_: 2;
|
||||||
unsigned length_: 30; // 1GB max
|
unsigned length_: 30; // 1GB max
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* cstr_;
|
char const* cstr_; // actually, a prefixed string, unless policy is noDup
|
||||||
union {
|
union {
|
||||||
ArrayIndex index_;
|
ArrayIndex index_;
|
||||||
StringStorage storage_;
|
StringStorage storage_;
|
||||||
@ -220,20 +228,25 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
Value(UInt64 value);
|
Value(UInt64 value);
|
||||||
#endif // if defined(JSON_HAS_INT64)
|
#endif // if defined(JSON_HAS_INT64)
|
||||||
Value(double value);
|
Value(double value);
|
||||||
Value(const char* value);
|
Value(const char* value); ///! Copy til first 0. (NULL causes to seg-fault.)
|
||||||
Value(const char* beginValue, const char* endValue);
|
Value(const char* beginValue, const char* endValue); ///! Copy all, incl zeroes.
|
||||||
/** \brief Constructs a value from a static string.
|
/** \brief Constructs a value from a static string.
|
||||||
|
|
||||||
* Like other value string constructor but do not duplicate the string for
|
* Like other value string constructor but do not duplicate the string for
|
||||||
* internal storage. The given string must remain alive after the call to this
|
* internal storage. The given string must remain alive after the call to this
|
||||||
* constructor.
|
* constructor.
|
||||||
|
* \note This works only for null-terminated strings. (We cannot change the
|
||||||
|
* size of this class, so we have nowhere to store the length,
|
||||||
|
* which might be computed later for various operations.)
|
||||||
|
*
|
||||||
* Example of usage:
|
* Example of usage:
|
||||||
* \code
|
* \code
|
||||||
* Json::Value aValue( StaticString("some text") );
|
* static StaticString foo("some text");
|
||||||
|
* Json::Value aValue(foo);
|
||||||
* \endcode
|
* \endcode
|
||||||
*/
|
*/
|
||||||
Value(const StaticString& value);
|
Value(const StaticString& value);
|
||||||
Value(const std::string& value);
|
Value(const std::string& value); ///! Copy data() til size(). Embedded zeroes too.
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
Value(const CppTL::ConstString& value);
|
Value(const CppTL::ConstString& value);
|
||||||
#endif
|
#endif
|
||||||
@ -260,8 +273,13 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
bool operator!=(const Value& other) const;
|
bool operator!=(const Value& other) const;
|
||||||
int compare(const Value& other) const;
|
int compare(const Value& other) const;
|
||||||
|
|
||||||
const char* asCString() const;
|
const char* asCString() const; ///! Embedded zeroes could cause you trouble!
|
||||||
std::string asString() const;
|
std::string asString() const; ///! Embedded zeroes are possible.
|
||||||
|
/** Get raw char* of string-value.
|
||||||
|
* \return false if !string. (Seg-fault if str or end are NULL.)
|
||||||
|
*/
|
||||||
|
bool getString(
|
||||||
|
char const** str, char const** end) const;
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
CppTL::ConstString asConstString() const;
|
CppTL::ConstString asConstString() const;
|
||||||
#endif
|
#endif
|
||||||
@ -352,19 +370,23 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
Value& append(const Value& value);
|
Value& append(const Value& value);
|
||||||
|
|
||||||
/// Access an object value by name, create a null member if it does not exist.
|
/// Access an object value by name, create a null member if it does not exist.
|
||||||
|
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
|
||||||
|
/// Exceeding that will cause an exception.
|
||||||
Value& operator[](const char* key);
|
Value& operator[](const char* key);
|
||||||
/// Access an object value by name, returns null if there is no member with
|
/// Access an object value by name, returns null if there is no member with
|
||||||
/// that name.
|
/// that name.
|
||||||
const Value& operator[](const char* key) const;
|
const Value& operator[](const char* key) const;
|
||||||
/// Access an object value by name, create a null member if it does not exist.
|
/// Access an object value by name, create a null member if it does not exist.
|
||||||
|
/// \param key may contain embedded nulls.
|
||||||
Value& operator[](const std::string& key);
|
Value& operator[](const std::string& key);
|
||||||
/// Access an object value by name, returns null if there is no member with
|
/// Access an object value by name, returns null if there is no member with
|
||||||
/// that name.
|
/// that name.
|
||||||
|
/// \param key may contain embedded nulls.
|
||||||
const Value& operator[](const std::string& key) const;
|
const Value& operator[](const std::string& key) const;
|
||||||
/** \brief Access an object value by name, create a null member if it does not
|
/** \brief Access an object value by name, create a null member if it does not
|
||||||
exist.
|
exist.
|
||||||
|
|
||||||
* If the object as no entry for that name, then the member name used to store
|
* If the object has no entry for that name, then the member name used to store
|
||||||
* the new entry is not duplicated.
|
* the new entry is not duplicated.
|
||||||
* Example of use:
|
* Example of use:
|
||||||
* \code
|
* \code
|
||||||
@ -384,11 +406,23 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
/// Return the member named key if it exist, defaultValue otherwise.
|
/// Return the member named key if it exist, defaultValue otherwise.
|
||||||
Value get(const char* key, const Value& defaultValue) const;
|
Value get(const char* key, const Value& defaultValue) const;
|
||||||
/// Return the member named key if it exist, defaultValue otherwise.
|
/// Return the member named key if it exist, defaultValue otherwise.
|
||||||
|
/// \param key may contain embedded nulls.
|
||||||
|
Value get(const char* key, const char* end, const Value& defaultValue) const;
|
||||||
|
/// Return the member named key if it exist, defaultValue otherwise.
|
||||||
|
/// \param key may contain embedded nulls.
|
||||||
Value get(const std::string& key, const Value& defaultValue) const;
|
Value get(const std::string& key, const Value& defaultValue) const;
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
/// Return the member named key if it exist, defaultValue otherwise.
|
/// Return the member named key if it exist, defaultValue otherwise.
|
||||||
Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
|
Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
|
||||||
#endif
|
#endif
|
||||||
|
/// Most general and efficient version of isMember()const, get()const,
|
||||||
|
/// and operator[]const
|
||||||
|
/// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30
|
||||||
|
Value const* find(char const* key, char const* end) const;
|
||||||
|
/// Most general and efficient version of object-mutators.
|
||||||
|
/// \note As stated elsewhere, behavior is undefined if (end-key) >= 2^30
|
||||||
|
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
|
||||||
|
Value const* demand(char const* key, char const* end);
|
||||||
/// \brief Remove and return the named member.
|
/// \brief Remove and return the named member.
|
||||||
///
|
///
|
||||||
/// Do nothing if it did not exist.
|
/// Do nothing if it did not exist.
|
||||||
@ -398,14 +432,21 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
/// \deprecated
|
/// \deprecated
|
||||||
Value removeMember(const char* key);
|
Value removeMember(const char* key);
|
||||||
/// Same as removeMember(const char*)
|
/// Same as removeMember(const char*)
|
||||||
|
/// \param key may contain embedded nulls.
|
||||||
/// \deprecated
|
/// \deprecated
|
||||||
Value removeMember(const std::string& key);
|
Value removeMember(const std::string& key);
|
||||||
|
/// Same as removeMember(const char* key, const char* end, Value* removed),
|
||||||
|
/// but 'key' is null-terminated.
|
||||||
|
bool removeMember(const char* key, Value* removed);
|
||||||
/** \brief Remove the named map member.
|
/** \brief Remove the named map member.
|
||||||
|
|
||||||
Update 'removed' iff removed.
|
Update 'removed' iff removed.
|
||||||
|
\param key may contain embedded nulls.
|
||||||
\return true iff removed (no exceptions)
|
\return true iff removed (no exceptions)
|
||||||
*/
|
*/
|
||||||
bool removeMember(const char* key, Value* removed);
|
bool removeMember(std::string const& key, Value* removed);
|
||||||
|
/// Same as removeMember(std::string const& key, Value* removed)
|
||||||
|
bool removeMember(const char* key, const char* end, Value* removed);
|
||||||
/** \brief Remove the indexed array element.
|
/** \brief Remove the indexed array element.
|
||||||
|
|
||||||
O(n) expensive operations.
|
O(n) expensive operations.
|
||||||
@ -415,9 +456,13 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
bool removeIndex(ArrayIndex i, Value* removed);
|
bool removeIndex(ArrayIndex i, Value* removed);
|
||||||
|
|
||||||
/// Return true if the object has a member named key.
|
/// Return true if the object has a member named key.
|
||||||
|
/// \note 'key' must be null-terminated.
|
||||||
bool isMember(const char* key) const;
|
bool isMember(const char* key) const;
|
||||||
/// Return true if the object has a member named key.
|
/// Return true if the object has a member named key.
|
||||||
|
/// \param key may contain embedded nulls.
|
||||||
bool isMember(const std::string& key) const;
|
bool isMember(const std::string& key) const;
|
||||||
|
/// Same as isMember(std::string const& key)const
|
||||||
|
bool isMember(const char* key, const char* end) const;
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
/// Return true if the object has a member named key.
|
/// Return true if the object has a member named key.
|
||||||
bool isMember(const CppTL::ConstString& key) const;
|
bool isMember(const CppTL::ConstString& key) const;
|
||||||
@ -463,7 +508,8 @@ Json::Value obj_value(Json::objectValue); // {}
|
|||||||
private:
|
private:
|
||||||
void initBasic(ValueType type, bool allocated = false);
|
void initBasic(ValueType type, bool allocated = false);
|
||||||
|
|
||||||
Value& resolveReference(const char* key, bool isStatic);
|
Value& resolveReference(const char* key);
|
||||||
|
Value& resolveReference(const char* key, const char* end);
|
||||||
|
|
||||||
struct CommentInfo {
|
struct CommentInfo {
|
||||||
CommentInfo();
|
CommentInfo();
|
||||||
@ -488,11 +534,12 @@ private:
|
|||||||
LargestUInt uint_;
|
LargestUInt uint_;
|
||||||
double real_;
|
double real_;
|
||||||
bool bool_;
|
bool bool_;
|
||||||
char* string_;
|
char* string_; // actually ptr to unsigned, followed by str, unless !allocated_
|
||||||
ObjectValues* map_;
|
ObjectValues* map_;
|
||||||
} value_;
|
} value_;
|
||||||
ValueType type_ : 8;
|
ValueType type_ : 8;
|
||||||
unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
|
||||||
|
// If not allocated_, string_ must be null-terminated.
|
||||||
CommentInfo* comments_;
|
CommentInfo* comments_;
|
||||||
|
|
||||||
// [start, limit) byte offsets in the source JSON text from which this Value
|
// [start, limit) byte offsets in the source JSON text from which this Value
|
||||||
@ -594,7 +641,12 @@ public:
|
|||||||
|
|
||||||
/// Return the member name of the referenced Value. "" if it is not an
|
/// Return the member name of the referenced Value. "" if it is not an
|
||||||
/// objectValue.
|
/// objectValue.
|
||||||
const char* memberName() const;
|
/// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.
|
||||||
|
char const* memberName() const;
|
||||||
|
/// Return the member name of the referenced Value, or NULL if it is not an
|
||||||
|
/// objectValue.
|
||||||
|
/// Better version than memberName(). Allows embedded nulls.
|
||||||
|
char const* memberName(char const** end) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Value& deref() const;
|
Value& deref() const;
|
||||||
@ -623,8 +675,8 @@ class JSON_API ValueConstIterator : public ValueIteratorBase {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
typedef const Value value_type;
|
typedef const Value value_type;
|
||||||
typedef unsigned int size_t;
|
//typedef unsigned int size_t;
|
||||||
typedef int difference_type;
|
//typedef int difference_type;
|
||||||
typedef const Value& reference;
|
typedef const Value& reference;
|
||||||
typedef const Value* pointer;
|
typedef const Value* pointer;
|
||||||
typedef ValueConstIterator SelfType;
|
typedef ValueConstIterator SelfType;
|
||||||
|
@ -1430,6 +1430,7 @@ bool OurReader::readObject(Token& tokenStart) {
|
|||||||
return addErrorAndRecover(
|
return addErrorAndRecover(
|
||||||
"Missing ':' after object member name", colon, tokenObjectEnd);
|
"Missing ':' after object member name", colon, tokenObjectEnd);
|
||||||
}
|
}
|
||||||
|
if (name.length() >= (1U<<30)) throw std::runtime_error("keylength >= 2^30");
|
||||||
Value& value = currentValue()[name];
|
Value& value = currentValue()[name];
|
||||||
nodes_.push(&value);
|
nodes_.push(&value);
|
||||||
bool ok = readValue();
|
bool ok = readValue();
|
||||||
|
@ -100,7 +100,40 @@ static inline char* duplicateStringValue(const char* value,
|
|||||||
return newString;
|
return newString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Free the string duplicated by duplicateStringValue().
|
/* Record the length as a prefix.
|
||||||
|
*/
|
||||||
|
static inline char* duplicateAndPrefixStringValue(
|
||||||
|
const char* value,
|
||||||
|
unsigned int length)
|
||||||
|
{
|
||||||
|
// Avoid an integer overflow in the call to malloc below by limiting length
|
||||||
|
// to a sane value.
|
||||||
|
JSON_ASSERT_MESSAGE(length <= (unsigned)Value::maxInt - sizeof(unsigned) - 1U,
|
||||||
|
"in Json::Value::duplicateAndPrefixStringValue(): "
|
||||||
|
"length too big for prefixing");
|
||||||
|
unsigned actualLength = length + sizeof(unsigned) + 1U;
|
||||||
|
char* newString = static_cast<char*>(malloc(actualLength));
|
||||||
|
JSON_ASSERT_MESSAGE(newString != 0,
|
||||||
|
"in Json::Value::duplicateAndPrefixStringValue(): "
|
||||||
|
"Failed to allocate string value buffer");
|
||||||
|
*reinterpret_cast<unsigned*>(newString) = length;
|
||||||
|
memcpy(newString + sizeof(unsigned), value, length);
|
||||||
|
newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later
|
||||||
|
return newString;
|
||||||
|
}
|
||||||
|
inline static void decodePrefixedString(
|
||||||
|
bool isPrefixed, char const* prefixed,
|
||||||
|
unsigned* length, char const** value)
|
||||||
|
{
|
||||||
|
if (!isPrefixed) {
|
||||||
|
*length = strlen(prefixed);
|
||||||
|
*value = prefixed;
|
||||||
|
} else {
|
||||||
|
*length = *reinterpret_cast<unsigned const*>(prefixed);
|
||||||
|
*value = prefixed + sizeof(unsigned);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().
|
||||||
*/
|
*/
|
||||||
static inline void releaseStringValue(char* value) { free(value); }
|
static inline void releaseStringValue(char* value) { free(value); }
|
||||||
|
|
||||||
@ -158,8 +191,6 @@ void Value::CommentInfo::setComment(const char* text, size_t len) {
|
|||||||
|
|
||||||
// Notes: policy_ indicates if the string was allocated when
|
// Notes: policy_ indicates if the string was allocated when
|
||||||
// a string is stored.
|
// a string is stored.
|
||||||
//
|
|
||||||
// TODO: Check for length > 1GB, in Reader.
|
|
||||||
|
|
||||||
Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {}
|
Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {}
|
||||||
|
|
||||||
@ -219,8 +250,9 @@ bool Value::CZString::operator==(const CZString& other) const {
|
|||||||
|
|
||||||
ArrayIndex Value::CZString::index() const { return index_; }
|
ArrayIndex Value::CZString::index() const { return index_; }
|
||||||
|
|
||||||
const char* Value::CZString::c_str() const { return cstr_; }
|
//const char* Value::CZString::c_str() const { return cstr_; }
|
||||||
|
const char* Value::CZString::data() const { return cstr_; }
|
||||||
|
unsigned Value::CZString::length() const { return storage_.length_; }
|
||||||
bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
|
bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////
|
||||||
@ -289,19 +321,19 @@ Value::Value(double value) {
|
|||||||
|
|
||||||
Value::Value(const char* value) {
|
Value::Value(const char* value) {
|
||||||
initBasic(stringValue, true);
|
initBasic(stringValue, true);
|
||||||
value_.string_ = duplicateStringValue(value);
|
value_.string_ = duplicateAndPrefixStringValue(value, strlen(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Value(const char* beginValue, const char* endValue) {
|
Value::Value(const char* beginValue, const char* endValue) {
|
||||||
initBasic(stringValue, true);
|
initBasic(stringValue, true);
|
||||||
value_.string_ =
|
value_.string_ =
|
||||||
duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue));
|
duplicateAndPrefixStringValue(beginValue, (unsigned int)(endValue - beginValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Value(const std::string& value) {
|
Value::Value(const std::string& value) {
|
||||||
initBasic(stringValue, true);
|
initBasic(stringValue, true);
|
||||||
value_.string_ =
|
value_.string_ =
|
||||||
duplicateStringValue(value.c_str(), (unsigned int)value.length());
|
duplicateAndPrefixStringValue(value.data(), (unsigned int)value.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Value(const StaticString& value) {
|
Value::Value(const StaticString& value) {
|
||||||
@ -312,7 +344,7 @@ Value::Value(const StaticString& value) {
|
|||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
Value::Value(const CppTL::ConstString& value) {
|
Value::Value(const CppTL::ConstString& value) {
|
||||||
initBasic(stringValue, true);
|
initBasic(stringValue, true);
|
||||||
value_.string_ = duplicateStringValue(value, value.length());
|
value_.string_ = duplicateAndPrefixStringValue(value, value.length());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -335,7 +367,11 @@ Value::Value(const Value& other)
|
|||||||
break;
|
break;
|
||||||
case stringValue:
|
case stringValue:
|
||||||
if (other.value_.string_) {
|
if (other.value_.string_) {
|
||||||
value_.string_ = duplicateStringValue(other.value_.string_);
|
unsigned len;
|
||||||
|
char const* str;
|
||||||
|
decodePrefixedString(other.allocated_, other.value_.string_,
|
||||||
|
&len, &str);
|
||||||
|
value_.string_ = duplicateAndPrefixStringValue(str, len);
|
||||||
allocated_ = true;
|
allocated_ = true;
|
||||||
} else {
|
} else {
|
||||||
value_.string_ = 0;
|
value_.string_ = 0;
|
||||||
@ -432,9 +468,23 @@ bool Value::operator<(const Value& other) const {
|
|||||||
case booleanValue:
|
case booleanValue:
|
||||||
return value_.bool_ < other.value_.bool_;
|
return value_.bool_ < other.value_.bool_;
|
||||||
case stringValue:
|
case stringValue:
|
||||||
return (value_.string_ == 0 && other.value_.string_) ||
|
{
|
||||||
(other.value_.string_ && value_.string_ &&
|
if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
|
||||||
strcmp(value_.string_, other.value_.string_) < 0);
|
if (other.value_.string_) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
unsigned this_len;
|
||||||
|
unsigned other_len;
|
||||||
|
char const* this_str;
|
||||||
|
char const* other_str;
|
||||||
|
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||||
|
decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
|
||||||
|
unsigned min_len = std::min(this_len, other_len);
|
||||||
|
int comp = memcmp(this_str, other_str, min_len);
|
||||||
|
if (comp < 0) return true;
|
||||||
|
if (comp > 0) return false;
|
||||||
|
return (this_len < other_len);
|
||||||
|
}
|
||||||
case arrayValue:
|
case arrayValue:
|
||||||
case objectValue: {
|
case objectValue: {
|
||||||
int delta = int(value_.map_->size() - other.value_.map_->size());
|
int delta = int(value_.map_->size() - other.value_.map_->size());
|
||||||
@ -474,9 +524,20 @@ bool Value::operator==(const Value& other) const {
|
|||||||
case booleanValue:
|
case booleanValue:
|
||||||
return value_.bool_ == other.value_.bool_;
|
return value_.bool_ == other.value_.bool_;
|
||||||
case stringValue:
|
case stringValue:
|
||||||
return (value_.string_ == other.value_.string_) ||
|
{
|
||||||
(other.value_.string_ && value_.string_ &&
|
if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
|
||||||
strcmp(value_.string_, other.value_.string_) == 0);
|
return (value_.string_ == other.value_.string_);
|
||||||
|
}
|
||||||
|
unsigned this_len;
|
||||||
|
unsigned other_len;
|
||||||
|
char const* this_str;
|
||||||
|
char const* other_str;
|
||||||
|
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||||
|
decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
|
||||||
|
if (this_len != other_len) return false;
|
||||||
|
int comp = memcmp(this_str, other_str, this_len);
|
||||||
|
return comp == 0;
|
||||||
|
}
|
||||||
case arrayValue:
|
case arrayValue:
|
||||||
case objectValue:
|
case objectValue:
|
||||||
return value_.map_->size() == other.value_.map_->size() &&
|
return value_.map_->size() == other.value_.map_->size() &&
|
||||||
@ -492,7 +553,20 @@ bool Value::operator!=(const Value& other) const { return !(*this == other); }
|
|||||||
const char* Value::asCString() const {
|
const char* Value::asCString() const {
|
||||||
JSON_ASSERT_MESSAGE(type_ == stringValue,
|
JSON_ASSERT_MESSAGE(type_ == stringValue,
|
||||||
"in Json::Value::asCString(): requires stringValue");
|
"in Json::Value::asCString(): requires stringValue");
|
||||||
return value_.string_;
|
if (value_.string_ == 0) return 0;
|
||||||
|
unsigned this_len;
|
||||||
|
char const* this_str;
|
||||||
|
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||||
|
return this_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::getString(char const** str, char const** end) const {
|
||||||
|
if (type_ != stringValue) return false;
|
||||||
|
if (value_.string_ == 0) return false;
|
||||||
|
unsigned length;
|
||||||
|
decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
|
||||||
|
*end = *str + length;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Value::asString() const {
|
std::string Value::asString() const {
|
||||||
@ -500,7 +574,13 @@ std::string Value::asString() const {
|
|||||||
case nullValue:
|
case nullValue:
|
||||||
return "";
|
return "";
|
||||||
case stringValue:
|
case stringValue:
|
||||||
return value_.string_ ? value_.string_ : "";
|
{
|
||||||
|
if (value_.string_ == 0) return "";
|
||||||
|
unsigned this_len;
|
||||||
|
char const* this_str;
|
||||||
|
decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
|
||||||
|
return std::string(this_str, this_len);
|
||||||
|
}
|
||||||
case booleanValue:
|
case booleanValue:
|
||||||
return value_.bool_ ? "true" : "false";
|
return value_.bool_ ? "true" : "false";
|
||||||
case intValue:
|
case intValue:
|
||||||
@ -516,7 +596,11 @@ std::string Value::asString() const {
|
|||||||
|
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
CppTL::ConstString Value::asConstString() const {
|
CppTL::ConstString Value::asConstString() const {
|
||||||
return CppTL::ConstString(asString().c_str());
|
unsigned len;
|
||||||
|
char const* str;
|
||||||
|
decodePrefixedString(allocated_, value_.string_,
|
||||||
|
&len, &str);
|
||||||
|
return CppTL::ConstString(str, len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -830,10 +914,6 @@ const Value& Value::operator[](int index) const {
|
|||||||
return (*this)[ArrayIndex(index)];
|
return (*this)[ArrayIndex(index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& Value::operator[](const char* key) {
|
|
||||||
return resolveReference(key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::initBasic(ValueType type, bool allocated) {
|
void Value::initBasic(ValueType type, bool allocated) {
|
||||||
type_ = type;
|
type_ = type;
|
||||||
allocated_ = allocated;
|
allocated_ = allocated;
|
||||||
@ -842,14 +922,37 @@ void Value::initBasic(ValueType type, bool allocated) {
|
|||||||
limit_ = 0;
|
limit_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& Value::resolveReference(const char* key, bool isStatic) {
|
// Access an object value by name, create a null member if it does not exist.
|
||||||
|
// @pre Type of '*this' is object or null.
|
||||||
|
// @param key is null-terminated.
|
||||||
|
Value& Value::resolveReference(const char* key) {
|
||||||
JSON_ASSERT_MESSAGE(
|
JSON_ASSERT_MESSAGE(
|
||||||
type_ == nullValue || type_ == objectValue,
|
type_ == nullValue || type_ == objectValue,
|
||||||
"in Json::Value::resolveReference(): requires objectValue");
|
"in Json::Value::resolveReference(): requires objectValue");
|
||||||
if (type_ == nullValue)
|
if (type_ == nullValue)
|
||||||
*this = Value(objectValue);
|
*this = Value(objectValue);
|
||||||
CZString actualKey(
|
CZString actualKey(
|
||||||
key, strlen(key), isStatic ? CZString::noDuplication : CZString::duplicateOnCopy);
|
key, strlen(key), CZString::noDuplication); // NOTE!
|
||||||
|
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
|
||||||
|
if (it != value_.map_->end() && (*it).first == actualKey)
|
||||||
|
return (*it).second;
|
||||||
|
|
||||||
|
ObjectValues::value_type defaultValue(actualKey, null);
|
||||||
|
it = value_.map_->insert(it, defaultValue);
|
||||||
|
Value& value = (*it).second;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @param key is not null-terminated.
|
||||||
|
Value& Value::resolveReference(char const* key, char const* end)
|
||||||
|
{
|
||||||
|
JSON_ASSERT_MESSAGE(
|
||||||
|
type_ == nullValue || type_ == objectValue,
|
||||||
|
"in Json::Value::resolveReference(key, end): requires objectValue");
|
||||||
|
if (type_ == nullValue)
|
||||||
|
*this = Value(objectValue);
|
||||||
|
CZString actualKey(
|
||||||
|
key, (end-key), CZString::duplicateOnCopy);
|
||||||
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
|
ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
|
||||||
if (it != value_.map_->end() && (*it).first == actualKey)
|
if (it != value_.map_->end() && (*it).first == actualKey)
|
||||||
return (*it).second;
|
return (*it).second;
|
||||||
@ -867,58 +970,77 @@ Value Value::get(ArrayIndex index, const Value& defaultValue) const {
|
|||||||
|
|
||||||
bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
|
bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
|
||||||
|
|
||||||
const Value& Value::operator[](const char* key) const {
|
Value const* Value::find(char const* key, char const* end) const
|
||||||
|
{
|
||||||
JSON_ASSERT_MESSAGE(
|
JSON_ASSERT_MESSAGE(
|
||||||
type_ == nullValue || type_ == objectValue,
|
type_ == nullValue || type_ == objectValue,
|
||||||
"in Json::Value::operator[](char const*)const: requires objectValue");
|
"in Json::Value::find(key, end, found): requires objectValue or nullValue");
|
||||||
if (type_ == nullValue)
|
if (type_ == nullValue) return NULL;
|
||||||
return null;
|
CZString actualKey(key, end-key, CZString::noDuplication);
|
||||||
CZString actualKey(key, strlen(key), CZString::noDuplication);
|
|
||||||
ObjectValues::const_iterator it = value_.map_->find(actualKey);
|
ObjectValues::const_iterator it = value_.map_->find(actualKey);
|
||||||
if (it == value_.map_->end())
|
if (it == value_.map_->end()) return NULL;
|
||||||
return null;
|
return &(*it).second;
|
||||||
return (*it).second;
|
}
|
||||||
|
const Value& Value::operator[](const char* key) const
|
||||||
|
{
|
||||||
|
Value const* found = find(key, key + strlen(key));
|
||||||
|
if (!found) return null;
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
Value const& Value::operator[](std::string const& key) const
|
||||||
|
{
|
||||||
|
Value const* found = find(key.data(), key.data() + key.length());
|
||||||
|
if (!found) return null;
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value& Value::operator[](const char* key) {
|
||||||
|
return resolveReference(key, key + strlen(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& Value::operator[](const std::string& key) {
|
Value& Value::operator[](const std::string& key) {
|
||||||
return (*this)[key.c_str()];
|
return resolveReference(key.data(), key.data() + key.length());
|
||||||
}
|
|
||||||
|
|
||||||
const Value& Value::operator[](const std::string& key) const {
|
|
||||||
return (*this)[key.c_str()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value& Value::operator[](const StaticString& key) {
|
Value& Value::operator[](const StaticString& key) {
|
||||||
return resolveReference(key, true);
|
return resolveReference(key.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
Value& Value::operator[](const CppTL::ConstString& key) {
|
Value& Value::operator[](const CppTL::ConstString& key) {
|
||||||
return (*this)[key.c_str()];
|
return resolveReference(key.c_str(), key.end_c_str());
|
||||||
}
|
}
|
||||||
|
Value const& Value::operator[](CppTL::ConstString const& key) const
|
||||||
const Value& Value::operator[](const CppTL::ConstString& key) const {
|
{
|
||||||
return (*this)[key.c_str()];
|
Value const* found = find(key.c_str(), key.end_c_str());
|
||||||
|
if (!found) return null;
|
||||||
|
return *found;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Value& Value::append(const Value& value) { return (*this)[size()] = value; }
|
Value& Value::append(const Value& value) { return (*this)[size()] = value; }
|
||||||
|
|
||||||
Value Value::get(const char* key, const Value& defaultValue) const {
|
Value Value::get(char const* key, char const* end, Value const& defaultValue) const
|
||||||
|
{
|
||||||
const Value* value = &((*this)[key]);
|
const Value* value = &((*this)[key]);
|
||||||
return value == &null ? defaultValue : *value;
|
return value == &null ? defaultValue : *value;
|
||||||
}
|
}
|
||||||
|
Value Value::get(char const* key, Value const& defaultValue) const
|
||||||
Value Value::get(const std::string& key, const Value& defaultValue) const {
|
{
|
||||||
|
return get(key, key + strlen(key), defaultValue);
|
||||||
|
}
|
||||||
|
Value Value::get(std::string const& key, Value const& defaultValue) const
|
||||||
|
{
|
||||||
return get(key.c_str(), defaultValue);
|
return get(key.c_str(), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Value::removeMember(const char* key, Value* removed) {
|
bool Value::removeMember(const char* key, const char* end, Value* removed)
|
||||||
|
{
|
||||||
if (type_ != objectValue) {
|
if (type_ != objectValue) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CZString actualKey(key, strlen(key), CZString::noDuplication);
|
CZString actualKey(key, end-key, CZString::noDuplication);
|
||||||
ObjectValues::iterator it = value_.map_->find(actualKey);
|
ObjectValues::iterator it = value_.map_->find(actualKey);
|
||||||
if (it == value_.map_->end())
|
if (it == value_.map_->end())
|
||||||
return false;
|
return false;
|
||||||
@ -926,19 +1048,27 @@ bool Value::removeMember(const char* key, Value* removed) {
|
|||||||
value_.map_->erase(it);
|
value_.map_->erase(it);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool Value::removeMember(const char* key, Value* removed)
|
||||||
Value Value::removeMember(const char* key) {
|
{
|
||||||
|
return removeMember(key, key + strlen(key), removed);
|
||||||
|
}
|
||||||
|
bool Value::removeMember(std::string const& key, Value* removed)
|
||||||
|
{
|
||||||
|
return removeMember(key.data(), key.data() + key.length(), removed);
|
||||||
|
}
|
||||||
|
Value Value::removeMember(const char* key)
|
||||||
|
{
|
||||||
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
|
JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
|
||||||
"in Json::Value::removeMember(): requires objectValue");
|
"in Json::Value::removeMember(): requires objectValue");
|
||||||
if (type_ == nullValue)
|
if (type_ == nullValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Value removed; // null
|
Value removed; // null
|
||||||
removeMember(key, &removed);
|
removeMember(key, key + strlen(key), &removed);
|
||||||
return removed; // still null if removeMember() did nothing
|
return removed; // still null if removeMember() did nothing
|
||||||
}
|
}
|
||||||
|
Value Value::removeMember(const std::string& key)
|
||||||
Value Value::removeMember(const std::string& key) {
|
{
|
||||||
return removeMember(key.c_str());
|
return removeMember(key.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,18 +1102,23 @@ Value Value::get(const CppTL::ConstString& key,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool Value::isMember(const char* key) const {
|
bool Value::isMember(char const* key, char const* end) const
|
||||||
const Value* value = &((*this)[key]);
|
{
|
||||||
return value != &null;
|
Value const* value = find(key, end);
|
||||||
|
return NULL != value;
|
||||||
}
|
}
|
||||||
|
bool Value::isMember(char const* key) const
|
||||||
bool Value::isMember(const std::string& key) const {
|
{
|
||||||
return isMember(key.c_str());
|
return isMember(key, key + strlen(key));
|
||||||
|
}
|
||||||
|
bool Value::isMember(std::string const& key) const
|
||||||
|
{
|
||||||
|
return isMember(key.data(), key.data() + key.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JSON_USE_CPPTL
|
#ifdef JSON_USE_CPPTL
|
||||||
bool Value::isMember(const CppTL::ConstString& key) const {
|
bool Value::isMember(const CppTL::ConstString& key) const {
|
||||||
return isMember(key.c_str());
|
return isMember(key.c_str(), key.end_c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -997,8 +1132,10 @@ Value::Members Value::getMemberNames() const {
|
|||||||
members.reserve(value_.map_->size());
|
members.reserve(value_.map_->size());
|
||||||
ObjectValues::const_iterator it = value_.map_->begin();
|
ObjectValues::const_iterator it = value_.map_->begin();
|
||||||
ObjectValues::const_iterator itEnd = value_.map_->end();
|
ObjectValues::const_iterator itEnd = value_.map_->end();
|
||||||
for (; it != itEnd; ++it)
|
for (; it != itEnd; ++it) {
|
||||||
members.push_back(std::string((*it).first.c_str()));
|
members.push_back(std::string((*it).first.data(),
|
||||||
|
(*it).first.length()));
|
||||||
|
}
|
||||||
return members;
|
return members;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
@ -77,26 +77,36 @@ void ValueIteratorBase::copy(const SelfType& other) {
|
|||||||
|
|
||||||
Value ValueIteratorBase::key() const {
|
Value ValueIteratorBase::key() const {
|
||||||
const Value::CZString czstring = (*current_).first;
|
const Value::CZString czstring = (*current_).first;
|
||||||
if (czstring.c_str()) {
|
if (czstring.data()) {
|
||||||
if (czstring.isStaticString())
|
if (czstring.isStaticString())
|
||||||
return Value(StaticString(czstring.c_str()));
|
return Value(StaticString(czstring.data()));
|
||||||
return Value(czstring.c_str());
|
return Value(czstring.data(), czstring.data() + czstring.length());
|
||||||
}
|
}
|
||||||
return Value(czstring.index());
|
return Value(czstring.index());
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt ValueIteratorBase::index() const {
|
UInt ValueIteratorBase::index() const {
|
||||||
const Value::CZString czstring = (*current_).first;
|
const Value::CZString czstring = (*current_).first;
|
||||||
if (!czstring.c_str())
|
if (!czstring.data())
|
||||||
return czstring.index();
|
return czstring.index();
|
||||||
return Value::UInt(-1);
|
return Value::UInt(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ValueIteratorBase::memberName() const {
|
char const* ValueIteratorBase::memberName() const {
|
||||||
const char* name = (*current_).first.c_str();
|
const char* name = (*current_).first.data();
|
||||||
return name ? name : "";
|
return name ? name : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char const* ValueIteratorBase::memberName(char const** end) const {
|
||||||
|
const char* name = (*current_).first.data();
|
||||||
|
if (!name) {
|
||||||
|
*end = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*end = name + (*current_).first.length();
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
// //////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////
|
||||||
// //////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////
|
||||||
// //////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
@ -50,6 +50,16 @@ static bool containsControlCharacter(const char* str) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool containsControlCharacter0(const char* str, unsigned len) {
|
||||||
|
char const* end = str + len;
|
||||||
|
while (end != str) {
|
||||||
|
if (isControlCharacter(*str) || 0==*str)
|
||||||
|
return true;
|
||||||
|
++str;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string valueToString(LargestInt value) {
|
std::string valueToString(LargestInt value) {
|
||||||
UIntToStringBuffer buffer;
|
UIntToStringBuffer buffer;
|
||||||
char* current = buffer + sizeof(buffer);
|
char* current = buffer + sizeof(buffer);
|
||||||
@ -184,6 +194,84 @@ std::string valueToQuotedString(const char* value) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
|
||||||
|
static char const* strnpbrk(char const* s, char const* accept, size_t n) {
|
||||||
|
assert((s || !n) && accept);
|
||||||
|
|
||||||
|
char const* const end = s + n;
|
||||||
|
for (char const* cur = s; cur < end; ++cur) {
|
||||||
|
int const c = *cur;
|
||||||
|
for (char const* a = accept; *a; ++a) {
|
||||||
|
if (*a == c) {
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static std::string valueToQuotedStringN(const char* value, unsigned length) {
|
||||||
|
if (value == NULL)
|
||||||
|
return "";
|
||||||
|
// Not sure how to handle unicode...
|
||||||
|
if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
|
||||||
|
!containsControlCharacter0(value, length))
|
||||||
|
return std::string("\"") + value + "\"";
|
||||||
|
// We have to walk value and escape any special characters.
|
||||||
|
// Appending to std::string is not efficient, but this should be rare.
|
||||||
|
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
||||||
|
std::string::size_type maxsize =
|
||||||
|
length * 2 + 3; // allescaped+quotes+NULL
|
||||||
|
std::string result;
|
||||||
|
result.reserve(maxsize); // to avoid lots of mallocs
|
||||||
|
result += "\"";
|
||||||
|
char const* end = value + length;
|
||||||
|
for (const char* c = value; c != end; ++c) {
|
||||||
|
switch (*c) {
|
||||||
|
case '\"':
|
||||||
|
result += "\\\"";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
result += "\\\\";
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
result += "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
result += "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
result += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
result += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
result += "\\t";
|
||||||
|
break;
|
||||||
|
// case '/':
|
||||||
|
// Even though \/ is considered a legal escape in JSON, a bare
|
||||||
|
// slash is also legal, so I see no reason to escape it.
|
||||||
|
// (I hope I am not misunderstanding something.)
|
||||||
|
// blep notes: actually escaping \/ may be useful in javascript to avoid </
|
||||||
|
// sequence.
|
||||||
|
// Should add a flag to allow this compatibility mode and prevent this
|
||||||
|
// sequence from occurring.
|
||||||
|
default:
|
||||||
|
if ((isControlCharacter(*c)) || (*c == 0)) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
|
||||||
|
<< std::setw(4) << static_cast<int>(*c);
|
||||||
|
result += oss.str();
|
||||||
|
} else {
|
||||||
|
result += *c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "\"";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Class Writer
|
// Class Writer
|
||||||
// //////////////////////////////////////////////////////////////////
|
// //////////////////////////////////////////////////////////////////
|
||||||
Writer::~Writer() {}
|
Writer::~Writer() {}
|
||||||
@ -248,7 +336,7 @@ void FastWriter::writeValue(const Value& value) {
|
|||||||
const std::string& name = *it;
|
const std::string& name = *it;
|
||||||
if (it != members.begin())
|
if (it != members.begin())
|
||||||
document_ += ',';
|
document_ += ',';
|
||||||
document_ += valueToQuotedString(name.c_str());
|
document_ += valueToQuotedStringN(name.data(), name.length());
|
||||||
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
|
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
|
||||||
writeValue(value[name]);
|
writeValue(value[name]);
|
||||||
}
|
}
|
||||||
@ -289,8 +377,15 @@ void StyledWriter::writeValue(const Value& value) {
|
|||||||
pushValue(valueToString(value.asDouble()));
|
pushValue(valueToString(value.asDouble()));
|
||||||
break;
|
break;
|
||||||
case stringValue:
|
case stringValue:
|
||||||
pushValue(valueToQuotedString(value.asCString()));
|
{
|
||||||
|
// Is NULL is possible for value.string_?
|
||||||
|
char const* str;
|
||||||
|
char const* end;
|
||||||
|
bool ok = value.getString(&str, &end);
|
||||||
|
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
|
||||||
|
else pushValue("");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case booleanValue:
|
case booleanValue:
|
||||||
pushValue(valueToString(value.asBool()));
|
pushValue(valueToString(value.asBool()));
|
||||||
break;
|
break;
|
||||||
@ -767,8 +862,15 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
|
|||||||
pushValue(valueToString(value.asDouble()));
|
pushValue(valueToString(value.asDouble()));
|
||||||
break;
|
break;
|
||||||
case stringValue:
|
case stringValue:
|
||||||
pushValue(valueToQuotedString(value.asCString()));
|
{
|
||||||
|
// Is NULL is possible for value.string_?
|
||||||
|
char const* str;
|
||||||
|
char const* end;
|
||||||
|
bool ok = value.getString(&str, &end);
|
||||||
|
if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
|
||||||
|
else pushValue("");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case booleanValue:
|
case booleanValue:
|
||||||
pushValue(valueToString(value.asBool()));
|
pushValue(valueToString(value.asBool()));
|
||||||
break;
|
break;
|
||||||
@ -787,7 +889,7 @@ void BuiltStyledStreamWriter::writeValue(Value const& value) {
|
|||||||
std::string const& name = *it;
|
std::string const& name = *it;
|
||||||
Value const& childValue = value[name];
|
Value const& childValue = value[name];
|
||||||
writeCommentBeforeValue(childValue);
|
writeCommentBeforeValue(childValue);
|
||||||
writeWithIndent(valueToQuotedString(name.c_str()));
|
writeWithIndent(valueToQuotedStringN(name.data(), name.length()));
|
||||||
*sout_ << colonSymbol_;
|
*sout_ << colonSymbol_;
|
||||||
writeValue(childValue);
|
writeValue(childValue);
|
||||||
if (++it == members.end()) {
|
if (++it == members.end()) {
|
||||||
|
@ -1539,6 +1539,56 @@ JSONTEST_FIXTURE(ValueTest, StaticString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(ValueTest, zeroes) {
|
||||||
|
std::string binary("hi", 3); // include trailing 0
|
||||||
|
JSONTEST_ASSERT_EQUAL(3, binary.length());
|
||||||
|
Json::StreamWriterBuilder b;
|
||||||
|
{
|
||||||
|
Json::Value root;
|
||||||
|
root = binary;
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char const top[] = "top";
|
||||||
|
Json::Value root;
|
||||||
|
root[top] = binary;
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(binary, root[top].asString());
|
||||||
|
Json::Value removed;
|
||||||
|
bool did;
|
||||||
|
did = root.removeMember(top, top + 3U,
|
||||||
|
&removed);
|
||||||
|
JSONTEST_ASSERT(did);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(binary, removed.asString());
|
||||||
|
did = root.removeMember(top, top + 3U,
|
||||||
|
&removed);
|
||||||
|
JSONTEST_ASSERT(!did);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(binary, removed.asString()); // still
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(ValueTest, zeroesInKeys) {
|
||||||
|
std::string binary("hi", 3); // include trailing 0
|
||||||
|
JSONTEST_ASSERT_EQUAL(3, binary.length());
|
||||||
|
Json::StreamWriterBuilder b;
|
||||||
|
{
|
||||||
|
Json::Value root;
|
||||||
|
root[binary] = "there";
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("there", root[binary].asString());
|
||||||
|
JSONTEST_ASSERT(!root.isMember("hi"));
|
||||||
|
JSONTEST_ASSERT(root.isMember(binary));
|
||||||
|
Json::Value removed;
|
||||||
|
bool did;
|
||||||
|
did = root.removeMember(binary.data(), binary.data() + binary.length(),
|
||||||
|
&removed);
|
||||||
|
JSONTEST_ASSERT(did);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("there", removed.asString());
|
||||||
|
did = root.removeMember(binary.data(), binary.data() + binary.length(),
|
||||||
|
&removed);
|
||||||
|
JSONTEST_ASSERT(!did);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("there", removed.asString()); // still
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct WriterTest : JsonTest::TestCase {};
|
struct WriterTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
|
JSONTEST_FIXTURE(WriterTest, dropNullPlaceholders) {
|
||||||
@ -1561,6 +1611,28 @@ JSONTEST_FIXTURE(StreamWriterTest, dropNullPlaceholders) {
|
|||||||
JSONTEST_ASSERT(Json::writeString(b, nullValue) == "");
|
JSONTEST_ASSERT(Json::writeString(b, nullValue) == "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(StreamWriterTest, writeZeroes) {
|
||||||
|
std::string binary("hi", 3); // include trailing 0
|
||||||
|
JSONTEST_ASSERT_EQUAL(3, binary.length());
|
||||||
|
std::string expected("\"hi\\u0000\""); // unicoded zero
|
||||||
|
Json::StreamWriterBuilder b;
|
||||||
|
{
|
||||||
|
Json::Value root;
|
||||||
|
root = binary;
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(binary, root.asString());
|
||||||
|
std::string out = Json::writeString(b, root);
|
||||||
|
JSONTEST_ASSERT_EQUAL(expected.size(), out.size());
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(expected, out);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Json::Value root;
|
||||||
|
root["top"] = binary;
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(binary, root["top"].asString());
|
||||||
|
std::string out = Json::writeString(b, root["top"]);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL(expected, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ReaderTest : JsonTest::TestCase {};
|
struct ReaderTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
|
JSONTEST_FIXTURE(ReaderTest, parseWithNoErrors) {
|
||||||
@ -2068,6 +2140,38 @@ JSONTEST_FIXTURE(CharReaderAllowSingleQuotesTest, issue182) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CharReaderAllowZeroesTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
|
JSONTEST_FIXTURE(CharReaderAllowZeroesTest, issue176) {
|
||||||
|
Json::CharReaderBuilder b;
|
||||||
|
b.settings_["allowSingleQuotes"] = true;
|
||||||
|
Json::Value root;
|
||||||
|
std::string errs;
|
||||||
|
Json::CharReader* reader(b.newCharReader());
|
||||||
|
{
|
||||||
|
char const doc[] = "{'a':true,\"b\":true}";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL(2u, root.size());
|
||||||
|
JSONTEST_ASSERT_EQUAL(true, root.get("a", false));
|
||||||
|
JSONTEST_ASSERT_EQUAL(true, root.get("b", false));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char const doc[] = "{'a': 'x', \"b\":'y'}";
|
||||||
|
bool ok = reader->parse(
|
||||||
|
doc, doc + std::strlen(doc),
|
||||||
|
&root, &errs);
|
||||||
|
JSONTEST_ASSERT(ok);
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("", errs);
|
||||||
|
JSONTEST_ASSERT_EQUAL(2u, root.size());
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("x", root["a"].asString());
|
||||||
|
JSONTEST_ASSERT_STRING_EQUAL("y", root["b"].asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct IteratorTest : JsonTest::TestCase {};
|
struct IteratorTest : JsonTest::TestCase {};
|
||||||
|
|
||||||
JSONTEST_FIXTURE(IteratorTest, distance) {
|
JSONTEST_FIXTURE(IteratorTest, distance) {
|
||||||
@ -2107,9 +2211,12 @@ int main(int argc, const char* argv[]) {
|
|||||||
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors);
|
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, offsetAccessors);
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, typeChecksThrowExceptions);
|
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, typeChecksThrowExceptions);
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, StaticString);
|
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, StaticString);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, zeroes);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, zeroesInKeys);
|
||||||
|
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
|
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
|
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, writeZeroes);
|
||||||
|
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors);
|
JSONTEST_REGISTER_FIXTURE(runner, ReaderTest, parseWithNoErrors);
|
||||||
JSONTEST_REGISTER_FIXTURE(
|
JSONTEST_REGISTER_FIXTURE(
|
||||||
@ -2136,6 +2243,8 @@ int main(int argc, const char* argv[]) {
|
|||||||
|
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowSingleQuotesTest, issue182);
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowSingleQuotesTest, issue182);
|
||||||
|
|
||||||
|
JSONTEST_REGISTER_FIXTURE(runner, CharReaderAllowZeroesTest, issue176);
|
||||||
|
|
||||||
JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, distance);
|
JSONTEST_REGISTER_FIXTURE(runner, IteratorTest, distance);
|
||||||
|
|
||||||
return runner.runCommandLine(argc, argv);
|
return runner.runCommandLine(argc, argv);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user