1#pragma once
2
3#include <string>
4#include <cstring>
5#include <cstddef>
6#include <type_traits>
7
8
9namespace detail
10{
11 bool startsWith(const std::string & s, const char * prefix, size_t prefix_size);
12 bool endsWith(const std::string & s, const char * suffix, size_t suffix_size);
13}
14
15
16inline bool startsWith(const std::string & s, const std::string & prefix)
17{
18 return detail::startsWith(s, prefix.data(), prefix.size());
19}
20
21inline bool endsWith(const std::string & s, const std::string & suffix)
22{
23 return detail::endsWith(s, suffix.data(), suffix.size());
24}
25
26
27/// With GCC, strlen is evaluated compile time if we pass it a constant
28/// string that is known at compile time.
29inline bool startsWith(const std::string & s, const char * prefix)
30{
31 return detail::startsWith(s, prefix, strlen(prefix));
32}
33
34inline bool endsWith(const std::string & s, const char * suffix)
35{
36 return detail::endsWith(s, suffix, strlen(suffix));
37}
38
39/// Given an integer, return the adequate suffix for
40/// printing an ordinal number.
41template <typename T>
42std::string getOrdinalSuffix(T n)
43{
44 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>,
45 "Unsigned integer value required");
46
47 const auto last_digit = n % 10;
48
49 if ((last_digit < 1 || last_digit > 3)
50 || ((n > 10) && (((n / 10) % 10) == 1)))
51 return "th";
52
53 switch (last_digit)
54 {
55 case 1: return "st";
56 case 2: return "nd";
57 case 3: return "rd";
58 default: return "th";
59 }
60}
61
62/// More efficient than libc, because doesn't respect locale. But for some functions table implementation could be better.
63
64inline bool isASCII(char c)
65{
66 return static_cast<unsigned char>(c) < 0x80;
67}
68
69inline bool isAlphaASCII(char c)
70{
71 return (c >= 'a' && c <= 'z')
72 || (c >= 'A' && c <= 'Z');
73}
74
75inline bool isNumericASCII(char c)
76{
77 /// This is faster than
78 /// return UInt8(UInt8(c) - UInt8('0')) < UInt8(10);
79 /// on Intel CPUs when compiled by gcc 8.
80 return (c >= '0' && c <= '9');
81}
82
83inline bool isHexDigit(char c)
84{
85 return isNumericASCII(c)
86 || (c >= 'a' && c <= 'f')
87 || (c >= 'A' && c <= 'F');
88}
89
90inline bool isAlphaNumericASCII(char c)
91{
92 return isAlphaASCII(c)
93 || isNumericASCII(c);
94}
95
96inline bool isWordCharASCII(char c)
97{
98 return isAlphaNumericASCII(c)
99 || c == '_';
100}
101
102inline bool isValidIdentifierBegin(char c)
103{
104 return isAlphaASCII(c)
105 || c == '_';
106}
107
108inline bool isWhitespaceASCII(char c)
109{
110 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
111}
112
113inline bool isControlASCII(char c)
114{
115 return static_cast<unsigned char>(c) <= 31;
116}
117
118/// Works assuming isAlphaASCII.
119inline char toLowerIfAlphaASCII(char c)
120{
121 return c | 0x20;
122}
123
124inline char toUpperIfAlphaASCII(char c)
125{
126 return c & (~0x20);
127}
128
129inline char alternateCaseIfAlphaASCII(char c)
130{
131 return c ^ 0x20;
132}
133
134inline bool equalsCaseInsensitive(char a, char b)
135{
136 return a == b || (isAlphaASCII(a) && alternateCaseIfAlphaASCII(a) == b);
137}
138
139
140template <typename F>
141std::string trim(const std::string & str, F && predicate)
142{
143 size_t cut_front = 0;
144 size_t cut_back = 0;
145 size_t size = str.size();
146
147 for (size_t i = 0; i < size; ++i)
148 {
149 if (predicate(str[i]))
150 ++cut_front;
151 else
152 break;
153 }
154
155 if (cut_front == size)
156 return {};
157
158 for (auto it = str.rbegin(); it != str.rend(); ++it)
159 {
160 if (predicate(*it))
161 ++cut_back;
162 else
163 break;
164 }
165
166 return str.substr(cut_front, size - cut_front - cut_back);
167}
168