41 template <
typename T,
typename CHAR_TYPE =
char>
42 concept string_or_char = std::is_constructible_v<std::basic_string_view<CHAR_TYPE>, T> || std::is_constructible_v<CHAR_TYPE, T>;
46 template <
typename T,
typename CHAR_TYPE =
char>
47 concept stringable = (std::ranges::contiguous_range<T> && std::is_convertible_v<std::ranges::range_value_t<T>, CHAR_TYPE>);
51 concept string8 = std::same_as<T, std::string> || std::same_as<T, std::u8string>;
54 concept stringable8 = std::convertible_to<T, std::string_view> || std::convertible_to<T, std::u8string_view>;
57 concept string_view8 = std::same_as<T, std::string_view> || std::same_as<T, std::u8string_view>;
61 concept string16 = (
sizeof(wchar_t) ==
sizeof(char16_t) && std::same_as<T, std::wstring>) || std::same_as<T, std::u16string>;
64 concept stringable16 = (
sizeof(wchar_t) ==
sizeof(char16_t) && std::convertible_to<T, std::wstring_view>) || std::convertible_to<T, std::u16string_view>;
67 concept string_view16 = (
sizeof(wchar_t) ==
sizeof(char16_t) && std::same_as<T, std::wstring_view>) || std::same_as<T, std::u16string_view>;
71 concept string32 = (
sizeof(wchar_t) ==
sizeof(char32_t) && std::same_as<T, std::wstring>) || std::same_as<T, std::u32string>;
74 concept stringable32 = (
sizeof(wchar_t) ==
sizeof(char32_t) && std::convertible_to<T, std::wstring_view>) || std::convertible_to<T, std::u32string_view>;
77 concept string_view32 = (
sizeof(wchar_t) ==
sizeof(char32_t) && std::same_as<T, std::wstring_view>) || std::same_as<T, std::u32string_view>;
80 using wide_char16_t = std::conditional_t<
sizeof(wchar_t) ==
sizeof(
char16_t), wchar_t,
char16_t>;
83 using wide_char32_t = std::conditional_t<
sizeof(wchar_t) ==
sizeof(
char32_t), wchar_t,
char32_t>;
85 template <
typename A,
typename B>
86 concept same_size_and_alignment =
sizeof(A) ==
sizeof(B) &&
alignof(A) ==
alignof(B);
90 concept charable = (std::is_trivially_copyable_v<T> && (
91 same_size_and_alignment<T, wchar_t> ||
92 same_size_and_alignment<T, char> ||
93 same_size_and_alignment<T, char8_t> ||
94 same_size_and_alignment<T, char16_t> ||
95 same_size_and_alignment<T, char32_t>
100 concept char_type = std::same_as<T, char> || std::same_as<T, wchar_t>;
103 template <
typename T>
104 concept utf_type = std::same_as<T, char8_t> || std::same_as<T, char16_t> || std::same_as<T, char32_t>;
107 template <
typename T>
111 template <
charable T>
112 using charable_utf_t = std::conditional_t<same_size_and_alignment<T, char8_t>, char8_t,
113 std::conditional_t<same_size_and_alignment<T, char16_t>, char16_t,
114 std::conditional_t<same_size_and_alignment<T, char32_t>, char32_t,
119 template <
charable T>
121 std::conditional_t<same_size_and_alignment<T, wide_char16_t>,
wide_char16_t,
122 std::conditional_t<same_size_and_alignment<T, wide_char32_t>,
wide_char32_t,
126 template <
charable T>
127 using best_stringable_type = std::conditional_t<stringable_base_type<T>,
143 template <
typename C =
char>
144 [[nodiscard]]
constexpr std::basic_string_view<C> make_sv(std::nullptr_t, std::nullptr_t)
noexcept {
return {}; }
146 template <
stringable_base_type CT, std::contiguous_iterator IT, std::contiguous_iterator IT2>
147 requires charable<std::iter_value_t<IT>>
148 [[nodiscard]]
constexpr auto make_sv(IT start, IT2 end)
noexcept(
noexcept(std::to_address(start))) {
149 return std::basic_string_view<CT>{
150 reinterpret_cast<CT const*
>(std::to_address(start)),
151 static_cast<size_t>(std::to_address(end) - std::to_address(start))
155 template <std::contiguous_iterator IT, std::contiguous_iterator IT2>
157 [[nodiscard]]
constexpr auto make_sv(IT start, IT2 end)
noexcept(
noexcept(std::to_address(start))) {
158 using char_type = best_stringable_type<std::iter_value_t<IT>>;
159 return make_sv<char_type, IT, IT2>(std::move(start), std::move(end));
162 template <
typename T>
164 [[nodiscard]]
constexpr auto make_sv(T&& single_char)
noexcept {
165 static_assert(!std::is_rvalue_reference_v<
decltype(single_char)>,
"cannot create string_view's from single char rvalues");
166 return make_sv(&single_char, &single_char + 1);
169 template <
charable T>
170 [[nodiscard]]
constexpr auto make_sv(
const T* str)
noexcept {
171 using CT = best_stringable_type<T>;
172 return str ? std::basic_string_view<CT>{
reinterpret_cast<const CT*
>(str) } : std::basic_string_view<CT>{};
175 template <
typename C>
176 [[nodiscard]]
constexpr std::basic_string_view<C> make_sv(std::basic_string_view<C>
id)
noexcept {
return id; }
177 template <
typename C>
178 [[nodiscard]]
constexpr std::basic_string_view<C> make_sv(std::basic_string<C>
const&
id)
noexcept {
return id; }
179 template <
typename C>
180 [[nodiscard]]
constexpr std::basic_string_view<C> make_sv(std::basic_string<C>&&
id)
noexcept =
delete;
182 template <std::ranges::range RANGE>
184 [[nodiscard]]
constexpr auto make_sv(RANGE&& range)
noexcept
186 return make_sv(std::ranges::begin(range), std::ranges::end(range));
189 template <
typename... NONARGS,
typename... ARGS>
190 [[nodiscard]]
constexpr auto make_string(ARGS&&... args) {
191 auto sv = make_sv<NONARGS...>(std::forward<ARGS>(args)...);
192 using char_type =
typename decltype(sv)::value_type;
193 return std::basic_string<char_type>{ sv };
205 template <
typename COUT,
typename CIN>
207 [[nodiscard]]
constexpr std::basic_string_view<COUT>
string_view_cast(std::basic_string_view<CIN>
id)
noexcept
209 return {
reinterpret_cast<COUT const*
>(
id.data()),
id.size() };
217 [[nodiscard]]
inline std::string
to_string(std::string_view from)
noexcept {
return std::string{ from }; }
218 [[nodiscard]]
inline std::string string_view_to_string(std::string_view from)
noexcept {
return std::string{ from }; }
220 [[nodiscard]]
inline std::string to_string(std::u8string_view from)
noexcept {
return std::string{ from.data(), from.data() + from.size() }; }
223 [[nodiscard]]
inline std::string
to_string(T
const& t)
requires requires { std::to_string(t); } {
return std::to_string(t); }
225 [[nodiscard]]
constexpr std::string
const& to_string(std::same_as<std::string>
auto const& s) {
return s; }
228 [[nodiscard]]
inline std::string to_string(std::optional<T>
const& o) {
if (o.has_value())
return std::to_string(o.value());
return "(empty)"; }
244 [[nodiscard]]
constexpr std::string_view
back(std::string_view child_to_back_up, std::string_view parent,
size_t n = 1) noexcept
246 return make_sv(std::max(child_to_back_up.data() - n, parent.data()), child_to_back_up.data() + child_to_back_up.size());
263 [[nodiscard]]
constexpr std::string_view
back(std::string_view child_to_back_up,
size_t n = 1) noexcept
265 return make_sv(child_to_back_up.data() - n, child_to_back_up.data() + child_to_back_up.size());
269 [[nodiscard]]
constexpr bool is_inside(std::string_view big_string, std::string_view smaller_string)
271 return smaller_string.data() - big_string.data() >= 0 && (smaller_string.data() + smaller_string.size()) - (big_string.data() + big_string.size()) < 0;
289 constexpr uint64_t
is_flag_set(std::array<uint64_t, 2> flags,
char32_t cp)
noexcept
291 const auto flag_el = (cp >> 6) & 1;
292 const auto bit = uint64_t(1) << (cp & 63);
293 return (flags[flag_el] & bit) != 0;
296 template <u
int64_t HIGH, u
int64_t LOW>
299 const auto low_mask = uint64_t(0) - (cp >> 6 == 0);
300 const auto high_mask = uint64_t(0) - (cp >> 6 == 1);
301 const auto bit = uint64_t(1) << (cp & 63);
303 return ((bit & low_mask) & LOW) or ((bit & high_mask) & HIGH);
306 constexpr void set_flag(std::array<uint64_t, 2>& flags,
char32_t cp)
noexcept
308 const auto flag_el = (cp >> 6) & 1;
309 const auto bit = uint64_t(1) << (cp & 63);
310 flags[flag_el] |= bit;
313 constexpr std::array<uint64_t, 2> get_flags_for(std::string_view str)
noexcept
315 std::array<uint64_t, 2> result{ 0,0 };
321 template <
typename FUNC>
322 constexpr std::array<uint64_t, 2> get_flags_for(FUNC&& pred)
noexcept
324 std::array<uint64_t, 2> result{ 0,0 };
325 for (
char32_t c = 0; c < 128; ++c)
340 [[nodiscard]]
constexpr bool isalpha(
char32_t cp)
noexcept {
return (cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122); }
341 [[nodiscard]]
constexpr bool isdigit(
char32_t cp)
noexcept {
return cp >= 48 && cp <= 57; }
342 [[nodiscard]]
constexpr bool isodigit(
char32_t cp)
noexcept {
return cp >= 48 && cp <= 55; }
343 [[nodiscard]]
constexpr bool isxdigit(
char32_t d)
noexcept {
return (d >= 48 && d <= 57) || (d >= 65 && d <= 70) || (d >= 97 && d <= 102); }
344 [[nodiscard]]
constexpr bool isXdigit(
char32_t d)
noexcept {
return (d >= 48 && d <= 57) || (d >= 65 && d <= 70); }
345 [[nodiscard]]
constexpr bool islxdigit(
char32_t d)
noexcept {
return (d >= 48 && d <= 57) || (d >= 97 && d <= 102); }
346 [[nodiscard]]
constexpr bool isalnum(
char32_t cp)
noexcept { return ::ghassanpl::string_ops::ascii::isdigit(cp) || ::ghassanpl::string_ops::ascii::isalpha(cp); }
347 [[nodiscard]]
constexpr bool isident(
char32_t cp)
noexcept { return ::ghassanpl::string_ops::ascii::isdigit(cp) || ::ghassanpl::string_ops::ascii::isalpha(cp) || cp == 95; }
348 [[nodiscard]]
constexpr bool isidentstart(
char32_t cp)
noexcept { return ::ghassanpl::string_ops::ascii::isalpha(cp) || cp == 95; }
349 [[nodiscard]]
constexpr bool isspace(
char32_t cp)
noexcept {
return (cp >= 9 && cp <= 13) || cp == 32; }
350 [[nodiscard]]
constexpr bool ispunct(
char32_t cp)
noexcept {
return (cp >= 33 && cp <= 47) || (cp >= 58 && cp <= 64) || (cp >= 91 && cp <= 96) || (cp >= 123 && cp <= 126); }
351 [[nodiscard]]
constexpr bool islower(
char32_t cp)
noexcept {
return cp >= 97 && cp <= 122; }
352 [[nodiscard]]
constexpr bool isupper(
char32_t cp)
noexcept {
return cp >= 65 && cp <= 90; }
353 [[nodiscard]]
constexpr bool iscntrl(
char32_t cp)
noexcept {
return cp == 0x7F || cp < 0x20; }
354 [[nodiscard]]
constexpr bool isblank(
char32_t cp)
noexcept {
return cp == 32 || cp == 9; }
355 [[nodiscard]]
constexpr bool isgraph(
char32_t cp)
noexcept {
return cp >= 33 && cp <= 126; }
356 [[nodiscard]]
constexpr bool isprint(
char32_t cp)
noexcept {
return cp >= 32 && cp <= 126; }
358 [[nodiscard]]
constexpr char32_t toupper(
char32_t cp)
noexcept {
359 return (cp >= 97 && cp <= 122) ? (cp ^ 0b100000) : cp;
361 [[nodiscard]]
constexpr char32_t tolower(
char32_t cp)
noexcept {
362 return (cp >= 65 && cp <= 90) ? (cp | 0b100000) : cp;
367 [[nodiscard]]
constexpr bool iscntrl1(
char32_t cp)
noexcept {
return (cp >= 0x7E && cp <= 0x9F); }
368 [[nodiscard]]
constexpr bool iscntrl(
char32_t cp)
noexcept { return ::ghassanpl::string_ops::ascii::iscntrl(cp) || iscntrl1(cp); }
376 consteval size_t count_chars() {
377 const auto v = std::views::iota(
char32_t{ 1 },
char32_t{ 127 });
378 return std::ranges::count_if(v, FUNC);
380 template <
typename T, auto FUNC>
381 constexpr auto compute_characters_matching() {
382 constexpr size_t char_count = count_chars<FUNC>();
383 std::array<T, char_count> result{};
384 std::ranges::copy_if(std::views::iota(T{ 1 }, T{ 127 }), result.begin(), FUNC);
392 constexpr inline auto alphabetic_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isalpha>();
393 constexpr inline auto digit_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isdigit>();
394 constexpr inline auto octal_digit_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isodigit>();
395 constexpr inline auto hex_digit_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isxdigit>();
396 constexpr inline auto alphanumeric_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isalnum>();
397 constexpr inline auto identifier_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isident>();
398 constexpr inline auto identifier_start_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isidentstart>();
399 constexpr inline auto whitespace_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isspace>();
400 constexpr inline auto punctuation_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::ispunct>();
401 constexpr inline auto lowercase_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::islower>();
402 constexpr inline auto uppercase_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isupper>();
403 constexpr inline auto control_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::iscntrl>();
404 constexpr inline auto blank_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isblank>();
405 constexpr inline auto graphical_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isgraph>();
406 constexpr inline auto printable_chars = detail::compute_characters_matching<char, ::ghassanpl::string_ops::ascii::isprint>();
410 template <
stringable T>
412 if (std::ranges::empty(str))
return false;
413 return ::ghassanpl::string_ops::ascii::isidentstart(*std::ranges::begin(str)) && std::ranges::all_of(std::views::drop(str, 1), ::ghassanpl::string_ops::ascii::isident);
417 template <
stringable T>
418 [[nodiscard]]
constexpr std::string tolower(T
const& str)
noexcept {
419 using std::ranges::begin;
420 using std::ranges::end;
422 if constexpr (std::ranges::sized_range<T>)
423 result.reserve(std::ranges::size(str));
424 std::transform(begin(str), end(str), std::back_inserter(result), [](
char cp) {
return (
char)::ghassanpl::string_ops::ascii::tolower(cp); });
429 [[nodiscard]]
constexpr std::string tolower(std::string str)
noexcept {
430 std::ranges::transform(str, std::ranges::begin(str), [](
char cp) {
return (
char)::ghassanpl::string_ops::ascii::tolower(cp); });
435 [[nodiscard]]
constexpr std::string tolower(
const char* str)
noexcept {
437 return tolower(std::string{ str });
442 template <
stringable T>
443 [[nodiscard]]
inline std::string toupper(T
const& str)
noexcept {
444 using std::ranges::begin;
445 using std::ranges::end;
447 if constexpr (std::ranges::sized_range<T>)
448 result.reserve(std::ranges::size(str));
449 std::transform(begin(str), end(str), std::back_inserter(result), [](
char cp) {
return (
char)::ghassanpl::string_ops::ascii::toupper(cp); });
454 [[nodiscard]]
inline std::string toupper(std::string str)
noexcept {
455 std::ranges::transform(str, std::ranges::begin(str), [](
char cp) {
return (
char)::ghassanpl::string_ops::ascii::toupper(cp); });
460 [[nodiscard]]
inline std::string toupper(
const char* str)
noexcept {
462 return toupper(std::string{ str });
467 [[nodiscard]]
inline std::string
capitalize(std::string str)
noexcept {
468 if (str.empty())
return str;
469 str[0] = (char)::ghassanpl::string_ops::ascii::toupper(str[0]);
474 [[nodiscard]]
constexpr char32_t number_to_digit(
int v)
noexcept {
return char32_t(v) + 48; }
476 [[nodiscard]]
constexpr char32_t number_to_xdigit(
int v)
noexcept {
return (v > 9) ? (char32_t(v - 10) + 65) : (char32_t(v) + 48); }
479 [[nodiscard]]
constexpr int digit_to_number(
char32_t cp)
noexcept {
return int(cp - 48); }
482 [[nodiscard]]
constexpr int xdigit_to_number(
char32_t cp)
noexcept {
return isdigit(cp) ? int(cp - 48) : ((int(cp) & ~0b100000) - 55); }
487 [[nodiscard]]
constexpr bool strings_equal_ignore_case(std::string_view sa, std::string_view sb)
489 return std::ranges::equal(sa, sb, [](
char a,
char b) { return ::ghassanpl::string_ops::ascii::toupper(a) == ::ghassanpl::string_ops::ascii::toupper(b); });
492 [[nodiscard]]
constexpr bool string_starts_with_ignore_case(std::string_view a, std::string_view b)
494 return strings_equal_ignore_case(a.substr(0, b.size()), b);
497 [[nodiscard]]
constexpr bool string_ends_with_ignore_case(std::string_view a, std::string_view b)
499 if (b.size() > a.size())
return false;
500 return strings_equal_ignore_case(a.substr(a.size() - b.size()), b);
503 [[nodiscard]]
constexpr auto string_find_ignore_case(std::string_view haystack, std::string_view pattern)
505 return std::ranges::search(
508 [](
char ch1,
char ch2) { return ::ghassanpl::string_ops::ascii::toupper(ch1) == ::ghassanpl::string_ops::ascii::toupper(ch2); }
512 [[nodiscard]]
constexpr auto string_find_last_ignore_case(std::string_view a, std::string_view b)
514 return std::ranges::find_end(
517 [](
char ch1,
char ch2) { return ::ghassanpl::string_ops::ascii::toupper(ch1) == ::ghassanpl::string_ops::ascii::toupper(ch2); }
521 [[nodiscard]]
constexpr auto string_contains_ignore_case(std::string_view a, std::string_view b)
523 return string_find_ignore_case(a, b) != a.end();
526 [[nodiscard]]
constexpr bool lexicographical_compare_ignore_case(std::string_view first, std::string_view second)
528 return std::ranges::lexicographical_compare(first, second,
529 [](
char a,
char b) { return ::ghassanpl::string_ops::ascii::toupper(a) < ::ghassanpl::string_ops::ascii::toupper(b); });
532 [[nodiscard]]
constexpr auto lexicographical_compare_ignore_case_three_way(std::string_view a, std::string_view b)
534 return std::lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end(),
535 [](
char ca,
char cb) { return ::ghassanpl::string_ops::ascii::toupper(ca) <=> ::ghassanpl::string_ops::ascii::toupper(cb); });
538 [[nodiscard]]
constexpr uint64_t hash_ignore_case(std::string_view str)
541 uint64_t result = 0xcbf29ce484222325;
542 for (
auto byte : str)
543 result = (result ^
static_cast<uint8_t
>(::ghassanpl::string_ops::ascii::toupper(
byte))) * 0x00000100000001b3U;
549 struct string_view_case_insensitive
551 std::string_view value;
553 constexpr bool operator ==(std::string_view a)
const noexcept {
return strings_equal_ignore_case(value, a); }
554 constexpr auto operator <=>(std::string_view a)
const noexcept {
return lexicographical_compare_ignore_case_three_way(value, a); }
556 constexpr bool operator ==(string_view_case_insensitive
const& other)
const noexcept {
return strings_equal_ignore_case(value, other.value); }
557 constexpr auto operator <=>(string_view_case_insensitive
const& other)
const noexcept {
return lexicographical_compare_ignore_case_three_way(value, other.value); }
562 [[nodiscard]]
consteval detail::string_view_case_insensitive
operator""_i(
const char* str,
size_t size)
noexcept {
return detail::string_view_case_insensitive{ std::string_view{str, str + size} }; }
569#pragma push_macro("isascii")
572 [[nodiscard]]
constexpr bool isascii(
char32_t cp)
noexcept {
return cp <= 127; }
573#pragma pop_macro("isascii")
576 [[nodiscard]]
constexpr bool is_ascii(
char32_t cp)
noexcept {
return cp <= 127; }
581#if __cpp_lib_string_contains
582 return str.contains(c);
584 return str.find(c) != std::string_view::npos;
588 [[nodiscard]]
constexpr auto data_end(std::string_view str)
noexcept {
return str.data() + str.size(); }
593 [[nodiscard]]
inline std::string_view
substr(std::string_view str, intptr_t start,
size_t count = std::string::npos)
noexcept
597 start = str.size() + start;
598 if (start < 0) start = 0;
600 if (start >=
static_cast<intptr_t
>(str.size()))
return {};
601 return str.substr(start,
static_cast<size_t>(count));
605 [[nodiscard]]
inline std::string_view
prefix(std::string_view str,
size_t count)
noexcept {
606 return str.substr(0, count);
610 [[nodiscard]]
inline std::string_view
without_suffix(std::string_view str,
size_t count)
noexcept {
611 return str.substr(0, str.size() - std::min(str.size(), count));
615 [[nodiscard]]
inline std::string_view
suffix(std::string_view str,
size_t count)
noexcept {
616 return str.substr(str.size() - std::min(count, str.size()));
620 [[nodiscard]]
inline std::string_view
without_prefix(std::string_view str,
size_t count)
noexcept {
621 return str.substr(std::min(str.size(), count));
626 str.erase(std::min(start + count, str.size()));
627 str.erase(0, std::min(start, str.size()));
633 const auto from_ = std::min(from,
to);
634 const auto to_ = std::max(from,
to);
642 str.erase(str.size() - std::min(str.size(), prefix_size));
649 str.erase(0, std::min(str.size(), prefix_size));
656 [[nodiscard]]
constexpr std::string_view trimmed_whitespace_right(std::string_view str)
noexcept {
return make_sv(str.begin(), std::find_if_not(str.rbegin(), str.rend(), ::ghassanpl::string_ops::ascii::isspace).base()); }
657 [[nodiscard]]
constexpr std::string_view trimmed_whitespace_left(std::string_view str)
noexcept {
return make_sv(std::ranges::find_if_not(str, ::ghassanpl::string_ops::ascii::isspace), str.end()); }
658 [[nodiscard]]
constexpr std::string_view trimmed_whitespace(std::string_view str)
noexcept {
return trimmed_whitespace_left(trimmed_whitespace_right(str)); }
659 [[nodiscard]]
constexpr std::string_view trimmed_until(std::string_view str,
char chr)
noexcept {
return make_sv(std::ranges::find(str, chr), str.end()); }
660 [[nodiscard]]
constexpr std::string_view trimmed(std::string_view str,
char chr)
noexcept {
return make_sv(std::ranges::find_if_not(str, [chr](
char c) {
return c == chr; }), str.end()); }
662 [[nodiscard]]
constexpr std::string trimmed_whitespace_right(std::string str)
noexcept { str.erase(std::find_if_not(str.rbegin(), str.rend(), ::ghassanpl::string_ops::ascii::isspace).base(), str.end());
return str; }
663 [[nodiscard]]
constexpr std::string trimmed_whitespace_left(std::string str)
noexcept { str.erase(str.begin(), std::ranges::find_if_not(str, ::ghassanpl::string_ops::ascii::isspace));
return str; }
664 [[nodiscard]]
constexpr std::string trimmed_whitespace(std::string str)
noexcept {
return trimmed_whitespace_left(trimmed_whitespace_right(std::move(str))); }
665 [[nodiscard]]
constexpr std::string trimmed_until(std::string str,
char chr)
noexcept { str.erase(str.begin(), std::ranges::find(str, chr));
return str; }
666 [[nodiscard]]
constexpr std::string trimmed(std::string str,
char chr)
noexcept { str.erase(str.begin(), std::ranges::find_if_not(str, [chr](
char c) { return c == chr; }));
return str; }
668 template <
typename FUNC>
669 requires std::is_invocable_r_v<bool, FUNC, char>
670 [[nodiscard]]
inline std::string_view trimmed_while(std::string_view str, FUNC&& func)
noexcept { return ::ghassanpl::string_ops::make_sv(std::find_if_not(str.begin(), str.end(), std::forward<FUNC>(func)), str.end()); }
674 constexpr void trim_whitespace_right(std::string_view& str)
noexcept { str = make_sv(str.begin(), std::find_if_not(str.rbegin(), str.rend(), ::ghassanpl::string_ops::ascii::isspace).base()); }
675 constexpr void trim_whitespace_left(std::string_view& str)
noexcept { str = make_sv(std::ranges::find_if_not(str, ::ghassanpl::string_ops::ascii::isspace), str.end()); }
676 constexpr void trim_whitespace(std::string_view& str)
noexcept { trim_whitespace_left(str);
trim_whitespace_right(str); }
677 constexpr void trim_until(std::string_view& str,
char chr)
noexcept { str = trimmed_until(str, chr); }
678 constexpr void trim(std::string_view& str,
char chr)
noexcept { str = trimmed(str, chr); }
679 template <
typename FUNC>
680 requires std::is_invocable_r_v<bool, FUNC, char>
681 constexpr void trim_while(std::string_view& str, FUNC&& func)
noexcept { str = trimmed_while(str, std::forward<FUNC>(func)); }
686 template <std::ranges::random_access_range T>
688 [[nodiscard]]
constexpr bool isany(
char32_t cp, T&& chars)
noexcept {
689 using char_type = std::ranges::range_value_t<T>;
690 return std::ranges::find(chars,
static_cast<char_type>(cp)) != std::ranges::end(chars);
693 constexpr bool isany(
char32_t c,
char32_t c2)
noexcept {
return c == c2; }
702 [[nodiscard]]
inline char consume(std::string_view& str)
706 const auto result = str[0];
707 str.remove_prefix(1);
713 [[nodiscard]]
inline bool consume(std::string_view& str,
char val)
715 if (str.starts_with(val))
717 str.remove_prefix(1);
725 [[nodiscard]]
inline bool consume(std::string_view& str, std::string_view val)
727 if (str.starts_with(val))
729 str.remove_prefix(val.size());
737 template <
typename... ARGS>
738 [[nodiscard]]
inline char consume_any(std::string_view& str, ARGS&&... args)
740 if (!str.empty() && (
isany(str[0], args) || ...))
742 const auto result = str[0];
743 str.remove_prefix(1);
751 template <
typename PRED>
752 requires std::is_invocable_r_v<bool, PRED, char>
753 [[nodiscard]]
inline char consume(std::string_view& str, PRED&&
pred)
755 if (!str.empty() &&
pred(str[0]))
757 const auto result = str[0];
758 str.remove_prefix(1);
765 [[nodiscard]]
inline char consume_or(std::string_view& str,
char or_else)
769 const auto result = str[0];
770 str.remove_prefix(1);
779 if (str.ends_with(val))
781 str.remove_suffix(1);
791 [[nodiscard]]
inline bool consume_at_end(std::string_view& str, std::string_view val)
793 if (str.ends_with(val))
795 str.remove_suffix(val.size());
803 template <
typename FUNC>
804 requires std::is_invocable_r_v<bool, FUNC, char>
807 const auto start = str.begin();
808 while (!str.empty() &&
pred(str[0]))
809 str.remove_prefix(1);
810 return make_sv(start, str.begin());
815 [[nodiscard]]
inline std::string_view
consume_while(std::string_view& str,
char c)
817 const auto start = str.begin();
818 while (str.starts_with(c))
819 str.remove_prefix(1);
820 return make_sv(start, str.begin());
825 template <
typename... ARGS>
828 const auto start = str.begin();
829 while (!str.empty() && (
isany(str[0], args) || ...))
830 str.remove_prefix(1);
831 return make_sv(start, str.begin());
836 template <
typename FUNC>
837 requires std::is_invocable_r_v<bool, FUNC, char>
840 const auto start = str.begin();
841 while (!str.empty() && !
pred(str[0]))
842 str.remove_prefix(1);
843 return make_sv(start, str.begin());
848 [[nodiscard]]
inline std::string_view
consume_until(std::string_view& str,
char c)
850 const auto start = str.begin();
851 while (!str.empty() && str[0] != c)
852 str.remove_prefix(1);
853 return make_sv(start, str.begin());
858 [[nodiscard]]
inline std::string_view
consume_until(std::string_view& str, std::string_view end)
860 const auto it = std::ranges::search(str, end).begin();
861 const auto result = make_sv(str.begin(), it);
862 str = { it, str.end() };
869 template <typename T>
870 requires std::constructible_from<std::string_view, T>
871 [[nodiscard]] inline std::string_view consume_until_any(std::string_view& str, T&& chars)
873 const auto charssv = std::string_view{ chars };
874 const auto start = str.begin();
875 while (!str.empty() && !charssv.contains(str[0]))
876 str.remove_prefix(1);
877 return make_sv(start, str.begin());
883 template <
typename... ARGS>
886 const auto start = str.begin();
887 while (!str.empty() && !(
isany(str[0], args) || ...))
888 str.remove_prefix(1);
889 return make_sv(start, str.begin());
898 const auto start = str.begin();
899 while (!str.empty() && str[0] != c)
900 str.remove_prefix(1);
902 return make_sv(start, str.begin());
909 const auto start = str.begin();
910 while (!str.empty() && str[0] != c)
911 str.remove_prefix(1);
912 auto result = make_sv(start, str.begin());
919 [[nodiscard]]
inline std::string_view
consume_n(std::string_view& str,
size_t n)
921 n = std::min(str.size(), n);
922 auto result = str.substr(0, n);
923 str.remove_prefix(n);
929 template <
typename FUNC>
930 requires std::is_invocable_r_v<bool, FUNC, char>
931 [[nodiscard]]
inline std::string_view
consume_n(std::string_view& str,
size_t n, FUNC&&
pred)
933 n = std::min(str.size(), n);
934 const auto start = str.begin();
935 while (n-- && !str.empty() &&
pred(str[0]))
936 str.remove_prefix(1);
937 return make_sv(start, str.begin());
955 template <
typename CALLBACK_TYPE>
956 requires std::is_invocable_r_v<bool, CALLBACK_TYPE, std::string_view&>
961 trim_whitespace_left(str);
964 trim_whitespace_left(str);
965 }
while (
consume(str, delimiter));
984 template <
typename CALLBACK_TYPE>
985 requires std::is_invocable_r_v<bool, CALLBACK_TYPE, std::string_view>
986 [[nodiscard]]
inline bool consume_delimited_list(std::string_view& str, std::string_view delimiter, std::string_view closer, CALLBACK_TYPE callback)
988 trim_whitespace_left(str);
991 trim_whitespace_left(str);
994 trim_whitespace_left(str);
1012 template <
typename FUNC>
1013 requires (std::is_invocable_v<FUNC, std::string_view, bool> && !std::is_invocable_v<FUNC, std::string_view>)
1014 constexpr auto split_invoke(FUNC& func, std::string_view sv,
bool is_final)
noexcept(
noexcept(func(std::string_view{},
true)))
1016 return func(sv, is_final);
1018 template <
typename FUNC>
1019 requires std::is_invocable_v<FUNC, std::string_view>
1020 constexpr auto split_invoke(FUNC& func, std::string_view sv,
bool is_final)
noexcept(
noexcept(func(std::string_view{})))
1028 template <
typename FUNC>
1029 constexpr auto split(std::string_view source,
char delim, FUNC&& func)
noexcept(
noexcept(split_invoke(func, std::string_view{},
true)))
1031 using invoke_result =
decltype(split_invoke(func, std::string_view{},
false));
1034 while ((next = source.find_first_of(delim)) != std::string::npos)
1036 if constexpr (std::is_constructible_v<bool, invoke_result>)
1038 if (
auto r = split_invoke(func, source.substr(0, next),
false); !r)
1042 split_invoke(func, source.substr(0, next),
false);
1044 source.remove_prefix(next + 1);
1046 return split_invoke(func, source,
true);
1052 template <
typename FUNC>
1053 constexpr void split(std::string_view source, std::string_view delim, FUNC&& func)
noexcept(
noexcept(split_invoke(func, std::string_view{},
true)))
1055 const size_t delim_size = delim.size();
1056 if (delim_size == 0)
return;
1059 while ((next = source.find(delim)) != std::string::npos)
1061 split_invoke(func, source.substr(0, next),
false);
1062 source.remove_prefix(next + delim_size);
1064 split_invoke(func, source,
true);
1070 template <
typename FUNC>
1071 constexpr void split_on_any(std::string_view source, std::string_view delim, FUNC&& func)
noexcept(
noexcept(split_invoke(func, std::string_view{},
true)))
1074 if (delim.empty())
return;
1077 while ((next = source.find_first_of(std::string_view{ delim })) != std::string::npos)
1079 split_invoke(func, source.substr(0, next),
false);
1080 source.remove_prefix(next + 1);
1082 split_invoke(func, source,
true);
1099 template <
typename DELIM_FUNC,
typename FUNC>
1100 requires std::is_invocable_r_v<size_t, DELIM_FUNC, std::string_view>
1101 inline void split_on(std::string_view source, DELIM_FUNC&& delim, FUNC&& func)
noexcept(
noexcept(split_invoke(func, std::string_view{},
true)) &&
noexcept(delim(std::string_view{})))
1105 end = delim(source);
1106 while (end != std::string::npos)
1108 split_invoke(func, source.substr(start, end - start),
false);
1111 auto next = delim(source.substr(end + 1));
1112 if (next == std::string::npos)
1114 end = next + end + 1;
1116 split_invoke(func, source.substr(start),
true);
1120 [[nodiscard]]
constexpr std::pair<std::string_view, std::string_view>
split_at(std::string_view src,
size_t split_at)
noexcept
1128 [[nodiscard]]
constexpr bool split_at(std::string_view src,
size_t split_at, std::string_view& first, std::string_view& second)
noexcept
1139 [[nodiscard]]
constexpr std::pair<std::string_view, std::string_view>
single_split(std::string_view src,
char delim)
noexcept
1141 return split_at(src, src.find_first_of(delim));
1146 [[nodiscard]]
constexpr std::pair<std::string_view, std::string_view>
single_split_last(std::string_view src,
char delim)
noexcept
1148 return split_at(src, src.find_last_of(delim));
1155 [[nodiscard]]
constexpr bool single_split(std::string_view src,
char delim, std::string_view& first, std::string_view& second)
noexcept
1157 return split_at(src, src.find_first_of(delim), first, second);
1164 [[nodiscard]]
constexpr bool single_split_last(std::string_view src,
char delim, std::string_view& first, std::string_view& second)
noexcept
1166 return split_at(src, src.find_last_of(delim), first, second);
1180 template <
typename FUNC>
1181 inline void natural_split(std::string_view source,
char delim, FUNC&& func)
noexcept(
noexcept(split_invoke(func, source, delim)))
1184 while ((next = source.find_first_of(delim)) != std::string::npos)
1186 split_invoke(func, source.substr(0, next),
false);
1187 source.remove_prefix(next + 1);
1189 if ((next = source.find_first_not_of(delim)) == std::string::npos)
1192 source.remove_prefix(next);
1195 if (!source.empty())
1196 split_invoke(func, source,
true);
1202 template <
typename RESULT_TYPE = std::
string_view,
string_or_
char DELIM>
1203 [[nodiscard]]
constexpr std::vector<RESULT_TYPE>
split(std::string_view source, DELIM&& delim)
noexcept
1205 std::vector<RESULT_TYPE> result;
1207 result.push_back(RESULT_TYPE{ str });
1215 template <
typename RESULT_TYPE = std::
string_view>
1216 [[nodiscard]]
constexpr std::vector<RESULT_TYPE>
split_on_any(std::string_view source, std::string_view delim)
noexcept
1219 std::vector<RESULT_TYPE> result;
1221 result.push_back(RESULT_TYPE{ str });
1230 template <
typename RESULT_TYPE = std::
string_view,
typename DELIM_FUNC>
1231 requires std::is_invocable_r_v<size_t, DELIM_FUNC, std::string_view>
1232 [[nodiscard]] std::vector<RESULT_TYPE>
split_on(std::string_view source, DELIM_FUNC&& delim)
noexcept(
noexcept(delim(std::string_view{})))
1234 std::vector<RESULT_TYPE> result;
1236 result.push_back(RESULT_TYPE{ str });
1243 template <
typename RESULT_TYPE = std::
string_view,
string_or_
char DELIM>
1244 [[nodiscard]]
inline std::vector<RESULT_TYPE>
natural_split(std::string_view source, DELIM&& delim)
noexcept
1246 std::vector<RESULT_TYPE> result;
1248 result.push_back(RESULT_TYPE{ str });
1262 template <
typename T>
1263 [[nodiscard]]
inline auto join(T&& source)
1265 std::stringstream strm;
1266 for (
auto&& p : std::forward<T>(source))
1272 template <
typename T,
string_or_
char DELIM>
1273 [[nodiscard]]
inline auto join(T&& source, DELIM
const& delim)
1275 std::stringstream strm;
1277 for (
auto&& p : std::forward<T>(source))
1279 if (!first) strm << delim;
1287 template <
typename... RANGES, string_or_char DELIM>
1288 [[nodiscard]]
inline auto join_multiple(DELIM
const& delim, RANGES&&... sources)
1290 std::stringstream strm;
1292 ([&]<
typename RANGE>(RANGE && source) {
1293 for (
auto&& p : std::forward<RANGE>(source))
1295 if (!first) strm << delim;
1299 }(std::forward<RANGES>(sources)), ...);
1306 template <
typename T,
string_or_
char DELIM,
string_or_
char LAST_DELIM>
1307 [[nodiscard]]
inline auto join_and(T&& source, DELIM
const& delim, LAST_DELIM&& last_delim)
1309 using std::ranges::begin;
1310 using std::ranges::end;
1313 std::stringstream strm;
1316 auto&& endit = end(source);
1317 for (
auto it = begin(source); it != endit; ++it)
1321 if (next(it) == endit)
1322 strm << std::forward<LAST_DELIM>(last_delim);
1336 template <
typename T,
string_or_
char DELIM,
string_or_
char LAST_DELIM,
typename FUNC>
1337 [[nodiscard]]
inline auto join_and(T&& source, DELIM
const& delim, LAST_DELIM&& last_delim, FUNC&& transform_func)
1339 using std::ranges::begin;
1340 using std::ranges::end;
1343 std::stringstream strm;
1346 auto&& endit = end(source);
1347 for (
auto it = begin(source); it != endit; ++it)
1351 if (next(it) == endit)
1352 strm << std::forward<LAST_DELIM>(last_delim);
1356 strm << transform_func(*it);
1365 template <
typename T,
typename FUNC,
string_or_
char DELIM>
1366 [[nodiscard]]
inline auto join(T&& source, DELIM
const& delim, FUNC&& transform_func)
1368 std::stringstream strm;
1370 for (
auto&& p : source)
1372 if (!first) strm << delim;
1373 strm << transform_func(p);
1381 template <
string_or_
char NEEDLE,
typename FUNC>
1382 inline void find_all(std::string_view subject, NEEDLE&& search, FUNC&& func)
1384 const auto search_sv = make_sv(search);
1386 if (search_sv.empty())
1390 while ((pos = subject.find(search, pos)) != std::string::npos)
1392 func(subject.substr(pos, search_sv.size()));
1393 pos += search_sv.size();
1397 template <
typename RESULT_TYPE = std::
string_view,
string_or_
char NEEDLE>
1398 inline std::vector<RESULT_TYPE> find_all(std::string_view subject, NEEDLE&& search)
1400 std::vector<RESULT_TYPE> result;
1401 find_all(subject, std::forward<NEEDLE>(search), [&](std::string_view sv) { result.push_back(RESULT_TYPE{ sv }); });
1411 template <
string_or_
char NEEDLE,
string_or_
char REPLACE>
1412 inline void replace(std::string& subject, NEEDLE&& search, REPLACE&& replace)
1417 const auto search_sv = make_sv(search);
1418 const auto replace_sv = make_sv(replace);
1420 if (search_sv.empty())
1424 while ((pos = subject.find(search, pos)) != std::string::npos)
1426 subject.replace(pos, search_sv.size(), replace_sv);
1427 pos += replace_sv.size();
1431 template <
string_or_
char NEEDLE,
string_or_
char REPLACE>
1432 [[nodiscard]]
inline std::string replaced(std::string subject, NEEDLE&& search, REPLACE&& replace)
1434 string_ops::replace(subject, std::forward<NEEDLE>(search), std::forward<REPLACE>(replace));
1438 template <
string_or_
char DELIMITER =
char,
string_or_
char ESCAPE =
char>
1439 inline void quote(std::string& subject, DELIMITER delimiter =
'"', ESCAPE escape =
'\\')
1441 const char replace_delim[]{ escape, delimiter,
'\0' };
1442 const char replace_escape[]{ escape, escape,
'\0' };
1443 if constexpr (
requires { delimiter != escape; })
1445 if (delimiter != escape)
1446 ::ghassanpl::string_ops::replace(subject, escape, replace_escape);
1449 ::ghassanpl::string_ops::replace(subject, escape, replace_escape);
1450 ::ghassanpl::string_ops::replace(subject, delimiter, replace_delim);
1451 subject.insert(subject.begin(), delimiter);
1452 subject += delimiter;
1455 template <
string_or_
char DELIMITER =
char,
string_or_
char ESCAPE =
char>
1456 [[nodiscard]]
inline std::string quoted(std::string&& subject, DELIMITER&& delimiter =
'"', ESCAPE&& escape =
'\\')
1458 ::ghassanpl::string_ops::quote(subject, std::forward<DELIMITER>(delimiter), std::forward<ESCAPE>(escape));
1462 template <
string_or_
char DELIMITER =
char,
string_or_
char ESCAPE =
char>
1463 [[nodiscard]]
inline std::string quoted(std::string_view subject, DELIMITER&& delimiter =
'"', ESCAPE&& escape =
'\\')
1465 auto result = std::string{ subject };
1466 ::ghassanpl::string_ops::quote(result, std::forward<DELIMITER>(delimiter), std::forward<ESCAPE>(escape));
1470 template <
string_or_
char DELIMITER =
char,
string_or_
char ESCAPE =
char>
1471 [[nodiscard]]
inline std::string quoted(
const char* subject, DELIMITER&& delimiter =
'"', ESCAPE&& escape =
'\\')
1473 return ::ghassanpl::string_ops::quoted(std::string{ subject }, std::forward<DELIMITER>(delimiter), std::forward<ESCAPE>(escape));
1476 template <
typename ESCAPE_FUNC>
1477 requires std::is_invocable_v<ESCAPE_FUNC, std::string_view>&& std::is_constructible_v<std::string_view, std::invoke_result_t<ESCAPE_FUNC, std::string_view>>
1478 inline void escape(std::string& subject, std::string_view chars_to_escape, ESCAPE_FUNC&& escape_func)
1480 if (chars_to_escape.empty())
1484 while ((pos = subject.find_first_of(chars_to_escape, pos)) != std::string::npos)
1486 auto escape_str = escape_func(subject.substr(pos, 1));
1487 subject.replace(pos, 1, escape_str);
1488 pos += escape_str.size();
1492 template <
typename ESCAPE_FUNC>
1493 requires std::is_invocable_v<ESCAPE_FUNC, char> && std::is_constructible_v<std::string_view, std::invoke_result_t<ESCAPE_FUNC, char>>
1494 inline void escape(std::string& subject, std::string_view chars_to_escape, ESCAPE_FUNC&& escape_func)
1496 if (chars_to_escape.empty())
1500 while ((pos = subject.find_first_of(chars_to_escape, pos)) != std::string::npos)
1502 auto escape_str = escape_func(subject[pos]);
1503 subject.replace(pos, 1, escape_str);
1504 pos += escape_str.size();
1508 template <
string_or_
char ESCAPE =
char>
1509 inline void escape(std::string& subject, std::string_view chars_to_escape, ESCAPE&& escape =
'\\')
1511 if (chars_to_escape.empty())
1514 auto escape_str = make_sv(escape);
1517 while ((pos = subject.find_first_of(chars_to_escape, pos)) != std::string::npos)
1519 subject.insert(pos, escape_str);
1520 pos += escape_str.size() + 1;
1524 template <
typename ESCAPE_FUNC,
typename ISPRINTABLE_FUNC = decltype(ascii::ispr
int)>
1525 inline void escape_non_printable(std::string& subject, ESCAPE_FUNC&& escape_func, ISPRINTABLE_FUNC&& isprintable_func = ascii::isprint)
1527 static_assert(std::is_invocable_v<ESCAPE_FUNC, char> && std::is_constructible_v<std::string_view, std::invoke_result_t<ESCAPE_FUNC, char>>,
1528 "escape function must be invocable with (char) and must return something convertible to string_view");
1529 static_assert(std::is_invocable_v<ISPRINTABLE_FUNC, char> && std::is_constructible_v<bool, std::invoke_result_t<ISPRINTABLE_FUNC, char>>,
1530 "isprintable function must be a predicate invocable with (char)");
1533 std::string::iterator it;
1534 while ((it = std::find_if_not(subject.begin() + pos, subject.end(), isprintable_func)) != subject.end())
1536 pos = it - subject.begin();
1537 auto escape_str = escape_func(subject[pos]);
1538 subject.replace(pos, 1, escape_str);
1539 pos += escape_str.size();
1543 inline void escape_non_printable(std::string& subject)
1545 escape_non_printable(subject, [](
char c) {
1546 return std::format(
"\\x{:02x}",
static_cast<uint8_t
>(c));
1550 template <
typename STR,
string_or_
char ESCAPE =
char>
1551 [[nodiscard]]
inline std::string
escaped(STR&& subject, std::string_view to_escape =
"\"\\", ESCAPE&& escape_str =
'\\')
1553 auto result = std::string{ subject };
1554 ::ghassanpl::string_ops::escape(result, to_escape, std::forward<ESCAPE>(escape_str));
1558 template <
typename STR>
1559 [[nodiscard]]
inline std::string escaped_non_printable(STR&& subject)
1561 auto result = std::string{ subject };
1562 ::ghassanpl::string_ops::escape_non_printable(result);
1569 [[nodiscard]]
inline std::string
url_encode(std::string_view text)
1574 if (ascii::isalnum(c) || c ==
'-' || c ==
'_' || c ==
'.' || c ==
'~')
1581 std::format_to(std::back_inserter(result),
"{:02X}", (
int)(
unsigned char)c);
1590 while (!text.empty())
1594 text.remove_prefix(1);
1595 if (text.size() < 2)
1598 std::from_chars(text.data(), text.data() + 2, val, 16);
1599 result += (char)val;
1600 text.remove_prefix(2);
1605 text.remove_prefix(1);
1610#if defined(__cpp_lib_format)
1614 template <
typename CHECKER>
1615 requires std::predicate<CHECKER, std::string_view>
1616 [[nodiscard]]
inline std::string unique_name(std::string_view base_name, CHECKER&& checker)
1618 std::string new_name = std::string{ base_name };
1620 while (!checker(std::string_view{ new_name }))
1621 new_name = std::format(
"{}{}", base_name, index++);
1626#if !__cpp_lib_to_chars && !defined(DOXYGEN)
1630 template <std::
integral T>
1631 [[nodiscard]]
constexpr auto from_chars(std::string_view str, T& value,
const int base = 10) noexcept {
1632 return std::from_chars(str.data(), str.data() + str.size(), value, base);
1635 template <std::
floating_po
int T>
1636 [[nodiscard]]
constexpr auto from_chars(std::string_view str, T& value,
const std::chars_format chars_format = std::chars_format::general)
noexcept {
1637 return std::from_chars(str.data(), str.data() + str.size(), value, chars_format);
1647 template <
bool SINGLE>
1652 struct split_range_iterator
1654 const char* RangeStart;
1655 const char* RangeEnd;
1656 const char* SourceEnd;
1657 std::string_view SplitChars;
1659 bool operator!=(
const split_range_iterator& other)
const {
return RangeStart != other.SourceEnd; }
1660 split_range_iterator& operator++(
int) {
1666 split_range_iterator operator++() {
1668 auto se = SourceEnd;
1669 auto sc = SplitChars;
1691 std::pair<const char*, const char*> operator*()
const {
return { RangeStart, RangeEnd }; }
1694 split_range_iterator begin() {
1695 split_range_iterator it = {
nullptr, mSource.data(), mSource.data() + mSource.size(), mSplit };
1700 split_range_iterator end() {
1701 auto se = mSource.data() + mSource.size();
1702 return { se, se, se, mSplit };
1712 std::string_view mSource;
1713 std::string_view mSplit;
1726 template <
typename RESULT_TYPE = std::
string_view,
typename T,
typename FUNC>
1727 requires std::is_arithmetic_v<T>&& std::is_invocable_r_v<T, FUNC, std::string_view>
1728 std::vector<RESULT_TYPE>
word_wrap(std::string_view _source, T max_width, FUNC width_getter, std::string_view split_chars =
" ")
1730 std::vector<RESULT_TYPE> result;
1732 auto space_width = width_getter(
" ");
1736 auto space_left = max_width;
1737 auto line_start = line.first;
1739 for (
auto r :
split_range<false>({ line.first, size_t(line.second - line.first) }, split_chars))
1741 const auto word_width = width_getter({ r.first, size_t(r.second - r.first) });
1742 const auto width = word_width + width_getter(std::string{ *r.second });
1743 if (width > space_left)
1745 result.push_back(RESULT_TYPE{ line_start, size_t(r.first - line_start) });
1746 space_left = max_width - word_width;
1747 line_start = r.first;
1751 space_left -= width;
1755 result.push_back(RESULT_TYPE{ line_start, size_t(line.second - line_start) });
1765 template <
typename RESULT_TYPE = std::
string_view,
typename T>
1766 requires std::is_arithmetic_v<T>
1767 std::vector<RESULT_TYPE>
word_wrap(std::string_view _source, T max_width, T letter_width)
1769 return ::ghassanpl::string_ops::word_wrap<RESULT_TYPE>(_source, max_width, [letter_width](std::string_view str) {
return T(str.size() * letter_width); });
1772 auto repeat_string(stringable
auto str,
size_t num)
1774 const auto sv = make_sv(str);
1775 std::basic_string<
typename decltype(sv)::value_type> result;
1776 result.reserve(num * sv.size());
1777 for (
size_t i = 0; i < num; ++i)
1782 inline size_t levenshtein_distance(std::string_view s1, std::string_view s2)
1784 if (s1.size() > s2.size())
1787 const auto min_size = s1.size();
1788 const auto max_size = s2.size();
1790 std::vector<size_t> lev_dist(min_size + 1);
1791 for (
size_t i = 0; i < lev_dist.size(); ++i)
1794 for (
size_t j = 1; j <= max_size; ++j)
1796 size_t previous_diagonal = lev_dist[0];
1797 size_t previous_diagonal_save{};
1800 for (
size_t i = 1; i <= min_size; ++i)
1802 previous_diagonal_save = lev_dist[i];
1803 if (s1[i - 1] == s2[j - 1])
1804 lev_dist[i] = previous_diagonal;
1806 lev_dist[i] = std::min({ lev_dist[i - 1], lev_dist[i], previous_diagonal }) + 1;
1807 previous_diagonal = previous_diagonal_save;
1811 return lev_dist[min_size];
1817 template <std::
integral T>
1818 [[nodiscard]]
inline auto string_to_number(std::string_view str,
size_t* idx =
nullptr,
int base = 10)
1820 const auto begin = str.data();
1821 auto end = str.data() + str.size();
1823 if (
auto res = std::from_chars(begin, end, value, base); idx && res.ec == std::error_code{})
1824 *idx =
size_t(res.ptr - begin);
1828 template <std::
floating_po
int T>
1829 [[nodiscard]]
inline auto string_to_number(std::string_view str,
size_t* idx =
nullptr, std::chars_format format = std::chars_format::general)
1831 const auto begin = str.data();
1832 auto end = str.data() + str.size();
1834 if (
auto res = std::from_chars(begin, end, value, format); idx && res.ec == std::error_code{})
1835 *idx =
size_t(res.ptr - begin);
1843 [[nodiscard]]
inline auto stob(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<uint8_t>(str, idx, base); }
1844 [[nodiscard]]
inline auto stoc(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<char>(str, idx, base); }
1845 [[nodiscard]]
inline auto stouc(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<unsigned char>(str, idx, base); }
1847 [[nodiscard]]
inline auto stoi(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<int>(str, idx, base); }
1848 [[nodiscard]]
inline auto stol(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<long>(str, idx, base); }
1849 [[nodiscard]]
inline auto stoll(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<long long>(str, idx, base); }
1850 [[nodiscard]]
inline auto stoul(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<unsigned long>(str, idx, base); }
1851 [[nodiscard]]
inline auto stoull(std::string_view str,
size_t* idx =
nullptr,
int base = 10) {
return detail::string_to_number<unsigned long long>(str, idx, base); }
1852 [[nodiscard]]
inline auto stof(std::string_view str,
size_t* idx =
nullptr, std::chars_format format = std::chars_format::general) {
return detail::string_to_number<float>(str, idx, format); }
1853 [[nodiscard]]
inline auto stod(std::string_view str,
size_t* idx =
nullptr, std::chars_format format = std::chars_format::general) {
return detail::string_to_number<double>(str, idx, format); }
1854 [[nodiscard]]
inline auto stold(std::string_view str,
size_t* idx =
nullptr, std::chars_format format = std::chars_format::general) {
return detail::string_to_number<long double>(str, idx, format); }
1858 template <std::
integral T = std::make_
unsigned_t<
size_t>>
1859 [[nodiscard]]
inline auto to_number(std::string_view str,
int base = 10) -> expected<T, std::from_chars_result> {
1860 const auto begin = str.data();
1861 auto end = str.data() + str.size();
1863 if (
auto res = std::from_chars(begin, end, value, base); res.ec != std::error_code{} || res.ptr != end)
1864 return unexpected(res);
1869 template <std::
floating_po
int T = std::
float_t>
1870 [[nodiscard]]
inline auto to_number(std::string_view str, std::chars_format format = std::chars_format::general) -> expected<T, std::from_chars_result> {
1871 const auto begin = str.data();
1872 auto end = str.data() + str.size();
1874 if (
auto res = std::from_chars(begin, end, value, format); res.ec != std::error_code{} || res.ptr != end)
1875 return unexpected(res);
1879 template <
typename CALLBACK_TYPE>
1880 requires std::invocable<CALLBACK_TYPE, size_t, std::string_view, std::string&>
1881 std::string callback_format(std::string_view fmt, CALLBACK_TYPE&& callback)
1886 while (!fmt.empty())
1892 std::ignore =
consume(fmt,
'{');
1901 std::string fmt_clause_str =
"{";
1904 throw std::format_error(
"missing '}' in format string");
1905 if (!fmt_clause.empty())
1909 aid = string_ops::stoull(num);
1910 fmt_clause_str += fmt_clause;
1912 fmt_clause_str +=
'}';
1913 callback(aid, std::string_view{ fmt_clause_str }, result);
1923#define GHPL_FORMAT_TEMPLATE typename... GHPL_ARGS
1925#define GHPL_FORMAT_ARGS std::format_string<GHPL_ARGS...> ghpl_fmt, GHPL_ARGS&&... ghpl_args
1927#define GHPL_FORMAT_FORWARD ghpl_fmt, ghpl_args...
1929#define GHPL_FORMAT_CALL std::format(ghpl_fmt, std::forward<GHPL_ARGS>(ghpl_args)...)
1931#define GHPL_PRINT_CALL std::print_unicode(ghpl_fmt, std::forward<GHPL_ARGS>(ghpl_args)...)