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