|
@@ -23,10 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
#include "irrString.h"
|
|
|
#include <cstdlib>
|
|
|
#include <string>
|
|
|
+#include <string_view>
|
|
|
#include <cstring>
|
|
|
#include <vector>
|
|
|
#include <limits>
|
|
|
-#include <map>
|
|
|
#include <sstream>
|
|
|
#include <iomanip>
|
|
|
#include <cctype>
|
|
@@ -76,15 +76,18 @@ struct FlagDesc {
|
|
|
|
|
|
// Try to avoid converting between wide and UTF-8 unless you need to
|
|
|
// input/output stuff via Irrlicht
|
|
|
-std::wstring utf8_to_wide(const std::string &input);
|
|
|
-std::string wide_to_utf8(const std::wstring &input);
|
|
|
+std::wstring utf8_to_wide(std::string_view input);
|
|
|
+std::string wide_to_utf8(std::wstring_view input);
|
|
|
+
|
|
|
+std::string urlencode(std::string_view str);
|
|
|
+std::string urldecode(std::string_view str);
|
|
|
|
|
|
-std::string urlencode(const std::string &str);
|
|
|
-std::string urldecode(const std::string &str);
|
|
|
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
|
|
|
std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask);
|
|
|
+
|
|
|
size_t mystrlcpy(char *dst, const char *src, size_t size) noexcept;
|
|
|
char *mystrtok_r(char *s, const char *sep, char **lasts) noexcept;
|
|
|
+
|
|
|
u64 read_seed(const char *str);
|
|
|
bool parseColorString(const std::string &value, video::SColor &color, bool quiet,
|
|
|
unsigned char default_alpha = 0xff);
|
|
@@ -115,23 +118,32 @@ inline std::string padStringRight(std::string str, size_t len)
|
|
|
*
|
|
|
* @return If no end could be removed then "" is returned.
|
|
|
*/
|
|
|
-inline std::string removeStringEnd(const std::string &str,
|
|
|
+inline std::string_view removeStringEnd(std::string_view str,
|
|
|
const char *ends[])
|
|
|
{
|
|
|
const char **p = ends;
|
|
|
|
|
|
for (; *p && (*p)[0] != '\0'; p++) {
|
|
|
- std::string end = *p;
|
|
|
+ std::string_view end(*p);
|
|
|
if (str.size() < end.size())
|
|
|
continue;
|
|
|
if (str.compare(str.size() - end.size(), end.size(), end) == 0)
|
|
|
return str.substr(0, str.size() - end.size());
|
|
|
}
|
|
|
|
|
|
- return "";
|
|
|
+ return std::string_view();
|
|
|
}
|
|
|
|
|
|
|
|
|
+#define MAKE_VARIANT(_name, _t0, _t1) \
|
|
|
+ template <typename T, typename... Args> \
|
|
|
+ inline auto _name(_t0 arg1, _t1 arg2, Args&&... args) \
|
|
|
+ { \
|
|
|
+ return (_name)(std::basic_string_view<T>(arg1), std::basic_string_view<T>(arg2), \
|
|
|
+ std::forward<Args>(args)...); \
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* Check two strings for equivalence. If \p case_insensitive is true
|
|
|
* then the case of the strings is ignored (default is false).
|
|
@@ -142,8 +154,8 @@ inline std::string removeStringEnd(const std::string &str,
|
|
|
* @return true if the strings match
|
|
|
*/
|
|
|
template <typename T>
|
|
|
-inline bool str_equal(const std::basic_string<T> &s1,
|
|
|
- const std::basic_string<T> &s2,
|
|
|
+inline bool str_equal(std::basic_string_view<T> s1,
|
|
|
+ std::basic_string_view<T> s2,
|
|
|
bool case_insensitive = false)
|
|
|
{
|
|
|
if (!case_insensitive)
|
|
@@ -159,6 +171,16 @@ inline bool str_equal(const std::basic_string<T> &s1,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+// For some reason an std::string will not implicitly get converted
|
|
|
+// to an std::basic_string_view<char> in the template case above, so we need
|
|
|
+// these three wrappers. It works if you take out the template parameters.
|
|
|
+// see also <https://stackoverflow.com/questions/68380141/>
|
|
|
+MAKE_VARIANT(str_equal, const std::basic_string<T> &, const std::basic_string<T> &)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_equal, std::basic_string_view<T>, const std::basic_string<T> &)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_equal, const std::basic_string<T> &, std::basic_string_view<T>)
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* Check whether \p str begins with the string prefix. If \p case_insensitive
|
|
@@ -171,8 +193,8 @@ inline bool str_equal(const std::basic_string<T> &s1,
|
|
|
* @return true if the str begins with prefix
|
|
|
*/
|
|
|
template <typename T>
|
|
|
-inline bool str_starts_with(const std::basic_string<T> &str,
|
|
|
- const std::basic_string<T> &prefix,
|
|
|
+inline bool str_starts_with(std::basic_string_view<T> str,
|
|
|
+ std::basic_string_view<T> prefix,
|
|
|
bool case_insensitive = false)
|
|
|
{
|
|
|
if (str.size() < prefix.size())
|
|
@@ -187,24 +209,17 @@ inline bool str_starts_with(const std::basic_string<T> &str,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * Check whether \p str begins with the string prefix. If \p case_insensitive
|
|
|
- * is true then the check is case insensitve (default is false; i.e. case is
|
|
|
- * significant).
|
|
|
- *
|
|
|
- * @param str
|
|
|
- * @param prefix
|
|
|
- * @param case_insensitive
|
|
|
- * @return true if the str begins with prefix
|
|
|
- */
|
|
|
-template <typename T>
|
|
|
-inline bool str_starts_with(const std::basic_string<T> &str,
|
|
|
- const T *prefix,
|
|
|
- bool case_insensitive = false)
|
|
|
-{
|
|
|
- return str_starts_with(str, std::basic_string<T>(prefix),
|
|
|
- case_insensitive);
|
|
|
-}
|
|
|
+// (same conversion issue here)
|
|
|
+MAKE_VARIANT(str_starts_with, const std::basic_string<T> &, const std::basic_string<T> &)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_starts_with, std::basic_string_view<T>, const std::basic_string<T> &)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_starts_with, const std::basic_string<T> &, std::basic_string_view<T>)
|
|
|
+
|
|
|
+// (the same but with char pointers, only for the prefix argument)
|
|
|
+MAKE_VARIANT(str_starts_with, const std::basic_string<T> &, const T*)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_starts_with, std::basic_string_view<T>, const T*)
|
|
|
|
|
|
|
|
|
/**
|
|
@@ -218,8 +233,8 @@ inline bool str_starts_with(const std::basic_string<T> &str,
|
|
|
* @return true if the str begins with suffix
|
|
|
*/
|
|
|
template <typename T>
|
|
|
-inline bool str_ends_with(const std::basic_string<T> &str,
|
|
|
- const std::basic_string<T> &suffix,
|
|
|
+inline bool str_ends_with(std::basic_string_view<T> str,
|
|
|
+ std::basic_string_view<T> suffix,
|
|
|
bool case_insensitive = false)
|
|
|
{
|
|
|
if (str.size() < suffix.size())
|
|
@@ -235,25 +250,20 @@ inline bool str_ends_with(const std::basic_string<T> &str,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+// (same conversion issue here)
|
|
|
+MAKE_VARIANT(str_ends_with, const std::basic_string<T> &, const std::basic_string<T> &)
|
|
|
|
|
|
-/**
|
|
|
- * Check whether \p str ends with the string suffix. If \p case_insensitive
|
|
|
- * is true then the check is case insensitve (default is false; i.e. case is
|
|
|
- * significant).
|
|
|
- *
|
|
|
- * @param str
|
|
|
- * @param suffix
|
|
|
- * @param case_insensitive
|
|
|
- * @return true if the str begins with suffix
|
|
|
- */
|
|
|
-template <typename T>
|
|
|
-inline bool str_ends_with(const std::basic_string<T> &str,
|
|
|
- const T *suffix,
|
|
|
- bool case_insensitive = false)
|
|
|
-{
|
|
|
- return str_ends_with(str, std::basic_string<T>(suffix),
|
|
|
- case_insensitive);
|
|
|
-}
|
|
|
+MAKE_VARIANT(str_ends_with, std::basic_string_view<T>, const std::basic_string<T> &)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_ends_with, const std::basic_string<T> &, std::basic_string_view<T>)
|
|
|
+
|
|
|
+// (the same but with char pointers, only for the suffix argument)
|
|
|
+MAKE_VARIANT(str_ends_with, const std::basic_string<T> &, const T*)
|
|
|
+
|
|
|
+MAKE_VARIANT(str_ends_with, std::basic_string_view<T>, const T*)
|
|
|
+
|
|
|
+
|
|
|
+#undef MAKE_VARIANT
|
|
|
|
|
|
|
|
|
/**
|
|
@@ -282,24 +292,21 @@ inline std::vector<std::basic_string<T> > str_split(
|
|
|
* @param str
|
|
|
* @return A copy of \p str converted to all lowercase characters.
|
|
|
*/
|
|
|
-inline std::string lowercase(const std::string &str)
|
|
|
+inline std::string lowercase(std::string_view str)
|
|
|
{
|
|
|
std::string s2;
|
|
|
-
|
|
|
- s2.reserve(str.size());
|
|
|
-
|
|
|
- for (char i : str)
|
|
|
- s2 += tolower(i);
|
|
|
-
|
|
|
+ s2.resize(str.size());
|
|
|
+ for (size_t i = 0; i < str.size(); i++)
|
|
|
+ s2[i] = tolower(str[i]);
|
|
|
return s2;
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* @param str
|
|
|
- * @return A copy of \p str with leading and trailing whitespace removed.
|
|
|
+ * @return A view of \p str with leading and trailing whitespace removed.
|
|
|
*/
|
|
|
-inline std::string trim(const std::string &str)
|
|
|
+inline std::string_view trim(std::string_view str)
|
|
|
{
|
|
|
size_t front = 0;
|
|
|
size_t back = str.size();
|
|
@@ -313,6 +320,26 @@ inline std::string trim(const std::string &str)
|
|
|
return str.substr(front, back - front);
|
|
|
}
|
|
|
|
|
|
+// If input was a temporary string keep it one to make sure patterns like
|
|
|
+// trim(func_that_returns_str()) are predictable regarding memory allocation
|
|
|
+// and don't lead to UAF. ↓ ↓ ↓
|
|
|
+
|
|
|
+/**
|
|
|
+ * @param str
|
|
|
+ * @return A copy of \p str with leading and trailing whitespace removed.
|
|
|
+ */
|
|
|
+inline std::string trim(std::string &&str)
|
|
|
+{
|
|
|
+ std::string ret(trim(std::string_view(str)));
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+// The above declaration causes ambiguity with char pointers so we have to fix that:
|
|
|
+inline std::string_view trim(const char *str)
|
|
|
+{
|
|
|
+ return trim(std::string_view(str));
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* Returns whether \p str should be regarded as (bool) true. Case and leading
|
|
@@ -320,7 +347,7 @@ inline std::string trim(const std::string &str)
|
|
|
* true are "y", "yes", "true" and any number that is not 0.
|
|
|
* @param str
|
|
|
*/
|
|
|
-inline bool is_yes(const std::string &str)
|
|
|
+inline bool is_yes(std::string_view str)
|
|
|
{
|
|
|
std::string s2 = lowercase(trim(str));
|
|
|
|
|
@@ -377,7 +404,7 @@ inline float mystof(const std::string &str)
|
|
|
template <typename T>
|
|
|
inline T from_string(const std::string &str)
|
|
|
{
|
|
|
- std::stringstream tmp(str);
|
|
|
+ std::istringstream tmp(str);
|
|
|
T t;
|
|
|
tmp >> t;
|
|
|
return t;
|
|
@@ -386,42 +413,6 @@ inline T from_string(const std::string &str)
|
|
|
/// Returns a 64-bit signed value represented by the string \p str (decimal).
|
|
|
inline s64 stoi64(const std::string &str) { return from_string<s64>(str); }
|
|
|
|
|
|
-#if __cplusplus < 201103L
|
|
|
-namespace std {
|
|
|
-
|
|
|
-/// Returns a string representing the value \p val.
|
|
|
-template <typename T>
|
|
|
-inline string to_string(T val)
|
|
|
-{
|
|
|
- ostringstream oss;
|
|
|
- oss << val;
|
|
|
- return oss.str();
|
|
|
-}
|
|
|
-#define DEFINE_STD_TOSTRING_FLOATINGPOINT(T) \
|
|
|
- template <> \
|
|
|
- inline string to_string<T>(T val) \
|
|
|
- { \
|
|
|
- ostringstream oss; \
|
|
|
- oss << std::fixed \
|
|
|
- << std::setprecision(6) \
|
|
|
- << val; \
|
|
|
- return oss.str(); \
|
|
|
- }
|
|
|
-DEFINE_STD_TOSTRING_FLOATINGPOINT(float)
|
|
|
-DEFINE_STD_TOSTRING_FLOATINGPOINT(double)
|
|
|
-DEFINE_STD_TOSTRING_FLOATINGPOINT(long double)
|
|
|
-
|
|
|
-#undef DEFINE_STD_TOSTRING_FLOATINGPOINT
|
|
|
-
|
|
|
-/// Returns a wide string representing the value \p val
|
|
|
-template <typename T>
|
|
|
-inline wstring to_wstring(T val)
|
|
|
-{
|
|
|
- return utf8_to_wide(to_string(val));
|
|
|
-}
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
/// Returns a string representing the decimal value of the 32-bit value \p i.
|
|
|
inline std::string itos(s32 i) { return std::to_string(i); }
|
|
|
/// Returns a string representing the decimal value of the 64-bit value \p i.
|
|
@@ -443,8 +434,8 @@ inline std::string ftos(float f)
|
|
|
* @param pattern The pattern to replace.
|
|
|
* @param replacement What to replace the pattern with.
|
|
|
*/
|
|
|
-inline void str_replace(std::string &str, const std::string &pattern,
|
|
|
- const std::string &replacement)
|
|
|
+inline void str_replace(std::string &str, std::string_view pattern,
|
|
|
+ std::string_view replacement)
|
|
|
{
|
|
|
std::string::size_type start = str.find(pattern, 0);
|
|
|
while (start != str.npos) {
|
|
@@ -454,7 +445,7 @@ inline void str_replace(std::string &str, const std::string &pattern,
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Escapes characters [ ] \ , ; that cannot be used in formspecs
|
|
|
+ * Escapes characters that cannot be used in formspecs
|
|
|
*/
|
|
|
inline void str_formspec_escape(std::string &str)
|
|
|
{
|
|
@@ -486,7 +477,7 @@ void str_replace(std::string &str, char from, char to);
|
|
|
*
|
|
|
* @see string_allowed_blacklist()
|
|
|
*/
|
|
|
-inline bool string_allowed(const std::string &str, const std::string &allowed_chars)
|
|
|
+inline bool string_allowed(std::string_view str, std::string_view allowed_chars)
|
|
|
{
|
|
|
return str.find_first_not_of(allowed_chars) == str.npos;
|
|
|
}
|
|
@@ -502,8 +493,8 @@ inline bool string_allowed(const std::string &str, const std::string &allowed_ch
|
|
|
|
|
|
* @see string_allowed()
|
|
|
*/
|
|
|
-inline bool string_allowed_blacklist(const std::string &str,
|
|
|
- const std::string &blacklisted_chars)
|
|
|
+inline bool string_allowed_blacklist(std::string_view str,
|
|
|
+ std::string_view blacklisted_chars)
|
|
|
{
|
|
|
return str.find_first_of(blacklisted_chars) == str.npos;
|
|
|
}
|
|
@@ -524,12 +515,12 @@ inline bool string_allowed_blacklist(const std::string &str,
|
|
|
* @param row_len The row length (in characters).
|
|
|
* @return A new string with the wrapping applied.
|
|
|
*/
|
|
|
-inline std::string wrap_rows(const std::string &from,
|
|
|
- unsigned row_len)
|
|
|
+inline std::string wrap_rows(std::string_view from, unsigned row_len)
|
|
|
{
|
|
|
std::string to;
|
|
|
+ to.reserve(from.size());
|
|
|
|
|
|
- size_t character_idx = 0;
|
|
|
+ unsigned character_idx = 0;
|
|
|
for (size_t i = 0; i < from.size(); i++) {
|
|
|
if (!IS_UTF8_MULTB_INNER(from[i])) {
|
|
|
// Wrap string after last inner byte of char
|
|
@@ -551,6 +542,7 @@ template <typename T>
|
|
|
inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
|
|
|
{
|
|
|
std::basic_string<T> res;
|
|
|
+ res.reserve(s.size());
|
|
|
|
|
|
for (size_t i = 0; i < s.length(); i++) {
|
|
|
if (s[i] == '\\') {
|
|
@@ -574,6 +566,7 @@ template <typename T>
|
|
|
std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
|
|
|
{
|
|
|
std::basic_string<T> output;
|
|
|
+ output.reserve(s.size());
|
|
|
size_t i = 0;
|
|
|
while (i < s.length()) {
|
|
|
if (s[i] == '\x1b') {
|
|
@@ -646,7 +639,7 @@ inline std::wstring unescape_translate(const std::wstring &s) {
|
|
|
* @return true if to_check is not empty and all characters in to_check are
|
|
|
* decimal digits, otherwise false
|
|
|
*/
|
|
|
-inline bool is_number(const std::string &to_check)
|
|
|
+inline bool is_number(std::string_view to_check)
|
|
|
{
|
|
|
for (char i : to_check)
|
|
|
if (!std::isdigit(i))
|
|
@@ -720,7 +713,7 @@ inline const std::string duration_to_string(int sec)
|
|
|
* @return A std::string
|
|
|
*/
|
|
|
inline std::string str_join(const std::vector<std::string> &list,
|
|
|
- const std::string &delimiter)
|
|
|
+ std::string_view delimiter)
|
|
|
{
|
|
|
std::ostringstream oss;
|
|
|
bool first = true;
|
|
@@ -738,17 +731,17 @@ inline std::string str_join(const std::vector<std::string> &list,
|
|
|
*/
|
|
|
inline std::string stringw_to_utf8(const irr::core::stringw &input)
|
|
|
{
|
|
|
- std::wstring str(input.c_str());
|
|
|
- return wide_to_utf8(str);
|
|
|
+ std::wstring_view sv(input.c_str(), input.size());
|
|
|
+ return wide_to_utf8(sv);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Create an irr::core:stringw from a UTF8 std::string.
|
|
|
*/
|
|
|
-inline irr::core::stringw utf8_to_stringw(const std::string &input)
|
|
|
+inline irr::core::stringw utf8_to_stringw(std::string_view input)
|
|
|
{
|
|
|
std::wstring str = utf8_to_wide(input);
|
|
|
- return irr::core::stringw(str.c_str());
|
|
|
+ return irr::core::stringw(str.c_str(), str.size());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -757,7 +750,7 @@ inline irr::core::stringw utf8_to_stringw(const std::string &input)
|
|
|
* and add a prefix to them
|
|
|
* 2. Remove 'unsafe' characters from the name by replacing them with '_'
|
|
|
*/
|
|
|
-std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix);
|
|
|
+std::string sanitizeDirName(std::string_view str, std::string_view optional_prefix);
|
|
|
|
|
|
/**
|
|
|
* Prints a sanitized version of a string without control characters.
|
|
@@ -765,12 +758,12 @@ std::string sanitizeDirName(const std::string &str, const std::string &optional_
|
|
|
* ASCII control characters are replaced with their hex encoding in angle
|
|
|
* brackets (e.g. "a\x1eb" -> "a<1e>b").
|
|
|
*/
|
|
|
-void safe_print_string(std::ostream &os, const std::string &str);
|
|
|
+void safe_print_string(std::ostream &os, std::string_view str);
|
|
|
|
|
|
/**
|
|
|
* Parses a string of form `(1, 2, 3)` to a v3f
|
|
|
*
|
|
|
- * @param str String
|
|
|
- * @return
|
|
|
+ * @param str string
|
|
|
+ * @return float vector
|
|
|
*/
|
|
|
-v3f str_to_v3f(const std::string &str);
|
|
|
+v3f str_to_v3f(std::string_view str);
|