diff options
Diffstat (limited to 'indra/llcommon')
27 files changed, 544 insertions, 438 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 2bafef5d1e..0aaa7433ce 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -66,6 +66,7 @@ set(llcommon_SOURCE_FILES llmetricperformancetester.cpp llmortician.cpp llmutex.cpp + llpointer.cpp llptrto.cpp llpredicate.cpp llprocess.cpp diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index a0394da281..404e0c71ce 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -163,6 +163,28 @@ constexpr U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only constexpr U8 SIM_ACCESS_DOWN = 254; constexpr U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT; +// map item types +constexpr U32 MAP_ITEM_TELEHUB = 0x01; +constexpr U32 MAP_ITEM_PG_EVENT = 0x02; +constexpr U32 MAP_ITEM_MATURE_EVENT = 0x03; +// constexpr U32 MAP_ITEM_POPULAR = 0x04; // No longer supported, 2009-03-02 KLW +// constexpr U32 MAP_ITEM_AGENT_COUNT = 0x05; +constexpr U32 MAP_ITEM_AGENT_LOCATIONS = 0x06; +constexpr U32 MAP_ITEM_LAND_FOR_SALE = 0x07; +constexpr U32 MAP_ITEM_CLASSIFIED = 0x08; +constexpr U32 MAP_ITEM_ADULT_EVENT = 0x09; +constexpr U32 MAP_ITEM_LAND_FOR_SALE_ADULT = 0x0a; + +// Region map layer numbers +constexpr S32 MAP_SIM_OBJECTS = 0; +constexpr S32 MAP_SIM_TERRAIN = 1; +constexpr S32 MAP_SIM_LAND_FOR_SALE = 2; // Transparent alpha overlay of land for sale +constexpr S32 MAP_SIM_IMAGE_TYPES = 3; // Number of map layers +constexpr S32 MAP_SIM_INFO_MASK = 0x00FFFFFF; // Agent access may be stuffed into upper byte +constexpr S32 MAP_SIM_LAYER_MASK = 0x0000FFFF; // Layer info is in lower 16 bits +constexpr S32 MAP_SIM_RETURN_NULL_SIMS = 0x00010000; +constexpr S32 MAP_SIM_PRELUDE = 0x00020000; + // attachment constants constexpr U8 ATTACHMENT_ADD = 0x80; diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index c532620daa..f92bb98ba6 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -93,6 +93,7 @@ bool LLApp::sDisableCrashlogger = false; LLScalarCond<LLApp::EAppStatus> LLApp::sStatus{LLApp::APP_STATUS_STOPPED}; LLAppErrorHandler LLApp::sErrorHandler = NULL; +bool gDisconnected = false; LLApp::LLApp() { diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 57f5a112d9..ce09c566a9 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -50,6 +50,8 @@ void clear_signals(); #endif +extern bool gDisconnected; + class LL_COMMON_API LLApp { public: diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp index 5205699b92..5545bb71af 100644 --- a/indra/llcommon/lldate.cpp +++ b/indra/llcommon/lldate.cpp @@ -41,6 +41,9 @@ #include "llstring.h" #include "llfasttimer.h" +#include <boost/iostreams/device/array.hpp> +#include <boost/iostreams/stream.hpp> + static const F64 LL_APR_USEC_PER_SEC = 1000000.0; // should be APR_USEC_PER_SEC, but that relies on INT64_C which // isn't defined in glib under our build set up for some reason @@ -64,7 +67,7 @@ std::string LLDate::asString() const { std::ostringstream stream; toStream(stream); - return stream.str(); + return std::move(stream).str(); } //@ brief Converts time in seconds since EPOCH @@ -184,7 +187,7 @@ bool LLDate::split(S32 *year, S32 *month, S32 *day, S32 *hour, S32 *min, S32 *se bool LLDate::fromString(const std::string& iso8601_date) { - std::istringstream stream(iso8601_date); + boost::iostreams::stream<boost::iostreams::array_source> stream(iso8601_date.data(), iso8601_date.size()); return fromStream(stream); } diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index b14464382b..6f5e57c3de 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -480,7 +480,7 @@ namespace } - typedef std::map<std::string, LLError::ELevel> LevelMap; + typedef std::unordered_map<std::string, LLError::ELevel> LevelMap; typedef std::vector<LLError::RecorderPtr> Recorders; typedef std::vector<LLError::CallSite*> CallSiteVector; @@ -501,7 +501,7 @@ namespace LevelMap mClassLevelMap; LevelMap mFileLevelMap; LevelMap mTagLevelMap; - std::map<std::string, unsigned int> mUniqueLogMessages; + std::unordered_map<std::string, unsigned int> mUniqueLogMessages; LLError::FatalFunction mCrashFunction; LLError::TimeFunction mTimeFunction; @@ -1404,7 +1404,7 @@ namespace LLError { std::ostringstream message_stream; - std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message); + auto messageIter = s->mUniqueLogMessages.find(message); if (messageIter != s->mUniqueLogMessages.end()) { messageIter->second++; diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index d254fa5407..3c58d1df22 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -70,7 +70,6 @@ namespace LLError Setting a level means log messages at that level or above. */ - LL_COMMON_API void setPrintLocation(bool); LL_COMMON_API void setDefaultLevel(LLError::ELevel); LL_COMMON_API ELevel getDefaultLevel(); LL_COMMON_API void setAlwaysFlush(bool flush); diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index a539e4fe28..f752e31563 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -187,13 +187,34 @@ static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollo CloseHandle(file_handle); return st_mode; } + // Retrieve last error before calling CloseHandle() + DWORD last_error = GetLastError(); + CloseHandle(file_handle); + set_errno_from_oserror(last_error); + } + else + { + set_errno_from_oserror(GetLastError()); } - // Retrieve last error and set errno before calling CloseHandle() - set_errno_from_oserror(GetLastError()); - if (file_handle != INVALID_HANDLE_VALUE) + // If CreateFileW approach failed (e.g., exFAT), try the simpler GetFileAttributesW() + // GetFileAttributesW() always follows symlinks, so we skip this fallback when dontFollowSymLink is true. + if (!dontFollowSymLink) { - CloseHandle(file_handle); + DWORD attributes = GetFileAttributesW(utf16path.c_str()); + if (attributes != INVALID_FILE_ATTRIBUTES) + { + bool is_directory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + unsigned short st_mode = is_directory ? S_IFDIR : S_IFREG; + st_mode |= (attributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE; + + // propagate user bits to group/other fields: + st_mode |= (st_mode & 0700) >> 3; + st_mode |= (st_mode & 0700) >> 6; + + return st_mode; + } + set_errno_from_oserror(GetLastError()); } return 0; } diff --git a/indra/llcommon/llheteromap.cpp b/indra/llcommon/llheteromap.cpp index 823bea7a3c..03dd7856b6 100644 --- a/indra/llcommon/llheteromap.cpp +++ b/indra/llcommon/llheteromap.cpp @@ -27,6 +27,6 @@ LLHeteroMap::~LLHeteroMap() // pair.second is the std::pair; pair.second.first is the void*; // pair.second.second points to the deleter function (pair.second.second)(pair.second.first); - pair.second.first = NULL; + pair.second.first = nullptr; } } diff --git a/indra/llcommon/llheteromap.h b/indra/llcommon/llheteromap.h index d8e6fefb17..211dfaae83 100644 --- a/indra/llcommon/llheteromap.h +++ b/indra/llcommon/llheteromap.h @@ -12,9 +12,10 @@ #if ! defined(LL_LLHETEROMAP_H) #define LL_LLHETEROMAP_H +#include <typeindex> #include <typeinfo> #include <utility> // std::pair -#include <map> +#include <unordered_map> /** * LLHeteroMap addresses an odd requirement. Usually when you want to put @@ -43,7 +44,7 @@ public: // Look up map entry by typeid(T). We don't simply use mMap[typeid(T)] // because that requires default-constructing T on every lookup. For // some kinds of T, that could be expensive. - TypeMap::iterator found = mMap.find(&typeid(T)); + TypeMap::iterator found = mMap.find(typeid(T)); if (found == mMap.end()) { // Didn't find typeid(T). Create an entry. Because we're storing @@ -52,8 +53,8 @@ public: void* ptr = new T(); void (*dlfn)(void*) = &deleter<T>; std::pair<TypeMap::iterator, bool> inserted = - mMap.insert(TypeMap::value_type(&typeid(T), - TypeMap::mapped_type(ptr, dlfn))); + mMap.emplace(typeid(T), + TypeMap::mapped_type(ptr, dlfn)); // Okay, now that we have an entry, claim we found it. found = inserted.first; } @@ -71,23 +72,9 @@ private: delete static_cast<T*>(p); } - // Comparing two std::type_info* values is tricky, because the standard - // does not guarantee that there will be only one type_info instance for a - // given type. In other words, &typeid(A) in one part of the program may - // not always equal &typeid(A) in some other part. Use special comparator. - struct type_info_ptr_comp - { - bool operator()(const std::type_info* lhs, const std::type_info* rhs) const - { - return lhs->before(*rhs); - } - }; - - // What we actually store is a map from std::type_info (permitting lookup + // What we actually store is a map from std::type_index (permitting lookup // by object type) to a void* pointer to the object PLUS its deleter. - typedef std::map< - const std::type_info*, std::pair<void*, void (*)(void*)>, - type_info_ptr_comp> + typedef std::unordered_map<std::type_index, std::pair<void*, void (*)(void*)>> TypeMap; TypeMap mMap; }; diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index b01ea0bfb1..b220afadfc 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -34,7 +34,6 @@ #include <list> #include <unordered_map> #include <boost/function.hpp> -#include <boost/unordered_map.hpp> #include "llerror.h" #include "llstl.h" @@ -267,7 +266,7 @@ namespace LLInitParam private: struct Inaccessable{}; public: - typedef std::map<std::string, T> value_name_map_t; + typedef std::unordered_map<std::string, T> value_name_map_t; typedef Inaccessable name_t; typedef TypeValues<T> type_value_t; typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; @@ -294,7 +293,7 @@ namespace LLInitParam static std::vector<std::string>* getPossibleValues() { - return NULL; + return nullptr; } void assignNamedValue(const Inaccessable& name) @@ -310,7 +309,7 @@ namespace LLInitParam return param_value_t::getValue(); } - static value_name_map_t* getValueNames() {return NULL;} + static value_name_map_t* getValueNames() { return nullptr; } }; // helper class to implement name value lookups @@ -321,7 +320,7 @@ namespace LLInitParam { typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t; public: - typedef typename std::map<std::string, T> value_name_map_t; + typedef typename std::unordered_map<std::string, T> value_name_map_t; typedef std::string name_t; typedef self_t type_value_t; typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; @@ -497,9 +496,9 @@ namespace LLInitParam typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&); typedef std::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t; - typedef std::map<const std::type_info*, parser_read_func_t> parser_read_func_map_t; - typedef std::map<const std::type_info*, parser_write_func_t> parser_write_func_map_t; - typedef std::map<const std::type_info*, parser_inspect_func_t> parser_inspect_func_map_t; + typedef std::unordered_map<std::type_index, parser_read_func_t> parser_read_func_map_t; + typedef std::unordered_map<std::type_index, parser_write_func_t> parser_write_func_map_t; + typedef std::unordered_map<std::type_index, parser_inspect_func_t> parser_inspect_func_map_t; public: @@ -514,7 +513,7 @@ namespace LLInitParam template <typename T> bool readValue(T& param, typename std::enable_if_t<!std::is_enum_v<T>>* dummy = 0) { - parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); + parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(typeid(T)); if (found_it != mParserReadFuncs->end()) { return found_it->second(*this, (void*)¶m); @@ -525,14 +524,14 @@ namespace LLInitParam template <typename T> bool readValue(T& param, typename std::enable_if_t<std::is_enum_v<T> >* dummy = 0) { - parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); + parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(typeid(T)); if (found_it != mParserReadFuncs->end()) { return found_it->second(*this, (void*)¶m); } else { - found_it = mParserReadFuncs->find(&typeid(S32)); + found_it = mParserReadFuncs->find(typeid(S32)); if (found_it != mParserReadFuncs->end()) { S32 int_value; @@ -546,7 +545,7 @@ namespace LLInitParam template <typename T> bool writeValue(const T& param, name_stack_t& name_stack) { - parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); + parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(typeid(T)); if (found_it != mParserWriteFuncs->end()) { return found_it->second(*this, (const void*)¶m, name_stack); @@ -557,7 +556,7 @@ namespace LLInitParam // dispatch inspection to registered inspection functions, for each parameter in a param block template <typename T> bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values) { - parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T)); + parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(typeid(T)); if (found_it != mParserInspectFuncs->end()) { found_it->second(name_stack, min_count, max_count, possible_values); @@ -574,16 +573,16 @@ namespace LLInitParam protected: template <typename T> - void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL) + void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = nullptr) { - mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func)); - mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func)); + mParserReadFuncs->emplace(typeid(T), read_func); + mParserWriteFuncs->emplace(typeid(T), write_func); } template <typename T> void registerInspectFunc(parser_inspect_func_t inspect_func) { - mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func)); + mParserInspectFuncs->emplace(typeid(T), inspect_func); } bool mParseSilently; @@ -614,7 +613,7 @@ namespace LLInitParam { struct UserData { - virtual ~UserData() {} + virtual ~UserData() = default; }; typedef bool(*merge_func_t)(Param&, const Param&, bool); @@ -665,7 +664,7 @@ namespace LLInitParam void aggregateBlockData(BlockDescriptor& src_block_data); void addParam(ParamDescriptorPtr param, const char* name); - typedef boost::unordered_map<const std::string, ParamDescriptorPtr> param_map_t; + typedef std::unordered_map<std::string, ParamDescriptorPtr, ll::string_hash, std::equal_to<>> param_map_t; typedef std::vector<ParamDescriptorPtr> param_list_t; typedef std::list<ParamDescriptorPtr> all_params_list_t; typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t; @@ -679,38 +678,26 @@ namespace LLInitParam class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed }; - //TODO: implement in terms of owned_ptr - template<typename T> + // TODO: implement in terms of owned_ptr + template<typename T> class LazyValue - { - public: - LazyValue() - : mPtr(NULL) - {} + { + public: + LazyValue() = default; - ~LazyValue() - { - delete mPtr; - } + ~LazyValue() { delete mPtr; } - LazyValue(const T& value) - { - mPtr = new T(value); - } + LazyValue(const T& value) { mPtr = new T(value); } - LazyValue(const LazyValue& other) - : mPtr(NULL) - { - *this = other; - } + LazyValue(const LazyValue& other) : mPtr(nullptr) { *this = other; } - LazyValue& operator = (const LazyValue& other) - { + LazyValue& operator=(const LazyValue& other) + { if (!other.mPtr) { delete mPtr; - mPtr = NULL; - } + mPtr = nullptr; + } else { if (!mPtr) @@ -721,23 +708,21 @@ namespace LLInitParam { *mPtr = *(other.mPtr); } - } - return *this; } + return *this; + } bool operator==(const LazyValue& other) const { - if (empty() || other.empty()) return false; + if (empty() || other.empty()) + return false; return *mPtr == *other.mPtr; } - bool empty() const - { - return mPtr == NULL; - } + bool empty() const { return mPtr == nullptr; } - void set(const T& other) - { + void set(const T& other) + { if (!mPtr) { mPtr = new T(other); @@ -748,36 +733,26 @@ namespace LLInitParam } } - const T& get() const - { - return *ensureInstance(); - } + const T& get() const { return *ensureInstance(); } - T& get() - { - return *ensureInstance(); - } + T& get() { return *ensureInstance(); } - operator const T&() const - { - return get(); - } + operator const T&() const { return get(); } - private: - // lazily allocate an instance of T - T* ensureInstance() const + private: + // lazily allocate an instance of T + T* ensureInstance() const + { + if (mPtr == nullptr) { - if (mPtr == NULL) - { - mPtr = new T(); - } - return mPtr; + mPtr = new T(); } + return mPtr; + } - private: - - mutable T* mPtr; - }; + private: + mutable T* mPtr = nullptr; + }; // root class of all parameter blocks @@ -864,7 +839,7 @@ namespace LLInitParam mParamProvided(false) {} - virtual ~BaseBlock() {} + virtual ~BaseBlock() = default; bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false); param_handle_t getHandleFromParam(const Param* param) const; diff --git a/indra/llcommon/llpointer.cpp b/indra/llcommon/llpointer.cpp new file mode 100755 index 0000000000..1bb7055b3a --- /dev/null +++ b/indra/llcommon/llpointer.cpp @@ -0,0 +1,43 @@ +/** + * @file llpointer.cpp + * @author Nat Goodspeed + * @date 2024-09-26 + * @brief Implementation for llpointer. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llpointer.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llerror.h" + +void LLPointerBase::wild_dtor(std::string_view msg) +{ +// LL_WARNS() << msg << LL_ENDL; + llassert_msg(false, msg); +} diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 048547e4cc..d2dcf530e5 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -26,8 +26,9 @@ #ifndef LLPOINTER_H #define LLPOINTER_H -#include "llerror.h" // *TODO: consider eliminating this -#include "llmutex.h" +#include <functional> +#include <string_view> +#include <utility> // std::swap() //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s @@ -42,8 +43,18 @@ //---------------------------------------------------------------------------- +class LLPointerBase +{ +protected: + // alert the coder that a referenced type's destructor did something very + // strange -- this is in a non-template base class so we can hide the + // implementation in llpointer.cpp + static void wild_dtor(std::string_view msg); +}; + // Note: relies on Type having ref() and unref() methods -template <class Type> class LLPointer +template <class Type> +class LLPointer: public LLPointerBase { public: template<typename Subclass> @@ -60,6 +71,13 @@ public: ref(); } + // Even though the template constructors below accepting + // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to + // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&) + // constructors, the compiler recognizes these as The Copy Constructor and + // The Move Constructor, respectively. In other words, even in the + // presence of the LLPointer<Subclass> constructors, we still must specify + // the LLPointer<Type> constructors. LLPointer(const LLPointer<Type>& ptr) : mPointer(ptr.mPointer) { @@ -98,39 +116,52 @@ public: const Type& operator*() const { return *mPointer; } Type& operator*() { return *mPointer; } - operator BOOL() const { return (mPointer != nullptr); } operator bool() const { return (mPointer != nullptr); } bool operator!() const { return (mPointer == nullptr); } bool isNull() const { return (mPointer == nullptr); } bool notNull() const { return (mPointer != nullptr); } operator Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } + template <typename Type1> + bool operator !=(Type1* ptr) const { return (mPointer != ptr); } + template <typename Type1> + bool operator ==(Type1* ptr) const { return (mPointer == ptr); } + template <typename Type1> + bool operator !=(const LLPointer<Type1>& ptr) const { return (mPointer != ptr.mPointer); } + template <typename Type1> + bool operator ==(const LLPointer<Type1>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } LLPointer<Type>& operator =(Type* ptr) { - assign(ptr); + // copy-and-swap idiom, see http://gotw.ca/gotw/059.htm + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } + // Even though the template assignment operators below accepting + // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to + // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&) + // assignment operators, the compiler recognizes these as Copy Assignment + // and Move Assignment, respectively. In other words, even in the presence + // of the LLPointer<Subclass> assignment operators, we still must specify + // the LLPointer<Type> operators. LLPointer<Type>& operator =(const LLPointer<Type>& ptr) { - assign(ptr); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } LLPointer<Type>& operator =(LLPointer<Type>&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } @@ -138,28 +169,32 @@ public: template<typename Subclass> LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) { - assign(ptr.get()); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } template<typename Subclass> LLPointer<Type>& operator =(LLPointer<Subclass>&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } // Just exchange the pointers, which will not change the reference counts. static void swap(LLPointer<Type>& a, LLPointer<Type>& b) { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; + using std::swap; // per Swappable convention + swap(a.mPointer, b.mPointer); + } + + // Put swap() overload in the global namespace, per Swappable convention + friend void swap(LLPointer<Type>& a, LLPointer<Type>& b) + { + LLPointer<Type>::swap(a, b); } protected: @@ -184,191 +219,19 @@ protected: temp->unref(); if (mPointer != nullptr) { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + wild_dtor("Unreference did assignment to non-NULL because of destructor"); unref(); } } } #endif // LL_LIBRARY_INCLUDE - void assign(const LLPointer<Type>& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - } - protected: Type* mPointer; }; -template <class Type> class LLConstPointer -{ - template<typename Subclass> - friend class LLConstPointer; -public: - LLConstPointer() : - mPointer(nullptr) - { - } - - LLConstPointer(const Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLConstPointer(const LLConstPointer<Type>& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - LLConstPointer(LLConstPointer<Type>&& ptr) noexcept - { - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer(const LLConstPointer<Subclass>& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - template<typename Subclass> - LLConstPointer(LLConstPointer<Subclass>&& ptr) noexcept : - mPointer(ptr.get()) - { - ptr.mPointer = nullptr; - } - - ~LLConstPointer() - { - unref(); - } - - const Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - const Type& operator*() const { return *mPointer; } - - operator BOOL() const { return (mPointer != nullptr); } - operator bool() const { return (mPointer != nullptr); } - bool operator!() const { return (mPointer == nullptr); } - bool isNull() const { return (mPointer == nullptr); } - bool notNull() const { return (mPointer != nullptr); } - - operator const Type*() const { return mPointer; } - bool operator !=(const Type* ptr) const { return (mPointer != ptr); } - bool operator ==(const Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLConstPointer<Type>& operator =(const Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - LLConstPointer<Type>& operator =(LLConstPointer<Type>&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - template<typename Subclass> - LLConstPointer<Type>& operator =(LLConstPointer<Subclass>&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; - } - - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b) - { - const Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } - -protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else // LL_LIBRARY_INCLUDE - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - const Type *temp = mPointer; - mPointer = nullptr; - temp->unref(); - if (mPointer != nullptr) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } -#endif // LL_LIBRARY_INCLUDE - -protected: - const Type* mPointer; -}; +template <typename Type> +using LLConstPointer = LLPointer<const Type>; template<typename Type> class LLCopyOnWritePointer : public LLPointer<Type> @@ -418,38 +281,26 @@ private: bool mStayUnique; }; -template<typename Type> -bool operator!=(Type* lhs, const LLPointer<Type>& rhs) +template<typename Type0, typename Type1> +bool operator!=(Type0* lhs, const LLPointer<Type1>& rhs) { return (lhs != rhs.get()); } -template<typename Type> -bool operator==(Type* lhs, const LLPointer<Type>& rhs) +template<typename Type0, typename Type1> +bool operator==(Type0* lhs, const LLPointer<Type1>& rhs) { return (lhs == rhs.get()); } -// boost hash adapter -template <class Type> -struct boost::hash<LLPointer<Type>> -{ - typedef LLPointer<Type> argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& s) const - { - return (std::size_t) s.get(); - } -}; - -// Adapt boost hash to std hash +// Specialize for std::hash namespace std { template<class Type> struct hash<LLPointer<Type>> { std::size_t operator()(LLPointer<Type> const& s) const noexcept { - return boost::hash<LLPointer<Type>>()(s); + return std::hash<Type*>()(s.get()); } }; } diff --git a/indra/llcommon/llpounceable.h b/indra/llcommon/llpounceable.h index e86098f20b..20561b0c65 100644 --- a/indra/llcommon/llpounceable.h +++ b/indra/llcommon/llpounceable.h @@ -38,9 +38,9 @@ #include "llsingleton.h" #include <boost/call_traits.hpp> #include <boost/utility/value_init.hpp> -#include <boost/unordered_map.hpp> #include <boost/signals2/signal.hpp> +#include <unordered_map> #include <type_traits> // Forward declare the user template, since we want to be able to point to it @@ -86,7 +86,7 @@ class LLPounceableQueueSingleton: // instance will call on the SAME LLPounceableQueueSingleton instance -- // given how class statics work. We must keep a separate queue for each // LLPounceable instance. Use a hash map for that. - typedef boost::unordered_map<owner_ptr, signal_t> map_t; + typedef std::unordered_map<owner_ptr, signal_t> map_t; public: // Disambiguate queues belonging to different LLPounceables. diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 3120fa0100..cbba2ceb87 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -176,13 +176,13 @@ public: // In general, our streambuf might contain a number of different // physical buffers; iterate over those. bool keepwriting = true; - for (auto bufi = buffer_sequence_begin(bufs), bufend = buffer_sequence_end(bufs); + for (auto bufi(boost::asio::buffer_sequence_begin(bufs)), bufend(boost::asio::buffer_sequence_end(bufs)); bufi != bufend && keepwriting; ++bufi) { // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents // Although apr_file_write() accepts const void*, we // manipulate const char* so we can increment the pointer. - const char* remainptr = static_cast<const char*>((*bufi).data()); + const char* remainptr = static_cast<const char*>(bufi->data()); std::size_t remainlen = boost::asio::buffer_size(*bufi); while (remainlen) { @@ -377,14 +377,14 @@ public: // In general, the mutable_buffer_sequence returned by prepare() might // contain a number of different physical buffers; iterate over those. std::size_t tocommit(0); - for (auto bufi = buffer_sequence_begin(bufs), bufend = buffer_sequence_end(bufs); + for (auto bufi(boost::asio::buffer_sequence_begin(bufs)), bufend(boost::asio::buffer_sequence_end(bufs)); bufi != bufend; ++bufi) { // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents std::size_t toread(boost::asio::buffer_size(*bufi)); apr_size_t gotten(toread); apr_status_t err = apr_file_read(mPipe, - static_cast<void*>((*bufi).data()), + bufi->data(), &gotten); // EAGAIN is exactly what we want from a nonblocking pipe. // Rather than waiting for data, it should return immediately. @@ -657,10 +657,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): // case), e.g. by calling operator(), returns a reference to *the same // instance* of the wrapped type that's stored in our Block subclass. // That's important! We know 'params' persists throughout this method - // call; but without that guarantee, we'd have to assume that converting - // one of its members to std::string might return a different (temp) - // instance. Capturing the c_str() from a temporary std::string is Bad Bad - // Bad. But armed with this knowledge, when you see params.cwd().c_str(), + // call; but without that guarantee, when you see params.cwd().c_str(), // grit your teeth and smile and carry on. if (params.cwd.isProvided()) diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 35335e1213..92f8ae3245 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -32,21 +32,11 @@ #include "llsingleton.h" #include "llstl.h" -template <typename T> -struct LLRegistryDefaultComparator -{ - bool operator()(const T& lhs, const T& rhs) const - { - using std::less; - return less<T>()(lhs, rhs); - } -}; - -template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> > +template <typename KEY, typename VALUE> class LLRegistry { public: - typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t; + typedef LLRegistry<KEY, VALUE> registry_t; typedef const KEY& ref_const_key_t; typedef const VALUE& ref_const_value_t; typedef const VALUE* ptr_const_value_t; @@ -54,9 +44,9 @@ public: class Registrar { - friend class LLRegistry<KEY, VALUE, COMPARATOR>; + friend class LLRegistry<KEY, VALUE>; public: - typedef std::map<KEY, VALUE, COMPARATOR> registry_map_t; + typedef std::map<KEY, VALUE> registry_map_t; bool add(ref_const_key_t key, ref_const_value_t value) { @@ -234,9 +224,9 @@ private: Registrar mDefaultRegistrar; }; -template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> > +template <typename KEY, typename VALUE, typename DERIVED_TYPE> class LLRegistrySingleton - : public LLRegistry<KEY, VALUE, COMPARATOR>, + : public LLRegistry<KEY, VALUE>, public LLSingleton<DERIVED_TYPE> { // This LLRegistrySingleton doesn't use LLSINGLETON(LLRegistrySingleton) @@ -244,7 +234,7 @@ class LLRegistrySingleton // LLRegistrySingleton. So each concrete subclass needs // LLSINGLETON(whatever) -- not this intermediate base class. public: - typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t; + typedef LLRegistry<KEY, VALUE> registry_t; typedef const KEY& ref_const_key_t; typedef const VALUE& ref_const_value_t; typedef VALUE* ptr_value_t; @@ -309,7 +299,7 @@ public: }; // convenience functions - typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t; + typedef typename LLRegistry<KEY, VALUE>::Registrar& ref_registrar_t; static ref_registrar_t currentRegistrar() { return singleton_t::instance().registry_t::currentRegistrar(); diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 77fe545c3f..ce39bc7af3 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -39,6 +39,9 @@ #include <limits> +#include <boost/iostreams/device/array.hpp> +#include <boost/iostreams/stream.hpp> + // Defend against a caller forcibly passing a negative number into an unsigned // size_t index param inline @@ -103,6 +106,9 @@ protected: U32 mUseCount; public: + static void destruct(Impl*& var); + ///< safely decrement or destroy var + static void reset(Impl*& var, Impl* impl); ///< safely set var to refer to the new impl (possibly shared) @@ -166,7 +172,7 @@ public: virtual const LLSD& ref(size_t) const { return undef(); } virtual LLSD::map_const_iterator beginMap() const { return endMap(); } - virtual LLSD::map_const_iterator endMap() const { static const std::map<String, LLSD> empty; return empty.end(); } + virtual LLSD::map_const_iterator endMap() const { static const LLSD::llsd_map_t empty; return empty.end(); } virtual LLSD::array_const_iterator beginArray() const { return endArray(); } virtual LLSD::array_const_iterator endArray() const { static const std::vector<LLSD> empty; return empty.end(); } @@ -345,18 +351,7 @@ namespace LLSD::Real ImplString::asReal() const { - F64 v = 0.0; - std::istringstream i_stream(mValue); - i_stream >> v; - - // we would probably like to ignore all trailing whitespace as - // well, but for now, simply eat the next character, and make - // sure we reached the end of the string. - // *NOTE: gcc 2.95 does not generate an eof() event on the - // stream operation above, so we manually get here to force it - // across platforms. - int c = i_stream.get(); - return ((EOF ==c) ? v : 0.0); + return llsd::string_to_real(mValue); } @@ -431,7 +426,7 @@ namespace class ImplMap final : public LLSD::Impl { private: - typedef std::map<LLSD::String, LLSD, std::less<>> DataMap; + using DataMap = LLSD::llsd_map_t; DataMap mData; @@ -457,7 +452,7 @@ namespace << it.second.asXMLRPCValue() << "</member>"; } os << "</struct>"; - return os.str(); + return std::move(os).str(); } virtual bool has(std::string_view) const; @@ -467,8 +462,13 @@ namespace using LLSD::Impl::ref; // Unhiding ref(size_t) virtual LLSD get(std::string_view) const; virtual LLSD getKeys() const; + void insert(std::string&& k, const LLSD& v); + void insert(std::string&& k, LLSD&& v); void insert(std::string_view k, const LLSD& v); + void insert(std::string_view k, LLSD&& v); virtual void erase(const LLSD::String&); + LLSD& ref(std::string&&); + virtual const LLSD& ref(std::string&&) const; LLSD& ref(std::string_view); virtual const LLSD& ref(std::string_view) const; @@ -525,18 +525,58 @@ namespace return keys; } + void ImplMap::insert(std::string&& k, const LLSD& v) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; + mData.emplace(std::move(k), v); + } + + void ImplMap::insert(std::string&& k, LLSD&& v) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; + mData.emplace(std::move(k), std::move(v)); + } + void ImplMap::insert(std::string_view k, const LLSD& v) { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; mData.emplace(k, v); } + void ImplMap::insert(std::string_view k, LLSD&& v) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; + mData.emplace(k, std::move(v)); + } + void ImplMap::erase(const LLSD::String& k) { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; mData.erase(k); } + LLSD& ImplMap::ref(std::string&& k) + { + DataMap::iterator i = mData.lower_bound(k); + if (i == mData.end() || mData.key_comp()(k, i->first)) + { + return mData.emplace_hint(i, std::make_pair(std::move(k), LLSD()))->second; + } + + return i->second; + } + + const LLSD& ImplMap::ref(std::string&& k) const + { + DataMap::const_iterator i = mData.lower_bound(k); + if (i == mData.end() || mData.key_comp()(k, i->first)) + { + return undef(); + } + + return i->second; + } + LLSD& ImplMap::ref(std::string_view k) { DataMap::iterator i = mData.lower_bound(k); @@ -598,7 +638,7 @@ namespace ImplArray(const DataVector& data) : mData(data) { } public: - ImplArray() { } + ImplArray() = default; virtual ImplArray& makeArray(Impl*&); @@ -615,7 +655,7 @@ namespace os << it.asXMLRPCValue(); } os << "</data></array>"; - return os.str(); + return std::move(os).str(); } using LLSD::Impl::get; // Unhiding get(LLSD::String) @@ -625,10 +665,13 @@ namespace virtual LLSD get(size_t) const; void set(size_t, const LLSD&); void insert(size_t, const LLSD&); + void insert(size_t, LLSD&&); LLSD& append(const LLSD&); + LLSD& append(LLSD&&); virtual void erase(size_t); LLSD& ref(size_t); virtual const LLSD& ref(size_t) const; + void reserve(size_t size) { mData.reserve(size); } LLSD::array_iterator beginArray() { return mData.begin(); } LLSD::array_iterator endArray() { return mData.end(); } @@ -690,12 +733,31 @@ namespace mData.insert(mData.begin() + index, v); } + void ImplArray::insert(size_t i, LLSD&& v) + { + NEGATIVE_EXIT(i); + DataVector::size_type index = i; + + if (index >= mData.size()) // tbd - sanity check limit for index ? + { + mData.resize(index + 1); + } + + mData.insert(mData.begin() + index, std::move(v)); + } + LLSD& ImplArray::append(const LLSD& v) { mData.push_back(v); return mData.back(); } + LLSD& ImplArray::append(LLSD&& v) + { + mData.push_back(std::move(v)); + return mData.back(); + } + void ImplArray::erase(size_t i) { NEGATIVE_EXIT(i); @@ -763,6 +825,14 @@ LLSD::Impl::~Impl() --sOutstandingCount; } +void LLSD::Impl::destruct(Impl*& var) +{ + if (var && var->mUseCount != STATIC_USAGE_COUNT && --var->mUseCount == 0) + { + delete var; + } +} + void LLSD::Impl::reset(Impl*& var, Impl* impl) { if (impl && impl->mUseCount != STATIC_USAGE_COUNT) @@ -961,7 +1031,7 @@ namespace LLSD::LLSD() : impl(0) { ALLOC_LLSD_OBJECT; } -LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, 0); } +LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::destruct(impl); } LLSD::LLSD(const LLSD& other) : impl(0) { ALLOC_LLSD_OBJECT; assign(other); } void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); } @@ -1037,13 +1107,31 @@ LLSD LLSD::emptyMap() bool LLSD::has(const std::string_view k) const { return safe(impl).has(k); } LLSD LLSD::get(const std::string_view k) const { return safe(impl).get(k); } LLSD LLSD::getKeys() const { return safe(impl).getKeys(); } +void LLSD::insert(std::string&& k, const LLSD& v) { makeMap(impl).insert(std::move(k), v); } +void LLSD::insert(std::string&& k, LLSD&& v) { makeMap(impl).insert(std::move(k), std::move(v)); } void LLSD::insert(std::string_view k, const LLSD& v) { makeMap(impl).insert(k, v); } +void LLSD::insert(std::string_view k, LLSD&& v) { makeMap(impl).insert(k, std::move(v)); } +LLSD& LLSD::with(std::string&& k, const LLSD& v) + { + makeMap(impl).insert(std::move(k), v); + return *this; + } +LLSD& LLSD::with(std::string&& k, LLSD&& v) + { + makeMap(impl).insert(std::move(k), std::move(v)); + return *this; + } LLSD& LLSD::with(std::string_view k, const LLSD& v) { makeMap(impl).insert(k, v); return *this; } +LLSD& LLSD::with(std::string_view k, LLSD&& v) + { + makeMap(impl).insert(k, std::move(v)); + return *this; + } void LLSD::erase(const String& k) { makeMap(impl).erase(k); } LLSD& LLSD::operator[](const std::string_view k) @@ -1051,6 +1139,13 @@ LLSD& LLSD::operator[](const std::string_view k) LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; return makeMap(impl).ref(k); } + +LLSD& LLSD::operator[](std::string&& k) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; + return makeMap(impl).ref(std::move(k)); +} + const LLSD& LLSD::operator[](const std::string_view k) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; @@ -1064,18 +1159,33 @@ LLSD LLSD::emptyArray() return v; } +LLSD LLSD::emptyReservedArray(size_t size) +{ + LLSD v; + makeArray(v.impl).reserve(size); + return v; +} + size_t LLSD::size() const { return safe(impl).size(); } LLSD LLSD::get(Integer i) const { return safe(impl).get(i); } void LLSD::set(Integer i, const LLSD& v){ makeArray(impl).set(i, v); } +void LLSD::set(Integer i, LLSD&& v) { makeArray(impl).set(i, std::move(v)); } void LLSD::insert(Integer i, const LLSD& v) { makeArray(impl).insert(i, v); } +void LLSD::insert(Integer i, LLSD&& v) { makeArray(impl).insert(i, std::move(v)); } LLSD& LLSD::with(Integer i, const LLSD& v) { makeArray(impl).insert(i, v); return *this; } +LLSD& LLSD::with(Integer i, LLSD&& v) + { + makeArray(impl).insert(i, std::move(v)); + return *this; + } LLSD& LLSD::append(const LLSD& v) { return makeArray(impl).append(v); } +LLSD& LLSD::append(LLSD&& v) { return makeArray(impl).append(std::move(v)); } void LLSD::erase(Integer i) { makeArray(impl).erase(i); } LLSD& LLSD::operator[](size_t i) @@ -1143,6 +1253,22 @@ LLSD::reverse_array_iterator LLSD::rendArray() { return makeArray(impl) namespace llsd { +LLSD::Real string_to_real(std::string_view in_string) +{ + LLSD::Real v = 0.0; + boost::iostreams::stream<boost::iostreams::array_source> i_stream(in_string.data(), in_string.size()); + i_stream >> v; + + // we would probably like to ignore all trailing whitespace as + // well, but for now, simply eat the next character, and make + // sure we reached the end of the string. + // *NOTE: gcc 2.95 does not generate an eof() event on the + // stream operation above, so we manually get here to force it + // across platforms. + int c = i_stream.get(); + return ((EOF == c) ? v : 0.0); +} + U32 allocationCount() { return LLSD::Impl::sAllocationCount; } U32 outstandingCount() { return LLSD::Impl::sOutstandingCount; } diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index d2b3548831..afe35a65ba 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -34,6 +34,7 @@ #include "stdtypes.h" #include "lldate.h" +#include "llstl.h" #include "lluri.h" #include "lluuid.h" @@ -321,11 +322,34 @@ public: bool has(const std::string_view) const; LLSD get(const std::string_view) const; LLSD getKeys() const; // Return an LLSD array with keys as strings + void insert(const char* k, const LLSD& v) + { + return insert(std::string_view(k), v); + } + void insert(const char* k , LLSD&& v) + { + return insert(std::string_view(k), std::move(v)); + } + void insert(std::string&&, const LLSD&); + void insert(std::string&&, LLSD&&); void insert(std::string_view, const LLSD&); + void insert(std::string_view, LLSD&&); void erase(const String&); + LLSD& with(const char* k, const LLSD& v) + { + return with(std::string_view(k), v); + } + LLSD& with(const char* k, LLSD&& v) + { + return with(std::string_view(k), std::move(v)); + } + LLSD& with(std::string&&, const LLSD&); + LLSD& with(std::string&&, LLSD&&); LLSD& with(std::string_view, const LLSD&); + LLSD& with(std::string_view, LLSD&&); LLSD& operator[](const std::string_view); + LLSD& operator[](std::string&&); LLSD& operator[](const char* c) { return c ? (*this)[std::string_view(c)] : *this; @@ -339,14 +363,22 @@ public: /** @name Array Values */ //@{ + // Allocate an empty array static LLSD emptyArray(); + // Allocate an array with internal storage reserved but not initialized like a std::vector + static LLSD emptyReservedArray(size_t size); + LLSD get(Integer) const; void set(Integer, const LLSD&); + void set(Integer, LLSD&&); void insert(Integer, const LLSD&); + void insert(Integer, LLSD&&); LLSD& append(const LLSD&); + LLSD& append(LLSD&&); void erase(Integer); LLSD& with(Integer, const LLSD&); + LLSD& with(Integer, LLSD&&); // accept size_t so we can index relative to size() const LLSD& operator[](size_t) const; @@ -366,8 +398,9 @@ public: //@{ size_t size() const; - typedef std::map<String, LLSD>::iterator map_iterator; - typedef std::map<String, LLSD>::const_iterator map_const_iterator; + using llsd_map_t = std::map<String, LLSD, std::less<>>; + typedef llsd_map_t::iterator map_iterator; + typedef llsd_map_t::const_iterator map_const_iterator; map_iterator beginMap(); map_iterator endMap(); @@ -512,6 +545,8 @@ LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd); namespace llsd { + // Used by LLSD::ImplString to convert string type to real + LLSD::Real string_to_real(std::string_view in_string); #ifdef LLSD_DEBUG_INFO /** @name Unit Testing Interface */ diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp index a4b45ed80d..71f35802c5 100644 --- a/indra/llcommon/llsdjson.cpp +++ b/indra/llcommon/llsdjson.cpp @@ -70,7 +70,7 @@ LLSD LlsdFromJson(const boost::json::value& val) const boost::json::array& array = val.as_array(); size_t size = array.size(); // allocate elements 0 .. (size() - 1) to avoid incremental allocation - if (! array.empty()) + if (!array.empty()) { result[size - 1] = LLSD(); } @@ -84,7 +84,7 @@ LLSD LlsdFromJson(const boost::json::value& val) result = LLSD::emptyMap(); for (const auto& element : val.as_object()) { - result[element.key()] = LlsdFromJson(element.value()); + result[std::string_view(element.key())] = LlsdFromJson(element.value()); } break; } diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 68a7bf0adf..f850238dff 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -775,7 +775,8 @@ S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) c // There must be a value for every key, thus // child_count must be greater than 0. parse_count += count; - map.insert(name, child); + map.insert(std::move(name), std::move(child)); // Move as name will be filled on next iteration + name.clear(); } else { @@ -822,7 +823,7 @@ S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_dept else { parse_count += count; - array.append(child); + array.append(std::move(child)); } c = get(istr); } @@ -841,7 +842,7 @@ bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const auto count = deserialize_string(istr, value, mMaxBytesLeft); if(PARSE_FAILURE == count) return false; account(count); - data = value; + data = std::move(value); return true; } @@ -872,10 +873,10 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const if(len) { value.resize(len); - account(fullread(istr, (char *)&value[0], len)); + account(fullread(istr, (char*)value.data(), len)); } c = get(istr); // strip off the trailing double-quote - data = value; + data = std::move(value); } else if(0 == strncmp("b64", buf, 3)) { @@ -885,7 +886,7 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const std::stringstream coded_stream; get(istr, *(coded_stream.rdbuf()), '\"'); c = get(istr); - std::string encoded(coded_stream.str()); + std::string encoded(std::move(coded_stream).str()); S32 len = apr_base64_decode_len(encoded.c_str()); std::vector<U8> value; if(len) @@ -894,7 +895,7 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const len = apr_base64_decode_binary(&value[0], encoded.c_str()); value.resize(len); } - data = value; + data = std::move(value); } else if(0 == strncmp("b16", buf, 3)) { @@ -925,7 +926,7 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const // copy the data out of the byte buffer value.insert(value.end(), byte_buffer, write); } - data = value; + data = std::move(value); } else { @@ -1077,7 +1078,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con } else { - data = value; + data = std::move(value); account(cnt); } if(istr.fail()) @@ -1094,7 +1095,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con std::string value; if(parseString(istr, value)) { - data = value; + data = std::move(value); } else { @@ -1159,7 +1160,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con value.resize(size); account(fullread(istr, (char*)&value[0], size)); } - data = value; + data = std::move(value); } if(istr.fail()) { @@ -1218,7 +1219,7 @@ S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) con // There must be a value for every key, thus child_count // must be greater than 0. parse_count += child_count; - map.insert(name, child); + map.insert(std::move(name), std::move(child)); } else { @@ -1238,13 +1239,12 @@ S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) con S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const { - array = LLSD::emptyArray(); U32 value_nbo = 0; read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ S32 size = (S32)ntohl(value_nbo); - // *FIX: This would be a good place to reserve some space in the - // array... + // Preallocate array to avoid incremental allocation + array = LLSD::emptyReservedArray(size); S32 parse_count = 0; S32 count = 0; @@ -1260,7 +1260,7 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) if(child_count) { parse_count += child_count; - array.append(child); + array.append(std::move(child)); } ++count; c = istr.peek(); @@ -1279,18 +1279,15 @@ bool LLSDBinaryParser::parseString( std::istream& istr, std::string& value) const { - // *FIX: This is memory inefficient. U32 value_nbo = 0; read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ S32 size = (S32)ntohl(value_nbo); if(mCheckLimits && (size > mMaxBytesLeft)) return false; if(size < 0) return false; - std::vector<char> buf; if(size) { - buf.resize(size); - account(fullread(istr, &buf[0], size)); - value.assign(buf.begin(), buf.end()); + value.resize(size); + account(fullread(istr, value.data(), size)); } return true; } @@ -1785,7 +1782,7 @@ llssize deserialize_string_delim( } } - value = write_buffer.str(); + value = std::move(write_buffer).str(); return count; } @@ -1806,15 +1803,12 @@ llssize deserialize_string_raw( { // We probably have a valid raw string. determine // the size, and read it. - // *FIX: This is memory inefficient. - auto len = strtol(buf + 1, NULL, 0); + auto len = strtol(buf + 1, nullptr, 0); if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE; - std::vector<char> buf; if(len) { - buf.resize(len); - count += fullread(istr, (char *)&buf[0], len); - value.assign(buf.begin(), buf.end()); + value.resize(len); + count += fullread(istr, value.data(), len); } c = istr.get(); ++count; @@ -2170,7 +2164,7 @@ std::string zip_llsd(LLSD& data) return std::string(); } - std::string source = llsd_strm.str(); + std::string source = std::move(llsd_strm).str(); U8 out[CHUNK]; diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp index ce416baa04..afe4fd63a6 100644 --- a/indra/llcommon/llsdserialize_xml.cpp +++ b/indra/llcommon/llsdserialize_xml.cpp @@ -31,6 +31,8 @@ #include <deque> #include "apr_base64.h" +#include <boost/iostreams/device/array.hpp> +#include <boost/iostreams/stream.hpp> #include <boost/regex.hpp> extern "C" @@ -645,7 +647,7 @@ void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Ch if (mCurrentKey.empty()) { return startSkipping(); } LLSD& map = *mStack.back(); - LLSD& newElement = map[mCurrentKey]; + LLSD& newElement = map[std::move(mCurrentKey)]; mStack.push_back(&newElement); mCurrentKey.clear(); @@ -709,7 +711,8 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) return; case ELEMENT_KEY: - mCurrentKey = mCurrentContent; + mCurrentKey = std::move(mCurrentContent); // This is safe to move as we are in the end element handler + mCurrentContent.clear(); // Ensure mCurrentContent is empty for subsequent use return; default: @@ -742,14 +745,22 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) } else { - value = LLSD(mCurrentContent).asInteger(); + // This must treat "1.23" not as an error, but as a number, which is + // then truncated down to an integer. Hence, this code doesn't call + // std::istringstream::operator>>(int&), which would not consume the + // ".23" portion. + + // Utilizes implementation used internally by LLSD::ImplString::asInteger + value = (int)llsd::string_to_real(mCurrentContent); } } break; case ELEMENT_REAL: { - value = LLSD(mCurrentContent).asReal(); + // Utilizes implementation used internally by LLSD::ImplString::asReal + value = llsd::string_to_real(mCurrentContent); + // removed since this breaks when locale has decimal separator that isn't '.' // investigated changing local to something compatible each time but deemed higher // risk that just using LLSD.asReal() each time. @@ -766,19 +777,19 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) break; case ELEMENT_STRING: - value = mCurrentContent; + value = std::move(mCurrentContent); // This is safe to move as we are in the end element handler and this is cleared below break; case ELEMENT_UUID: - value = LLSD(mCurrentContent).asUUID(); + value = LLUUID(mCurrentContent); break; case ELEMENT_DATE: - value = LLSD(mCurrentContent).asDate(); + value = LLDate(mCurrentContent); break; case ELEMENT_URI: - value = LLSD(mCurrentContent).asURI(); + value = LLURI(mCurrentContent); break; case ELEMENT_BINARY: @@ -787,15 +798,14 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) // created by python and other non-linden systems - DEV-39358 // Fortunately we have very little binary passing now, // so performance impact shold be negligible. + poppy 2009-09-04 - boost::regex r; - r.assign("\\s"); + static const boost::regex r("\\s"); std::string stripped = boost::regex_replace(mCurrentContent, r, ""); S32 len = apr_base64_decode_len(stripped.c_str()); std::vector<U8> data; data.resize(len); len = apr_base64_decode_binary(&data[0], stripped.c_str()); data.resize(len); - value = data; + value = std::move(data); break; } diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 3fba8602ee..e6989211ae 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -25,10 +25,10 @@ #ifndef LLSINGLETON_H #define LLSINGLETON_H -#include <boost/unordered_set.hpp> #include <initializer_list> #include <list> #include <typeinfo> +#include <unordered_set> #include <vector> #include "mutex.h" #include "lockstatic.h" @@ -61,7 +61,7 @@ private: static vec_t dep_sort(); // we directly depend on these other LLSingletons - typedef boost::unordered_set<LLSingletonBase*> set_t; + typedef std::unordered_set<LLSingletonBase*> set_t; set_t mDepends; protected: diff --git a/indra/llcommon/llstaticstringtable.h b/indra/llcommon/llstaticstringtable.h index 66ba3487c4..edff955ee7 100644 --- a/indra/llcommon/llstaticstringtable.h +++ b/indra/llcommon/llstaticstringtable.h @@ -29,9 +29,10 @@ #define LL_STATIC_STRING_TABLE_H #include "lldefs.h" -#include <boost/unordered_map.hpp> #include "llstl.h" +#include <unordered_map> + class LLStaticHashedString { public: @@ -74,7 +75,7 @@ struct LLStaticStringHasher template< typename MappedObject > class LL_COMMON_API LLStaticStringTable - : public boost::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > + : public std::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > { }; diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index 7d41c42ba7..7a1c7caf82 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -34,6 +34,7 @@ #include <vector> #include <list> #include <set> +#include <typeindex> #include <typeinfo> #ifdef LL_LINUX @@ -229,12 +230,10 @@ void delete_and_clear_array(T*& ptr) template <typename T> inline typename T::mapped_type get_ptr_in_map(const T& inmap, typename T::key_type const& key) { - // Typedef here avoids warnings because of new c++ naming rules. - typedef typename T::const_iterator map_iter; - map_iter iter = inmap.find(key); + auto iter = inmap.find(key); if(iter == inmap.end()) { - return NULL; + return nullptr; } else { @@ -243,8 +242,8 @@ inline typename T::mapped_type get_ptr_in_map(const T& inmap, typename T::key_ty }; // helper function which returns true if key is in inmap. -template <typename K, typename T> -inline bool is_in_map(const std::map<K,T>& inmap, const K& key) +template <typename T> +inline bool is_in_map(const T& inmap, typename T::key_type const& key) { if(inmap.find(key) == inmap.end()) { @@ -260,12 +259,10 @@ inline bool is_in_map(const std::map<K,T>& inmap, const K& key) // To replace LLSkipMap getIfThere, use: // get_if_there(map, key, 0) // WARNING: Make sure default_value (generally 0) is not a valid map entry! -template <typename K, typename T> -inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value) +template <typename T> +inline typename T::mapped_type get_if_there(const T& inmap, typename T::key_type const& key, typename T::mapped_type default_value) { - // Typedef here avoids warnings because of new c++ naming rules. - typedef typename std::map<K,T>::const_iterator map_iter; - map_iter iter = inmap.find(key); + auto iter = inmap.find(key); if(iter == inmap.end()) { return default_value; @@ -709,5 +706,24 @@ struct ll_template_cast_impl<DEST, SOURCE> \ } \ } +// Transparent string hashing helper for use with std::unordered_* +// std::unordered_map<std::string, val, ll::string_hash, std::equal_to<>> +namespace ll +{ + struct string_hash + { + using is_transparent = void; + [[nodiscard]] size_t operator()(char const* rhs) const { return std::hash<std::string_view>{}(rhs); } + [[nodiscard]] size_t operator()(std::string_view rhs) const { return std::hash<std::string_view>{}(rhs); } + [[nodiscard]] size_t operator()(const std::string& rhs) const { return std::hash<std::string>{}(rhs); } + }; +} // namespace ll + +// Specialize ostream for std::type_index to allow log output +inline std::ostream& operator<<(std::ostream& s, std::type_index type) +{ + s << type.name(); + return s; +} #endif // LL_LLSTL_H diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index ca1cf03c4d..f91aadccc0 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -26,6 +26,7 @@ #ifndef LL_LLUUID_H #define LL_LLUUID_H +#include <functional> #include <iostream> #include <set> #include <vector> @@ -176,15 +177,27 @@ namespace std { inline size_t operator()(const LLUUID& id) const noexcept { - return (size_t)id.getDigest64(); + size_t h = 0; + // Golden ratio hash with avalanche mixing + // Process 8 bytes at a time by manually constructing 64-bit values + // Shift by 31: mixes upper half into lower half for better bit distribution + // Shift by 47: ensures highest bits influence final hash output + for (int i = 0; i < UUID_BYTES; i += 8) { + size_t chunk = (size_t)id.mData[i] | ((size_t)id.mData[i+1] << 8) | + ((size_t)id.mData[i+2] << 16) | ((size_t)id.mData[i+3] << 24) | + ((size_t)id.mData[i+4] << 32) | ((size_t)id.mData[i+5] << 40) | + ((size_t)id.mData[i+6] << 48) | ((size_t)id.mData[i+7] << 56); + h ^= (chunk * 0x9e3779b97f4a7c15ULL) ^ (h >> 31) ^ (h >> 47); + } + return h; } }; } -// For use with boost containers. +// For use with boost::container_hash inline size_t hash_value(const LLUUID& id) noexcept { - return (size_t)id.getDigest64(); + return std::hash<LLUUID>{}(id); } #endif // LL_LLUUID_H diff --git a/indra/llcommon/llwatchdog.cpp b/indra/llcommon/llwatchdog.cpp index fa240a9ed7..1bc1283d0b 100644 --- a/indra/llcommon/llwatchdog.cpp +++ b/indra/llcommon/llwatchdog.cpp @@ -28,6 +28,7 @@ #include "linden_common.h" #include "llwatchdog.h" +#include "llmutex.h" #include "llthread.h" constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index 0407d6c3e9..67c23358ed 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -281,12 +281,30 @@ bool LL::WorkQueue::done() bool LL::WorkQueue::post(const Work& callable) { - return mQueue.pushIfOpen(callable); + try + { + return mQueue.pushIfOpen(callable); + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("LLCoros") << "Bad memory allocation in WorkQueue::post" << LL_ENDL; + return false; + } } bool LL::WorkQueue::tryPost(const Work& callable) { - return mQueue.tryPush(callable); + try + { + return mQueue.tryPush(callable); + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS("LLCoros") << "Bad memory allocation in WorkQueue::tryPost" << LL_ENDL; + return false; + } } LL::WorkQueue::Work LL::WorkQueue::pop_() |
