diff options
Diffstat (limited to 'indra/llcommon/llstring.h')
-rw-r--r-- | indra/llcommon/llstring.h | 190 |
1 files changed, 181 insertions, 9 deletions
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index db716b1431..b15a85cb2a 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -36,9 +36,12 @@ //#include <locale> #include <iomanip> #include <algorithm> +#include <functional> #include <vector> #include <map> +#include <type_traits> #include "llformat.h" +#include "stdtypes.h" #if LL_LINUX #include <wctype.h> @@ -212,6 +215,9 @@ public: static std::string getDatetimeCode (std::string key); + static void splitString(const std::string& text, char delimiter, + std::function<void(const std::string&)> handler); + // Express a value like 1234567 as "1.23M" static std::string getReadableNumber(F64 num); }; @@ -313,6 +319,14 @@ public: static void trim(string_type& string) { trimHead(string); trimTail(string); } static void truncate(string_type& string, size_type count); + // if string startsWith prefix, remove it and return true + static bool removePrefix(string_type& string, const string_type& prefix); + // if string startsWith prefix, return (string without prefix, true), else (string, false) + static std::pair<string_type, bool> withoutPrefix(const string_type& string, const string_type& prefix); + // like removePrefix() + static bool removeSuffix(string_type& string, const string_type& suffix); + static std::pair<string_type, bool> withoutSuffix(const string_type& string, const string_type& suffix); + static void toUpper(string_type& string); static void toLower(string_type& string); @@ -433,6 +447,65 @@ public: static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); } + // Join non-empty strings from values using value itself and delimiter + template<class C> + static std::string join(const C& values, T delimiter = ',') + { + std::string result; + for (const std::string& value : values) + { + if (!value.empty()) + { + if (!result.empty()) + { + result += delimiter; + } + result += value; + } + } + return result; + } + + // Join non-empty strings from values using stringify(value) and delimiter + template<class C, class V> + static std::string join(const C& values, std::function<std::string(const V&)> stringify, T delimiter = ',') + { + std::string result; + for (const V& value : values) + { + std::string string = stringify(value); + if (!string.empty()) + { + if (!result.empty()) + { + result += delimiter; + } + result += string; + } + } + return result; + } + + // Join non-empty strings from values using stringify(index, value) and delimiter + template<class C, class V> + static std::string join(const C& values, std::function<std::string(size_t index, const V&)> stringify, T delimiter = ',') + { + std::string result; + for (size_t i = 0; i < values.size(); ++i) + { + std::string string = stringify(i, values[i]); + if (!string.empty()) + { + if (!result.empty()) + { + result += delimiter; + } + result += string; + } + } + return result; + } + #ifdef _DEBUG LL_COMMON_API static void testHarness(); #endif @@ -521,11 +594,52 @@ struct ll_convert_impl TO operator()(const FROM& in) const; }; -// Use a function template to get the nice ll_convert<TO>(from_value) API. -template<typename TO, typename FROM> -TO ll_convert(const FROM& in) +/** + * somefunction(ll_convert(data)) + * target = ll_convert(data) + * totype otherfunc(const fromtype& data) + * { + * // ... + * return ll_convert(data); + * } + * all infer both the FROM type and the TO type. + */ +template <typename FROM> +class ll_convert +{ +private: + const FROM& mRef; + +public: + ll_convert(const FROM& ref): mRef(ref) {} + + inline operator const FROM&() const + { + return mRef; + } + + template <typename TO, + std::enable_if_t<! std::is_same_v<std::decay_t<TO>, std::decay_t<FROM>>, bool> =true> + inline operator TO() const + { + return ll_convert_impl<TO, std::decay_t<const FROM>>()(mRef); + } +}; + +// When the TO type must be explicit, use a function template to get +// ll_convert_to<TO>(from_value) API. +template<typename SAME> +const SAME& ll_convert_to(const SAME& in) +{ + return in; +} + +template<typename TO, + typename FROM, + std::enable_if_t<! std::is_same_v<std::decay_t<TO>, std::decay_t<FROM>>, bool> =true> +TO ll_convert_to(const FROM& in) { - return ll_convert_impl<TO, FROM>()(in); + return ll_convert_impl<TO, std::decay_t<const FROM>>()(in); } // degenerate case @@ -579,8 +693,8 @@ inline size_t ll_convert_length<char> (const char* zstr) { return std::strl // and longname(const string&, len) so calls written pre-ll_convert() will // work. Most of these overloads will be unified once we turn on C++17 and can // use std::string_view. -// It also uses aliasmacro to ensure that both ll_convert<OUTSTR>(const char*) -// and ll_convert<OUTSTR>(const string&) will work. +// It also uses aliasmacro to ensure that both ll_convert(const char*) +// and ll_convert(const string&) will work. #define ll_convert_forms(aliasmacro, OUTSTR, INSTR, longname) \ LL_COMMON_API OUTSTR longname(const INSTR::value_type* in, size_t len); \ inline auto longname(const INSTR& in, size_t len) \ @@ -670,7 +784,11 @@ ll_convert_forms(ll_convert_alias, LLWString, std::string, utf8str_to_ // Same function, better name. JC inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); } -LL_COMMON_API std::ptrdiff_t wchar_to_utf8chars(llwchar inchar, char* outchars); +// return a UTF-8 string representation of a single llwchar, which we +// occasionally require: +// cheaper than ll_convert_to<std::string>(LLWString(1, inchar)) +LL_COMMON_API std::string wchar_to_utf8chars(llwchar inchar); +ll_convert_alias(std::string, llwchar, wchar_to_utf8chars(in)); ll_convert_forms(ll_convert_alias, std::string, LLWString, wstring_to_utf8str); ll_convert_forms(ll_convert_u16_alias, std::string, llutf16string, utf16str_to_utf8str); @@ -823,7 +941,7 @@ LL_COMMON_API std::string ll_convert_string_to_utf8_string(const std::string& in template<typename STRING> STRING windows_message(unsigned long error) { - return ll_convert<STRING>(windows_message<std::wstring>(error)); + return ll_convert(windows_message<std::wstring>(error)); } /// There's only one real implementation @@ -1467,6 +1585,60 @@ void LLStringUtilBase<T>::trimTail(string_type& string) } } +// if string startsWith prefix, remove it and return true +template<class T> +bool LLStringUtilBase<T>::removePrefix(string_type& string, const string_type& prefix) +{ + bool found{ startsWith(string, prefix) }; + if (found) + { + string.erase(0, prefix.length()); + } + return found; +} + +// if string startsWith prefix, return (string without prefix, true), else (string, false) +template<class T> +std::pair<typename LLStringUtilBase<T>::string_type, bool> +LLStringUtilBase<T>::withoutPrefix(const string_type& string, const string_type& prefix) +{ + bool found{ startsWith(string, prefix) }; + if (! found) + { + return { string, false }; + } + else + { + return { string.substr(prefix.length()), true }; + } +} + +// like removePrefix() +template<class T> +bool LLStringUtilBase<T>::removeSuffix(string_type& string, const string_type& suffix) +{ + bool found{ endsWith(string, suffix) }; + if (found) + { + string.erase(string.length() - suffix.length()); + } + return found; +} + +template<class T> +std::pair<typename LLStringUtilBase<T>::string_type, bool> +LLStringUtilBase<T>::withoutSuffix(const string_type& string, const string_type& suffix) +{ + bool found{ endsWith(string, suffix) }; + if (! found) + { + return { string, false }; + } + else + { + return { string.substr(0, string.length() - suffix.length()), true }; + } +} // Replace line feeds with carriage return-line feed pairs. //static @@ -1835,7 +2007,7 @@ auto LLStringUtilBase<T>::getoptenv(const std::string& key) -> std::optional<str if (found) { // return populated std::optional - return { ll_convert<string_type>(*found) }; + return { ll_convert_to<string_type>(*found) }; } else { |