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 "architecture.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#include <cstdlib>
22
23namespace tt {
24
25[[nodiscard]] constexpr bool is_upper(char c) noexcept
26{
27 return c >= 'A' && c <= 'Z';
28}
29
30[[nodiscard]] constexpr bool is_lower(char c) noexcept
31{
32 return c >= 'a' && c <= 'z';
33}
34
35[[nodiscard]] constexpr bool is_alpha(char c) noexcept
36{
37 return is_upper(c) || is_lower(c);
38}
39
40[[nodiscard]] constexpr bool is_digit(char c) noexcept
41{
42 return c >= '0' && c <= '9';
43}
44
45[[nodiscard]] constexpr bool is_alpha_num(char c) noexcept
46{
47 return is_alpha(c) || is_digit(c);
48}
49
50[[nodiscard]] constexpr bool is_line_feed(char c) noexcept
51{
52 return c == '\r' || c == '\n' || c == '\f' || c == '\v';
53}
54
55[[nodiscard]] constexpr bool is_white_space(char c) noexcept
56{
57 return c == ' ' || c == '\t' || is_line_feed(c);
58}
59
60[[nodiscard]] constexpr bool is_number_first(char c) noexcept
61{
62 return is_digit(c) || c == '+' || c == '-';
63}
64
65[[nodiscard]] constexpr bool is_name_first(char c) noexcept
66{
67 return is_alpha(c) || c == '_' || c == '$';
68}
69
70[[nodiscard]] constexpr bool is_name_next(char c) noexcept
71{
72 return is_alpha_num(c) || c == '_' || c == '$';
73}
74
75[[nodiscard]] constexpr bool is_quote(char c) noexcept
76{
77 return c == '"' || c == '\'' || c == '`';
78}
79
80[[nodiscard]] constexpr bool is_open_bracket(char c) noexcept
81{
82 return c == '(' || c == '{' || c == '[';
83}
84
85[[nodiscard]] constexpr bool is_close_bracket(char c) noexcept
86{
87 return c == ')' || c == '}' || c == ']';
88}
89
90[[nodiscard]] constexpr bool is_operator(char c) noexcept
91{
92 return !is_alpha_num(c) && c != '_' && !is_white_space(c) && !is_quote(c) && !is_open_bracket(c) && !is_close_bracket(c);
93}
94
95[[nodiscard]] constexpr char to_lower(char c) noexcept
96{
97 return (c >= 'A' and c <= 'Z') ? (c - 'A') + 'a' : c;
98}
99
100[[nodiscard]] constexpr char to_upper(char c) noexcept
101{
102 return (c >= 'a' and c <= 'z') ? (c - 'a') + 'A' : c;
103}
104
105[[nodiscard]] inline std::string to_lower(std::string_view str) noexcept
106{
107 std::string r;
108 r.reserve(size(str));
109
110 for (ttlet c : str) {
111 r += to_lower(c);
112 }
113
114 return r;
115}
116
117[[nodiscard]] inline std::string to_upper(std::string_view str) noexcept
118{
119 std::string r;
120 r.reserve(size(str));
121
122 for (ttlet c : str) {
123 r += to_upper(c);
124 }
125
126 return r;
127}
128
131[[nodiscard]] inline std::string normalize_lf(std::string_view str) noexcept
132{
133 std::string r;
134 r.reserve(size(str));
135
136 auto found_cr = false;
137 for (ttlet c : str) {
138 if (found_cr) {
139 // This is Microsoft or old-Apple, we replace the previous carriage-return
140 // with a line-feed and emit the current character.
141 [[unlikely]] r += '\n';
142 if (c != '\r' && c != '\n') {
143 r += c;
144 }
145
146 } else if (c != '\r') {
147 // Emit any non-carriage return character.
148 [[likely]] r += c;
149 }
150
151 found_cr = c == '\r';
152 }
153 if (found_cr) {
154 r += '\n';
155 }
156
157 return r;
158}
159
163[[nodiscard]] inline std::string make_identifier(std::string_view str) noexcept
164{
165 std::string r;
166 r.reserve(size(str));
167
168 r += is_name_first(str.front()) ? str.front() : '_';
169 for (ttlet c : str.substr(1)) {
170 r += is_name_next(c) ? c : '_';
171 }
172
173 return r;
174}
175
180[[nodiscard]] inline std::string make_slug(std::string_view str) noexcept
181{
182 std::string r;
183 r.reserve(size(str));
184
185 size_t dash_count = 0;
186 for (ttlet c : str) {
187 if (is_alpha_num(c)) {
188 dash_count = 0;
189 r += to_lower(c);
190 } else if (dash_count++ == 0) {
191 r += '-';
192 }
193 }
194
195 return r;
196}
197
204[[nodiscard]] inline std::string make_title(std::string_view str) noexcept
205{
206 std::string r;
207 r.reserve(size(str));
208
209 // Do not start with a space.
210 size_t space_count = 1;
211 size_t letter_count = 0;
212 for (ttlet c : str) {
213 if (is_alpha_num(c)) {
214 if (is_digit(c)) {
215 r += c;
216 } else if (letter_count++ == 0) {
217 r += to_upper(c);
218 } else {
219 r += to_lower(c);
220 }
221 space_count = 0;
222
223 } else if (space_count++ == 0) {
224 r += ' ';
225 letter_count = 0;
226 }
227 }
228
229 if (space_count) {
230 // Strip trailing space.
231 r.resize(r.size() - 1);
232 }
233 return r;
234}
235
236[[nodiscard]] constexpr uint32_t fourcc(char const txt[5]) noexcept
237{
238 return (
239 (static_cast<uint32_t>(txt[0]) << 24) | (static_cast<uint32_t>(txt[1]) << 16) | (static_cast<uint32_t>(txt[2]) << 8) |
240 static_cast<uint32_t>(txt[3]));
241}
242
243[[nodiscard]] constexpr uint32_t fourcc(uint8_t const *txt) noexcept
244{
245 return (
246 (static_cast<uint32_t>(txt[0]) << 24) | (static_cast<uint32_t>(txt[1]) << 16) | (static_cast<uint32_t>(txt[2]) << 8) |
247 static_cast<uint32_t>(txt[3]));
248}
249
250[[nodiscard]] inline std::string fourcc_to_string(uint32_t x) noexcept
251{
252 char c_str[5];
253 c_str[0] = narrow_cast<char>((x >> 24) & 0xff);
254 c_str[1] = narrow_cast<char>((x >> 16) & 0xff);
255 c_str[2] = narrow_cast<char>((x >> 8) & 0xff);
256 c_str[3] = narrow_cast<char>(x & 0xff);
257 c_str[4] = 0;
258
259 return {c_str};
260}
261
262constexpr size_t string_size(sizeable auto str) noexcept
263{
264 return std::size(str);
265}
266
267constexpr size_t string_size(auto str) noexcept
268{
269 return 1;
270}
271
272template<typename FirstNeedle, typename... Needles>
273[[nodiscard]] std::pair<size_t, size_t>
274string_find_any(std::string_view haystack, size_t pos, FirstNeedle const &first_needle, Needles const &...needles) noexcept
275{
276 using std::size;
277
278 size_t first = haystack.find(first_needle, pos);
279 size_t last = first + string_size(first_needle);
280
281 if (first == std::string_view::npos) {
282 first = size(haystack);
283 last = size(haystack);
284 }
285
286 if constexpr (sizeof...(Needles) != 0) {
287 ttlet[other_first, other_last] = string_find_any(haystack, pos, needles...);
288 if (other_first < first) {
289 first = other_first;
290 last = other_last;
291 }
292 }
293
294 return {first, last};
295}
296
297template<typename StringType, typename... Needles>
298[[nodiscard]] std::vector<StringType> _split(std::string_view haystack, Needles const &...needles) noexcept
299{
300 auto r = std::vector<StringType>{};
301
302 std::string_view::size_type current_pos = 0;
303
304 while (current_pos < std::size(haystack)) {
305 ttlet[needle_first, needle_last] = string_find_any(haystack, current_pos, needles...);
306 r.push_back(StringType{haystack.substr(current_pos, needle_first - current_pos)});
307 current_pos = needle_last;
308 }
309
310 return r;
311}
312
313template<typename... Needles>
314[[nodiscard]] std::vector<std::string> split(std::string_view haystack, Needles const &...needles) noexcept
315{
316 return _split<std::string>(haystack, needles...);
317}
318
319[[nodiscard]] inline std::vector<std::string> split(std::string_view haystack) noexcept
320{
321 return split(haystack, ' ');
322}
323
324template<typename... Needles>
325[[nodiscard]] std::vector<std::string_view> split_view(std::string_view haystack, Needles const &...needles) noexcept
326{
327 return _split<std::string_view>(haystack, needles...);
328}
329
330[[nodiscard]] inline std::vector<std::string_view> split_view(std::string_view haystack) noexcept
331{
332 return split_view(haystack, ' ');
333}
334
335template<typename CharT>
336[[nodiscard]] std::basic_string<CharT>
337join(std::vector<std::basic_string<CharT>> const &list, std::basic_string_view<CharT> const joiner = {}) noexcept
338{
339 std::string r;
340
341 if (list.size() > 1) {
342 size_t final_size = (list.size() - 1) * joiner.size();
343 for (ttlet &item : list) {
344 final_size += item.size();
345 }
346 r.reserve(final_size);
347 }
348
349 size_t i = 0;
350 for (ttlet &item : list) {
351 if (i++ != 0) {
352 r += joiner;
353 }
354 r += item;
355 }
356 return r;
357}
358
359template<typename CharT>
360[[nodiscard]] std::basic_string<CharT>
361join(std::vector<std::basic_string<CharT>> const &list, std::basic_string<CharT> const &joiner) noexcept
362{
363 return join(list, std::basic_string_view<CharT>{joiner});
364}
365
366template<typename CharT>
367[[nodiscard]] std::basic_string<CharT> join(std::vector<std::basic_string<CharT>> const &list, CharT const *joiner) noexcept
368{
369 return join(list, std::basic_string_view<CharT>{joiner});
370}
371
372[[nodiscard]] inline std::string join(std::vector<std::string_view> const &list, std::string_view const joiner = {}) noexcept
373{
374 std::string r;
375
376 if (list.size() > 1) {
377 size_t final_size = (list.size() - 1) * joiner.size();
378 for (ttlet &item : list) {
379 final_size += item.size();
380 }
381 r.reserve(final_size);
382 }
383
384 int64_t i = 0;
385 for (ttlet &item : list) {
386 if (i++ > 0) {
387 r += joiner;
388 }
389 r += item;
390 }
391 return r;
392}
393
396template<typename It>
397[[nodiscard]] inline std::pair<int, int> count_line_and_columns(It begin, It const end)
398{
399 int line = 1;
400 int column = 1;
401
402 for (; begin != end; begin++) {
403 switch (*begin) {
404 case '\n': line++; [[fallthrough]];
405 case '\r': column = 1; break;
406 case '\t': column = ((((column - 1) / 8) + 1) * 8) + 1; break;
407 default: column++;
408 }
409 }
410 return {line, column};
411}
412
416template<typename T, size_t N>
417constexpr auto to_array_without_last(T (&rhs)[N]) noexcept
418{
419 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
420 for (size_t i = 0; i != (N - 1); ++i) {
421 r[i] = rhs[i];
422 }
423 return r;
424}
425
429template<typename T, size_t N>
430constexpr auto to_array_without_last(T(&&rhs)[N]) noexcept
431{
432 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
433 for (size_t i = 0; i != (N - 1); ++i) {
434 r[i] = std::move(rhs[i]);
435 }
436 return r;
437}
438
439[[nodiscard]] inline std::string lstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
440{
441 auto first = front_strip(std::begin(haystack), std::end(haystack), std::begin(needle), std::end(needle));
442 return std::string{first, std::end(haystack)};
443}
444
445[[nodiscard]] inline std::string rstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
446{
447 auto last = back_strip(std::begin(haystack), std::end(haystack), std::begin(needle), std::end(needle));
448 return std::string{std::begin(haystack), last};
449}
450
451[[nodiscard]] inline std::string strip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
452{
453 auto first = front_strip(std::begin(haystack), std::end(haystack), std::begin(needle), std::end(needle));
454 auto last = back_strip(first, std::end(haystack), std::begin(needle), std::end(needle));
455 return std::string{first, last};
456}
457
465[[nodiscard]] inline std::vector<std::string> ZZWSTR_to_string(wchar_t *first, wchar_t *last, ssize_t nr_strings = -1)
466{
467 auto r = std::vector<std::string>{};
468
469 while (first != last) {
470 auto it_zero = std::find(first, last, wchar_t{0});
471 if (it_zero == last) {
472 throw parse_error("Could not find terminating zero of a string.");
473 }
474
475 auto ws = std::wstring_view{first, narrow_cast<size_t>(it_zero - first)};
476 if (ws.empty()) {
477 // The list is terminated with an empty string.
478 break;
479 }
480
481 r.push_back(tt::to_string(ws));
482
483 // Continue after the zero terminator.
484 first = it_zero + 1;
485 }
486
487 if (nr_strings != -1 && std::ssize(r) != nr_strings) {
488 throw parse_error("Unexpected number of string in list.");
489 }
490
491 return r;
492}
493
497[[nodiscard]] inline char *make_cstr(char const *c_str, size_t size = -1) noexcept
498{
499 if (size == -1) {
500 size = std::strlen(c_str);
501 }
502
503 auto r = new char[size + 1];
504 std::memcpy(r, c_str, size + 1);
505 return r;
506}
507
511[[nodiscard]] inline char *make_cstr(std::string const &s) noexcept
512{
513 return make_cstr(s.c_str(), s.size());
514}
515
516} // namespace tt
T begin(T... args)
T end(T... args)
T find(T... args)
T memcpy(T... args)
T move(T... args)
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)
T strlen(T... args)
T ws(T... args)