HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
strings.hpp
1// Copyright Take Vos 2019-2022.
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 "char_maps/module.hpp"
8#include "algorithm.hpp"
9#include "utility/module.hpp"
10#include <string>
11#include <string_view>
12#include <iterator>
13#include <vector>
14#include <tuple>
15#include <type_traits>
16#include <cstdlib>
17#include <bit>
18
19hi_warning_push();
20// C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
21// make_cstr() is used for arguments passed to tt_main() for compatibility we need to create
22// those using new/delete.
23hi_warning_ignore_msvc(26409);
24
25namespace hi::inline v1 {
26
27[[nodiscard]] constexpr bool is_upper(char c) noexcept
28{
29 return c >= 'A' && c <= 'Z';
30}
31
32[[nodiscard]] constexpr bool is_lower(char c) noexcept
33{
34 return c >= 'a' && c <= 'z';
35}
36
37[[nodiscard]] constexpr bool is_alpha(char c) noexcept
38{
39 return is_upper(c) || is_lower(c);
40}
41
42[[nodiscard]] constexpr bool is_digit(char c) noexcept
43{
44 return c >= '0' && c <= '9';
45}
46
47[[nodiscard]] constexpr bool is_alpha_num(char c) noexcept
48{
49 return is_alpha(c) || is_digit(c);
50}
51
52[[nodiscard]] constexpr bool is_line_feed(char c) noexcept
53{
54 return c == '\r' || c == '\n' || c == '\f' || c == '\v';
55}
56
57[[nodiscard]] constexpr bool is_white_space(char c) noexcept
58{
59 return c == ' ' || c == '\t' || is_line_feed(c);
60}
61
62[[nodiscard]] constexpr bool is_number_first(char c) noexcept
63{
64 return is_digit(c) || c == '+' || c == '-';
65}
66
67[[nodiscard]] constexpr bool is_name_first(char c) noexcept
68{
69 return is_alpha(c) or c == '_' or c == '$' or (c & 0x80);
70}
71
72[[nodiscard]] constexpr bool is_name_next(char c) noexcept
73{
74 return is_alpha_num(c) or c == '_' or c == '$' or (c & 0x80);
75}
76
77[[nodiscard]] constexpr bool is_quote(char c) noexcept
78{
79 return c == '"' || c == '\'' || c == '`';
80}
81
82[[nodiscard]] constexpr bool is_open_bracket(char c) noexcept
83{
84 return c == '(' || c == '{' || c == '[';
85}
86
87[[nodiscard]] constexpr bool is_close_bracket(char c) noexcept
88{
89 return c == ')' || c == '}' || c == ']';
90}
91
92[[nodiscard]] constexpr bool is_operator(char c) noexcept
93{
94 return !is_alpha_num(c) && c != '_' && !is_white_space(c) && !is_quote(c) && !is_open_bracket(c) && !is_close_bracket(c);
95}
96
97[[nodiscard]] constexpr bool is_digit(std::string_view str) noexcept
98{
99 for (hilet c : str) {
100 if (not is_digit(c)) {
101 return false;
102 }
103 }
104 return true;
105}
106
107[[nodiscard]] constexpr bool is_alpha(std::string_view str) noexcept
108{
109 for (hilet c : str) {
110 if (not is_alpha(c)) {
111 return false;
112 }
113 }
114 return true;
115}
116
117[[nodiscard]] constexpr char to_lower(char c) noexcept
118{
119 return (c >= 'A' and c <= 'Z') ? (c - 'A') + 'a' : c;
120}
121
122[[nodiscard]] constexpr char to_upper(char c) noexcept
123{
124 return (c >= 'a' and c <= 'z') ? (c - 'a') + 'A' : c;
125}
126
127[[nodiscard]] inline std::string to_lower(std::string_view str) noexcept
128{
129 std::string r;
130 r.reserve(size(str));
131
132 for (hilet c : str) {
133 r += to_lower(c);
134 }
135
136 return r;
137}
138
139[[nodiscard]] inline std::string to_upper(std::string_view str) noexcept
140{
141 std::string r;
142 r.reserve(size(str));
143
144 for (hilet c : str) {
145 r += to_upper(c);
146 }
147
148 return r;
149}
150
156[[nodiscard]] constexpr std::string to_title(std::string_view rhs) noexcept
157{
158 auto r = std::string{rhs};
159
160 bool first = true;
161 for (auto& c : r) {
162 if (first) {
163 c = to_upper(c);
164 first = false;
165 } else if (c == ' ') {
166 first = true;
167 } else {
168 c = to_lower(c);
169 }
170 }
171
172 return r;
173}
174
180template<size_t N>
181[[nodiscard]] constexpr fixed_string<N> to_title(fixed_string<N> const &rhs) noexcept
182{
183 auto r = rhs;
184
185 bool first = true;
186 for (auto &c: r) {
187 if (first) {
188 c = to_upper(c);
189 first = false;
190 } else if (c == ' ') {
191 first = true;
192 } else {
193 c = to_lower(c);
194 }
195 }
196
197 return r;
198}
199
202[[nodiscard]] inline std::string normalize_lf(std::string_view str) noexcept
203{
204 std::string r;
205 r.reserve(size(str));
206
207 auto found_cr = false;
208 for (hilet c : str) {
209 if (found_cr) {
210 // This is Microsoft or old-Apple, we replace the previous carriage-return
211 // with a line-feed and emit the current character.
212 [[unlikely]] r += '\n';
213 if (c != '\r' && c != '\n') {
214 r += c;
215 }
216
217 } else if (c != '\r') {
218 // Emit any non-carriage return character.
219 [[likely]] r += c;
220 }
221
222 found_cr = c == '\r';
223 }
224 if (found_cr) {
225 r += '\n';
226 }
227
228 return r;
229}
230
234[[nodiscard]] inline std::string make_identifier(std::string_view str) noexcept
235{
236 std::string r;
237 r.reserve(size(str));
238
239 r += is_name_first(str.front()) ? str.front() : '_';
240 for (hilet c : str.substr(1)) {
241 r += is_name_next(c) ? c : '_';
242 }
243
244 return r;
245}
246
251[[nodiscard]] inline std::string make_slug(std::string_view str) noexcept
252{
253 std::string r;
254 r.reserve(size(str));
255
256 std::size_t dash_count = 0;
257 for (hilet c : str) {
258 if (is_alpha_num(c)) {
259 dash_count = 0;
260 r += to_lower(c);
261 } else if (dash_count++ == 0) {
262 r += '-';
263 }
264 }
265
266 return r;
267}
268
275[[nodiscard]] inline std::string make_title(std::string_view str) noexcept
276{
277 std::string r;
278 r.reserve(size(str));
279
280 // Do not start with a space.
281 std::size_t space_count = 1;
282 std::size_t letter_count = 0;
283 for (hilet c : str) {
284 if (is_alpha_num(c)) {
285 if (is_digit(c)) {
286 r += c;
287 } else if (letter_count++ == 0) {
288 r += to_upper(c);
289 } else {
290 r += to_lower(c);
291 }
292 space_count = 0;
293
294 } else if (space_count++ == 0) {
295 r += ' ';
296 letter_count = 0;
297 }
298 }
299
300 if (space_count) {
301 // Strip trailing space.
302 r.resize(r.size() - 1);
303 }
304 return r;
305}
306
307template<typename T, size_t N>
308[[nodiscard]] constexpr uint32_t fourcc(T const (&txt)[N]) noexcept requires(sizeof(T) == 1 and (N == 4 or N == 5))
309{
310 auto r = uint32_t{};
311 r |= truncate<uint8_t>(txt[0]);
312 r <<= 8;
313 r |= truncate<uint8_t>(txt[1]);
314 r <<= 8;
315 r |= truncate<uint8_t>(txt[2]);
316 r <<= 8;
317 r |= truncate<uint8_t>(txt[3]);
318
319 if constexpr (N == 5) {
320 hi_axiom(txt[4] == 0);
321 }
322 return r;
323}
324
325[[nodiscard]] constexpr uint32_t fourcc_from_cstr(char const *txt) noexcept
326{
328 return
329 (char_cast<uint32_t>(txt[0]) << 24) |
330 (char_cast<uint32_t>(txt[1]) << 16) |
331 (char_cast<uint32_t>(txt[2]) << 8) |
332 char_cast<uint32_t>(txt[3]);
333}
334
335[[nodiscard]] inline std::string fourcc_to_string(uint32_t x) noexcept
336{
337 auto r = std::string{};
338 r += truncate<char>((x >> 24) & 0xff);
339 r += truncate<char>((x >> 16) & 0xff);
340 r += truncate<char>((x >> 8) & 0xff);
341 r += truncate<char>(x & 0xff);
342 return r;
343}
344
345constexpr std::size_t string_size(sizeable auto str) noexcept
346{
347 return size(str);
348}
349
350constexpr std::size_t string_size(auto str) noexcept
351{
352 return 1;
353}
354
355template<typename FirstNeedle, typename... Needles>
357string_find_any(std::string_view haystack, std::size_t pos, FirstNeedle const& first_needle, Needles const&...needles) noexcept
358{
359 using std::size;
360
361 std::size_t first = haystack.find(first_needle, pos);
362 std::size_t last = first + string_size(first_needle);
363
364 if (first == std::string_view::npos) {
365 first = size(haystack);
366 last = size(haystack);
367 }
368
369 if constexpr (sizeof...(Needles) != 0) {
370 hilet[other_first, other_last] = string_find_any(haystack, pos, needles...);
371 if (other_first < first) {
372 first = other_first;
373 last = other_last;
374 }
375 }
376
377 return {first, last};
378}
379
380template<typename StringType, typename... Needles>
381[[nodiscard]] std::vector<StringType> _split(std::string_view haystack, Needles const&...needles) noexcept
382{
383 auto r = std::vector<StringType>{};
384
385 std::string_view::size_type current_pos = 0;
386
387 while (current_pos < size(haystack)) {
388 hilet[needle_first, needle_last] = string_find_any(haystack, current_pos, needles...);
389 r.push_back(StringType{haystack.substr(current_pos, needle_first - current_pos)});
390 current_pos = needle_last;
391 }
392
393 return r;
394}
395
396template<typename... Needles>
397[[nodiscard]] std::vector<std::string> split(std::string_view haystack, Needles const&...needles) noexcept
398{
399 return _split<std::string>(haystack, needles...);
400}
401
402[[nodiscard]] inline std::vector<std::string> split(std::string_view haystack) noexcept
403{
404 return split(haystack, ' ');
405}
406
407template<typename... Needles>
408[[nodiscard]] std::vector<std::string_view> split_view(std::string_view haystack, Needles const&...needles) noexcept
409{
410 return _split<std::string_view>(haystack, needles...);
411}
412
413[[nodiscard]] inline std::vector<std::string_view> split_view(std::string_view haystack) noexcept
414{
415 return split_view(haystack, ' ');
416}
417
418template<typename CharT>
419[[nodiscard]] std::basic_string<CharT>
420join(std::vector<std::basic_string<CharT>> const& list, std::basic_string_view<CharT> const joiner = {}) noexcept
421{
422 std::string r;
423
424 if (list.size() > 1) {
425 std::size_t final_size = (list.size() - 1) * joiner.size();
426 for (hilet& item : list) {
427 final_size += item.size();
428 }
429 r.reserve(final_size);
430 }
431
432 std::size_t i = 0;
433 for (hilet& item : list) {
434 if (i++ != 0) {
435 r += joiner;
436 }
437 r += item;
438 }
439 return r;
440}
441
442template<typename CharT>
443[[nodiscard]] std::basic_string<CharT>
444join(std::vector<std::basic_string<CharT>> const& list, std::basic_string<CharT> const& joiner) noexcept
445{
446 return join(list, std::basic_string_view<CharT>{joiner});
447}
448
449template<typename CharT>
450[[nodiscard]] std::basic_string<CharT> join(std::vector<std::basic_string<CharT>> const& list, CharT const *joiner) noexcept
451{
452 return join(list, std::basic_string_view<CharT>{joiner});
453}
454
455[[nodiscard]] inline std::string join(std::vector<std::string_view> const& list, std::string_view const joiner = {}) noexcept
456{
457 std::string r;
458
459 if (list.size() > 1) {
460 std::size_t final_size = (list.size() - 1) * joiner.size();
461 for (hilet item : list) {
462 final_size += item.size();
463 }
464 r.reserve(final_size);
465 }
466
467 int64_t i = 0;
468 for (hilet item : list) {
469 if (i++ > 0) {
470 r += joiner;
471 }
472 r += item;
473 }
474 return r;
475}
476
479template<typename It>
480[[nodiscard]] inline std::pair<int, int> count_line_and_columns(It begin, It const end)
481{
482 int line = 1;
483 int column = 1;
484
485 for (; begin != end; begin++) {
486 switch (*begin) {
487 case '\n':
488 line++;
489 [[fallthrough]];
490 case '\r':
491 column = 1;
492 break;
493 case '\t':
494 column = ((((column - 1) / 8) + 1) * 8) + 1;
495 break;
496 default:
497 column++;
498 }
499 }
500 return {line, column};
501}
502
506template<typename T, std::size_t N>
507constexpr auto to_array_without_last(T (&rhs)[N]) noexcept
508{
509 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
510 for (std::size_t i = 0; i != (N - 1); ++i) {
511 r[i] = rhs[i];
512 }
513 return r;
514}
515
519template<typename T, std::size_t N>
520constexpr auto to_array_without_last(T(&&rhs)[N]) noexcept
521{
522 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
523 for (std::size_t i = 0; i != (N - 1); ++i) {
524 r[i] = std::move(rhs[i]);
525 }
526 return r;
527}
528
529[[nodiscard]] inline std::string lstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
530{
531 auto first = front_strip(begin(haystack), end(haystack), begin(needle), end(needle));
532 return std::string{first, end(haystack)};
533}
534
535[[nodiscard]] inline std::string rstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
536{
537 auto last = back_strip(begin(haystack), end(haystack), begin(needle), end(needle));
538 return std::string{begin(haystack), last};
539}
540
541[[nodiscard]] inline std::string strip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
542{
543 auto first = front_strip(begin(haystack), end(haystack), begin(needle), end(needle));
544 auto last = back_strip(first, end(haystack), begin(needle), end(needle));
545 return std::string{first, last};
546}
547
555[[nodiscard]] inline std::vector<std::string> ZZWSTR_to_string(wchar_t *first, wchar_t *last, ssize_t nr_strings = -1)
556{
557 auto r = std::vector<std::string>{};
558
559 while (first != last) {
560 auto it_zero = std::find(first, last, wchar_t{0});
561 if (it_zero == last) {
562 throw parse_error("Could not find terminating zero of a string.");
563 }
564
565 hilet ws = std::wstring_view{first, narrow_cast<std::size_t>(it_zero - first)};
566 if (ws.empty()) {
567 // The list is terminated with an empty string.
568 break;
569 }
570
572
573 // Continue after the zero terminator.
574 first = it_zero + 1;
575 }
576
577 if (nr_strings != -1 && ssize(r) != nr_strings) {
578 throw parse_error("Unexpected number of string in list.");
579 }
580
581 return r;
582}
583
587[[nodiscard]] inline char *make_cstr(char const *c_str, std::size_t size = -1) noexcept
588{
589 if (size == -1) {
590 size = std::strlen(c_str);
591 }
592
593 auto r = new char[size + 1];
594 std::memcpy(r, c_str, size + 1);
595 return r;
596}
597
601[[nodiscard]] inline char *make_cstr(std::string const& s) noexcept
602{
603 return make_cstr(s.c_str(), s.size());
604}
605
606
607
608} // namespace hi::inline v1
609
610hi_warning_pop();
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:238
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:223
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
constexpr std::string to_string(std::u32string_view rhs) noexcept
Conversion from UTF-32 to UTF-8.
Definition to_string.hpp:215
DOXYGEN BUG.
Definition algorithm.hpp:13
constexpr std::string to_title(std::string_view rhs) noexcept
Convert the current string to using title case.
Definition strings.hpp:156
std::string make_slug(std::string_view str) noexcept
Create a slug from a string.
Definition strings.hpp:251
constexpr auto to_array_without_last(T(&rhs)[N]) noexcept
Create an std::array from a one dimensional array, without the last element.
Definition strings.hpp:507
DataIt front_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the front side.
Definition algorithm.hpp:320
char * make_cstr(char const *c_str, std::size_t size=-1) noexcept
Copy a std::string to new memory.
Definition strings.hpp:587
std::vector< std::string > ZZWSTR_to_string(wchar_t *first, wchar_t *last, ssize_t nr_strings=-1)
Convert a win32 zero terminated list of zero terminated strings.
Definition strings.hpp:555
std::string make_title(std::string_view str) noexcept
Create a title from a string.
Definition strings.hpp:275
std::string make_identifier(std::string_view str) noexcept
Encode a string to be usable as an id.
Definition strings.hpp:234
DataIt back_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the back side.
Definition algorithm.hpp:341
std::string normalize_lf(std::string_view str) noexcept
Normalize string to use only line-feeds.
Definition strings.hpp:202
std::pair< int, int > count_line_and_columns(It begin, It const end)
Definition strings.hpp:480
std::ptrdiff_t ssize_t
Signed size/index into an array.
Definition utility.hpp:189
A string which may be used as a none-type template parameter.
Definition fixed_string.hpp:36
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)