HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
strings.hpp
1// Copyright Take Vos 2019-2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "algorithm.hpp"
8#include "cast.hpp"
9#include "required.hpp"
10#include "assert.hpp"
11#include "os_detect.hpp"
12#include "concepts.hpp"
13#include "exception.hpp"
14#include "codec/UTF.hpp"
15#include <string>
16#include <string_view>
17#include <iterator>
18#include <vector>
19#include <tuple>
20#include <type_traits>
21
22namespace tt {
23
24[[nodiscard]] constexpr bool isUpper(char c) noexcept
25{
26 return c >= 'A' && c <= 'Z';
27}
28
29[[nodiscard]] constexpr bool isLower(char c) noexcept
30{
31 return c >= 'a' && c <= 'z';
32}
33
34[[nodiscard]] constexpr bool isAlpha(char c) noexcept
35{
36 return isUpper(c) || isLower(c);
37}
38
39[[nodiscard]] constexpr bool isDigit(char c) noexcept
40{
41 return c >= '0' && c <= '9';
42}
43
44[[nodiscard]] constexpr bool isAlphaNum(char c) noexcept
45{
46 return isAlpha(c) || isDigit(c);
47}
48
49[[nodiscard]] constexpr bool isLinefeed(char c) noexcept
50{
51 return c == '\r' || c == '\n' || c == '\f' || c == '\v';
52}
53
54[[nodiscard]] constexpr bool isWhitespace(char c) noexcept
55{
56 return c == ' ' || c == '\t' || isLinefeed(c);
57}
58
59[[nodiscard]] constexpr bool isNumberFirst(char c) noexcept
60{
61 return isDigit(c) || c == '+' || c == '-';
62}
63
64[[nodiscard]] constexpr bool isNameFirst(char c) noexcept
65{
66 return isAlpha(c) || c == '_' || c == '$';
67}
68
69[[nodiscard]] constexpr bool isNameNext(char c) noexcept
70{
71 return isAlphaNum(c) || c == '_' || c == '$';
72}
73
74[[nodiscard]] constexpr bool isQuote(char c) noexcept
75{
76 return c == '"' || c == '\'' || c == '`';
77}
78
79[[nodiscard]] constexpr bool isOpenBracket(char c) noexcept
80{
81 return c == '(' || c == '{' || c == '[';
82}
83
84[[nodiscard]] constexpr bool isCloseBracket(char c) noexcept
85{
86 return c == ')' || c == '}' || c == ']';
87}
88
89[[nodiscard]] constexpr bool isOperator(char c) noexcept
90{
91 return !isAlphaNum(c) && c != '_' && !isWhitespace(c) && !isQuote(c) && !isOpenBracket(c) && !isCloseBracket(c);
92}
93
94[[nodiscard]] inline std::string to_lower(std::string_view str) noexcept
95{
97 r.reserve(size(str));
98
99 for (ttlet c : str) {
100 r += (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
101 }
102
103 return r;
104}
105
106[[nodiscard]] inline std::string to_upper(std::string_view str) noexcept
107{
108 std::string r;
109 r.reserve(size(str));
110
111 for (ttlet c : str) {
112 r += (c >= 'a' && c <= 'z') ? (c - 'a') + 'A' : c;
113 }
114
115 return r;
116}
117
120[[nodiscard]] inline std::string normalize_lf(std::string_view str) noexcept
121{
122 std::string r;
123 r.reserve(size(str));
124
125 auto found_cr = false;
126 for (ttlet c : str) {
127 if (found_cr) {
128 // This is Microsoft or old-Apple, we replace the previous carriage-return
129 // with a line-feed and emit the current character.
130 [[unlikely]] r += '\n';
131 if (c != '\r' && c != '\n') {
132 r += c;
133 }
134
135 } else if (c != '\r') {
136 // Emit any non-carriage return character.
137 [[likely]] r += c;
138 }
139
140 found_cr = c == '\r';
141 }
142 if (found_cr) {
143 r += '\n';
144 }
145
146 return r;
147}
148
152[[nodiscard]] inline std::string id_encode(std::string_view str) noexcept
153{
154 std::string r;
155 r.reserve(size(str));
156
157 r += isNameFirst(str.front()) ? str.front() : '_';
158 for (ttlet c : str.substr(1)) {
159 r += isNameNext(c) ? c : '_';
160 }
161
162 return r;
163}
164
165[[nodiscard]] constexpr uint32_t fourcc(char const txt[5]) noexcept
166{
167 return (
168 (static_cast<uint32_t>(txt[0]) << 24) | (static_cast<uint32_t>(txt[1]) << 16) | (static_cast<uint32_t>(txt[2]) << 8) |
169 static_cast<uint32_t>(txt[3]));
170}
171
172[[nodiscard]] constexpr uint32_t fourcc(uint8_t const *txt) noexcept
173{
174 return (
175 (static_cast<uint32_t>(txt[0]) << 24) | (static_cast<uint32_t>(txt[1]) << 16) | (static_cast<uint32_t>(txt[2]) << 8) |
176 static_cast<uint32_t>(txt[3]));
177}
178
179[[nodiscard]] inline std::string fourcc_to_string(uint32_t x) noexcept
180{
181 char c_str[5];
182 c_str[0] = narrow_cast<char>((x >> 24) & 0xff);
183 c_str[1] = narrow_cast<char>((x >> 16) & 0xff);
184 c_str[2] = narrow_cast<char>((x >> 8) & 0xff);
185 c_str[3] = narrow_cast<char>(x & 0xff);
186 c_str[4] = 0;
187
188 return {c_str};
189}
190
191constexpr size_t string_size(sizeable auto str) noexcept
192{
193 return std::size(str);
194}
195
196constexpr size_t string_size(auto str) noexcept
197{
198 return 1;
199}
200
201template<typename FirstNeedle, typename... Needles>
202[[nodiscard]] std::pair<size_t, size_t>
203 string_find_any(std::string_view haystack, size_t pos, FirstNeedle const &first_needle, Needles const &...needles) noexcept
204{
205 using std::size;
206
207 size_t first = haystack.find(first_needle, pos);
208 size_t last = first + string_size(first_needle);
209
210 if (first == std::string_view::npos) {
211 first = size(haystack);
212 last = size(haystack);
213 }
214
215 if constexpr (sizeof...(Needles) != 0) {
216 ttlet [other_first, other_last] = string_find_any(haystack, pos, needles...);
217 if (other_first < first) {
218 first = other_first;
219 last = other_last;
220 }
221 }
222
223 return {first, last};
224}
225
226template<typename StringType, typename... Needles>
227[[nodiscard]] std::vector<StringType> _split(std::string_view haystack, Needles const &...needles) noexcept
228{
229 auto r = std::vector<StringType>{};
230
231 std::string_view::size_type current_pos = 0;
232
233 while (current_pos < std::size(haystack)) {
234 ttlet [needle_first, needle_last] = string_find_any(haystack, current_pos, needles...);
235 r.push_back(StringType{haystack.substr(current_pos, needle_first - current_pos)});
236 current_pos = needle_last;
237 }
238
239 return r;
240}
241
242template<typename... Needles>
243[[nodiscard]] std::vector<std::string> split(std::string_view haystack, Needles const &...needles) noexcept
244{
245 return _split<std::string>(haystack, needles...);
246}
247
248[[nodiscard]] inline std::vector<std::string> split(std::string_view haystack) noexcept
249{
250 return split(haystack, ' ');
251}
252
253template<typename... Needles>
254[[nodiscard]] std::vector<std::string_view> split_view(std::string_view haystack, Needles const &...needles) noexcept
255{
256 return _split<std::string_view>(haystack, needles...);
257}
258
259[[nodiscard]] inline std::vector<std::string_view> split_view(std::string_view haystack) noexcept
260{
261 return split_view(haystack, ' ');
262}
263
264template<typename CharT>
265[[nodiscard]] std::basic_string<CharT>
266join(std::vector<std::basic_string<CharT>> const &list, std::basic_string_view<CharT> const joiner = {}) noexcept
267{
268 std::string r;
269
270 if (list.size() > 1) {
271 size_t final_size = (list.size() - 1) * joiner.size();
272 for (ttlet &item : list) {
273 final_size += item.size();
274 }
275 r.reserve(final_size);
276 }
277
278 size_t i = 0;
279 for (ttlet &item : list) {
280 if (i++ != 0) {
281 r += joiner;
282 }
283 r += item;
284 }
285 return r;
286}
287
288template<typename CharT>
289[[nodiscard]] std::basic_string<CharT>
290join(std::vector<std::basic_string<CharT>> const &list, std::basic_string<CharT> const &joiner) noexcept
291{
292 return join(list, std::basic_string_view<CharT>{joiner});
293}
294
295template<typename CharT>
296[[nodiscard]] std::basic_string<CharT> join(std::vector<std::basic_string<CharT>> const &list, CharT const *joiner) noexcept
297{
298 return join(list, std::basic_string_view<CharT>{joiner});
299}
300
301[[nodiscard]] inline std::string join(std::vector<std::string_view> const &list, std::string_view const joiner = {}) noexcept
302{
303 std::string r;
304
305 if (list.size() > 1) {
306 size_t final_size = (list.size() - 1) * joiner.size();
307 for (ttlet &item : list) {
308 final_size += item.size();
309 }
310 r.reserve(final_size);
311 }
312
313 int64_t i = 0;
314 for (ttlet &item : list) {
315 if (i++ > 0) {
316 r += joiner;
317 }
318 r += item;
319 }
320 return r;
321}
322
325template<typename It>
326[[nodiscard]] inline std::pair<int, int> count_line_and_columns(It begin, It const end)
327{
328 int line = 1;
329 int column = 1;
330
331 for (; begin != end; begin++) {
332 switch (*begin) {
333 case '\n': line++; [[fallthrough]];
334 case '\r': column = 1; break;
335 case '\t': column = ((((column - 1) / 8) + 1) * 8) + 1; break;
336 default: column++;
337 }
338 }
339 return {line, column};
340}
341
345template<typename T, size_t N>
346constexpr auto to_array_without_last(T (&rhs)[N]) noexcept
347{
348 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
349 for (size_t i = 0; i != (N - 1); ++i) {
350 r[i] = rhs[i];
351 }
352 return r;
353}
354
358template<typename T, size_t N>
359constexpr auto to_array_without_last(T(&&rhs)[N]) noexcept
360{
361 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
362 for (size_t i = 0; i != (N - 1); ++i) {
363 r[i] = std::move(rhs[i]);
364 }
365 return r;
366}
367
368[[nodiscard]] inline std::string lstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
369{
370 auto first = front_strip(std::begin(haystack), std::end(haystack), std::begin(needle), std::end(needle));
371 return std::string{first, std::end(haystack)};
372}
373
374[[nodiscard]] inline std::string rstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
375{
376 auto last = back_strip(std::begin(haystack), std::end(haystack), std::begin(needle), std::end(needle));
377 return std::string{std::begin(haystack), last};
378}
379
380[[nodiscard]] inline std::string strip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
381{
382 auto first = front_strip(std::begin(haystack), std::end(haystack), std::begin(needle), std::end(needle));
383 auto last = back_strip(first, std::end(haystack), std::begin(needle), std::end(needle));
384 return std::string{first, last};
385}
386
394[[nodiscard]] inline std::vector<std::string> ZZWSTR_to_string(wchar_t *first, wchar_t *last, ssize_t nr_strings=-1)
395{
396 auto r = std::vector<std::string>{};
397
398 while (first != last) {
399 auto it_zero = std::find(first, last, wchar_t{0});
400 if (it_zero == last) {
401 throw parse_error("Could not find terminating zero of a string.");
402 }
403
404 auto ws = std::wstring_view{first, narrow_cast<size_t>(it_zero - first)};
405 if (ws.empty()) {
406 // The list is terminated with an empty string.
407 break;
408 }
409
410 r.push_back(tt::to_string(ws));
411
412 // Continue after the zero terminator.
413 first = it_zero + 1;
414 }
415
416 if (nr_strings != -1 && std::ssize(r) != nr_strings) {
417 throw parse_error("Unexpected number of string in list.");
418 }
419
420 return r;
421}
422
423} // namespace tt
T begin(T... args)
T end(T... args)
T find(T... args)
T move(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T ws(T... args)