Merge pull request #197 from cdunn2001/allow-zero

allow zeroes in strings
This commit is contained in:
Christopher Dunn 2015-03-03 00:44:19 -06:00
commit 9e49e3d84a
6 changed files with 509 additions and 98 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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;
} }
// //

View File

@ -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;
}
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////

View File

@ -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()) {

View File

@ -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);