2021-03-09 22:24:35 +00:00
|
|
|
#include "str.hpp"
|
2018-10-03 11:00:30 +00:00
|
|
|
|
2018-12-12 02:17:40 +00:00
|
|
|
#include <algorithm>
|
2019-07-16 23:27:09 +00:00
|
|
|
#include <cctype>
|
2018-10-03 11:00:30 +00:00
|
|
|
#include <cstring>
|
2020-08-31 20:07:17 +00:00
|
|
|
#include <cassert>
|
2018-10-03 11:00:30 +00:00
|
|
|
#include <string>
|
2019-07-02 21:28:28 +00:00
|
|
|
#include <set>
|
2018-10-03 11:00:30 +00:00
|
|
|
|
2022-07-28 16:07:38 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
#include <stringapiset.h>
|
|
|
|
#include <llarp/win32/exception.hpp>
|
|
|
|
#endif
|
|
|
|
|
2018-10-03 11:00:30 +00:00
|
|
|
namespace llarp
|
|
|
|
{
|
2019-07-02 09:06:29 +00:00
|
|
|
bool
|
2020-05-01 19:51:15 +00:00
|
|
|
CaselessLessThan::operator()(std::string_view lhs, std::string_view rhs) const
|
2019-06-28 10:10:01 +00:00
|
|
|
{
|
2020-02-22 19:21:08 +00:00
|
|
|
const size_t s = std::min(lhs.size(), rhs.size());
|
2020-04-07 18:38:56 +00:00
|
|
|
for (size_t i = 0; i < s; ++i)
|
2019-07-02 21:28:28 +00:00
|
|
|
{
|
2019-07-06 17:03:40 +00:00
|
|
|
auto l = std::tolower(lhs[i]);
|
|
|
|
auto r = std::tolower(rhs[i]);
|
2019-07-02 21:28:28 +00:00
|
|
|
|
2020-04-07 18:38:56 +00:00
|
|
|
if (l < r)
|
2019-07-06 17:03:40 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2020-04-07 18:38:56 +00:00
|
|
|
if (l > r)
|
2019-07-06 17:03:40 +00:00
|
|
|
{
|
|
|
|
return false;
|
2019-07-02 21:28:28 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-22 19:21:08 +00:00
|
|
|
|
|
|
|
return lhs.size() < rhs.size();
|
2019-07-02 21:28:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2020-05-01 19:51:15 +00:00
|
|
|
IsFalseValue(std::string_view str)
|
2019-07-02 21:28:28 +00:00
|
|
|
{
|
2020-05-01 19:51:15 +00:00
|
|
|
static const std::set<std::string_view, CaselessLessThan> vals{"no", "false", "0", "off"};
|
2019-07-02 21:28:28 +00:00
|
|
|
|
|
|
|
return vals.count(str) > 0;
|
2018-10-03 11:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2020-05-01 19:51:15 +00:00
|
|
|
IsTrueValue(std::string_view str)
|
2018-10-03 11:00:30 +00:00
|
|
|
{
|
2020-05-01 19:51:15 +00:00
|
|
|
static const std::set<std::string_view, CaselessLessThan> vals{"yes", "true", "1", "on"};
|
2019-07-02 21:28:28 +00:00
|
|
|
|
|
|
|
return vals.count(str) > 0;
|
2018-10-03 11:00:30 +00:00
|
|
|
}
|
|
|
|
|
2020-02-22 19:55:53 +00:00
|
|
|
constexpr static char whitespace[] = " \t\n\r\f\v";
|
2020-02-23 02:23:19 +00:00
|
|
|
|
2020-05-01 19:51:15 +00:00
|
|
|
std::string_view
|
|
|
|
TrimWhitespace(std::string_view str)
|
2020-02-22 19:55:53 +00:00
|
|
|
{
|
|
|
|
size_t begin = str.find_first_not_of(whitespace);
|
2020-05-01 19:51:15 +00:00
|
|
|
if (begin == std::string_view::npos)
|
2020-02-22 19:55:53 +00:00
|
|
|
{
|
2020-02-23 02:23:19 +00:00
|
|
|
str.remove_prefix(str.size());
|
|
|
|
return str;
|
2020-02-22 19:55:53 +00:00
|
|
|
}
|
2020-02-23 02:23:19 +00:00
|
|
|
str.remove_prefix(begin);
|
|
|
|
|
|
|
|
size_t end = str.find_last_not_of(whitespace);
|
2020-05-01 19:51:15 +00:00
|
|
|
if (end != std::string_view::npos)
|
2020-02-23 02:23:19 +00:00
|
|
|
str.remove_suffix(str.size() - end - 1);
|
|
|
|
|
|
|
|
return str;
|
2020-02-22 19:55:53 +00:00
|
|
|
}
|
2020-03-20 04:38:27 +00:00
|
|
|
|
2020-08-31 20:07:17 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
|
|
|
std::vector<std::string_view>
|
|
|
|
split(std::string_view str, const std::string_view delim, bool trim)
|
|
|
|
{
|
|
|
|
std::vector<std::string_view> results;
|
|
|
|
// Special case for empty delimiter: splits on each character boundary:
|
|
|
|
if (delim.empty())
|
|
|
|
{
|
|
|
|
results.reserve(str.size());
|
|
|
|
for (size_t i = 0; i < str.size(); i++)
|
|
|
|
results.emplace_back(str.data() + i, 1);
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t pos = str.find(delim); pos != std::string_view::npos; pos = str.find(delim))
|
|
|
|
{
|
|
|
|
if (!trim || !results.empty() || pos > 0)
|
|
|
|
results.push_back(str.substr(0, pos));
|
|
|
|
str.remove_prefix(pos + delim.size());
|
|
|
|
}
|
|
|
|
if (!trim || str.size())
|
|
|
|
results.push_back(str);
|
|
|
|
else
|
|
|
|
while (!results.empty() && results.back().empty())
|
|
|
|
results.pop_back();
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string_view>
|
|
|
|
split_any(std::string_view str, const std::string_view delims, bool trim)
|
|
|
|
{
|
|
|
|
if (delims.empty())
|
|
|
|
return split(str, delims, trim);
|
|
|
|
std::vector<std::string_view> results;
|
|
|
|
for (size_t pos = str.find_first_of(delims); pos != std::string_view::npos;
|
|
|
|
pos = str.find_first_of(delims))
|
|
|
|
{
|
|
|
|
if (!trim || !results.empty() || pos > 0)
|
|
|
|
results.push_back(str.substr(0, pos));
|
|
|
|
size_t until = str.find_first_not_of(delims, pos + 1);
|
|
|
|
if (until == std::string_view::npos)
|
|
|
|
str.remove_prefix(str.size());
|
|
|
|
else
|
|
|
|
str.remove_prefix(until);
|
|
|
|
}
|
|
|
|
if (!trim || str.size())
|
|
|
|
results.push_back(str);
|
|
|
|
else
|
|
|
|
while (!results.empty() && results.back().empty())
|
|
|
|
results.pop_back();
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
trim(std::string_view& s)
|
|
|
|
{
|
|
|
|
constexpr auto simple_whitespace = " \t\r\n"sv;
|
|
|
|
auto pos = s.find_first_not_of(simple_whitespace);
|
|
|
|
if (pos == std::string_view::npos)
|
|
|
|
{ // whole string is whitespace
|
|
|
|
s.remove_prefix(s.size());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
s.remove_prefix(pos);
|
|
|
|
pos = s.find_last_not_of(simple_whitespace);
|
|
|
|
assert(pos != std::string_view::npos);
|
|
|
|
s.remove_suffix(s.size() - (pos + 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
lowercase_ascii_string(std::string src)
|
|
|
|
{
|
|
|
|
for (char& ch : src)
|
|
|
|
if (ch >= 'A' && ch <= 'Z')
|
|
|
|
ch = ch + ('a' - 'A');
|
|
|
|
return src;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string
|
|
|
|
friendly_duration(std::chrono::nanoseconds dur)
|
|
|
|
{
|
2022-07-18 20:04:30 +00:00
|
|
|
const double dsecs = std::chrono::duration<double>(dur).count();
|
|
|
|
return fmt::format(
|
|
|
|
dur >= 24h ? "{0}d{1}h{2}m{3}s"
|
|
|
|
: dur >= 1h ? "{1}h{2}m{3}s"
|
|
|
|
: dur >= 1min ? "{2}m{3}s"
|
|
|
|
: dur >= 1s ? "{4:.3f}s"
|
|
|
|
: dur >= 1ms ? "{5:.3f}s"
|
|
|
|
: dur >= 1us ? u8"{6:.3f}µs"
|
|
|
|
: "{7}ns",
|
|
|
|
dur / 24h,
|
|
|
|
dur / 1h,
|
|
|
|
dur / 1min,
|
|
|
|
dur / 1s,
|
|
|
|
dsecs,
|
|
|
|
dsecs * 1'000,
|
|
|
|
dsecs * 1'000'000,
|
|
|
|
dur.count());
|
2020-08-31 20:07:17 +00:00
|
|
|
}
|
|
|
|
|
2022-07-28 16:07:38 +00:00
|
|
|
std::wstring
|
|
|
|
to_wide(std::string data)
|
|
|
|
{
|
|
|
|
std::wstring buf;
|
|
|
|
buf.resize(data.size());
|
|
|
|
#ifdef _WIN32
|
|
|
|
// win32 specific codepath because balmer made windows so that man may suffer
|
|
|
|
if (MultiByteToWideChar(CP_UTF8, 0, data.c_str(), data.size(), buf.data(), buf.size()) == 0)
|
|
|
|
throw win32::error{GetLastError(), "failed to convert string to wide string"};
|
|
|
|
|
|
|
|
#else
|
|
|
|
// this dumb but probably works (i guess?)
|
|
|
|
std::transform(
|
|
|
|
data.begin(), data.end(), buf.begin(), [](const auto& ch) -> wchar_t { return ch; });
|
|
|
|
#endif
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2018-10-03 11:00:30 +00:00
|
|
|
} // namespace llarp
|