2018-02-04 03:22:01 +01:00
|
|
|
// Matrix Construct
|
|
|
|
//
|
|
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice is present in all copies. The
|
|
|
|
// full license for this software is available in the LICENSE file.
|
2007-01-25 07:40:21 +01:00
|
|
|
|
2016-08-13 05:05:54 +02:00
|
|
|
#pragma once
|
2017-10-12 02:42:07 +02:00
|
|
|
#define HAVE_IRCD_STRINGOPS_H
|
2016-08-13 05:05:54 +02:00
|
|
|
|
2017-03-14 19:39:26 +01:00
|
|
|
//
|
2017-10-02 06:14:34 +02:00
|
|
|
// Misc string utilities
|
2017-03-14 19:39:26 +01:00
|
|
|
//
|
2017-08-28 23:51:22 +02:00
|
|
|
namespace ircd
|
|
|
|
{
|
2017-10-25 18:31:52 +02:00
|
|
|
// wrapper to find(T) != npos
|
|
|
|
template<class T> bool has(const string_view &, const T &);
|
2018-09-14 15:44:18 +02:00
|
|
|
inline bool ihas(const string_view &s, const string_view &t);
|
2019-08-08 01:31:30 +02:00
|
|
|
inline size_t ifind(const string_view &s, const string_view &t);
|
2017-10-25 18:31:52 +02:00
|
|
|
|
2020-03-13 19:10:43 +01:00
|
|
|
// Multi-string table suite; returns index past the end on no-match
|
2020-03-15 01:20:12 +01:00
|
|
|
using string_views = vector_view<const string_view>;
|
2020-04-03 22:25:13 +02:00
|
|
|
size_t indexof(const string_view &, const string_views &) noexcept;
|
2020-03-13 19:10:43 +01:00
|
|
|
|
2018-01-20 15:58:49 +01:00
|
|
|
// return view without any trailing characters contained in c
|
|
|
|
string_view rstripa(const string_view &str, const string_view &c);
|
|
|
|
|
|
|
|
// return view without any leading characters contained in c
|
|
|
|
string_view lstripa(const string_view &str, const string_view &c);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2018-01-20 15:58:49 +01:00
|
|
|
// return view without leading occurrences of c
|
2017-08-28 23:51:22 +02:00
|
|
|
string_view lstrip(const string_view &str, const char &c = ' ');
|
2018-01-20 15:58:49 +01:00
|
|
|
string_view lstrip(string_view str, const string_view &c);
|
2018-03-03 12:26:59 +01:00
|
|
|
string_view lstrip(string_view str, const string_view &c, size_t n);
|
2020-01-24 05:55:13 +01:00
|
|
|
string_view lstrip(string_view str, const char &c, size_t n);
|
2018-01-20 15:58:49 +01:00
|
|
|
|
|
|
|
// return view without trailing occurrences of c
|
|
|
|
string_view rstrip(const string_view &str, const char &c = ' ');
|
|
|
|
string_view rstrip(string_view str, const string_view &c);
|
2018-03-03 12:26:59 +01:00
|
|
|
string_view rstrip(string_view str, const string_view &c, size_t n);
|
2020-01-24 05:55:13 +01:00
|
|
|
string_view rstrip(string_view str, const char &c, size_t n);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2018-01-20 15:58:49 +01:00
|
|
|
// return view without leading and trailing occurrences of c
|
2017-08-28 23:51:22 +02:00
|
|
|
string_view strip(const string_view &str, const char &c = ' ');
|
|
|
|
string_view strip(const string_view &str, const string_view &c);
|
2020-07-23 21:54:52 +02:00
|
|
|
string_view strip(const string_view &str, const string_view &c, const size_t n);
|
|
|
|
string_view strip(const string_view &str, const char &c, const size_t n);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2017-10-03 12:56:06 +02:00
|
|
|
// split view on first match of delim; delim not included; if no delim then .second empty
|
2017-08-28 23:51:22 +02:00
|
|
|
std::pair<string_view, string_view> split(const string_view &str, const char &delim = ' ');
|
|
|
|
std::pair<string_view, string_view> split(const string_view &str, const string_view &delim);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2017-10-03 12:56:06 +02:00
|
|
|
// split view on last match of delim; delim not included; if no delim then .second empty
|
2017-08-28 23:51:22 +02:00
|
|
|
std::pair<string_view, string_view> rsplit(const string_view &str, const char &delim = ' ');
|
|
|
|
std::pair<string_view, string_view> rsplit(const string_view &str, const string_view &delim);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
|
|
|
// view between first match of delim a and first match of delim b after it
|
2017-08-28 23:51:22 +02:00
|
|
|
string_view between(const string_view &str, const string_view &a, const string_view &b);
|
|
|
|
string_view between(const string_view &str, const char &a = '(', const char &b = ')');
|
2017-10-02 06:14:34 +02:00
|
|
|
|
|
|
|
// test string endswith delim; or any of the delims in iterable
|
2017-08-28 23:51:22 +02:00
|
|
|
bool endswith(const string_view &str, const string_view &val);
|
|
|
|
bool endswith(const string_view &str, const char &val);
|
|
|
|
template<class It> bool endswith_any(const string_view &str, const It &begin, const It &end);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2017-10-03 13:00:57 +02:00
|
|
|
// count occurrences of val at end of string
|
|
|
|
size_t endswith_count(const string_view &str, const char &val);
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
// test string startswith delim; or any of the delims in iterable
|
2017-08-28 23:51:22 +02:00
|
|
|
bool startswith(const string_view &str, const string_view &val);
|
|
|
|
bool startswith(const string_view &str, const char &val);
|
2017-10-01 04:59:14 +02:00
|
|
|
template<class It> bool startswith_any(const string_view &str, const It &begin, const It &end);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2017-10-03 13:00:57 +02:00
|
|
|
// count occurrences of val at start of string
|
|
|
|
size_t startswith_count(const string_view &str, const char &val);
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
// test string is surrounded by val (ex. surrounded by quote characters)
|
2017-09-15 21:04:52 +02:00
|
|
|
bool surrounds(const string_view &str, const string_view &val);
|
|
|
|
bool surrounds(const string_view &str, const char &val);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
|
|
|
// pop trailing char from view
|
|
|
|
char chop(string_view &str);
|
|
|
|
|
|
|
|
// remove trailing from view and return num chars removed
|
|
|
|
size_t chomp(string_view &str, const char &c = '\n');
|
|
|
|
size_t chomp(string_view &str, const string_view &c);
|
|
|
|
template<class T, class delim> size_t chomp(iterators<T>, const delim &d);
|
|
|
|
|
|
|
|
// Convenience to strip quotes
|
|
|
|
string_view unquote(const string_view &str);
|
2017-08-28 23:51:22 +02:00
|
|
|
std::string unquote(std::string &&);
|
2017-10-02 06:14:34 +02:00
|
|
|
|
2017-10-12 02:57:50 +02:00
|
|
|
std::string replace(std::string, const char &before, const char &after);
|
2018-02-28 09:18:47 +01:00
|
|
|
std::string replace(std::string, const string_view &before, const string_view &after);
|
2017-10-12 02:57:50 +02:00
|
|
|
std::string replace(const string_view &, const char &before, const string_view &after);
|
2018-03-25 23:57:09 +02:00
|
|
|
std::string replace(const string_view &, const string_view &before, const string_view &after);
|
2020-08-10 13:41:26 +02:00
|
|
|
string_view replace(const mutable_buffer &, const char &before, const char &after) noexcept;
|
|
|
|
string_view replace(const mutable_buffer &out, const string_view &in, const char &before, const char &after) noexcept;
|
2017-10-12 02:57:50 +02:00
|
|
|
|
2019-08-03 06:02:40 +02:00
|
|
|
// Change a single character's case
|
|
|
|
using std::tolower;
|
|
|
|
using std::toupper;
|
|
|
|
|
|
|
|
// Change case for all characters.
|
|
|
|
string_view tolower(const mutable_buffer &out, const string_view &in) noexcept;
|
|
|
|
string_view toupper(const mutable_buffer &out, const string_view &in) noexcept;
|
|
|
|
|
2017-10-16 06:24:35 +02:00
|
|
|
// Truncate view at maximum length
|
|
|
|
string_view trunc(const string_view &, const size_t &max);
|
2017-08-28 23:51:22 +02:00
|
|
|
}
|
2016-08-31 08:58:07 +02:00
|
|
|
|
2017-10-16 06:24:35 +02:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::trunc(const string_view &s,
|
|
|
|
const size_t &max)
|
|
|
|
{
|
|
|
|
return { s.data(), std::min(s.size(), max) };
|
|
|
|
}
|
|
|
|
|
2019-06-26 09:55:12 +02:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::replace(const mutable_buffer &out,
|
|
|
|
const string_view &in,
|
|
|
|
const char &before,
|
|
|
|
const char &after)
|
2020-08-10 13:41:26 +02:00
|
|
|
noexcept
|
2019-06-26 09:55:12 +02:00
|
|
|
{
|
2019-06-26 10:22:52 +02:00
|
|
|
const size_t cpsz
|
|
|
|
{
|
|
|
|
std::min(size(in), size(out))
|
|
|
|
};
|
|
|
|
|
|
|
|
// Branch for in-place copy if in and out are the same. Note that
|
|
|
|
// if this branch is not taken they cannot overlap in any way.
|
|
|
|
if(data(in) == data(out))
|
|
|
|
return replace(mutable_buffer(data(out), cpsz), before, after);
|
|
|
|
|
2020-08-10 13:09:19 +02:00
|
|
|
assert(!overlap(out, in));
|
2020-08-10 13:41:26 +02:00
|
|
|
char *const __restrict__ outp
|
2019-06-26 09:55:12 +02:00
|
|
|
{
|
2020-08-10 13:41:26 +02:00
|
|
|
begin(out)
|
2019-06-26 09:55:12 +02:00
|
|
|
};
|
|
|
|
|
2020-08-10 13:41:26 +02:00
|
|
|
const char *const __restrict__ inp
|
2019-06-26 09:55:12 +02:00
|
|
|
{
|
2020-08-10 13:41:26 +02:00
|
|
|
begin(in)
|
2019-06-26 09:55:12 +02:00
|
|
|
};
|
2019-06-26 10:22:52 +02:00
|
|
|
|
2020-08-10 13:41:26 +02:00
|
|
|
for(size_t i(0); i < cpsz; ++i)
|
|
|
|
outp[i] = inp[i] != before? inp[i]: after;
|
|
|
|
|
2020-08-10 13:09:19 +02:00
|
|
|
return string_view
|
|
|
|
{
|
2020-08-10 13:41:26 +02:00
|
|
|
begin(out), cpsz
|
2020-08-10 13:09:19 +02:00
|
|
|
};
|
2019-06-26 09:55:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::replace(const mutable_buffer &s,
|
|
|
|
const char &before,
|
|
|
|
const char &after)
|
2020-08-10 13:41:26 +02:00
|
|
|
noexcept
|
2019-06-26 09:55:12 +02:00
|
|
|
{
|
2020-08-10 13:41:26 +02:00
|
|
|
char *const __restrict__ p
|
|
|
|
{
|
|
|
|
begin(s)
|
|
|
|
};
|
|
|
|
|
|
|
|
for(size_t i(0); i < size(s); ++i)
|
|
|
|
p[i] = p[i] != before? p[i]: after;
|
|
|
|
|
2020-08-10 13:09:19 +02:00
|
|
|
return string_view
|
|
|
|
{
|
|
|
|
data(s), size(s)
|
|
|
|
};
|
2019-06-26 09:55:12 +02:00
|
|
|
}
|
|
|
|
|
2018-03-25 23:57:09 +02:00
|
|
|
inline std::string
|
|
|
|
ircd::replace(const string_view &s,
|
|
|
|
const string_view &before,
|
|
|
|
const string_view &after)
|
|
|
|
{
|
|
|
|
return replace(std::string{s}, before, after);
|
|
|
|
}
|
|
|
|
|
2018-02-28 09:18:47 +01:00
|
|
|
inline std::string
|
|
|
|
ircd::replace(std::string s,
|
|
|
|
const string_view &before,
|
|
|
|
const string_view &after)
|
|
|
|
{
|
|
|
|
size_t p(s.find(data(before), 0, size(before)));
|
|
|
|
for(; p != s.npos; p = s.find(data(before), p + size(after), size(before)))
|
|
|
|
s.replace(p, size(before), data(after), size(after));
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2017-10-12 02:57:50 +02:00
|
|
|
inline std::string
|
|
|
|
ircd::replace(std::string s,
|
|
|
|
const char &before,
|
|
|
|
const char &after)
|
|
|
|
{
|
2020-08-10 13:41:26 +02:00
|
|
|
const auto &res
|
|
|
|
{
|
|
|
|
replace(mutable_buffer(s), before, after)
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(res.data() == s.data());
|
2017-10-12 02:57:50 +02:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Remove quotes on an std::string. Only operates on an rvalue reference so
|
|
|
|
/// that a copy of the string is not created when no quotes are found, and
|
|
|
|
/// movements can take place if they are. This overload is not needed often;
|
|
|
|
/// use string_view.
|
2017-03-31 00:50:54 +02:00
|
|
|
inline std::string
|
|
|
|
ircd::unquote(std::string &&str)
|
|
|
|
{
|
2017-11-01 23:54:20 +01:00
|
|
|
if(endswith(string_view{str}, '"'))
|
2017-03-31 00:50:54 +02:00
|
|
|
str.pop_back();
|
|
|
|
|
2017-11-01 23:54:20 +01:00
|
|
|
if(startswith(string_view{str}, '"'))
|
2017-03-31 00:50:54 +02:00
|
|
|
str = str.substr(1);
|
|
|
|
|
|
|
|
return std::move(str);
|
|
|
|
}
|
2017-03-14 19:39:26 +01:00
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Common convenience to remove quotes around the view of the string
|
2017-03-18 04:26:04 +01:00
|
|
|
inline ircd::string_view
|
2017-10-02 06:14:34 +02:00
|
|
|
ircd::unquote(const string_view &str)
|
2017-03-14 19:39:26 +01:00
|
|
|
{
|
2020-07-23 21:55:55 +02:00
|
|
|
return strip(str, '"', 1);
|
2017-10-02 06:14:34 +02:00
|
|
|
}
|
2017-03-18 04:26:04 +01:00
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Chomps delim from all of the string views in the iterable (iterators<T> are
|
|
|
|
/// the T::iterator pair {begin(t), end(t)} of an iterable T) and returns the
|
|
|
|
/// total number of characters removed from all operations.
|
|
|
|
template<class T,
|
|
|
|
class delim>
|
|
|
|
size_t
|
|
|
|
ircd::chomp(iterators<T> its,
|
|
|
|
const delim &d)
|
|
|
|
{
|
|
|
|
return std::accumulate(begin(its), end(its), size_t(0), [&d]
|
|
|
|
(auto ret, const auto &s)
|
|
|
|
{
|
|
|
|
return ret += chomp(s, d);
|
|
|
|
});
|
|
|
|
}
|
2017-03-18 04:26:04 +01:00
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Removes all characters from the end of the view starting with the last
|
|
|
|
/// instance of c. Different from rstrip() in that this will remove more than
|
|
|
|
/// just the delim from the end; it removes both the delim and everything after
|
|
|
|
/// it from wherever the last delim may be. Removes nothing if no delim is.
|
|
|
|
inline size_t
|
|
|
|
ircd::chomp(string_view &str,
|
|
|
|
const char &c)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_last_of(c));
|
|
|
|
if(pos == string_view::npos)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
assert(str.size() - pos == 1);
|
|
|
|
str = str.substr(0, pos);
|
|
|
|
return 1;
|
2017-03-14 19:39:26 +01:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Removes all characters from the end of the view starting with the last
|
|
|
|
/// instance of c. This matches the entire delim string c to chomp it and
|
|
|
|
/// everything after it.
|
|
|
|
inline size_t
|
|
|
|
ircd::chomp(string_view &str,
|
|
|
|
const string_view &c)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_last_of(c));
|
|
|
|
if(pos == string_view::npos)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
assert(str.size() - pos == c.size());
|
|
|
|
str = str.substr(0, pos);
|
|
|
|
return c.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Removes any last character from the view, modifying the view, and returning
|
|
|
|
/// that character.
|
|
|
|
inline char
|
|
|
|
ircd::chop(string_view &str)
|
|
|
|
{
|
|
|
|
return !str.empty()? str.pop_back() : '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test if a string starts and ends with character
|
2017-09-15 21:04:52 +02:00
|
|
|
inline bool
|
|
|
|
ircd::surrounds(const string_view &str,
|
|
|
|
const char &val)
|
|
|
|
{
|
|
|
|
return str.size() >= 2 && str.front() == val && str.back() == val;
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string starts and ends with a string
|
2017-09-15 21:04:52 +02:00
|
|
|
inline bool
|
|
|
|
ircd::surrounds(const string_view &str,
|
|
|
|
const string_view &val)
|
|
|
|
{
|
|
|
|
return startswith(str, val) && endswith(str, val);
|
|
|
|
}
|
|
|
|
|
2017-10-03 13:00:57 +02:00
|
|
|
/// Count occurrences of val at end of string
|
|
|
|
inline size_t
|
|
|
|
ircd::startswith_count(const string_view &str,
|
|
|
|
const char &v)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_first_not_of(v));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos == string_view::npos?
|
|
|
|
str.size():
|
|
|
|
str.size() - pos - 1;
|
2017-10-03 13:00:57 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string starts with any of the values in the iterable
|
2017-10-01 04:59:14 +02:00
|
|
|
template<class It>
|
|
|
|
bool
|
|
|
|
ircd::startswith_any(const string_view &str,
|
|
|
|
const It &begin,
|
|
|
|
const It &end)
|
|
|
|
{
|
|
|
|
return std::any_of(begin, end, [&str](const auto &val)
|
|
|
|
{
|
|
|
|
return startswith(str, val);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string starts with a character
|
2016-09-28 23:16:14 +02:00
|
|
|
inline bool
|
2016-11-29 16:23:38 +01:00
|
|
|
ircd::startswith(const string_view &str,
|
2016-09-28 23:16:14 +02:00
|
|
|
const char &val)
|
|
|
|
{
|
2018-04-07 07:22:55 +02:00
|
|
|
return !str.empty() && str.front() == val;
|
2016-09-28 23:16:14 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string starts with a string
|
2016-08-31 08:58:07 +02:00
|
|
|
inline bool
|
2016-11-29 16:23:38 +01:00
|
|
|
ircd::startswith(const string_view &str,
|
|
|
|
const string_view &val)
|
2016-08-31 08:58:07 +02:00
|
|
|
{
|
2018-01-20 15:58:49 +01:00
|
|
|
return !str.empty() && str.substr(0, val.size()) == val;
|
2016-08-31 08:58:07 +02:00
|
|
|
}
|
|
|
|
|
2017-10-03 13:00:57 +02:00
|
|
|
/// Count occurrences of val at end of string
|
|
|
|
inline size_t
|
|
|
|
ircd::endswith_count(const string_view &str,
|
|
|
|
const char &v)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_last_not_of(v));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos == string_view::npos?
|
|
|
|
str.size():
|
|
|
|
str.size() - pos - 1;
|
2017-10-03 13:00:57 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string ends with any of the values in iterable
|
2016-08-31 08:58:07 +02:00
|
|
|
template<class It>
|
|
|
|
bool
|
2016-11-29 16:23:38 +01:00
|
|
|
ircd::endswith_any(const string_view &str,
|
2016-08-31 08:58:07 +02:00
|
|
|
const It &begin,
|
|
|
|
const It &end)
|
|
|
|
{
|
|
|
|
return std::any_of(begin, end, [&str](const auto &val)
|
|
|
|
{
|
|
|
|
return endswith(str, val);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string ends with character
|
2016-09-28 23:16:14 +02:00
|
|
|
inline bool
|
2016-11-29 16:23:38 +01:00
|
|
|
ircd::endswith(const string_view &str,
|
2016-09-28 23:16:14 +02:00
|
|
|
const char &val)
|
|
|
|
{
|
2018-04-07 07:22:55 +02:00
|
|
|
return !str.empty() && str.back() == val;
|
2016-09-28 23:16:14 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Test if a string ends with a string
|
2016-08-31 08:58:07 +02:00
|
|
|
inline bool
|
2016-11-29 16:23:38 +01:00
|
|
|
ircd::endswith(const string_view &str,
|
|
|
|
const string_view &val)
|
2016-08-31 08:58:07 +02:00
|
|
|
{
|
2018-01-20 15:58:49 +01:00
|
|
|
const ssize_t off(str.size() - val.size());
|
|
|
|
return !str.empty() && off >= 0 && str.substr(off) == val;
|
2016-08-31 08:58:07 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// View a string between the first match of a and the first match of b
|
|
|
|
/// after a.
|
2016-11-29 16:23:38 +01:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::between(const string_view &str,
|
|
|
|
const string_view &a,
|
|
|
|
const string_view &b)
|
2016-08-31 08:58:07 +02:00
|
|
|
{
|
|
|
|
return split(split(str, a).second, b).first;
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// View a string between the first match of a and the first match of b
|
|
|
|
/// after a.
|
2017-03-24 00:08:59 +01:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::between(const string_view &str,
|
|
|
|
const char &a,
|
|
|
|
const char &b)
|
|
|
|
{
|
|
|
|
return split(split(str, a).second, b).first;
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Split a string on the last match of delim. Delim not included; no match
|
|
|
|
/// will return original str in pair.first, pair.second empty.
|
2016-11-29 16:23:38 +01:00
|
|
|
inline std::pair<ircd::string_view, ircd::string_view>
|
|
|
|
ircd::rsplit(const string_view &str,
|
|
|
|
const string_view &delim)
|
2016-10-26 05:16:28 +02:00
|
|
|
{
|
2020-04-05 05:51:48 +02:00
|
|
|
using pair = std::pair<ircd::string_view, ircd::string_view>;
|
|
|
|
|
2017-09-24 11:18:20 +02:00
|
|
|
const auto pos(str.rfind(delim));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
pair { str.substr(0, pos), str.substr(pos + delim.size()) }:
|
|
|
|
pair { str, string_view{} };
|
2016-10-26 05:16:28 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Split a string on the last match of delim. Delim not included; no match
|
|
|
|
/// will return original str in pair.first, pair.second empty.
|
2017-03-20 12:29:57 +01:00
|
|
|
inline std::pair<ircd::string_view, ircd::string_view>
|
|
|
|
ircd::rsplit(const string_view &str,
|
|
|
|
const char &delim)
|
|
|
|
{
|
2020-04-05 05:51:48 +02:00
|
|
|
using pair = std::pair<ircd::string_view, ircd::string_view>;
|
|
|
|
|
2017-03-20 12:29:57 +01:00
|
|
|
const auto pos(str.find_last_of(delim));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
pair { str.substr(0, pos), str.substr(pos + 1) }:
|
|
|
|
pair { str, string_view{} };
|
2017-03-20 12:29:57 +01:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Split a string on the first match of delim. Delim not included; no match
|
|
|
|
/// will return original str in pair.first, pair.second empty.
|
2016-11-29 16:23:38 +01:00
|
|
|
inline std::pair<ircd::string_view, ircd::string_view>
|
|
|
|
ircd::split(const string_view &str,
|
|
|
|
const string_view &delim)
|
2016-08-31 08:58:07 +02:00
|
|
|
{
|
2020-04-05 05:51:48 +02:00
|
|
|
using pair = std::pair<ircd::string_view, ircd::string_view>;
|
|
|
|
|
2016-08-31 08:58:07 +02:00
|
|
|
const auto pos(str.find(delim));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
pair { str.substr(0, pos), str.substr(pos + delim.size()) }:
|
|
|
|
pair { str, string_view{} };
|
2016-08-31 08:58:07 +02:00
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Split a string on the first match of delim. Delim not included; no match
|
|
|
|
/// will return original str in pair.first, pair.second empty.
|
2017-03-20 12:29:57 +01:00
|
|
|
inline std::pair<ircd::string_view, ircd::string_view>
|
|
|
|
ircd::split(const string_view &str,
|
|
|
|
const char &delim)
|
|
|
|
{
|
2020-04-05 05:51:48 +02:00
|
|
|
using pair = std::pair<ircd::string_view, ircd::string_view>;
|
|
|
|
|
2017-03-20 12:29:57 +01:00
|
|
|
const auto pos(str.find(delim));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
pair { str.substr(0, pos), str.substr(pos + 1) }:
|
|
|
|
pair { str, string_view{} };
|
2017-03-20 12:29:57 +01:00
|
|
|
}
|
|
|
|
|
2020-07-23 21:54:52 +02:00
|
|
|
/// Remove n leading and trailing instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::strip(const string_view &str,
|
|
|
|
const string_view &c,
|
|
|
|
const size_t n)
|
|
|
|
{
|
|
|
|
return lstrip(rstrip(str, c, n), c, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove n leading and trailing instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::strip(const string_view &str,
|
|
|
|
const char &c,
|
|
|
|
const size_t n)
|
|
|
|
{
|
|
|
|
return lstrip(rstrip(str, c, n), c, n);
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Remove leading and trailing instances of c from the returned view
|
2017-04-05 01:06:18 +02:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::strip(const string_view &str,
|
|
|
|
const string_view &c)
|
|
|
|
{
|
|
|
|
return lstrip(rstrip(str, c), c);
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Remove leading and trailing instances of c from the returned view
|
2017-04-05 01:06:18 +02:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::strip(const string_view &str,
|
|
|
|
const char &c)
|
|
|
|
{
|
|
|
|
return lstrip(rstrip(str, c), c);
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Remove trailing instances of c from the returned view
|
2017-04-05 01:06:18 +02:00
|
|
|
inline ircd::string_view
|
2018-01-20 15:58:49 +01:00
|
|
|
ircd::rstrip(string_view str,
|
2017-04-05 01:06:18 +02:00
|
|
|
const string_view &c)
|
|
|
|
{
|
2018-01-20 15:58:49 +01:00
|
|
|
while(endswith(str, c))
|
|
|
|
str = str.substr(0, size(str) - size(c));
|
|
|
|
|
|
|
|
return str;
|
2017-04-05 01:06:18 +02:00
|
|
|
}
|
|
|
|
|
2018-03-03 12:26:59 +01:00
|
|
|
/// Remove trailing instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::rstrip(string_view str,
|
|
|
|
const string_view &c,
|
|
|
|
size_t n)
|
|
|
|
{
|
|
|
|
while(endswith(str, c) && n--)
|
|
|
|
str = str.substr(0, size(str) - size(c));
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2020-01-24 05:55:13 +01:00
|
|
|
/// Remove trailing instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::rstrip(string_view str,
|
|
|
|
const char &c,
|
|
|
|
size_t n)
|
|
|
|
{
|
|
|
|
while(endswith(str, c) && n--)
|
|
|
|
str.pop_back();
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2017-10-02 06:14:34 +02:00
|
|
|
/// Remove trailing instances of c from the returned view
|
2017-04-05 01:06:18 +02:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::rstrip(const string_view &str,
|
|
|
|
const char &c)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_last_not_of(c));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
string_view{str.substr(0, pos + 1)}:
|
|
|
|
str;
|
2017-04-05 01:06:18 +02:00
|
|
|
}
|
|
|
|
|
2018-01-20 15:58:49 +01:00
|
|
|
/// Remove leading instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::lstrip(string_view str,
|
|
|
|
const string_view &c)
|
|
|
|
{
|
2018-09-04 09:11:56 +02:00
|
|
|
if(c)
|
|
|
|
while(startswith(str, c))
|
|
|
|
str = str.substr(size(c));
|
2018-01-20 15:58:49 +01:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2018-03-03 12:26:59 +01:00
|
|
|
/// Remove n leading instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::lstrip(string_view str,
|
|
|
|
const string_view &c,
|
|
|
|
size_t n)
|
|
|
|
{
|
2018-09-04 09:11:56 +02:00
|
|
|
if(c)
|
|
|
|
while(startswith(str, c) && n--)
|
|
|
|
str = str.substr(size(c));
|
2018-03-03 12:26:59 +01:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2020-01-24 05:55:13 +01:00
|
|
|
/// Remove n leading instances of c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::lstrip(string_view str,
|
|
|
|
const char &c,
|
|
|
|
size_t n)
|
|
|
|
{
|
|
|
|
while(startswith(str, c) && n--)
|
|
|
|
str.pop_front();
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2017-11-16 02:19:13 +01:00
|
|
|
/// Remove leading instances of c from the returned view.
|
2017-03-24 00:08:59 +01:00
|
|
|
inline ircd::string_view
|
|
|
|
ircd::lstrip(const string_view &str,
|
|
|
|
const char &c)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_first_not_of(c));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
string_view{str.substr(pos)}:
|
|
|
|
string_view{str.data(), size_t{0}};
|
2017-03-24 00:08:59 +01:00
|
|
|
}
|
|
|
|
|
2018-01-20 15:58:49 +01:00
|
|
|
/// Remove leading instances of any character in c from the returned view
|
2017-03-24 00:08:59 +01:00
|
|
|
inline ircd::string_view
|
2018-01-20 15:58:49 +01:00
|
|
|
ircd::lstripa(const string_view &str,
|
|
|
|
const string_view &c)
|
2017-03-24 00:08:59 +01:00
|
|
|
{
|
|
|
|
const auto pos(str.find_first_not_of(c));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
string_view{str.substr(pos)}:
|
|
|
|
str;
|
2018-01-20 15:58:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove trailing instances of any character in c from the returned view
|
|
|
|
inline ircd::string_view
|
|
|
|
ircd::rstripa(const string_view &str,
|
|
|
|
const string_view &c)
|
|
|
|
{
|
|
|
|
const auto pos(str.find_last_not_of(c));
|
2020-04-05 05:51:48 +02:00
|
|
|
return pos != string_view::npos?
|
|
|
|
string_view{str.substr(0, pos + 1)}:
|
|
|
|
str;
|
2017-03-24 00:08:59 +01:00
|
|
|
}
|
|
|
|
|
2018-09-14 15:44:18 +02:00
|
|
|
inline bool
|
|
|
|
ircd::ihas(const string_view &s,
|
|
|
|
const string_view &t)
|
|
|
|
{
|
2019-08-08 01:31:30 +02:00
|
|
|
return ifind(s, t) != string_view::npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline size_t
|
|
|
|
ircd::ifind(const string_view &s,
|
|
|
|
const string_view &t)
|
|
|
|
{
|
|
|
|
const auto pos
|
2018-09-14 15:44:18 +02:00
|
|
|
{
|
2019-08-08 01:31:30 +02:00
|
|
|
std::search(begin(s), end(s), begin(t), end(t), []
|
|
|
|
(const auto &a, const auto &b)
|
|
|
|
{
|
|
|
|
return tolower(a) == tolower(b);
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
const auto ret
|
|
|
|
{
|
|
|
|
size_t(std::distance(begin(s), pos))
|
|
|
|
};
|
|
|
|
|
|
|
|
return ret < size(s)? ret : string_view::npos;
|
2018-09-14 15:44:18 +02:00
|
|
|
}
|
|
|
|
|
2017-10-25 18:31:52 +02:00
|
|
|
template<class T>
|
2019-08-25 01:38:40 +02:00
|
|
|
inline bool
|
2017-10-25 18:31:52 +02:00
|
|
|
ircd::has(const string_view &s,
|
|
|
|
const T &t)
|
|
|
|
{
|
|
|
|
return s.find(t) != s.npos;
|
|
|
|
}
|