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 "../utility/utility.hpp"
8#include "../char_maps/char_maps.hpp"
9#include "algorithm_misc.hpp"
10#include "../macros.hpp"
11#include <string>
12#include <string_view>
13#include <iterator>
14#include <vector>
15#include <tuple>
16#include <type_traits>
17#include <cstdlib>
18#include <bit>
19
20hi_export_module(hikogui.algorithm.strings);
21
22hi_warning_push();
23// C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
24// make_cstr() is used for arguments passed to tt_main() for compatibility we need to create
25// those using new/delete.
26hi_warning_ignore_msvc(26409);
27
28hi_export namespace hi::inline v1 {
29
30[[nodiscard]] constexpr bool is_upper(char c) noexcept
31{
32 return c >= 'A' && c <= 'Z';
33}
34
35[[nodiscard]] constexpr bool is_lower(char c) noexcept
36{
37 return c >= 'a' && c <= 'z';
38}
39
40[[nodiscard]] constexpr bool is_alpha(char c) noexcept
41{
42 return is_upper(c) || is_lower(c);
43}
44
45[[nodiscard]] constexpr bool is_digit(char c) noexcept
46{
47 return c >= '0' && c <= '9';
48}
49
50[[nodiscard]] constexpr bool is_alpha_num(char c) noexcept
51{
52 return is_alpha(c) || is_digit(c);
53}
54
55[[nodiscard]] constexpr bool is_line_feed(char c) noexcept
56{
57 return c == '\r' || c == '\n' || c == '\f' || c == '\v';
58}
59
60[[nodiscard]] constexpr bool is_white_space(char c) noexcept
61{
62 return c == ' ' || c == '\t' || is_line_feed(c);
63}
64
65[[nodiscard]] constexpr bool is_number_first(char c) noexcept
66{
67 return is_digit(c) || c == '+' || c == '-';
68}
69
70[[nodiscard]] constexpr bool is_name_first(char c) noexcept
71{
72 return is_alpha(c) or c == '_' or c == '$' or (c & 0x80);
73}
74
75[[nodiscard]] constexpr bool is_name_next(char c) noexcept
76{
77 return is_alpha_num(c) or c == '_' or c == '$' or (c & 0x80);
78}
79
80[[nodiscard]] constexpr bool is_quote(char c) noexcept
81{
82 return c == '"' || c == '\'' || c == '`';
83}
84
85[[nodiscard]] constexpr bool is_open_bracket(char c) noexcept
86{
87 return c == '(' || c == '{' || c == '[';
88}
89
90[[nodiscard]] constexpr bool is_close_bracket(char c) noexcept
91{
92 return c == ')' || c == '}' || c == ']';
93}
94
95[[nodiscard]] constexpr bool is_operator(char c) noexcept
96{
97 return !is_alpha_num(c) && c != '_' && !is_white_space(c) && !is_quote(c) && !is_open_bracket(c) && !is_close_bracket(c);
98}
99
100[[nodiscard]] constexpr bool is_digit(std::string_view str) noexcept
101{
102 for (auto const c : str) {
103 if (not is_digit(c)) {
104 return false;
105 }
106 }
107 return true;
108}
109
110[[nodiscard]] constexpr bool is_alpha(std::string_view str) noexcept
111{
112 for (auto const c : str) {
113 if (not is_alpha(c)) {
114 return false;
115 }
116 }
117 return true;
118}
119
120[[nodiscard]] constexpr char to_lower(char c) noexcept
121{
122 return (c >= 'A' and c <= 'Z') ? (c - 'A') + 'a' : c;
123}
124
125[[nodiscard]] constexpr char to_upper(char c) noexcept
126{
127 return (c >= 'a' and c <= 'z') ? (c - 'a') + 'A' : c;
128}
129
130[[nodiscard]] inline std::string to_lower(std::string_view str) noexcept
131{
132 std::string r;
133 r.reserve(size(str));
134
135 for (auto const c : str) {
136 r += to_lower(c);
137 }
138
139 return r;
140}
141
142[[nodiscard]] inline std::string to_upper(std::string_view str) noexcept
143{
144 std::string r;
145 r.reserve(size(str));
146
147 for (auto const c : str) {
148 r += to_upper(c);
149 }
150
151 return r;
152}
153
159[[nodiscard]] constexpr std::string to_title(std::string_view rhs) noexcept
160{
161 auto r = std::string{rhs};
162
163 bool first = true;
164 for (auto& c : r) {
165 if (first) {
166 c = to_upper(c);
167 first = false;
168 } else if (c == ' ') {
169 first = true;
170 } else {
171 c = to_lower(c);
172 }
173 }
174
175 return r;
176}
177
183template<size_t N>
184[[nodiscard]] constexpr fixed_string<N> to_title(fixed_string<N> const &rhs) noexcept
185{
186 auto r = rhs;
187
188 bool first = true;
189 for (auto &c: r) {
190 if (first) {
191 c = to_upper(c);
192 first = false;
193 } else if (c == ' ') {
194 first = true;
195 } else {
196 c = to_lower(c);
197 }
198 }
199
200 return r;
201}
202
205[[nodiscard]] inline std::string normalize_lf(std::string_view str) noexcept
206{
207 std::string r;
208 r.reserve(size(str));
209
210 auto found_cr = false;
211 for (auto const c : str) {
212 if (found_cr) {
213 // This is Microsoft or old-Apple, we replace the previous carriage-return
214 // with a line-feed and emit the current character.
215 [[unlikely]] r += '\n';
216 if (c != '\r' && c != '\n') {
217 r += c;
218 }
219
220 } else if (c != '\r') {
221 // Emit any non-carriage return character.
222 [[likely]] r += c;
223 }
224
225 found_cr = c == '\r';
226 }
227 if (found_cr) {
228 r += '\n';
229 }
230
231 return r;
232}
233
237[[nodiscard]] inline std::string make_identifier(std::string_view str) noexcept
238{
239 std::string r;
240 r.reserve(size(str));
241
242 r += is_name_first(str.front()) ? str.front() : '_';
243 for (auto const c : str.substr(1)) {
244 r += is_name_next(c) ? c : '_';
245 }
246
247 return r;
248}
249
254[[nodiscard]] constexpr std::string make_slug(std::string_view str) noexcept
255{
256 std::string r;
257 r.reserve(size(str));
258
259 std::size_t dash_count = 0;
260 for (auto const c : str) {
261 if (is_alpha_num(c)) {
262 dash_count = 0;
263 r += to_lower(c);
264 } else if (dash_count++ == 0) {
265 r += '-';
266 }
267 }
268
269 return r;
270}
271
272[[nodiscard]] constexpr bool is_slug(std::string_view str) noexcept
273{
274 for (auto const c : str) {
275 if (not (is_alpha_num(c) or c == '-')) {
276 return false;
277 }
278 }
279 return true;
280}
281
288[[nodiscard]] inline std::string make_title(std::string_view str) noexcept
289{
290 std::string r;
291 r.reserve(size(str));
292
293 // Do not start with a space.
294 std::size_t space_count = 1;
295 std::size_t letter_count = 0;
296 for (auto const c : str) {
297 if (is_alpha_num(c)) {
298 if (is_digit(c)) {
299 r += c;
300 } else if (letter_count++ == 0) {
301 r += to_upper(c);
302 } else {
303 r += to_lower(c);
304 }
305 space_count = 0;
306
307 } else if (space_count++ == 0) {
308 r += ' ';
309 letter_count = 0;
310 }
311 }
312
313 if (space_count) {
314 // Strip trailing space.
315 r.resize(r.size() - 1);
316 }
317 return r;
318}
319
320template<typename T, size_t N>
321[[nodiscard]] constexpr uint32_t fourcc(T const (&txt)[N]) noexcept requires(sizeof(T) == 1 and (N == 4 or N == 5))
322{
323 auto r = uint32_t{};
324 r |= truncate<uint8_t>(txt[0]);
325 r <<= 8;
326 r |= truncate<uint8_t>(txt[1]);
327 r <<= 8;
328 r |= truncate<uint8_t>(txt[2]);
329 r <<= 8;
330 r |= truncate<uint8_t>(txt[3]);
331
332 if constexpr (N == 5) {
333 hi_axiom(txt[4] == 0);
334 }
335 return r;
336}
337
338[[nodiscard]] constexpr uint32_t fourcc_from_cstr(char const *txt) noexcept
339{
340 hi_assert_not_null(txt);
341 return
342 (char_cast<uint32_t>(txt[0]) << 24) |
343 (char_cast<uint32_t>(txt[1]) << 16) |
344 (char_cast<uint32_t>(txt[2]) << 8) |
345 char_cast<uint32_t>(txt[3]);
346}
347
348[[nodiscard]] inline std::string fourcc_to_string(uint32_t x) noexcept
349{
350 auto r = std::string{};
351 r += truncate<char>((x >> 24) & 0xff);
352 r += truncate<char>((x >> 16) & 0xff);
353 r += truncate<char>((x >> 8) & 0xff);
354 r += truncate<char>(x & 0xff);
355 return r;
356}
357
358constexpr std::size_t string_size(sizeable auto str) noexcept
359{
360 return size(str);
361}
362
363constexpr std::size_t string_size(auto str) noexcept
364{
365 return 1;
366}
367
368template<typename FirstNeedle, typename... Needles>
370string_find_any(std::string_view haystack, std::size_t pos, FirstNeedle const& first_needle, Needles const&...needles) noexcept
371{
372 using std::size;
373
374 std::size_t first = haystack.find(first_needle, pos);
375 std::size_t last = first + string_size(first_needle);
376
377 if (first == std::string_view::npos) {
378 first = size(haystack);
379 last = size(haystack);
380 }
381
382 if constexpr (sizeof...(Needles) != 0) {
383 auto const[other_first, other_last] = string_find_any(haystack, pos, needles...);
384 if (other_first < first) {
385 first = other_first;
386 last = other_last;
387 }
388 }
389
390 return {first, last};
391}
392
393template<typename StringType, typename... Needles>
394[[nodiscard]] std::vector<StringType> _split(std::string_view haystack, Needles const&...needles) noexcept
395{
396 auto r = std::vector<StringType>{};
397
398 std::string_view::size_type current_pos = 0;
399
400 while (current_pos < size(haystack)) {
401 auto const[needle_first, needle_last] = string_find_any(haystack, current_pos, needles...);
402 r.push_back(StringType{haystack.substr(current_pos, needle_first - current_pos)});
403 current_pos = needle_last;
404 }
405
406 return r;
407}
408
409template<typename... Needles>
410[[nodiscard]] std::vector<std::string> split(std::string_view haystack, Needles const&...needles) noexcept
411{
412 return _split<std::string>(haystack, needles...);
413}
414
415[[nodiscard]] inline std::vector<std::string> split(std::string_view haystack) noexcept
416{
417 return split(haystack, ' ');
418}
419
420template<typename... Needles>
421[[nodiscard]] std::vector<std::string_view> split_view(std::string_view haystack, Needles const&...needles) noexcept
422{
423 return _split<std::string_view>(haystack, needles...);
424}
425
426[[nodiscard]] inline std::vector<std::string_view> split_view(std::string_view haystack) noexcept
427{
428 return split_view(haystack, ' ');
429}
430
431template<typename CharT>
432[[nodiscard]] std::basic_string<CharT>
433join(std::vector<std::basic_string<CharT>> const& list, std::basic_string_view<CharT> const joiner = {}) noexcept
434{
435 std::string r;
436
437 if (list.size() > 1) {
438 std::size_t final_size = (list.size() - 1) * joiner.size();
439 for (auto const& item : list) {
440 final_size += item.size();
441 }
442 r.reserve(final_size);
443 }
444
445 std::size_t i = 0;
446 for (auto const& item : list) {
447 if (i++ != 0) {
448 r += joiner;
449 }
450 r += item;
451 }
452 return r;
453}
454
455template<typename CharT>
456[[nodiscard]] std::basic_string<CharT>
457join(std::vector<std::basic_string<CharT>> const& list, std::basic_string<CharT> const& joiner) noexcept
458{
459 return join(list, std::basic_string_view<CharT>{joiner});
460}
461
462template<typename CharT>
463[[nodiscard]] std::basic_string<CharT> join(std::vector<std::basic_string<CharT>> const& list, CharT const *joiner) noexcept
464{
465 return join(list, std::basic_string_view<CharT>{joiner});
466}
467
468[[nodiscard]] inline std::string join(std::vector<std::string_view> const& list, std::string_view const joiner = {}) noexcept
469{
470 std::string r;
471
472 if (list.size() > 1) {
473 std::size_t final_size = (list.size() - 1) * joiner.size();
474 for (auto const item : list) {
475 final_size += item.size();
476 }
477 r.reserve(final_size);
478 }
479
480 int64_t i = 0;
481 for (auto const item : list) {
482 if (i++ > 0) {
483 r += joiner;
484 }
485 r += item;
486 }
487 return r;
488}
489
492template<typename It>
493[[nodiscard]] inline std::pair<int, int> count_line_and_columns(It begin, It const end)
494{
495 int line = 1;
496 int column = 1;
497
498 for (; begin != end; begin++) {
499 switch (*begin) {
500 case '\n':
501 line++;
502 [[fallthrough]];
503 case '\r':
504 column = 1;
505 break;
506 case '\t':
507 column = ((((column - 1) / 8) + 1) * 8) + 1;
508 break;
509 default:
510 column++;
511 }
512 }
513 return {line, column};
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] = rhs[i];
525 }
526 return r;
527}
528
532template<typename T, std::size_t N>
533constexpr auto to_array_without_last(T(&&rhs)[N]) noexcept
534{
535 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
536 for (std::size_t i = 0; i != (N - 1); ++i) {
537 r[i] = std::move(rhs[i]);
538 }
539 return r;
540}
541
542[[nodiscard]] inline std::string lstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
543{
544 auto first = front_strip(begin(haystack), end(haystack), begin(needle), end(needle));
545 return std::string{first, end(haystack)};
546}
547
548[[nodiscard]] inline std::string rstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
549{
550 auto last = back_strip(begin(haystack), end(haystack), begin(needle), end(needle));
551 return std::string{begin(haystack), last};
552}
553
554[[nodiscard]] inline std::string strip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
555{
556 auto first = front_strip(begin(haystack), end(haystack), begin(needle), end(needle));
557 auto last = back_strip(first, end(haystack), begin(needle), end(needle));
558 return std::string{first, last};
559}
560
561} // namespace hi::inline v1
562
563hi_warning_pop();
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
constexpr std::string make_slug(std::string_view str) noexcept
Create a slug from a string.
Definition strings.hpp:254
constexpr std::string to_title(std::string_view rhs) noexcept
Convert the current string to using title case.
Definition strings.hpp:159
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:520
DataIt front_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the front side.
Definition algorithm_misc.hpp:327
std::string make_title(std::string_view str) noexcept
Create a title from a string.
Definition strings.hpp:288
std::string make_identifier(std::string_view str) noexcept
Encode a string to be usable as an id.
Definition strings.hpp:237
DataIt back_strip(DataIt data_first, DataIt data_last, ValueIt value_first, ValueIt value_last) noexcept
Strip data from the back side.
Definition algorithm_misc.hpp:348
std::string normalize_lf(std::string_view str) noexcept
Normalize string to use only line-feeds.
Definition strings.hpp:205
std::pair< int, int > count_line_and_columns(It begin, It const end)
Definition strings.hpp:493
T begin(T... args)
T end(T... args)
T move(T... args)
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)