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 "unicode/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 hi::inline v1 {
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) or c == '_' or c == '$' or (c & 0x80);
68}
69
70[[nodiscard]] constexpr bool is_name_next(char c) noexcept
71{
72 return is_alpha_num(c) or c == '_' or c == '$' or (c & 0x80);
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 bool is_digit(std::string_view str) noexcept
96{
97 for (hilet c: str) {
98 if (not is_digit(c)) {
99 return false;
100 }
101 }
102 return true;
103}
104
105[[nodiscard]] constexpr bool is_alpha(std::string_view str) noexcept
106{
107 for (hilet c : str) {
108 if (not is_alpha(c)) {
109 return false;
110 }
111 }
112 return true;
113}
114
115[[nodiscard]] constexpr char to_lower(char c) noexcept
116{
117 return (c >= 'A' and c <= 'Z') ? (c - 'A') + 'a' : c;
118}
119
120[[nodiscard]] constexpr char to_upper(char c) noexcept
121{
122 return (c >= 'a' and c <= 'z') ? (c - 'a') + 'A' : c;
123}
124
125[[nodiscard]] inline std::string to_lower(std::string_view str) noexcept
126{
127 std::string r;
128 r.reserve(size(str));
129
130 for (hilet c : str) {
131 r += to_lower(c);
132 }
133
134 return r;
135}
136
137[[nodiscard]] inline std::string to_upper(std::string_view str) noexcept
138{
139 std::string r;
140 r.reserve(size(str));
141
142 for (hilet c : str) {
143 r += to_upper(c);
144 }
145
146 return r;
147}
148
151[[nodiscard]] inline std::string normalize_lf(std::string_view str) noexcept
152{
153 std::string r;
154 r.reserve(size(str));
155
156 auto found_cr = false;
157 for (hilet c : str) {
158 if (found_cr) {
159 // This is Microsoft or old-Apple, we replace the previous carriage-return
160 // with a line-feed and emit the current character.
161 [[unlikely]] r += '\n';
162 if (c != '\r' && c != '\n') {
163 r += c;
164 }
165
166 } else if (c != '\r') {
167 // Emit any non-carriage return character.
168 [[likely]] r += c;
169 }
170
171 found_cr = c == '\r';
172 }
173 if (found_cr) {
174 r += '\n';
175 }
176
177 return r;
178}
179
183[[nodiscard]] inline std::string make_identifier(std::string_view str) noexcept
184{
185 std::string r;
186 r.reserve(size(str));
187
188 r += is_name_first(str.front()) ? str.front() : '_';
189 for (hilet c : str.substr(1)) {
190 r += is_name_next(c) ? c : '_';
191 }
192
193 return r;
194}
195
200[[nodiscard]] inline std::string make_slug(std::string_view str) noexcept
201{
202 std::string r;
203 r.reserve(size(str));
204
205 std::size_t dash_count = 0;
206 for (hilet c : str) {
207 if (is_alpha_num(c)) {
208 dash_count = 0;
209 r += to_lower(c);
210 } else if (dash_count++ == 0) {
211 r += '-';
212 }
213 }
214
215 return r;
216}
217
224[[nodiscard]] inline std::string make_title(std::string_view str) noexcept
225{
226 std::string r;
227 r.reserve(size(str));
228
229 // Do not start with a space.
230 std::size_t space_count = 1;
231 std::size_t letter_count = 0;
232 for (hilet c : str) {
233 if (is_alpha_num(c)) {
234 if (is_digit(c)) {
235 r += c;
236 } else if (letter_count++ == 0) {
237 r += to_upper(c);
238 } else {
239 r += to_lower(c);
240 }
241 space_count = 0;
242
243 } else if (space_count++ == 0) {
244 r += ' ';
245 letter_count = 0;
246 }
247 }
248
249 if (space_count) {
250 // Strip trailing space.
251 r.resize(r.size() - 1);
252 }
253 return r;
254}
255
256[[nodiscard]] constexpr uint32_t fourcc(char const txt[5]) noexcept
257{
258 return (
259 (static_cast<uint32_t>(txt[0]) << 24) | (static_cast<uint32_t>(txt[1]) << 16) | (static_cast<uint32_t>(txt[2]) << 8) |
260 static_cast<uint32_t>(txt[3]));
261}
262
263[[nodiscard]] constexpr uint32_t fourcc(uint8_t const *txt) noexcept
264{
265 return (
266 (static_cast<uint32_t>(txt[0]) << 24) | (static_cast<uint32_t>(txt[1]) << 16) | (static_cast<uint32_t>(txt[2]) << 8) |
267 static_cast<uint32_t>(txt[3]));
268}
269
270[[nodiscard]] inline std::string fourcc_to_string(uint32_t x) noexcept
271{
272 char c_str[5];
273 c_str[0] = static_cast<char>((x >> 24) & 0xff);
274 c_str[1] = static_cast<char>((x >> 16) & 0xff);
275 c_str[2] = static_cast<char>((x >> 8) & 0xff);
276 c_str[3] = static_cast<char>(x & 0xff);
277 c_str[4] = 0;
278
279 return {c_str};
280}
281
282constexpr std::size_t string_size(sizeable auto str) noexcept
283{
284 return size(str);
285}
286
287constexpr std::size_t string_size(auto str) noexcept
288{
289 return 1;
290}
291
292template<typename FirstNeedle, typename... Needles>
294string_find_any(std::string_view haystack, std::size_t pos, FirstNeedle const &first_needle, Needles const &...needles) noexcept
295{
296 using std::size;
297
298 std::size_t first = haystack.find(first_needle, pos);
299 std::size_t last = first + string_size(first_needle);
300
301 if (first == std::string_view::npos) {
302 first = size(haystack);
303 last = size(haystack);
304 }
305
306 if constexpr (sizeof...(Needles) != 0) {
307 hilet[other_first, other_last] = string_find_any(haystack, pos, needles...);
308 if (other_first < first) {
309 first = other_first;
310 last = other_last;
311 }
312 }
313
314 return {first, last};
315}
316
317template<typename StringType, typename... Needles>
318[[nodiscard]] std::vector<StringType> _split(std::string_view haystack, Needles const &...needles) noexcept
319{
320 auto r = std::vector<StringType>{};
321
322 std::string_view::size_type current_pos = 0;
323
324 while (current_pos < size(haystack)) {
325 hilet[needle_first, needle_last] = string_find_any(haystack, current_pos, needles...);
326 r.push_back(StringType{haystack.substr(current_pos, needle_first - current_pos)});
327 current_pos = needle_last;
328 }
329
330 return r;
331}
332
333template<typename... Needles>
334[[nodiscard]] std::vector<std::string> split(std::string_view haystack, Needles const &...needles) noexcept
335{
336 return _split<std::string>(haystack, needles...);
337}
338
339[[nodiscard]] inline std::vector<std::string> split(std::string_view haystack) noexcept
340{
341 return split(haystack, ' ');
342}
343
344template<typename... Needles>
345[[nodiscard]] std::vector<std::string_view> split_view(std::string_view haystack, Needles const &...needles) noexcept
346{
347 return _split<std::string_view>(haystack, needles...);
348}
349
350[[nodiscard]] inline std::vector<std::string_view> split_view(std::string_view haystack) noexcept
351{
352 return split_view(haystack, ' ');
353}
354
355template<typename CharT>
356[[nodiscard]] std::basic_string<CharT>
357join(std::vector<std::basic_string<CharT>> const &list, std::basic_string_view<CharT> const joiner = {}) noexcept
358{
359 std::string r;
360
361 if (list.size() > 1) {
362 std::size_t final_size = (list.size() - 1) * joiner.size();
363 for (hilet &item : list) {
364 final_size += item.size();
365 }
366 r.reserve(final_size);
367 }
368
369 std::size_t i = 0;
370 for (hilet &item : list) {
371 if (i++ != 0) {
372 r += joiner;
373 }
374 r += item;
375 }
376 return r;
377}
378
379template<typename CharT>
380[[nodiscard]] std::basic_string<CharT>
381join(std::vector<std::basic_string<CharT>> const &list, std::basic_string<CharT> const &joiner) noexcept
382{
383 return join(list, std::basic_string_view<CharT>{joiner});
384}
385
386template<typename CharT>
387[[nodiscard]] std::basic_string<CharT> join(std::vector<std::basic_string<CharT>> const &list, CharT const *joiner) noexcept
388{
389 return join(list, std::basic_string_view<CharT>{joiner});
390}
391
392[[nodiscard]] inline std::string join(std::vector<std::string_view> const &list, std::string_view const joiner = {}) noexcept
393{
394 std::string r;
395
396 if (list.size() > 1) {
397 std::size_t final_size = (list.size() - 1) * joiner.size();
398 for (hilet &item : list) {
399 final_size += item.size();
400 }
401 r.reserve(final_size);
402 }
403
404 int64_t i = 0;
405 for (hilet &item : list) {
406 if (i++ > 0) {
407 r += joiner;
408 }
409 r += item;
410 }
411 return r;
412}
413
416template<typename It>
417[[nodiscard]] inline std::pair<int, int> count_line_and_columns(It begin, It const end)
418{
419 int line = 1;
420 int column = 1;
421
422 for (; begin != end; begin++) {
423 switch (*begin) {
424 case '\n': line++; [[fallthrough]];
425 case '\r': column = 1; break;
426 case '\t': column = ((((column - 1) / 8) + 1) * 8) + 1; break;
427 default: column++;
428 }
429 }
430 return {line, column};
431}
432
436template<typename T, std::size_t N>
437constexpr auto to_array_without_last(T (&rhs)[N]) noexcept
438{
439 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
440 for (std::size_t i = 0; i != (N - 1); ++i) {
441 r[i] = rhs[i];
442 }
443 return r;
444}
445
449template<typename T, std::size_t N>
450constexpr auto to_array_without_last(T(&&rhs)[N]) noexcept
451{
452 auto r = std::array<std::remove_cv_t<T>, N - 1>{};
453 for (std::size_t i = 0; i != (N - 1); ++i) {
454 r[i] = std::move(rhs[i]);
455 }
456 return r;
457}
458
459[[nodiscard]] inline std::string lstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
460{
461 auto first = front_strip(begin(haystack), end(haystack), begin(needle), end(needle));
462 return std::string{first, end(haystack)};
463}
464
465[[nodiscard]] inline std::string rstrip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
466{
467 auto last = back_strip(begin(haystack), end(haystack), begin(needle), end(needle));
468 return std::string{begin(haystack), last};
469}
470
471[[nodiscard]] inline std::string strip(std::string_view haystack, std::string needle = " \t\r\n\f") noexcept
472{
473 auto first = front_strip(begin(haystack), end(haystack), begin(needle), end(needle));
474 auto last = back_strip(first, end(haystack), begin(needle), end(needle));
475 return std::string{first, last};
476}
477
485[[nodiscard]] inline std::vector<std::string> ZZWSTR_to_string(wchar_t *first, wchar_t *last, ssize_t nr_strings = -1)
486{
487 auto r = std::vector<std::string>{};
488
489 while (first != last) {
490 auto it_zero = std::find(first, last, wchar_t{0});
491 if (it_zero == last) {
492 throw parse_error("Could not find terminating zero of a string.");
493 }
494
495 auto ws = std::wstring_view{first, narrow_cast<std::size_t>(it_zero - first)};
496 if (ws.empty()) {
497 // The list is terminated with an empty string.
498 break;
499 }
500
501 r.push_back(hi::to_string(ws));
502
503 // Continue after the zero terminator.
504 first = it_zero + 1;
505 }
506
507 if (nr_strings != -1 && ssize(r) != nr_strings) {
508 throw parse_error("Unexpected number of string in list.");
509 }
510
511 return r;
512}
513
517[[nodiscard]] inline char *make_cstr(char const *c_str, std::size_t size = -1) noexcept
518{
519 if (size == -1) {
520 size = std::strlen(c_str);
521 }
522
523 auto r = new char[size + 1];
524 std::memcpy(r, c_str, size + 1);
525 return r;
526}
527
531[[nodiscard]] inline char *make_cstr(std::string const &s) noexcept
532{
533 return make_cstr(s.c_str(), s.size());
534}
535
536} // namespace hi::inline v1
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
Functions and macros for handling architectural difference between compilers, CPUs and operating syst...
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)