2016-07-14 05:22:09 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <cstring>
|
2017-07-27 21:05:33 +00:00
|
|
|
#include <cstddef>
|
2018-05-05 12:31:47 +00:00
|
|
|
#include <type_traits>
|
2016-07-14 05:22:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
bool startsWith(const std::string & s, const char * prefix, size_t prefix_size);
|
|
|
|
bool endsWith(const std::string & s, const char * suffix, size_t suffix_size);
|
2016-07-14 05:22:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline bool startsWith(const std::string & s, const std::string & prefix)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return detail::startsWith(s, prefix.data(), prefix.size());
|
2016-07-14 05:22:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool endsWith(const std::string & s, const std::string & suffix)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return detail::endsWith(s, suffix.data(), suffix.size());
|
2016-07-14 05:22:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-15 21:27:34 +00:00
|
|
|
/// With GCC, strlen is evaluated compile time if we pass it a constant
|
|
|
|
/// string that is known at compile time.
|
2016-07-14 05:22:09 +00:00
|
|
|
inline bool startsWith(const std::string & s, const char * prefix)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return detail::startsWith(s, prefix, strlen(prefix));
|
2016-07-14 05:22:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool endsWith(const std::string & s, const char * suffix)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return detail::endsWith(s, suffix, strlen(suffix));
|
2016-07-14 05:22:09 +00:00
|
|
|
}
|
2016-09-15 21:27:34 +00:00
|
|
|
|
|
|
|
/// Given an integer, return the adequate suffix for
|
|
|
|
/// printing an ordinal number.
|
|
|
|
template <typename T>
|
|
|
|
std::string getOrdinalSuffix(T n)
|
|
|
|
{
|
2017-12-25 04:01:46 +00:00
|
|
|
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>,
|
2017-04-01 07:20:54 +00:00
|
|
|
"Unsigned integer value required");
|
2016-09-15 21:27:34 +00:00
|
|
|
|
2017-07-27 21:05:33 +00:00
|
|
|
const auto last_digit = n % 10;
|
2016-09-15 21:27:34 +00:00
|
|
|
|
2017-07-27 21:05:33 +00:00
|
|
|
if ((last_digit < 1 || last_digit > 3)
|
|
|
|
|| ((n > 10) && (((n / 10) % 10) == 1)))
|
2017-04-01 07:20:54 +00:00
|
|
|
return "th";
|
2017-07-27 21:05:33 +00:00
|
|
|
|
|
|
|
switch (last_digit)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-07-27 21:05:33 +00:00
|
|
|
case 1: return "st";
|
|
|
|
case 2: return "nd";
|
|
|
|
case 3: return "rd";
|
|
|
|
default: return "th";
|
|
|
|
};
|
2016-09-15 21:27:34 +00:00
|
|
|
}
|
2016-08-16 21:23:53 +00:00
|
|
|
|
2017-07-12 21:45:47 +00:00
|
|
|
/// More efficient than libc, because doesn't respect locale. But for some functions table implementation could be better.
|
2016-08-16 21:23:53 +00:00
|
|
|
|
|
|
|
inline bool isASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return static_cast<unsigned char>(c) < 0x80;
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isAlphaASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return (c >= 'a' && c <= 'z')
|
|
|
|
|| (c >= 'A' && c <= 'Z');
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isNumericASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return (c >= '0' && c <= '9');
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
2017-07-12 21:45:47 +00:00
|
|
|
inline bool isHexDigit(char c)
|
|
|
|
{
|
|
|
|
return isNumericASCII(c)
|
|
|
|
|| (c >= 'a' && c <= 'f')
|
|
|
|
|| (c >= 'A' && c <= 'F');
|
|
|
|
}
|
|
|
|
|
2016-08-16 21:23:53 +00:00
|
|
|
inline bool isAlphaNumericASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return isAlphaASCII(c)
|
|
|
|
|| isNumericASCII(c);
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool isWordCharASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return isAlphaNumericASCII(c)
|
|
|
|
|| c == '_';
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
2017-04-27 20:58:07 +00:00
|
|
|
inline bool isValidIdentifierBegin(char c)
|
|
|
|
{
|
|
|
|
return isAlphaASCII(c)
|
|
|
|
|| c == '_';
|
|
|
|
}
|
|
|
|
|
2016-08-17 05:38:51 +00:00
|
|
|
inline bool isWhitespaceASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
2016-08-17 05:38:51 +00:00
|
|
|
}
|
|
|
|
|
2018-06-06 18:05:35 +00:00
|
|
|
inline bool isControlASCII(char c)
|
|
|
|
{
|
2018-06-14 19:54:11 +00:00
|
|
|
return static_cast<unsigned char>(c) <= 31;
|
2018-06-06 18:05:35 +00:00
|
|
|
}
|
|
|
|
|
2016-08-16 21:23:53 +00:00
|
|
|
/// Works assuming isAlphaASCII.
|
|
|
|
inline char toLowerIfAlphaASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return c | 0x20;
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline char toUpperIfAlphaASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return c & (~0x20);
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline char alternateCaseIfAlphaASCII(char c)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return c ^ 0x20;
|
2016-08-16 21:23:53 +00:00
|
|
|
}
|
2016-12-30 05:13:14 +00:00
|
|
|
|
|
|
|
inline bool equalsCaseInsensitive(char a, char b)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return a == b || (isAlphaASCII(a) && alternateCaseIfAlphaASCII(a) == b);
|
2016-12-30 05:13:14 +00:00
|
|
|
}
|
|
|
|
|
2018-06-16 02:14:37 +00:00
|
|
|
|
2018-06-16 02:31:41 +00:00
|
|
|
template <typename F>
|
|
|
|
std::string trim(const std::string & str, F && predicate)
|
|
|
|
{
|
|
|
|
size_t cut_front = 0;
|
|
|
|
size_t cut_back = 0;
|
|
|
|
size_t size = str.size();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
if (predicate(str[i]))
|
|
|
|
++cut_front;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cut_front == size)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
for (auto it = str.rbegin(); it != str.rend(); ++it)
|
|
|
|
{
|
|
|
|
if (predicate(*it))
|
|
|
|
++cut_back;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return str.substr(cut_front, size - cut_front - cut_back);
|
|
|
|
}
|