summaryrefslogtreecommitdiff
path: root/indra/llcommon/llstring.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llstring.h')
-rw-r--r--indra/llcommon/llstring.h190
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
{