SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
String.cpp
Go to the documentation of this file.
1#include <sourcepp/String.h>
2
3#include <algorithm>
4#include <cctype>
5#include <format>
6#include <random>
7#include <ranges>
8#include <sstream>
9
10namespace {
11
12std::mt19937& getRandomGenerator() {
13 static std::random_device random_device{};
14 static std::mt19937 generator{random_device()};
15 return generator;
16}
17
18} // namespace
19
20using namespace sourcepp;
21
22bool string::contains(std::string_view s, char c) {
23 return std::ranges::find(s, c) != s.end();
24}
25
26bool string::matches(std::string_view in, std::string_view search) {
27 int inPos = 0, searchPos = 0;
28 for ( ; inPos < in.length() && searchPos < search.length(); inPos++, searchPos++) {
29 if (search[searchPos] == '%') {
30 if (++searchPos == search.length()) {
31 return false;
32 }
33 switch (search[searchPos]) {
34 default:
35 case '?': // wildcard
36 break;
37 case 'w': // whitespace
38 if (!std::isspace(in[inPos])) return false;
39 break;
40 case 'a': // letter
41 if (!(in[inPos] >= 'a' && in[inPos] <= 'z' || in[inPos] >= 'A' && in[inPos] <= 'Z')) return false;
42 break;
43 case 'u': // uppercase letter
44 if (!(in[inPos] >= 'A' && in[inPos] <= 'Z')) return false;
45 break;
46 case 'l': // lowercase letter
47 if (!(in[inPos] >= 'a' && in[inPos] <= 'z')) return false;
48 break;
49 case 'd': // digit
50 if (!std::isdigit(in[inPos])) return false;
51 break;
52 case '%': // escaped percent
53 if (in[inPos] != '%') return false;
54 break;
55 }
56 } else if (in[inPos] != search[searchPos]) {
57 return false;
58 }
59 }
60 return inPos == in.length() && searchPos == search.length();
61}
62
63bool string::iequals(std::string_view s1, std::string_view s2) {
64 return std::ranges::equal(s1, s2, [](char a, char b) { return std::tolower(a) == std::tolower(b); });
65}
66
67// https://stackoverflow.com/a/217605
68
69void string::ltrim(std::string& s) {
70 s.erase(s.begin(), std::ranges::find_if(s, [](char c) { return !std::isspace(c); }));
71}
72
73std::string_view string::ltrim(std::string_view s) {
74 while (!s.empty() && std::isspace(s[0])) {
75 s.remove_prefix(1);
76 }
77 return s;
78}
79
80void string::rtrim(std::string& s) {
81 s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(c); }).base(), s.end());
82}
83
84std::string_view string::rtrim(std::string_view s) {
85 while (!s.empty() && std::isspace(s[s.size() - 1])) {
86 s.remove_suffix(1);
87 }
88 return s;
89}
90
91void string::trim(std::string& s) {
92 rtrim(s);
93 ltrim(s);
94}
95
96std::string_view string::trim(std::string_view s) {
97 return ltrim(rtrim(s));
98}
99
100void string::trimInternal(std::string& s) {
101 s.erase(std::ranges::unique(s, [](char lhs, char rhs) { return lhs == rhs && std::isspace(lhs); }).begin(), s.end());
102}
103
104std::string string::trimInternal(std::string_view s) {
105 std::string out{s};
106 trimInternal(out);
107 return out;
108}
109
110void string::ltrim(std::string& s, std::string_view chars) {
111 s.erase(s.begin(), std::ranges::find_if(s, [chars](char c) {
112 return !contains(chars, c);
113 }));
114}
115
116std::string_view string::ltrim(std::string_view s, std::string_view chars) {
117 while (!s.empty() && contains(chars, s[0])) {
118 s.remove_prefix(1);
119 }
120 return s;
121}
122
123void string::rtrim(std::string& s, std::string_view chars) {
124 s.erase(std::find_if(s.rbegin(), s.rend(), [chars](char c) {
125 return !contains(chars, c);
126 }).base(), s.end());
127}
128
129std::string_view string::rtrim(std::string_view s, std::string_view chars) {
130 while (!s.empty() && contains(chars, s[s.size() - 1])) {
131 s.remove_suffix(1);
132 }
133 return s;
134}
135
136void string::trim(std::string& s, std::string_view chars) {
137 rtrim(s, chars);
138 ltrim(s, chars);
139}
140
141std::string_view string::trim(std::string_view s, std::string_view chars) {
142 return ltrim(rtrim(s, chars), chars);
143}
144
145void string::trimInternal(std::string& s, std::string_view chars) {
146 s.erase(std::ranges::unique(s, [chars](char lhs, char rhs) { return lhs == rhs && std::ranges::find(chars, lhs) != chars.end(); }).begin(), s.end());
147}
148
149std::string string::trimInternal(std::string_view s, std::string_view chars) {
150 std::string out{s};
151 trimInternal(out, chars);
152 return out;
153}
154
155// https://stackoverflow.com/a/46931770
156
157std::vector<std::string> string::split(std::string_view s, char delim) {
158 std::vector<std::string> result;
159 std::stringstream ss(std::string{s});
160 std::string item;
161 while (std::getline(ss, item, delim)) {
162 result.push_back(item);
163 }
164 return result;
165}
166
167void string::toLower(std::string& input) {
168 std::ranges::transform(input, input.begin(), [](unsigned char c){ return std::tolower(c); });
169}
170
171std::string string::toLower(std::string_view input) {
172 std::string out{input};
173 toLower(out);
174 return out;
175}
176
177void string::toUpper(std::string& input) {
178 std::ranges::transform(input, input.begin(), [](unsigned char c){ return std::toupper(c); });
179}
180
181std::string string::toUpper(std::string_view input) {
182 std::string out{input};
183 toUpper(out);
184 return out;
185}
186
187std::string string::createRandom(uint16_t length, std::string_view chars) {
188 auto& generator = ::getRandomGenerator();
189 std::uniform_int_distribution distribution{0, static_cast<int>(chars.length() - 1)};
190
191 std::string out;
192 for (uint16_t i = 0; i < length; i++) {
193 out += chars[distribution(generator)];
194 }
195
196 return out;
197}
198
200 static constexpr std::string_view chars = "0123456789abcdef";
201
202 auto& generator = ::getRandomGenerator();
203 std::uniform_int_distribution distribution{0, static_cast<int>(chars.length() - 1)};
204
205 std::string out;
206 for (uint16_t i = 0; i < 8; i++) {
207 out += chars[distribution(generator)];
208 }
209 out += '-';
210 for (uint16_t i = 0; i < 3; i++) {
211 for (uint16_t j = 0; j < 4; j++) {
212 out += chars[distribution(generator)];
213 }
214 out += '-';
215 }
216 for (uint16_t i = 0; i < 12; i++) {
217 out += chars[distribution(generator)];
218 }
219
220 return out;
221}
222
223std::string string::padNumber(int64_t number, int width) {
224 return std::format("{:0>{}}", number, width);
225}
226
227void string::normalizeSlashes(std::string& path, bool stripSlashPrefix, bool stripSlashSuffix) {
228 std::ranges::replace(path, '\\', '/');
229 if (stripSlashPrefix && path.starts_with('/')) {
230 path = path.substr(1);
231 }
232 if (stripSlashSuffix && path.ends_with('/')) {
233 path.pop_back();
234 }
235}
236
237void string::denormalizeSlashes(std::string& path, bool stripSlashPrefix, bool stripSlashSuffix) {
238 std::ranges::replace(path, '/', '\\');
239 if (stripSlashPrefix && path.starts_with('\\')) {
240 path = path.substr(1);
241 }
242 if (stripSlashSuffix && path.ends_with('\\')) {
243 path.pop_back();
244 }
245}
246
247std::from_chars_result string::toBool(std::string_view number, bool& out, int base) {
248 uint8_t tmp;
249 const auto result = std::from_chars(number.data(), number.data() + number.size(), tmp, base);
250 out = tmp;
251 return result;
252}
253
254std::vector<std::byte> string::decodeHex(std::string_view hex) {
255 static constexpr auto hexChar2Int = [](char c) -> uint8_t {
256 if (c >= '0' && c <= '9') {
257 return c - '0';
258 }
259 if (c >= 'a' && c <= 'f') {
260 return c - 'a' + 10;
261 }
262 if (c >= 'A' && c <= 'F') {
263 return c - 'A' + 10;
264 }
265 return 0;
266 };
267
268 const bool remainder = hex.size() % 2;
269
270 std::vector<std::byte> hexData;
271 hexData.reserve(hex.size() / 2 + remainder);
272 if (remainder) {
273 hexData.push_back(static_cast<std::byte>(hexChar2Int(hex.front())));
274 }
275 for (int i = remainder; i < hex.length(); i += 2) {
276 hexData.push_back(static_cast<std::byte>(hexChar2Int(hex[i]) << 4 | hexChar2Int(hex[i + 1])));
277 }
278 return hexData;
279}
280
281std::string string::encodeHex(std::span<const std::byte> hex) {
282 std::string hexStr;
283 hexStr.reserve(hex.size() * 2);
284 for (const auto byte : hex) {
285 hexStr.append(std::format("{:02x}", static_cast<int>(byte)));
286 }
287 return hexStr;
288}
std::string createRandom(uint16_t length=32, std::string_view chars="0123456789_abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ")
Definition String.cpp:187
std::string padNumber(int64_t number, int width)
Definition String.cpp:223
std::vector< std::byte > decodeHex(std::string_view hex)
Definition String.cpp:254
bool contains(std::string_view s, char c)
Definition String.cpp:22
void normalizeSlashes(std::string &path, bool stripSlashPrefix=false, bool stripSlashSuffix=true)
Definition String.cpp:227
std::from_chars_result toBool(std::string_view number, bool &out, int base=10)
Definition String.cpp:247
void ltrim(std::string &s)
Definition String.cpp:69
std::vector< std::string > split(std::string_view s, char delim)
Definition String.cpp:157
void denormalizeSlashes(std::string &path, bool stripSlashPrefix=false, bool stripSlashSuffix=true)
Definition String.cpp:237
bool matches(std::string_view in, std::string_view search)
A very basic regex-like pattern checker for ASCII strings.
Definition String.cpp:26
void trimInternal(std::string &s)
Definition String.cpp:100
void rtrim(std::string &s)
Definition String.cpp:80
void toUpper(std::string &input)
Definition String.cpp:177
void trim(std::string &s)
Definition String.cpp:91
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:63
void toLower(std::string &input)
Definition String.cpp:167
std::string generateUUIDv4()
Definition String.cpp:199
std::string encodeHex(std::span< const std::byte > hex)
Definition String.cpp:281