From 25342bac131e5408c8be62ebd86254e5b6250f2a Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 2 Mar 2015 18:05:26 -0600 Subject: [PATCH] support UTF-8 for const methods --- include/json/value.h | 34 ++++++++++++- src/lib_json/json_value.cpp | 96 ++++++++++++++++++++++++------------- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/include/json/value.h b/include/json/value.h index 51bcdbb..38d64fe 100644 --- a/include/json/value.h +++ b/include/json/value.h @@ -112,6 +112,12 @@ private: * * It is possible to iterate over the list of a #objectValue values using * the getMemberNames() method. + * + * \note Value string-length fit in size_t, but keys must fit in unsigned. + * (The reason is an implementation detail.) The readers 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 { friend class ValueIteratorBase; @@ -352,19 +358,25 @@ Json::Value obj_value(Json::objectValue); // {} Value& append(const Value& value); /// 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); /// Access an object value by name, returns null if there is no member with /// that name. const Value& operator[](const char* key) const; /// 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); /// Access an object value by name, returns null if there is no member with /// that name. + /// \param key may contain embedded nulls. const Value& operator[](const std::string& key) const; /** \brief Access an object value by name, create a null member if it does not exist. - * If the object as no entry for that name, then the member name used to store + * \param key may contain embedded nulls. + * + * If the object has no entry for that name, then the member name used to store * the new entry is not duplicated. * Example of use: * \code @@ -384,11 +396,19 @@ Json::Value obj_value(Json::objectValue); // {} /// Return the member named key if it exist, defaultValue otherwise. Value get(const char* key, const Value& defaultValue) const; /// 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; #ifdef JSON_USE_CPPTL /// Return the member named key if it exist, defaultValue otherwise. Value get(const CppTL::ConstString& key, const Value& defaultValue) const; #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; /// \brief Remove and return the named member. /// /// Do nothing if it did not exist. @@ -398,14 +418,21 @@ Json::Value obj_value(Json::objectValue); // {} /// \deprecated Value removeMember(const char* key); /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. /// \deprecated 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. Update 'removed' iff removed. + \param key may contain embedded nulls. \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. O(n) expensive operations. @@ -417,7 +444,10 @@ Json::Value obj_value(Json::objectValue); // {} /// Return true if the object has a member named key. bool isMember(const char* key) const; /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. 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 /// Return true if the object has a member named key. bool isMember(const CppTL::ConstString& key) const; diff --git a/src/lib_json/json_value.cpp b/src/lib_json/json_value.cpp index a5d2d74..3378e76 100644 --- a/src/lib_json/json_value.cpp +++ b/src/lib_json/json_value.cpp @@ -867,28 +867,35 @@ Value Value::get(ArrayIndex index, const Value& defaultValue) const { 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( type_ == nullValue || type_ == objectValue, - "in Json::Value::operator[](char const*)const: requires objectValue"); - if (type_ == nullValue) - return null; - CZString actualKey(key, strlen(key), CZString::noDuplication); + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, end-key, CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) - return null; - return (*it).second; + if (it == value_.map_->end()) return NULL; + return &(*it).second; } - -Value& Value::operator[](const std::string& key) { +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return null; + return *found; +} +Value& Value::operator[](const std::string& key) +{ return (*this)[key.c_str()]; } - -const Value& Value::operator[](const std::string& key) const { - return (*this)[key.c_str()]; +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 StaticString& key) { +Value& Value::operator[](const StaticString& key) +{ return resolveReference(key, true); } @@ -896,29 +903,37 @@ Value& Value::operator[](const StaticString& key) { Value& Value::operator[](const CppTL::ConstString& key) { return (*this)[key.c_str()]; } - -const Value& Value::operator[](const CppTL::ConstString& key) const { - return (*this)[key.c_str()]; +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return null; + return *found; } #endif 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]); return value == &null ? defaultValue : *value; } - -Value Value::get(const std::string& key, const Value& defaultValue) const { +Value Value::get(char const* key, Value const& 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); } -bool Value::removeMember(const char* key, Value* removed) { +bool Value::removeMember(const char* key, const char* end, Value* removed) +{ if (type_ != objectValue) { return false; } - CZString actualKey(key, strlen(key), CZString::noDuplication); + CZString actualKey(key, end-key, CZString::noDuplication); ObjectValues::iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) return false; @@ -926,19 +941,27 @@ bool Value::removeMember(const char* key, Value* removed) { value_.map_->erase(it); return true; } - -Value Value::removeMember(const char* key) { +bool Value::removeMember(const char* key, Value* removed) +{ + 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, "in Json::Value::removeMember(): requires objectValue"); if (type_ == nullValue) return null; Value removed; // null - removeMember(key, &removed); + removeMember(key, key + strlen(key), &removed); 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()); } @@ -972,13 +995,18 @@ Value Value::get(const CppTL::ConstString& key, } #endif -bool Value::isMember(const char* key) const { - const Value* value = &((*this)[key]); - return value != &null; +bool Value::isMember(char const* key, char const* end) const +{ + Value const* value = find(key, end); + return NULL != value; } - -bool Value::isMember(const std::string& key) const { - return isMember(key.c_str()); +bool Value::isMember(const char* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(const std::string& key) const +{ + return isMember(key.data(), key.data() + key.length()); } #ifdef JSON_USE_CPPTL