simstr 1.8.1
Yet another strings library
 
Loading...
Searching...
No Matches
sstring.h
1/*
2* ver. 1.8.1
3 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
4 * Классы для работы со строками
5* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6* Classes for working with strings
7 */
8
19#pragma once
20
21#ifndef __has_declspec_attribute
22#define __has_declspec_attribute(x) 0
23#endif
24const bool isWindowsOs = // NOLINT
25#ifdef _WIN32
26 true
27#else
28 false
29#endif
30 ;
31
32#ifdef SIMSTR_IN_SHARED
33 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
34 #ifdef SIMSTR_EXPORT
35 #define SIMSTR_API __declspec(dllexport)
36 #else
37 #define SIMSTR_API __declspec(dllimport)
38 #endif
39 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
40 #define SIMSTR_API __attribute__((visibility("default")))
41 #else
42 #define SIMSTR_API
43 #endif
44#else
45 #define SIMSTR_API
46#endif
47
48#define IN_FULL_SIMSTR
49#include "strexpr.h"
50
51#include <format>
52#include <unordered_map>
53#include <atomic>
54#include <memory>
55#include <string.h>
56#include <iostream>
57#include <cmath>
58
59#ifdef _WIN32
60#include <stdio.h>
61#endif
62
63#ifdef _MSC_VER
64// warning C4201 : nonstandard extension used : nameless struct / union
65#pragma warning(disable : 4201)
66#endif
67
68namespace simstr {
69
70template<typename T>
71struct unicode_traits {}; // NOLINT
72
73template<>
74struct unicode_traits<u8s> {
75 // Эти операции с utf-8 могут изменить длину строки
76 // Поэтому их специализации отличаются
77 // В функцию помимо текста и адреса буфера для записи передается размер буфера
78 // Возвращает длину получающейся строки.
79 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
80 // обработанные символы, для повторного возобновления работы,
81 // а для оставшихся символов считается нужный размер буфера.
82 // These utf-8 operations can change the length of the string
83 // Therefore their specializations are different
84 // In addition to the text and address of the buffer for writing, the buffer size is passed to the function
85 // Returns the length of the resulting string.
86 // If the resulting string does not fit into the allocated buffer, pointers are set to the last
87 // processed characters to resume work again,
88 // and for the remaining characters the required buffer size is calculated.
89 static SIMSTR_API size_t upper(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
90 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
91 static SIMSTR_API size_t upper_len(const u8s* src, size_t len);
92 static SIMSTR_API size_t lower_len(const u8s* src, size_t len);
93
94 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
95
96 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
97 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
98};
99
100template<>
101struct unicode_traits<u16s> {
102 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
103 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
104
105 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
106 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
107 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
108};
109
110template<>
111struct unicode_traits<u32s> {
112 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
113 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
114
115 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
116 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
117 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
118};
119
120template<>
121struct unicode_traits<wchar_t> {
122 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
123 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
124 }
125 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
126 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
127 }
128
129 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
130 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
131 }
132 static size_t hashia(const wchar_t* src, size_t s) {
133 return unicode_traits<wchar_type>::hashia(to_w(src), s);
134 }
135 static size_t hashiu(const wchar_t* src, size_t s) {
136 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
137 }
138};
139
140template<>
141struct unicode_traits<char8_t> {
142 static size_t upper(const ubs*& src, size_t lenStr, ubs*& dest, size_t lenBuf) {
143 return unicode_traits<char>::upper((const u8s*&)src, lenStr, (u8s*&)dest, lenBuf);
144 }
145 static size_t lower(const ubs*& src, size_t len, ubs*& dest, size_t lenBuf) {
146 return unicode_traits<char>::lower((const u8s*&)src, len, (u8s*&)dest, lenBuf);
147 }
148 static size_t upper_len(const ubs* src, size_t len) {
149 return unicode_traits<char>::upper_len((const u8s*)src, len);
150 }
151 static size_t lower_len(const ubs* src, size_t len) {
152 return unicode_traits<char>::lower_len((const u8s*)src, len);
153 }
154
155 static int compareiu(const char8_t* text1, size_t len1, const char8_t* text2, size_t len2) {
156 return unicode_traits<char>::compareiu((const char*)text1, len1, (const char*)text2, len2);
157 }
158 static size_t hashia(const char8_t* src, size_t s) {
159 return unicode_traits<char>::hashia((const char*)src, s);
160 }
161 static size_t hashiu(const char8_t* src, size_t s) {
162 return unicode_traits<char>::hashiu((const char*)src, s);
163 }
164};
165
166#if defined(_MSC_VER) && _MSC_VER <= 1933
167template<typename K, typename... Args>
168using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
169#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
170template<typename K, typename... Args>
171using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
172#else
173template<typename K, typename... Args>
174using FmtString = std::basic_string_view<K>;
175#endif
176
177template<typename K>
178SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
179
192template<typename K, typename StrRef, typename Impl, bool Mutable>
193class str_algs : public str_src_algs<K, StrRef, Impl, Mutable> {
194 constexpr const Impl& d() const noexcept {
195 return *static_cast<const Impl*>(this);
196 }
197 constexpr size_t _len() const noexcept {
198 return d().length();
199 }
200 constexpr const K* _str() const noexcept {
201 return d().symbols();
202 }
203 constexpr bool _is_empty() const noexcept {
204 return d().is_empty();
205 }
206
207public:
208 using symb_type = K;
209 using str_piece = StrRef;
210 using traits = ch_traits<K>;
211 using uni = unicode_traits<K>;
212 using uns_type = std::make_unsigned_t<K>;
213 using my_type = Impl;
214 using base = str_src_algs<K, StrRef, Impl, Mutable>;
215 str_algs() = default;
216
217 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
218 if (!len)
219 return _is_empty() ? 0 : 1;
220 return uni::compareiu(_str(), _len(), text, len);
221 }
230 int compare_iu(str_piece text) const noexcept { // NOLINT
231 return compare_iu(text.symbols(), text.length());
232 }
233
241 bool equal_iu(str_piece text) const noexcept { // NOLINT
242 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
243 }
244
252 bool less_iu(str_piece text) const noexcept { // NOLINT
253 return compare_iu(text.symbols(), text.length()) < 0;
254 }
255 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
256 // Does this string begin with the specified substring, insensitive to unicode case
257 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
258 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
259 }
266 bool starts_with_iu(str_piece prefix) const noexcept {
267 return starts_with_iu(prefix.symbols(), prefix.length());
268 }
269 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
270 // Whether the string ends with the specified substring, case insensitive UNICODE
271 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
272 size_t myLen = _len();
273 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
274 }
281 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
282 return ends_with_iu(suffix.symbols(), suffix.length());
283 }
284
292 template<typename R = my_type>
293 R upperred() const {
294 return R::upperred_from(d());
295 }
296
304 template<typename R = my_type>
305 R lowered() const {
306 return R::lowered_from(d());
307 }
308
314 template<bool SkipWS = true, bool AllowPlus = true>
315 std::optional<double> to_double() const noexcept {
316 size_t len = _len();
317 const K* ptr = _str();
318 if constexpr (SkipWS) {
319 while (len && uns_type(*ptr) <= ' ') {
320 len--;
321 ptr++;
322 }
323 }
324 if constexpr (AllowPlus) {
325 if (len && *ptr == K('+')) {
326 ptr++;
327 len--;
328 }
329 }
330 if (!len) {
331 return {};
332 }
333 #ifdef __linux__
334 if constexpr(sizeof(K) == 1 && requires {std::from_chars(std::declval<const K*>(), std::declval<const K*>(), std::declval<double&>()); }) {
335 double d{};
336 if (std::from_chars((const K*)ptr, (const K*)ptr + len, d).ec == std::errc{}) {
337 return d;
338 }
339 return {};
340 }
341 #endif
342 if constexpr (sizeof(K) == 1) {
343 return impl_to_double((const char*)ptr, (const char*)ptr + len);
344 } else if constexpr (sizeof(K) == 2) {
345 return impl_to_double((const char16_t*)ptr, (const char16_t*)ptr + len);
346 } else {
347 return impl_to_double((const char32_t*)ptr, (const char32_t*)ptr + len);
348 }
349 }
350
356 void as_number(double& t) const {
357 auto res = to_double();
358 t = res ? *res : std::nan("0");
359 }
360
372 template<ToIntNumber T>
373 constexpr void as_number(T& t) const {
375 }
376
388 template<typename R = str_piece>
389 constexpr std::optional<R> strip_prefix_iu(str_piece prefix) const {
390 if (starts_with_iu(prefix)) {
391 return R{operator()(prefix.length())};
392 }
393 return {};
394 }
395
407 template<typename R = str_piece>
408 constexpr std::optional<R> strip_suffix_iu(str_piece suffix) const {
409 if (ends_with_iu(suffix)) {
410 return R{operator()(0, -suffix.length())};
411 }
412 return {};
413 }
414
426 template<typename R = str_piece>
427 constexpr R trimmed_prefix_iu(str_piece prefix, size_t max_count = 0) const {
428 str_piece res = *this;
429 while(res.starts_with_iu(prefix)) {
430 res = res(prefix.length());
431 if (--max_count == 0) {
432 break;
433 }
434 }
435 return res;
436 }
437
449 template<typename R = str_piece>
450 constexpr R trimmed_suffix_iu(str_piece suffix) const {
451 str_piece res = *this;
452 while(res.ends_with_iu(suffix)) {
453 res = res(0, -suffix.length());
454 }
455 return res;
456 }
457};
458
459/*
460* Базовая структура с информацией о строке.
461* Это структура для не владеющих строк.
462* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
463* в результате размер класса не увеличивается
464* Basic structure with string information.
465* This is the structure for non-owning strings.
466* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
467* as a result the class size does not increase
468*/
469
480template<typename K>
481struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
482 using symb_type = K;
483 using my_type = simple_str<K>;
484
485 const symb_type* str;
486 size_t len;
487
488 constexpr simple_str() = default;
489
490 constexpr simple_str(str_src<K> src) : str(src.str), len(src.len){}
491
496 template<typename T, size_t N = const_lit_for<K, T>::Count>
497 constexpr simple_str(T&& v) noexcept : str((const K*)v), len(N - 1) {}
502 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
507 template<typename A>
508 constexpr simple_str(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
513 constexpr simple_str(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
518 constexpr size_t length() const noexcept {
519 return len;
520 }
521
525 constexpr const symb_type* symbols() const noexcept {
526 return str;
527 }
528
532 constexpr bool is_empty() const noexcept {
533 return len == 0;
534 }
535
541 constexpr bool is_same(simple_str<K> other) const noexcept {
542 return str == other.str && len == other.len;
543 }
544
550 constexpr bool is_part_of(simple_str<K> other) const noexcept {
551 return str >= other.str && str + len <= other.str + other.len;
552 }
553
561 constexpr K operator[](size_t idx) const {
562 return str[idx];
563 }
564
572 constexpr my_type& remove_prefix(size_t delta) {
573 str += delta;
574 len -= delta;
575 return *this;
576 }
577
585 constexpr my_type& remove_suffix(size_t delta) {
586 len -= delta;
587 return *this;
588 }
589};
590
591template<typename K>
592struct simple_str_selector {
593 using type = simple_str<K>;
594};
595
616template<typename K>
617struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
618 using symb_type = K;
619 using my_type = simple_str_nt<K>;
620 using base = simple_str<K>;
621
622 constexpr static const K empty_string[1] = {0};
623
624 simple_str_nt() = default;
641 template<typename T> requires is_one_of_type<std::remove_cvref_t<T>, const K*, K*>::value
642 constexpr explicit simple_str_nt(T&& p) noexcept {
643 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
644 base::str = base::len ? p : empty_string;
645 }
646
650 template<typename T, size_t N = const_lit_for<K, T>::Count>
651 constexpr simple_str_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
652
657 constexpr simple_str_nt(const K* p, size_t l) noexcept : base(p, l) {}
658
659 template<StrType<K> T>
660 constexpr simple_str_nt(T&& t) {
661 base::str = t.symbols();
662 base::len = t.length();
663 }
668 template<typename A>
669 constexpr simple_str_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
670
671 static const my_type empty_str;
680 constexpr my_type to_nts(size_t from) {
681 if (from > base::len) {
682 from = base::len;
683 }
684 return {base::str + from, base::len - from};
685 }
686};
687
688template<typename K>
689inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
690
691template<typename K>
692using Splitter = SplitterBase<K, simple_str<K>>;
693
694using ssa = simple_str<u8s>;
695using ssb = simple_str<ubs>;
696using ssw = simple_str<wchar_t>;
697using ssu = simple_str<u16s>;
698using ssuu = simple_str<u32s>;
699using stra = simple_str_nt<u8s>;
700using strb = simple_str_nt<ubs>;
701using strw = simple_str_nt<wchar_t>;
702using stru = simple_str_nt<u16s>;
703using struu = simple_str_nt<u32s>;
704
705template<typename Src, typename Dest>
706struct utf_convert_selector;
707
708template<typename Src>
709struct utf_convert_selector<Src, Src> {
710 static size_t need_len(const Src* src, size_t srcLen) {
711 return srcLen;
712 }
713 static size_t convert(const Src* src, size_t srcLen, Src* dest) {
714 ch_traits<Src>::copy(dest, src, srcLen + 1);
715 return srcLen;
716 }
717};
718
719template<>
720struct utf_convert_selector<u8s, u16s> {
721 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
722 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
723};
724
725template<>
726struct utf_convert_selector<u8s, u32s> {
727 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
728 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
729};
730
731template<>
732struct utf_convert_selector<u16s, u8s> {
733 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
734 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
735};
736
737template<>
738struct utf_convert_selector<u16s, u32s> {
739 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
740 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
741};
742
743template<>
744struct utf_convert_selector<u32s, u8s> {
745 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
746 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
747};
748
749template<>
750struct utf_convert_selector<u32s, u16s> {
751 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
752 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
753};
754
769template<typename K, typename Impl>
770class from_utf_convertible {
771protected:
772 from_utf_convertible() = default;
773 using my_type = Impl;
774 /*
775 Эти методы должен реализовать класс-наследник.
776 вызывается только при создании объекта
777 init(size_t size)
778 */
779 template<typename O>
780 requires(!std::is_same_v<O, K>)
781 void init_from_utf_convertible(simple_str<O> init) {
782 using from_t = to_base_char_t<O>;
783 using to_t = to_base_char_t<K>;
784
785 using worker = utf_convert_selector<from_t, to_t>;
786 Impl* d = static_cast<Impl*>(this);
787 size_t len = init.length();
788 if (!len)
789 d->create_empty();
790 else {
791 size_t need = worker::need_len((const from_t*)init.symbols(), len);
792 K* str = d->init(need);
793 str[need] = 0;
794 worker::convert((const from_t*)init.symbols(), len, (to_t*)str);
795 }
796 }
797};
798
807template<typename From, typename To> requires (!std::is_same_v<From, To>)
808struct expr_utf : expr_to_std_string<expr_utf<From, To>> {
809 using symb_type = To;
810 using from_t = to_base_char_t<From>;
811 using to_t = to_base_char_t<To>;
812 using worker = utf_convert_selector<from_t, to_t>;
813
814 simple_str<From> source_;
815
816 constexpr expr_utf(simple_str<From> source) : source_(source){}
817
818 size_t length() const noexcept {
819 return worker::need_len((const from_t*)source_.symbols(), source_.length());
820 }
821 To* place(To* ptr) const noexcept {
822 return ptr + worker::convert((const from_t*)source_.symbols(), source_.length(), (to_t*)ptr);
823 }
824};
825
838template<typename To, typename From> requires (!std::is_same_v<From, To>)
840 return {from};
841}
842
843template<is_one_of_char_v K, bool upper>
844struct expr_change_case : expr_to_std_string<expr_change_case<K, upper>>{
845 using symb_type = K;
846
847 str_src<K> src_;
848 mutable size_t len_{};
849
850 template<StrSource S>
851 expr_change_case(S&& s) : src_(std::forward<S>(s)){}
852
853 constexpr size_t length() const noexcept {
854 if constexpr (sizeof(K) > 1) {
855 return src_.length();
856 } else {
857 if constexpr (upper) {
858 len_ = unicode_traits<K>::upper_len(src_.str, src_.len);
859 } else {
860 len_ = unicode_traits<K>::lower_len(src_.str, src_.len);
861 }
862 return len_;
863 }
864 }
865 constexpr K* place(K* ptr) const noexcept {
866 if constexpr (sizeof(K) > 1) {
867 if constexpr (upper) {
868 unicode_traits<K>::upper(src_.str, src_.len, ptr);
869 } else {
870 unicode_traits<K>::lower(src_.str, src_.len, ptr);
871 }
872 return ptr + src_.len;
873 } else {
874 const K* src = src_.str;
875 if constexpr (upper) {
876 unicode_traits<K>::upper(src, src_.len, ptr, len_);
877 } else {
878 unicode_traits<K>::lower(src, src_.len, ptr, len_);
879 }
880 return ptr;
881 }
882 }
883};
884
899template<is_one_of_char_v K>
900struct e_upper : expr_change_case<K, true> {
901 using base = expr_change_case<K, true>;
902 using base::base;
903};
904
905template<StrSource S>
907
922template<is_one_of_char_v K>
923struct e_lower : expr_change_case<K, false> {
924 using base = expr_change_case<K, false>;
925 using base::base;
926};
927
928template<StrSource S>
930
935template<typename A, typename K>
936concept storable_str = requires {
937 A::is_str_storable == true;
938 std::is_same_v<typename A::symb_type, K>;
939};
940
945template<typename A, typename K>
946concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
947
952template<typename A, typename K>
954
991template<typename K, typename Impl, typename Allocator>
992class str_storable : protected Allocator {
993public:
994 using my_type = Impl;
995 using traits = ch_traits<K>;
996 using allocator_t = Allocator;
997 using s_str = simple_str<K>;
998 using s_str_nt = simple_str_nt<K>;
999
1000protected:
1005 constexpr allocator_t& allocator() {
1006 return *static_cast<Allocator*>(this);
1007 }
1008 constexpr const allocator_t& allocator() const {
1009 return *static_cast<const Allocator*>(this);
1010 }
1011
1012 using uni = unicode_traits<K>;
1013
1014 constexpr Impl& d() noexcept {
1015 return *static_cast<Impl*>(this);
1016 }
1017 constexpr const Impl& d() const noexcept {
1018 return *static_cast<const Impl*>(this);
1019 }
1020
1027 template<typename... Args>
1028 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
1029
1036 constexpr void init_from_str_other(s_str other) {
1037 if (other.length()) {
1038 K* ptr = d().init(other.length());
1039 traits::copy(ptr, other.symbols(), other.length());
1040 ptr[other.length()] = 0;
1041 } else
1042 d().create_empty();
1043 }
1044
1052 constexpr void init_str_repeat(size_t repeat, s_str pattern) {
1053 size_t l = pattern.length(), allLen = l * repeat;
1054 if (allLen) {
1055 K* ptr = d().init(allLen);
1056 for (size_t i = 0; i < repeat; i++) {
1057 traits::copy(ptr, pattern.symbols(), l);
1058 ptr += l;
1059 }
1060 *ptr = 0;
1061 } else
1062 d().create_empty();
1063 }
1064
1072 constexpr void init_symb_repeat(size_t count, K pad) {
1073 if (count) {
1074 K* str = d().init(count);
1075 traits::assign(str, count, pad);
1076 str[count] = 0;
1077 } else
1078 d().create_empty();
1079 }
1080
1092 template<StrExprForType<K> A>
1093 constexpr void init_str_expr(const A& expr) {
1094 size_t len = expr.length();
1095 if (len)
1096 *expr.place((typename A::symb_type*)d().init(len)) = 0;
1097 else
1098 d().create_empty();
1099 }
1100
1114 template<StrType<K> From>
1115 void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
1116 auto findes = f.find_all(pattern, offset, maxCount);
1117 if (!findes.size()) {
1118 new (this) my_type{f};
1119 return;
1120 }
1121 size_t srcLen = f.length();
1122 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
1123
1124 if (!newSize) {
1125 new (this) my_type{};
1126 return;
1127 }
1128
1129 K* ptr = d().init(newSize);
1130 const K* src = f.symbols();
1131 size_t from = 0;
1132 for (const auto& s: findes) {
1133 size_t copyLen = s - from;
1134 if (copyLen) {
1135 traits::copy(ptr, src + from, copyLen);
1136 ptr += copyLen;
1137 }
1138 if (repl.len) {
1139 traits::copy(ptr, repl.str, repl.len);
1140 ptr += repl.len;
1141 }
1142 from = s + pattern.len;
1143 }
1144 srcLen -= from;
1145 if (srcLen) {
1146 traits::copy(ptr, src + from, srcLen);
1147 ptr += srcLen;
1148 }
1149 *ptr = 0;
1150 }
1151
1152public:
1153
1154 inline static constexpr bool is_str_storable = true;
1161 constexpr operator const K*() const noexcept {
1162 return d().symbols();
1163 }
1164
1172 constexpr s_str_nt to_nts(size_t from = 0) const {
1173 size_t len = d().length();
1174 if (from >= len) {
1175 from = len;
1176 }
1177 return {d().symbols() + from, len - from};
1178 }
1179
1185 constexpr operator s_str_nt() const {
1186 return {d().symbols(), d().length()};
1187 }
1188
1226 template<typename T, typename... Args>
1227 requires std::is_constructible_v<allocator_t, Args...>
1228 static my_type join(const T& strings, s_str delimiter, bool tail = false, bool skip_empty = false, Args&&... args) {
1229 my_type result(std::forward<Args>(args)...);
1230 if (strings.size()) {
1231 if (strings.size() == 1 && (!delimiter.length() || !tail)) {
1232 result = strings.front();
1233 } else {
1234 size_t commonLen = 0;
1235 for (const auto& t: strings) {
1236 size_t len = t.length();
1237 if (len > 0 || !skip_empty) {
1238 if (commonLen > 0) {
1239 commonLen += delimiter.len;
1240 }
1241 commonLen += len;
1242 }
1243 }
1244 commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
1245 if (commonLen) {
1246 K* ptr = result.init(commonLen);
1247 K* write = ptr;
1248 for (const auto& t: strings) {
1249 size_t copyLen = t.length();
1250 if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
1251 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1252 write += delimiter.len;
1253 }
1254 ch_traits<K>::copy(write, t.symbols(), copyLen);
1255 write += copyLen;
1256 }
1257 if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
1258 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1259 write += delimiter.len;
1260 }
1261 *write = 0;
1262 } else {
1263 result.create_empty();
1264 }
1265 }
1266 }
1267 return result;
1268 }
1269
1277 template<StrType<K> From, typename... Args>
1278 requires std::is_constructible_v<allocator_t, Args...>
1279 static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
1280 return my_type{e_ascii_upper<K>{f}, std::forward<Args>(args)...};
1281 }
1282
1290 template<StrType<K> From, typename... Args>
1291 requires std::is_constructible_v<allocator_t, Args...>
1292 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
1293 return my_type{e_ascii_lower<K>{f}, std::forward<Args>(args)...};
1294 }
1295
1307 template<StrType<K> From, typename... Args>
1308 requires std::is_constructible_v<allocator_t, Args...>
1309 static my_type upperred_from(const From& f, Args&&... args) {
1310 return my_type{e_upper<K>{f}, std::forward<Args>(args)...};
1311 }
1312
1324 template<StrType<K> From, typename... Args>
1325 requires std::is_constructible_v<allocator_t, Args...>
1326 static my_type lowered_from(const From& f, Args&&... args) {
1327 return my_type{e_lower<K>{f}, std::forward<Args>(args)...};
1328 }
1329
1345 template<StrType<K> From, typename... Args>
1346 requires std::is_constructible_v<allocator_t, Args...>
1347 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
1348 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
1349 }
1350};
1351
1356template<typename A>
1357concept AllocatableNoSized = requires(A& a, size_t size, void* void_ptr) {
1358 { a.allocate(size) } -> std::same_as<void*>;
1359 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
1360};
1361
1366template<typename A>
1367concept AllocatableSized = requires(A& a, size_t size, void* void_ptr) {
1368 { a.allocate(size) } -> std::same_as<void*>;
1369 { a.deallocate(void_ptr, size) } noexcept -> std::same_as<void>;
1370};
1371
1376template<typename A>
1378
1379struct printf_selector {
1380 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
1381 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
1382 if constexpr (sizeof(K) == 1) {
1383 #ifndef _WIN32
1384 return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
1385 #else
1386 // Поддерживает позиционные параметры
1387 // Supports positional parameters
1388 return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1389 #endif
1390 } else {
1391 #ifndef _WIN32
1392 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1393 #else
1394 // Поддерживает позиционные параметры
1395 // Supports positional parameters
1396 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1397 #endif
1398 }
1399 }
1400 template<typename K> requires (is_one_of_std_char_v<K>)
1401 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
1402 if constexpr (std::is_same_v<K, u8s>) {
1403 #ifndef _WIN32
1404 return std::vsnprintf(buffer, count, format, args);
1405 #else
1406 // Поддерживает позиционные параметры
1407 // Supports positional parameters
1408 return _vsprintf_p(buffer, count, format, args);
1409 #endif
1410 } else {
1411 #ifndef _WIN32
1412 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
1413 #else
1414 // Поддерживает позиционные параметры
1415 // Supports positional parameters
1416 return _vswprintf_p(buffer, count, format, args);
1417 #endif
1418 }
1419 }
1420};
1421
1422inline size_t grow2(size_t ret, size_t currentCapacity) {
1423 return ret <= currentCapacity ? ret : ret * 2;
1424}
1425
1464template<typename K, typename Impl>
1466public:
1467 using my_type = Impl;
1468
1469private:
1470 Impl& d() {
1471 return *static_cast<Impl*>(this);
1472 }
1473 const Impl& d() const {
1474 return *static_cast<const Impl*>(this);
1475 }
1476 size_t _len() const noexcept {
1477 return d().length();
1478 }
1479 const K* _str() const noexcept {
1480 return d().symbols();
1481 }
1482 using str_piece = simple_str<K>;
1483 using symb_type = K;
1484 using traits = ch_traits<K>;
1485 using uni = unicode_traits<K>;
1486 using uns_type = std::make_unsigned_t<K>;
1487
1488 template<typename Op>
1489 Impl& make_trim_op(const Op& op) {
1490 str_piece me = d(), pos = op(me);
1491 if (me.length() != pos.length()) {
1492 if (me.symbols() != pos.symbols())
1493 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
1494 d().set_size(pos.length());
1495 }
1496 return d();
1497 }
1498
1499 template<auto Op>
1500 Impl& commonChangeCase() {
1501 size_t len = _len();
1502 if (len)
1503 Op(_str(), len, str());
1504 return d();
1505 }
1506 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
1507 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
1508 // GCC still does not allow full specialization of a nested class within a class,
1509 // only partial. Resources additive unused parameter template.
1510 template<typename T, bool Dummy = true>
1511 struct CaseTraits {
1512 static Impl& upper(Impl& obj) {
1513 return obj.template commonChangeCase<unicode_traits<K>::upper>();
1514 }
1515 static Impl& lower(Impl& obj) {
1516 return obj.template commonChangeCase<unicode_traits<K>::lower>();
1517 }
1518 };
1519
1520 template<auto Op>
1521 Impl& utf8CaseChange() {
1522 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1523 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1524 size_t len = _len();
1525 if (len) {
1526 u8s* writePos = str();
1527 const u8s *startData = writePos, *readPos = writePos;
1528 size_t newLen = Op(readPos, len, writePos, len);
1529 if (newLen < len) {
1530 // Строка просто укоротилась
1531 // The string was simply shortened
1532 d().set_size(newLen);
1533 } else if (newLen > len) {
1534 // Строка не влезла в буфер.
1535 // The string did not fit into the buffer.
1536 size_t readed = static_cast<size_t>(readPos - startData);
1537 size_t writed = static_cast<size_t>(writePos - startData);
1538 d().set_size(newLen);
1539 startData = str(); // при изменении размера могло изменится | may change when resizing
1540 readPos = startData + readed;
1541 writePos = const_cast<u8s*>(startData) + writed;
1542 Op(readPos, len - readed, writePos, newLen - writed);
1543 }
1544 }
1545 return d();
1546 }
1547 template<bool Dummy>
1548 struct CaseTraits<u8s, Dummy> {
1549 static Impl& upper(Impl& obj) {
1550 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
1551 }
1552 static Impl& lower(Impl& obj) {
1553 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
1554 }
1555 };
1556
1557 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
1558 Impl& makeTrim(T&& pattern) {
1559 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
1560 }
1561
1562 template<TrimSides S, bool withSpaces>
1563 Impl& makeTrim(str_piece pattern) {
1564 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
1565 }
1566
1567public:
1574 K* str() noexcept {
1575 return d().str();
1576 }
1577
1583 explicit operator K*() noexcept {
1584 return str();
1585 }
1586
1592 Impl& trim() {
1593 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
1594 }
1595
1601 Impl& trim_left() {
1602 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
1603 }
1604
1610 Impl& trim_right() {
1611 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
1612 }
1613
1621 template<typename T, size_t N = const_lit_for<K, T>::Count>
1622 requires is_const_pattern<N>
1623 Impl& trim(T&& pattern) {
1624 return makeTrim<TrimSides::TrimAll, false>(pattern);
1625 }
1626
1634 template<typename T, size_t N = const_lit_for<K, T>::Count>
1635 requires is_const_pattern<N>
1636 Impl& trim_left(T&& pattern) {
1637 return makeTrim<TrimSides::TrimLeft, false>(pattern);
1638 }
1639
1647 template<typename T, size_t N = const_lit_for<K, T>::Count>
1648 requires is_const_pattern<N>
1649 Impl& trim_right(T&& pattern) {
1650 return makeTrim<TrimSides::TrimRight, false>(pattern);
1651 }
1652
1660 template<typename T, size_t N = const_lit_for<K, T>::Count>
1661 requires is_const_pattern<N>
1662 Impl& trim_with_spaces(T&& pattern) {
1663 return makeTrim<TrimSides::TrimAll, true>(pattern);
1664 }
1665
1673 template<typename T, size_t N = const_lit_for<K, T>::Count>
1674 requires is_const_pattern<N>
1675 Impl& trim_left_with_spaces(T&& pattern) {
1676 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1677 }
1678
1686 template<typename T, size_t N = const_lit_for<K, T>::Count>
1687 requires is_const_pattern<N>
1688 Impl& trim_right_with_wpaces(T&& pattern) {
1689 return makeTrim<TrimSides::TrimRight, true>(pattern);
1690 }
1691
1699 Impl& trim(str_piece pattern) {
1700 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
1701 }
1702
1710 Impl& trim_left(str_piece pattern) {
1711 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
1712 }
1713
1721 Impl& trim_right(str_piece pattern) {
1722 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
1723 }
1724
1732 Impl& trim_with_spaces(str_piece pattern) {
1733 return makeTrim<TrimSides::TrimAll, true>(pattern);
1734 }
1735
1743 Impl& trim_left_with_spaces(str_piece pattern) {
1744 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1745 }
1746
1754 Impl& trim_right_with_spaces(str_piece pattern) {
1755 return makeTrim<TrimSides::TrimRight, true>(pattern);
1756 }
1757
1764 K* ptr = str();
1765 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1766 K s = *ptr;
1767 if (isAsciiLower(s))
1768 *ptr = s & ~0x20;
1769 }
1770 return d();
1771 }
1772
1779 K* ptr = str();
1780 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1781 K s = *ptr;
1782 if (isAsciiUpper(s))
1783 *ptr = s | 0x20;
1784 }
1785 return d();
1786 }
1787
1797 Impl& upper() {
1798 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1799 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1800 return CaseTraits<K>::upper(d());
1801 }
1802
1812 Impl& lower() {
1813 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1814 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1815 return CaseTraits<K>::lower(d());
1816 }
1817
1818private:
1819 template<typename T>
1820 Impl& changeImpl(size_t from, size_t len, T expr) {
1821 size_t myLen = _len();
1822 if (from > myLen) {
1823 from = myLen;
1824 }
1825 if (from + len > myLen) {
1826 len = myLen - from;
1827 }
1828 K* buffer = str();
1829 size_t otherLen = expr.length();
1830 if (len == otherLen) {
1831 expr.place(buffer + from);
1832 } else {
1833 size_t tailLen = myLen - from - len;
1834 if (len > otherLen) {
1835 expr.place(buffer + from);
1836 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1837 d().set_size(myLen - (len - otherLen));
1838 } else {
1839 buffer = d().set_size(myLen + otherLen - len);
1840 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1841 expr.place(buffer + from);
1842 }
1843 }
1844 return d();
1845 }
1846
1847 template<typename T>
1848 Impl& appendImpl(T expr) {
1849 if (size_t len = expr.length(); len) {
1850 size_t size = _len();
1851 expr.place(d().set_size(size + len) + size);
1852 }
1853 return d();
1854 }
1855
1856 template<typename T>
1857 Impl& appendFromImpl(size_t pos, T expr) {
1858 if (pos > _len())
1859 pos = _len();
1860 if (size_t len = expr.length())
1861 expr.place(d().set_size(pos + len) + pos);
1862 else
1863 d().set_size(pos);
1864 return d();
1865 }
1866
1867public:
1868 inline static constexpr bool is_str_mutable = true;
1877 Impl& append(str_piece other) {
1878 return appendImpl<str_piece>(other);
1879 }
1880
1888 template<StrExprForType<K> A>
1889 Impl& append(const A& expr) {
1890 return appendImpl<const A&>(expr);
1891 }
1892
1900 Impl& operator+=(str_piece other) {
1901 return appendImpl<str_piece>(other);
1902 }
1903
1911 template<StrExprForType<K> A>
1912 Impl& operator+=(const A& expr) {
1913 return appendImpl<const A&>(expr);
1914 }
1915
1929 Impl& append_in(size_t pos, str_piece other) {
1930 return appendFromImpl<str_piece>(pos, other);
1931 }
1932
1946 template<StrExprForType<K> A>
1947 Impl& append_in(size_t pos, const A& expr) {
1948 return appendFromImpl<const A&>(pos, expr);
1949 }
1950
1962 Impl& change(size_t from, size_t len, str_piece other) {
1963 return changeImpl<str_piece>(from, len, other);
1964 }
1965
1977 template<StrExprForType<K> A>
1978 Impl& change(size_t from, size_t len, const A& expr) {
1979 return changeImpl<const A&>(from, len, expr);
1980 }
1981
1991 Impl& insert(size_t to, str_piece other) {
1992 return changeImpl<str_piece>(to, 0, other);
1993 }
1994
2004 template<StrExprForType<K> A>
2005 Impl& insert(size_t to, const A& expr) {
2006 return changeImpl<const A&>(to, 0, expr);
2007 }
2008
2018 Impl& remove(size_t from, size_t len) {
2019 return changeImpl<const empty_expr<K>&>(from, len, {});
2020 }
2021
2029 Impl& prepend(str_piece other) {
2030 return changeImpl<str_piece>(0, 0, other);
2031 }
2032
2040 template<StrExprForType<K> A>
2041 Impl& prepend(const A& expr) {
2042 return changeImpl<const A&>(0, 0, expr);
2043 }
2044
2058 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2059 offset = d().find(pattern, offset);
2060 if (offset == str::npos) {
2061 return d();
2062 }
2063 if (!maxCount)
2064 maxCount--;
2065 size_t replLength = repl.length(), patternLength = pattern.length();
2066
2067 if (patternLength == replLength) {
2068 // Заменяем inplace на подстроку такой же длины
2069 // Replace inplace with a substring of the same length
2070 K* ptr = str();
2071 while (maxCount--) {
2072 traits::copy(ptr + offset, repl.symbols(), replLength);
2073 offset = d().find(pattern, offset + replLength);// replLength == patternLength
2074 if (offset == str::npos)
2075 break;
2076 }
2077 } else if (patternLength > replLength) {
2078 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
2079 // Replace with a shorter piece, the length of the text will decrease, go from left to right
2080 K* ptr = str();
2081 traits::copy(ptr + offset, repl.symbols(), replLength);
2082 size_t posWrite = offset + replLength;
2083 offset += patternLength;
2084
2085 while (--maxCount) {
2086 size_t idx = d().find(pattern, offset);
2087 if (idx == str::npos)
2088 break;
2089 size_t lenOfPiece = idx - offset;
2090 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
2091 posWrite += lenOfPiece;
2092 traits::copy(ptr + posWrite, repl.symbols(), replLength);
2093 posWrite += replLength;
2094 offset = idx + patternLength;
2095 }
2096 size_t tailLen = _len() - offset;
2097 traits::move(ptr + posWrite, ptr + offset, tailLen);
2098 d().set_size(posWrite + tailLen);
2099 } else {
2100 struct replace_grow_helper {
2101 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
2102 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
2103 my_type& source;
2104 const str_piece pattern;
2105 const str_piece repl;
2106 size_t maxCount;
2107 const size_t delta;
2108 size_t all_delta{};
2109 K* reserve_for_copy{};
2110 size_t end_of_piece{};
2111 size_t total_length{};
2112
2113 void replace(size_t offset) {
2114 size_t found[16] = {offset};
2115 maxCount--;
2116 offset += pattern.length();
2117 all_delta += delta;
2118 size_t idx = 1;
2119 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
2120 found[idx] = source.find(pattern, offset);
2121 if (found[idx] == str::npos) {
2122 break;
2123 }
2124 offset = found[idx] + pattern.length();
2125 all_delta += delta;
2126 }
2127 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
2128 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
2129 }
2130 // Теперь делаем свои замены
2131 // Now we make our replacements
2132 if (!reserve_for_copy) {
2133 // Только начинаем
2134 // Just getting started
2135 end_of_piece = source.length();
2136 total_length = end_of_piece + all_delta;
2137 reserve_for_copy = source.alloc_for_copy(total_length);
2138 }
2139 K* dst_start = reserve_for_copy;
2140 const K* src_start = source.symbols();
2141 while(idx-- > 0) {
2142 size_t pos = found[idx] + pattern.length();
2143 size_t lenOfPiece = end_of_piece - pos;
2144 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
2145 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
2146 all_delta -= delta;
2147 end_of_piece = found[idx];
2148 }
2149 if (!all_delta && reserve_for_copy != src_start) {
2150 ch_traits<K>::copy(dst_start, src_start, found[0]);
2151 }
2152 }
2153 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
2154 helper.replace(offset);
2155 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
2156 }
2157 return d();
2158 }
2159
2175 template<StrType<K> From>
2176 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2177 if (pattern.length() >= repl.length()) {
2178 K* dst = d().reserve_no_preserve(f.length());
2179 const K* src = f.symbols();
2180 size_t delta = 0;
2181 if (maxCount == 0) {
2182 maxCount--;
2183 }
2184 size_t src_length = f.length(), start = 0;
2185 while (maxCount--) {
2186 offset = f.find(pattern, offset);
2187 if (offset == str::npos) {
2188 break;
2189 }
2190 size_t piece_len = offset - start;
2191 if (piece_len) {
2192 ch_traits<K>::copy(dst, src + start, piece_len);
2193 dst += piece_len;
2194 }
2195 if (repl.length()) {
2196 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
2197 dst += repl.length();
2198 }
2199 delta += pattern.length() - repl.length();
2200 offset += pattern.length();
2201 start = offset;
2202 }
2203 if (start < src_length) {
2204 ch_traits<K>::copy(dst, src + start, src_length - start);
2205 }
2206 d().set_size(src_length - delta);
2207 } else {
2208 d() = f;
2209 replace(pattern, repl, offset, maxCount);
2210 }
2211 return d();
2212 }
2213
2237 template<typename Op>
2238 Impl& fill(size_t from, const Op& fillFunction) {
2239 size_t size = _len();
2240 if (from > size)
2241 from = size;
2242 size_t capacity = d().capacity();
2243 K* ptr = str();
2244 capacity -= from;
2245 for (;;) {
2246 size_t needSize = static_cast<size_t>(fillFunction(ptr + from, capacity));
2247 if (capacity >= needSize) {
2248 d().set_size(from + needSize);
2249 break;
2250 }
2251 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
2252 capacity = d().capacity() - from;
2253 }
2254 return d();
2255 }
2256
2264 template<typename Op>
2265 requires std::is_invocable_v<Op, K*, size_t>
2266 Impl& operator<<(const Op& fillFunction) {
2267 return fill(0, fillFunction);
2268 }
2269
2277 template<typename Op>
2278 requires std::is_invocable_v<Op, K*, size_t>
2279 Impl& operator<<=(const Op& fillFunction) {
2280 return fill(_len(), fillFunction);
2281 }
2282
2290 template<typename Op>
2291 requires std::is_invocable_v<Op, my_type&>
2292 Impl& operator<<(const Op& fillFunction) {
2293 fillFunction(d());
2294 return d();
2295 }
2296
2310 template<typename... T> requires (is_one_of_std_char_v<K>)
2311 Impl& printf_from(size_t from, const K* format, T&&... args) {
2312 size_t size = _len();
2313 if (from > size)
2314 from = size;
2315 size_t capacity = d().capacity();
2316 K* ptr = str();
2317 capacity -= from;
2318
2319 int result = 0;
2320 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
2321 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
2322 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
2323 // Here's a dirty hack for u8s and wide_char. u8s version of snprintf immediately returns the size of the required buffer if it is small
2324 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
2325 // Therefore, for them you need to stupidly increase the buffer at random until it fits
2326 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
2327 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2328 if (result > (int)capacity) {
2329 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
2330 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
2331 }
2332 } else {
2333 for (;;) {
2334 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2335 if (result < 0) {
2336 // Не хватило буфера или ошибка конвертации.
2337 // Попробуем увеличить буфер в два раза
2338 // Not enough buffer or conversion error.
2339 // Let's try to double the buffer
2340 capacity *= 2;
2341 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
2342 } else
2343 break;
2344 }
2345 }
2346 if (result < 0)
2347 d().set_size(static_cast<size_t>(traits::length(_str())));
2348 else
2349 d().set_size(from + result);
2350 return d();
2351 }
2352
2364 template<typename... T> requires (is_one_of_std_char_v<K>)
2365 Impl& printf(const K* format, T&&... args) {
2366 return printf_from(0, format, std::forward<T>(args)...);
2367 }
2368
2380 template<typename... T> requires (is_one_of_std_char_v<K>)
2381 Impl& append_printf(const K* format, T&&... args) {
2382 return printf_from(_len(), format, std::forward<T>(args)...);
2383 }
2384
2385 struct writer {
2386 my_type* store;
2387 K* ptr;
2388 const K* end;
2389 size_t max_write;
2390 size_t writed = 0;
2391 inline static K pad;
2392 K& operator*() const {
2393 return *ptr;
2394 }
2395 writer& operator++() {
2396 if (writed < max_write) {
2397 ++ptr;
2398 if (ptr == end) {
2399 size_t l = ptr - store->begin();
2400 store->set_size(l);
2401 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
2402 end = store->end();
2403 }
2404 } else {
2405 ptr = &pad;
2406 }
2407 return *this;
2408 }
2409 writer operator++(int) {
2410 auto ret = *this;
2411 operator++();
2412 return ret;
2413 }
2414
2415 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
2416 writer() = default;
2417 writer(const writer&) = delete;
2418 writer& operator=(const writer&) noexcept = delete;
2419 writer(writer&&) noexcept = default;
2420 writer& operator=(writer&&) noexcept = default;
2421 using difference_type = int;
2422 };
2423 using fmt_type = to_std_char_t<K>;
2438 template<typename... T> requires (is_one_of_std_char_v<K>)
2439 Impl& format_from(size_t from, const FmtString<fmt_type, T...>& format, T&&... args) {
2440 size_t size = _len();
2441 if (from > size)
2442 from = size;
2443 size_t capacity = d().capacity();
2444 K* ptr = str();
2445
2446 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
2447 std::forward<decltype(format)>(format), std::forward<T>(args)...);
2448 d().set_size(result.ptr - _str());
2449 return d();
2450 }
2451
2467 template<typename... T> requires (is_one_of_std_char_v<K>)
2468 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
2469 size_t size = _len();
2470 if (from > size)
2471 from = size;
2472 size_t capacity = d().capacity();
2473 K* ptr = str();
2474
2475 if constexpr (std::is_same_v<K, u8s>) {
2476 auto result = std::vformat_to(
2477 writer{d(), ptr + from, ptr + capacity, max_write},
2478 std::basic_string_view<K>{format.symbols(), format.length()},
2479 std::make_format_args(args...));
2480 d().set_size(result.ptr - _str());
2481 } else {
2482 auto result = std::vformat_to(
2483 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
2484 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
2485 std::make_wformat_args(std::forward<T>(args)...));
2486 d().set_size(result.ptr - _str());
2487 }
2488 return d();
2489 }
2490
2502 template<typename... T> requires (is_one_of_std_char_v<K>)
2503 Impl& format(const FmtString<fmt_type, T...>& pattern, T&&... args) {
2504 return format_from(0, pattern, std::forward<T>(args)...);
2505 }
2506
2518 template<typename... T> requires (is_one_of_std_char_v<K>)
2519 Impl& append_formatted(const FmtString<fmt_type, T...>& format, T&&... args) {
2520 return format_from(_len(), format, std::forward<T>(args)...);
2521 }
2522
2534 template<typename... T> requires (is_one_of_std_char_v<K>)
2535 Impl& vformat(str_piece format, T&&... args) {
2536 return vformat_from(0, -1, format, std::forward<T>(args)...);
2537 }
2538
2550 template<typename... T> requires (is_one_of_std_char_v<K>)
2551 Impl& append_vformatted(str_piece format, T&&... args) {
2552 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
2553 }
2554
2568 template<typename... T> requires (is_one_of_std_char_v<K>)
2569 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
2570 return vformat_from(0, max_write, format, std::forward<T>(args)...);
2571 }
2572
2586 template<typename... T> requires (is_one_of_std_char_v<K>)
2587 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
2588 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
2589 }
2590
2600 template<typename Op, typename... Args>
2601 Impl& with(const Op& fillFunction, Args&&... args) {
2602 fillFunction(d(), std::forward<Args>(args)...);
2603 return d();
2604 }
2605};
2606
2607template<typename K>
2608struct SharedStringData {
2609 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
2610 inline static constexpr size_t mask = (size_t(-1)) >> 1, check = ~mask;
2611
2612 SharedStringData() {
2613 ref_ = 1;
2614 }
2615 SharedStringData(size_t capacity) {
2616 ref_ = 1 | check;
2617 reinterpret_cast<size_t*>(this)[-1] = (capacity + 1) * sizeof(K) + sizeof(*this) + sizeof(size_t);
2618 }
2619 K* str() const {
2620 return (K*)(this + 1);
2621 }
2622 void incr() {
2623 ref_.fetch_add(1, std::memory_order_relaxed);
2624 }
2625 void decr(AllocatableNoSized auto& allocator) {
2626 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2627 if (val == 1) {
2628 allocator.deallocate(this);
2629 }
2630 }
2631 void decr(AllocatableSized auto& allocator, size_t len) {
2632 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2633 if ((val & mask) == 1) {
2634 if (val & check) {
2635 // Это блок от перемещённой lstring, перед ним записан размер выделенной памяти.
2636 // This is a block from the moved lstring, the size of the allocated memory is written in front of it.
2637 size_t* ptr = reinterpret_cast<size_t*>(this);
2638 ptr--;
2639 allocator.deallocate(ptr, *ptr);
2640 } else {
2641 allocator.deallocate(this, (len + 1) * sizeof(K) + sizeof(*this));
2642 }
2643 }
2644 }
2645 static SharedStringData* create(size_t l, Allocatable auto& allocator) {
2646 size_t size = sizeof(SharedStringData) + (l + 1) * sizeof(K);
2647 return new (allocator.allocate(size)) SharedStringData();
2648 }
2649 static SharedStringData* from_str(const K* p) {
2650 return (SharedStringData*)p - 1;
2651 }
2652 K* place(K* p, size_t len) {
2653 ch_traits<K>::copy(p, str(), len);
2654 return p + len;
2655 }
2656};
2657
2658// Дефолтный аллокатор для строк, может работать статически
2659// Default allocator for strings, can work statically
2660class string_common_allocator {
2661public:
2662 void* allocate(size_t bytes) {
2663 return ::operator new(bytes);
2664 }
2665 void deallocate(void* address) noexcept {
2666 ::operator delete(address);
2667 }
2668};
2669
2670string_common_allocator default_string_allocator_selector(...);
2671// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
2672// объявите функцию
2673// ваш_тип_аллокатора default_string_allocator_selector(int);
2674// If you want to set your default allocator for strings, before including sstring.h
2675// declare a function
2676// your_allocator_type default_string_allocator_selector(int);
2677using allocator_string = decltype(default_string_allocator_selector(int(0)));
2678
2679template<typename K, Allocatable Allocator>
2680class sstring;
2681
2682/*
2683* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
2684* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
2685* Укажем ему явно.
2686* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
2687* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
2688* Let's tell him explicitly.
2689*/
2690
2711template<typename K, size_t N, bool forShared = false, Allocatable Allocator = allocator_string>
2712class decl_empty_bases lstring :
2713 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
2714 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
2715 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
2716 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
2717 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
2718public:
2719 using symb_type = K;
2721 using allocator_t = Allocator;
2722
2723 enum : size_t {
2725 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
2726 };
2727
2728protected:
2729 enum : size_t {
2730 extra = forShared ? sizeof(SharedStringData<K>) + (AllocatableSized<Allocator> ? sizeof(size_t) : 0) : 0,
2731 };
2732
2733 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
2734 using base_storable = str_storable<K, my_type, Allocator>;
2735 using base_mutable = str_mutable<K, my_type>;
2736 using base_utf = from_utf_convertible<K, my_type>;
2737 using traits = ch_traits<K>;
2738 using s_str = base_storable::s_str;
2739
2740 friend base_storable;
2741 friend base_mutable;
2742 friend base_utf;
2743 friend class sstring<K, Allocator>;
2744
2745 K* data_;
2746 size_t size_;
2747
2748 union {
2749 size_t capacity_;
2750 K local_[LocalCapacity + 1];
2751 };
2752
2753 constexpr void create_empty() {
2754 data_ = local_;
2755 size_ = 0;
2756 local_[0] = 0;
2757 }
2758 constexpr static size_t calc_capacity(size_t s) {
2759 const int al = alignof(std::max_align_t) < 16 ? 16 : alignof(std::max_align_t);
2760 size_t real_need = (s + 1) * sizeof(K) + extra;
2761 size_t aligned_alloced = (real_need + al - 1) / al * al;
2762 return (aligned_alloced - extra) / sizeof(K) - 1;
2763 }
2764
2765 constexpr K* init(size_t s) {
2766 size_t need_cap = s;
2767 if (need_cap > LocalCapacity) {
2768 need_cap = calc_capacity(s);
2769 data_ = alloc_place(need_cap);
2770 capacity_ = need_cap;
2771 } else {
2772 data_ = local_;
2773 }
2774 size_ = s;
2775 return str();
2776 }
2777 // Методы для себя | Methods for yourself
2778 constexpr bool is_alloced() const noexcept {
2779 return data_ != local_;
2780 }
2781
2782 constexpr void dealloc() {
2783 if (is_alloced()) {
2784 if constexpr (AllocatableNoSized<Allocator>) {
2785 base_storable::allocator().deallocate(to_real_address(data_));
2786 } else {
2787 base_storable::allocator().deallocate(to_real_address(data_), (capacity_ + 1) * sizeof(K) + extra);
2788 }
2789 data_ = local_;
2790 }
2791 }
2792
2793 constexpr static K* to_real_address(void* ptr) {
2794 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
2795 }
2796 constexpr static K* from_real_address(void* ptr) {
2797 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
2798 }
2799
2800 constexpr K* alloc_place(size_t newSize) {
2801 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
2802 }
2803 // Вызывается при replace, когда меняют на более длинную замену
2804 // Called on replace when changing to a longer replacement
2805 constexpr K* alloc_for_copy(size_t newSize) {
2806 if (capacity() >= newSize) {
2807 // Замена войдёт в текущий буфер
2808 // Replacement will go into the current buffer
2809 return data_;
2810 }
2811 return alloc_place(calc_capacity(newSize));
2812 }
2813 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
2814 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
2815 constexpr void set_from_copy(K* ptr, size_t newSize) {
2816 if (ptr != data_) {
2817 // Да, копировали в новый буфер
2818 // Yes, copied to a new buffer
2819 dealloc();
2820 data_ = ptr;
2821 capacity_ = calc_capacity(newSize);
2822 }
2823 size_ = newSize;
2824 data_[newSize] = 0;
2825 }
2826 constexpr void copy_from_another(K* buf, const K* src, size_t size) {
2827 // Реальный размер буфера всегда кратен sizeof(void*), поэтому копируя по sizeof(void*) байтов, мы не выйдем за пределы буфера
2828 // The actual buffer size is always a multiple of sizeof(void*), so by copying sizeof(void*) bytes at a time, we won't go beyond the buffer's limits.
2829 size_t need_copy_bytes = (size + 1) * sizeof(K);
2830 size_t cnt = (need_copy_bytes + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*);
2831 traits::copy(buf, src, cnt / sizeof(K));
2832 }
2833public:
2840 template<typename... Args>
2841 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
2842 constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2843 : base_storable(std::forward<Args>(args)...) {
2844 create_empty();
2845 }
2846
2855 template<typename... Args>
2856 requires std::is_constructible_v<allocator_t, Args...>
2857 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2859 }
2860
2870 template<typename... Args>
2871 requires std::is_constructible_v<allocator_t, Args...>
2872 constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2873 base_storable::init_str_repeat(repeat, pattern);
2874 }
2875
2885 template<typename... Args>
2886 requires std::is_constructible_v<allocator_t, Args...>
2887 constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2889 }
2890
2904 template<typename... Args>
2905 requires std::is_constructible_v<allocator_t, Args...>
2906 constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2908 }
2909
2925 template<StrType<K> From, typename... Args>
2926 requires std::is_constructible_v<allocator_t, Args...>
2927 constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
2928 : base_storable(std::forward<Args>(args)...) {
2929 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
2930 }
2931
2932 constexpr lstring() {
2933 create_empty();
2934 }
2935
2936 constexpr ~lstring() {
2937 dealloc();
2938 }
2939
2946 constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
2947 struct copy{uint64_t p[2];};
2948 constexpr size_t short_str = sizeof(copy) / sizeof(K);
2949 if constexpr (LocalCapacity >= short_str - 1) {
2950 if (other.size_ < short_str) {
2951 data_ = local_;
2952 size_ = other.size_;
2953 *(copy*)local_ = *(const copy*)other.local_;
2954 return;
2955 }
2956 }
2957 if (size_t size = other.size_) {
2958 copy_from_another(init(size), other.symbols(), size);
2959 } else {
2960 create_empty();
2961 }
2962 }
2963
2971 template<typename... Args>
2972 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
2973 constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2974 if (other.size_) {
2975 copy_from_another(init(other.size_), other.symbols(), other.size_);
2976 } else {
2977 create_empty();
2978 }
2979 }
2980
2988 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
2989 requires std::is_constructible_v<allocator_t, Args...>
2990 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2991 if constexpr (I > 1) {
2992 K* ptr = init(I - 1);
2993 traits::copy(ptr, (const K*)value, I - 1);
2994 ptr[I - 1] = K{};
2995 } else {
2996 create_empty();
2997 }
2998 }
2999
3005 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3006 if (other.size_) {
3007 size_ = other.size_;
3008 if (other.is_alloced()) {
3009 data_ = other.data_;
3010 other.data_ = other.local_;
3011 capacity_ = other.capacity_;
3012 } else {
3013 data_ = local_;
3014 copy_from_another(data_, other.local_, size_);
3015 }
3016 other.size_ = 0;
3017 other.local_[0] = 0;
3018 } else {
3019 create_empty();
3020 }
3021 }
3022
3030 template<typename Op, typename... Args>
3031 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
3032 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3033 create_empty();
3034 this->operator<<(op);
3035 }
3036 template<typename O>
3037 requires(!std::is_same_v<O, K>)
3038 lstring(simple_str<O> init) {
3039 this->init_from_utf_convertible(init);
3040 }
3041
3042 template<typename O, typename I, bool M>
3043 requires(!std::is_same_v<O, K>)
3044 lstring(const str_algs<O, simple_str<O>, I, M>& init) {
3045 this->init_from_utf_convertible(init.to_str());
3046 }
3047
3048 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
3049 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
3050 // copy and swap for assignments is not very applicable here, since for strings with a large local buffer, an extra copy, even by moving, will be expensive
3051 // Therefore, we implement the copy and move assignment separately
3052
3061 my_type& operator=(const my_type& other) {
3062 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3063 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
3064 if (&other != this) {
3065 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
3066 size_ = other.size_;
3067 }
3068 return *this;
3069 }
3070
3078 my_type& operator=(my_type&& other) noexcept {
3079 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3080 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
3081 if (&other != this) {
3082 dealloc();
3083 if (other.is_alloced()) {
3084 data_ = other.data_;
3085 capacity_ = other.capacity_;
3086 } else {
3087 traits::copy(data_, other.local_, other.size_ + 1);
3088 }
3089 base_storable::allocator() = std::move(other.allocator());
3090 size_ = other.size_;
3091 other.create_empty();
3092 }
3093 return *this;
3094 }
3095
3096 my_type& assign(const K* other, size_t len) {
3097 if (len) {
3098 bool isIntersect = other >= data_ && other + len <= data_ + size_;
3099 if (isIntersect) {
3100 // Особый случай, нам пытаются присвоить кусок нашей же строки.
3101 // Просто переместим текст в буфере, и установим новый размер
3102 // A special case, they are trying to assign us a piece of our own string.
3103 // Just move the text in the buffer and set a new size
3104 if (other > data_) {
3105 traits::move(data_, other, len);
3106 }
3107 } else {
3108 traits::copy(reserve_no_preserve(len), other, len);
3109 }
3110 }
3111 size_ = len;
3112 data_[size_] = 0;
3113 return *this;
3114 }
3123 my_type& operator=(simple_str<K> other) {
3124 return assign(other.str, other.len);
3125 }
3126
3134 template<typename T, size_t S = const_lit_for<K, T>::Count>
3135 my_type& operator=(T&& other) {
3136 return assign((const K*)other, S - 1);
3137 }
3138
3148 my_type& operator=(const StrExprForType<K> auto& expr) {
3149 size_t newLen = expr.length();
3150 if (newLen) {
3151 expr.place(reserve_no_preserve(newLen));
3152 }
3153 size_ = newLen;
3154 data_[size_] = 0;
3155 return *this;
3156 }
3157
3158 constexpr size_t length() const noexcept {
3159 return size_;
3160 }
3161
3162 constexpr const K* symbols() const noexcept {
3163 return data_;
3164 }
3165
3166 constexpr K* str() noexcept {
3167 return data_;
3168 }
3169
3170 constexpr bool is_empty() const noexcept {
3171 return size_ == 0;
3172 }
3173
3174 constexpr bool empty() const noexcept {
3175 return size_ == 0;
3176 }
3177
3178 constexpr size_t capacity() const noexcept {
3179 return is_alloced() ? capacity_ : LocalCapacity;
3180 }
3181
3193 constexpr K* reserve_no_preserve(size_t newSize) {
3194 if (newSize > capacity()) {
3195 newSize = calc_capacity(newSize);
3196 K* newData = alloc_place(newSize);
3197 dealloc();
3198 data_ = newData;
3199 capacity_ = newSize;
3200 }
3201 return data_;
3202 }
3203
3215 constexpr K* reserve(size_t newSize) {
3216 if (newSize > capacity()) {
3217 newSize = calc_capacity(newSize);
3218 K* newData = alloc_place(newSize);
3219 traits::copy(newData, data_, size_);
3220 dealloc();
3221 data_ = newData;
3222 capacity_ = newSize;
3223 }
3224 return data_;
3225 }
3226
3238 constexpr K* set_size(size_t newSize) {
3239 size_t cap = capacity();
3240 if (newSize > cap) {
3241 size_t needPlace = newSize;
3242 if (needPlace < (cap + 1) * 2) {
3243 needPlace = (cap + 1) * 2 - 1;
3244 }
3245 reserve(needPlace);
3246 }
3247 size_ = newSize;
3248 data_[newSize] = 0;
3249 return data_;
3250 }
3251
3255 constexpr bool is_local() const noexcept {
3256 return !is_alloced();
3257 }
3258
3264 constexpr void define_size() {
3265 size_t cap = capacity();
3266 for (size_t i = 0; i < cap; i++) {
3267 if (data_[i] == 0) {
3268 size_ = i;
3269 return;
3270 }
3271 }
3272 size_ = cap;
3273 data_[size_] = 0;
3274 }
3275
3281 constexpr void shrink_to_fit() {
3282 size_t need_capacity = calc_capacity(size_);
3283 if (is_alloced() && capacity_ > need_capacity) {
3284 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
3285 traits::copy(newData, data_, size_ + 1);
3286 base_storable::allocator().deallocate(to_real_address(data_));
3287 data_ = newData;
3288
3289 if (size_ > LocalCapacity) {
3290 capacity_ = need_capacity;
3291 }
3292 }
3293 }
3294
3295 constexpr void clear() {
3296 set_size(0);
3297 }
3298
3299 constexpr void reset() {
3300 dealloc();
3301 local_[0] = 0;
3302 size_ = 0;
3303 }
3304};
3305
3306template<size_t N = 15>
3307using lstringa = lstring<u8s, N>;
3308template<size_t N = 15>
3309using lstringb = lstring<ubs, N>;
3310template<size_t N = 15>
3311using lstringw = lstring<wchar_t, N>;
3312template<size_t N = 15>
3313using lstringu = lstring<u16s, N>;
3314template<size_t N = 15>
3315using lstringuu = lstring<u32s, N>;
3316
3317template<size_t N = 15>
3318using lstringsa = lstring<u8s, N, true>;
3319template<size_t N = 15>
3320using lstringsb = lstring<ubs, N, true>;
3321template<size_t N = 15>
3322using lstringsw = lstring<wchar_t, N, true>;
3323template<size_t N = 15>
3324using lstringsu = lstring<u16s, N, true>;
3325template<size_t N = 15>
3326using lstringsuu = lstring<u32s, N, true>;
3327
3328
3329template<typename T, typename K = typename const_lit<T>::symb_type>
3330auto getLiteralType(T&&) {
3331 return K{};
3332};
3333
3334template<size_t Arch, size_t L>
3335inline constexpr const size_t _local_count = 0;
3336
3337template<>
3338inline constexpr const size_t _local_count<8, 1> = 23;
3339template<>
3340inline constexpr const size_t _local_count<8, 2> = 15;
3341template<>
3342inline constexpr const size_t _local_count<8, 4> = 7;
3343template<>
3344inline constexpr const size_t _local_count<4, 1> = 15;
3345template<>
3346inline constexpr const size_t _local_count<4, 2> = 11;
3347template<>
3348inline constexpr const size_t _local_count<4, 4> = 5;
3349
3350template<typename T>
3351constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
3352
3405template<typename K, Allocatable Allocator = allocator_string>
3406class decl_empty_bases sstring :
3407 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
3408 public str_storable<K, sstring<K, Allocator>, Allocator>,
3409 public null_terminated<K, sstring<K, Allocator>>,
3410 public from_utf_convertible<K, sstring<K, Allocator>> {
3411public:
3412 using symb_type = K;
3413 using uns_type = std::make_unsigned_t<K>;
3414 using my_type = sstring<K, Allocator>;
3415 using allocator_t = Allocator;
3416
3417 enum { LocalCount = local_count<K> };
3418
3419protected:
3420 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
3421 using base_storable = str_storable<K, my_type, Allocator>;
3422 using base_utf = from_utf_convertible<K, my_type>;
3423 using traits = ch_traits<K>;
3424 using uni = unicode_traits<K>;
3425 using s_str = base_storable::s_str;
3426
3427 friend base_storable;
3428 friend base_utf;
3429
3430 enum Types { Local, Constant, Shared };
3431
3432 union {
3433 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
3434 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
3435 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
3436 // и в итоге после символов строки получается 0, как и надо!
3437 // When we have a short string, it lies in the object itself, and in localRemain
3438 // writes how many more characters can be entered. When a string takes up everything
3439 // possible location, then localRemain becomes 0, type in this case is also 0,
3440 // and as a result, after the characters of the string we get 0, as it should!
3441 struct {
3442 K buf_[LocalCount]; // Локальный буфер строки | Local string buffer
3443 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
3444 uns_type type_ : 2;
3445 };
3446 struct {
3447 union {
3448 // Указатель на конcтантную строку | Pointer to a constant string
3449 const K* cstr_;
3450 // Указатель на строку, перед которой лежит SharedStringData
3451 // Pointer to the string preceded by SharedStringData
3452 const K* sstr_;
3453 };
3454 size_t bigLen_; // Длина не локальной строки | Non-local string length
3455 };
3456 };
3457
3458 constexpr void create_empty() {
3459 type_ = Local;
3460 localRemain_ = LocalCount;
3461 buf_[0] = 0;
3462 }
3463 constexpr K* init(size_t s) {
3464 if (s > LocalCount) {
3465 type_ = Shared;
3466 localRemain_ = 0;
3467 bigLen_ = s;
3468 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
3469 return (K*)sstr_;
3470 } else {
3471 type_ = Local;
3472 localRemain_ = LocalCount - s;
3473 return buf_;
3474 }
3475 }
3476
3477 void dealloc() {
3478 if (type_ == Shared) {
3479 if constexpr (AllocatableNoSized<Allocator>) {
3480 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3481 } else {
3482 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator(), bigLen_);
3483 }
3484 }
3485 }
3486public:
3487
3488 sstring() {
3489 create_empty();
3490 }
3491
3498 template<typename... Args>
3499 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
3500 sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
3501 : base_storable(std::forward<Args>(args)...) {
3502 create_empty();
3503 }
3504
3513 template<typename... Args>
3514 requires std::is_constructible_v<allocator_t, Args...>
3515 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3517 }
3518
3528 template<typename... Args>
3529 requires std::is_constructible_v<allocator_t, Args...>
3530 sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3531 base_storable::init_str_repeat(repeat, pattern);
3532 }
3533
3543 template<typename... Args>
3544 requires std::is_constructible_v<allocator_t, Args...>
3545 sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3547 }
3548
3562 template<typename... Args>
3563 requires std::is_constructible_v<allocator_t, Args...>
3564 constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3566 }
3567
3583 template<StrType<K> From, typename... Args>
3584 requires std::is_constructible_v<allocator_t, Args...>
3585 sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
3586 : base_storable(std::forward<Args>(args)...) {
3587 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3588 }
3589
3590 static const sstring<K> empty_str;
3592 constexpr ~sstring() {
3593 dealloc();
3594 }
3595
3601 constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
3602 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3603 if (type_ == Shared)
3604 SharedStringData<K>::from_str(sstr_)->incr();
3605 }
3606
3612 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3613 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3614 other.create_empty();
3615 }
3616
3627 template<size_t N>
3628 constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
3629 size_t size = src.length();
3630 if (size) {
3631 if (src.is_alloced()) {
3632 // Там динамический буфер, выделенный с запасом для SharedStringData и size_t.
3633 // There is a dynamic buffer allocated with a reserve for SharedStringData and size_t.
3634 K* str = src.str();
3635 if (size > LocalCount) {
3636 // Просто присвоим его себе.
3637 // Let's just assign it to ourselves.
3638 sstr_ = str;
3639 bigLen_ = size;
3640 type_ = Shared;
3641 localRemain_ = 0;
3642 if constexpr (AllocatableNoSized<Allocator>) {
3643 new (SharedStringData<K>::from_str(str)) SharedStringData<K>;
3644 } else {
3645 new (SharedStringData<K>::from_str(str)) SharedStringData<K>(src.capacity_);
3646 }
3647 } else {
3648 // Скопируем локально
3649 // Copy locally
3650 type_ = Local;
3651 localRemain_ = LocalCount - size;
3652 traits::copy(buf_, str, size + 1);
3653 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
3654 // Let's free that buffer; a local string's buffer is not shared with others
3655 src.dealloc();
3656 }
3657 } else {
3658 // Копируем из локального буфера
3659 // Copy from local buffer
3660 K* str = init(src.size_);
3661 traits::copy(str, src.symbols(), size + 1);
3662 }
3663 src.create_empty();
3664 } else
3665 create_empty();
3666 }
3667
3678 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
3679 requires std::is_constructible_v<allocator_t, Args...>
3680 sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3681 type_ = Constant;
3682 localRemain_ = 0;
3683 cstr_ = (const K*)s;
3684 bigLen_ = N - 1;
3685 }
3686
3692 template<typename O> requires(!std::is_same_v<O, K>)
3694 this->init_from_utf_convertible(init);
3695 }
3696
3697 template<typename O, typename I, bool M> requires(!std::is_same_v<O, K>)
3698 sstring(const str_algs<O, simple_str<O>, I, M>& init) {
3699 this->init_from_utf_convertible(init.to_str());
3700 }
3701
3702 constexpr void swap(my_type&& other) noexcept {
3703 char buf[sizeof(buf_) + sizeof(K)];
3704 memcpy(buf, buf_, sizeof(buf));
3705 memcpy(buf_, other.buf_, sizeof(buf));
3706 memcpy(other.buf_, buf, sizeof(buf));
3707
3708 std::swap(base_storable::allocator(), other.allocator());
3709 }
3718 constexpr my_type& operator=(my_type other) noexcept {
3719 swap(std::move(other));
3720 return *this;
3721 }
3722
3730 constexpr my_type& operator=(simple_str<K> other) {
3731 return operator=(my_type{other, base_storable::allocator()});
3732 }
3733
3741 template<typename T, size_t N = const_lit_for<K, T>::Count>
3742 constexpr my_type& operator=(T&& other) {
3743 return operator=(my_type{std::forward<T>(other), base_storable::allocator()});
3744 }
3745
3753 template<size_t N, bool forShared, typename A>
3754 constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
3755 return operator=(my_type{other.to_str(), base_storable::allocator()});
3756 }
3757
3765 template<size_t N>
3766 constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
3767 return operator=(my_type{std::move(other)});
3768 }
3769
3779 constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
3780 return operator=(my_type{expr, base_storable::allocator()});
3781 }
3782
3788 constexpr my_type& make_empty() noexcept {
3789 dealloc();
3790 create_empty();
3791 return *this;
3792 }
3793
3794 constexpr const K* symbols() const noexcept {
3795 return type_ == Local ? buf_ : cstr_;
3796 }
3797
3798 constexpr size_t length() const noexcept {
3799 return type_ == Local ? LocalCount - localRemain_ : bigLen_;
3800 }
3801
3802 constexpr bool is_empty() const noexcept {
3803 return length() == 0;
3804 }
3805
3806 constexpr bool empty() const noexcept {
3807 return is_empty();
3808 }
3809
3821 template<typename... T>
3822 static my_type printf(const K* pattern, T&&... args) {
3823 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
3824 }
3825
3835 template<typename... T>
3836 static my_type format(const FmtString<to_std_char_t<K>, T...>& fmtString, T&&... args) {
3837 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
3838 }
3839
3849 template<typename... T>
3850 static my_type vformat(simple_str<K> fmtString, T&&... args) {
3851 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
3852 }
3853};
3854
3855template<typename K, Allocatable Allocator>
3856inline const sstring<K> sstring<K, Allocator>::empty_str{};
3857
3858struct no_alloc{};
3859
3860template<typename K, size_t N>
3861class decl_empty_bases cestring :
3862 public str_algs<K, simple_str<K>, cestring<K, N>, true>,
3863 public str_storable<K, cestring<K, N>, no_alloc>,
3864 public null_terminated<K, lstring<K, N>>
3865 //, public from_utf_convertible<K, lstring<K, N, forShared, Allocator>>
3866{
3867public:
3868 using symb_type = K;
3869 using my_type = cestring<K, N>;
3870
3871 enum : size_t {
3873 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
3874 };
3875
3876protected:
3877
3878 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
3879 using base_storable = str_storable<K, my_type, no_alloc>;
3880 //using base_utf = from_utf_convertible<K, my_type>;
3881 using traits = ch_traits<K>;
3882 using s_str = base_storable::s_str;
3883
3884 friend base_storable;
3885 //friend base_utf;
3886 const K* cstr_{};
3887 size_t size_{};
3888 bool is_cstr_{};
3889 K local_[LocalCapacity + 1]{};
3890
3891 constexpr void create_empty() {
3892 is_cstr_ = false;
3893 size_ = 0;
3894 local_[0] = 0;
3895 }
3896
3897 constexpr K* init(size_t s) {
3898 size_ = s;
3899 if (size_ > LocalCapacity) {
3900 throw std::bad_alloc{};
3901 }
3902 is_cstr_ = false;
3903 return local_;
3904 }
3905public:
3907 constexpr size_t length() const noexcept {
3908 return size_;
3909 }
3911 constexpr const K* symbols() const noexcept {
3912 return is_cstr_ ? cstr_ : local_;
3913 }
3915 constexpr bool is_empty() const noexcept {
3916 return size_ == 0;
3917 }
3919 constexpr bool empty() const noexcept {
3920 return size_ == 0;
3921 }
3923 constexpr size_t capacity() const noexcept {
3924 return LocalCapacity;
3925 }
3930 constexpr cestring() noexcept = default;
3931
3938 constexpr cestring(s_str other) : base_storable() {
3939 base_storable::init_from_str_other(other);
3940 }
3949 constexpr cestring(size_t repeat, s_str pattern) : base_storable() {
3950 base_storable::init_str_repeat(repeat, pattern);
3951 }
3960 constexpr cestring(size_t count, K pad) : base_storable() {
3961 base_storable::init_symb_repeat(count, pad);
3962 }
3975 constexpr cestring(const StrExprForType<K> auto& expr) : base_storable() {
3976 base_storable::init_str_expr(expr);
3977 }
3992 template<StrType<K> From>
3993 constexpr cestring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0)
3994 : base_storable() {
3995 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3996 }
3997
3999 constexpr ~cestring() {}
4000
4009 template<typename T, size_t M = const_lit_for<K, T>::Count>
4010 constexpr cestring(T&& s) : base_storable(), cstr_((const K*)s), size_(M - 1), is_cstr_(true), local_{0} {}
4011};
4012
4013template<typename K>
4014consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
4015 if constexpr (std::is_same_v<K, u8s>)
4016 return s8;
4017 if constexpr (std::is_same_v<K, ubs>)
4018 return sb;
4019 if constexpr (std::is_same_v<K, uws>)
4020 return sw;
4021 if constexpr (std::is_same_v<K, u16s>)
4022 return s16;
4023 if constexpr (std::is_same_v<K, u32s>)
4024 return s32;
4025}
4026
4027#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
4028
4029template<typename K, template<typename C> typename H>
4030struct StoreType {
4031 simple_str<K> str;
4032 size_t hash;
4033 char node[sizeof(sstring<K>)];
4034
4035 const simple_str_nt<K>& to_nt() const noexcept {
4036 return static_cast<const simple_str_nt<K>&>(str);
4037 }
4038 const sstring<K>& to_str() const noexcept {
4039 return *reinterpret_cast<const sstring<K>*>(node);
4040 }
4041 bool operator==(const StoreType& other) const {
4042 return hash == other.hash && typename H<K>::eql{}(*this, other);
4043 }
4044};
4045
4046template<bool Wide>
4047struct fnv_const {
4048 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
4049 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
4050};
4051
4052template<>
4053struct fnv_const<false> {
4054 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
4055 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
4056};
4057
4058using fnv = fnv_const<sizeof(size_t) == 8>;
4059
4060template<typename K>
4061inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
4062 size_t h = fnv::basis;
4063 for (size_t i = 0; i < l; i++) {
4064 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
4065 }
4066 return h;
4067};
4068
4069template<typename K>
4070inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
4071 size_t h = fnv::basis;
4072 for (size_t i = 0; i < l; i++) {
4073 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
4074 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
4075 }
4076 return h;
4077};
4078
4079template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
4080inline constexpr size_t fnv_hash(T&& value) {
4081 size_t h = fnv::basis;
4082 for (size_t i = 0; i < N - 1; i++) {
4083 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
4084 }
4085 return h;
4086};
4087
4088template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
4089inline constexpr size_t fnv_hash_ia(T&& value) {
4090 size_t h = fnv::basis;
4091 for (size_t i = 0; i < N - 1; i++) {
4092 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
4093 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
4094 }
4095 return h;
4096};
4097
4098template<typename K>
4099inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
4100 return fnv_hash(ptr, l);
4101};
4102
4103template<typename K>
4104inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
4105 return fnv_hash_ia(ptr, l);
4106};
4107
4108template<typename K>
4109struct str_exact;
4110
4111static_assert(std::is_trivially_copyable_v<StoreType<u8s, str_exact>>, "Store type must be trivially copyable");
4112
4113
4114template<typename K, typename V>
4115std::allocator<std::pair<K, V>> default_hashstrmap_allocator_selector(...);
4116// Если вы хотите задать свой дефолтный аллокатор для hashStrmap, перед включение sstring.h
4117// объявите функцию
4118// template<typename K, typename V>
4119// ваш_тип_аллокатора default_hashstrmap_allocator_selector(int);
4120// If you want to set your default allocator for hashStrMap, before including sstring.h
4121// declare a function
4122// template<typename K, typename V>
4123// your_allocator_type default_hashstrmap_allocator_selector(int);
4124template<typename K, template<typename C> typename H, typename V>
4125using allocator_hashstrmap = decltype(default_hashstrmap_allocator_selector<const StoreType<K, H>, V>(int(0)));
4126
4221template<typename K, typename T, template<typename C> typename HE = str_exact, typename A = allocator_hashstrmap<K, HE, T>>
4222class hashStrMap : public std::unordered_map<StoreType<K, HE>, T, typename HE<K>::hash, typename HE<K>::eql, A> {
4223protected:
4224 using InStore = StoreType<K, HE>;
4225
4226public:
4227 using hasher = typename HE<K>::hash;
4228 using comparator = typename HE<K>::eql;
4229 using my_type = hashStrMap<K, T, HE, A>;
4230 using hash_t = std::unordered_map<StoreType<K, HE>, T, hasher, comparator, A>;
4231
4232 hashStrMap() = default;
4233 hashStrMap(const my_type& other) : hash_t(other) {
4234 for (const auto& [k, v] : *this) {
4235 InStore& stored = const_cast<InStore&>(k);
4236 sstring<K> tmp = *(sstring<K>*)stored.node;
4237 new (stored.node) sstring<K>(std::move(tmp));
4238 stored.str.str = stored.to_str().symbols();
4239 }
4240 }
4241 ~hashStrMap() {
4242 for (auto& k: *this)
4243 ((sstring<K>*)k.first.node)->~sstring();
4244 }
4245
4246 hashStrMap(my_type&& o) = default;
4247
4248 my_type& operator=(const my_type& other) {
4249 hash_t::operator=(other);
4250 for (const auto& [k, v] : *this) {
4251 InStore& stored = const_cast<InStore&>(k);
4252 sstring<K> tmp = *(sstring<K>*)stored.node;
4253 new (stored.node) sstring<K>(std::move(tmp));
4254 stored.str.str = stored.to_str().symbols();
4255 }
4256 return *this;
4257 };
4258 my_type& operator=(my_type&&) = default;
4259
4260 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
4261 for (const auto& e: init)
4262 emplace(e.first, e.second);
4263 }
4264
4265 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
4266
4267 hashStrMap(init_str&& init) {
4268 for (const auto& e: init)
4269 emplace(e.first, e.second);
4270 }
4271
4272 // При входе хэш должен быть уже посчитан
4273 // When entering, the hash must already be calculated
4274 template<typename... ValArgs>
4275 auto try_emplace(const InStore& key, ValArgs&&... args) {
4276 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
4277 if (it.second) {
4278 InStore& stored = const_cast<InStore&>(it.first->first);
4279 new (stored.node) sstring<K>(key.str);
4280 stored.str.str = stored.to_str().symbols();
4281 }
4282 return it;
4283 }
4284
4285 static InStore toStoreType(simple_str<K> key) {
4286 return {key, hasher{}(key)};
4287 }
4288
4289 template<typename Key, typename... ValArgs>
4290 requires(std::is_convertible_v<Key, simple_str<K>>)
4291 auto try_emplace(Key&& key, ValArgs&&... args) {
4292 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
4293 if (it.second) {
4294 InStore& stored = const_cast<InStore&>(it.first->first);
4295 new (stored.node) sstring<K>(std::forward<Key>(key));
4296 stored.str.str = stored.to_str().symbols();
4297 }
4298 return it;
4299 }
4300
4301 template<typename... ValArgs>
4302 auto emplace(const InStore& key, ValArgs&&... args) {
4303 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
4304 if (!it.second) {
4305 it.first->second = T(std::forward<ValArgs>(args)...);
4306 }
4307 return it;
4308 }
4309
4310 template<typename Key, typename... ValArgs>
4311 requires(std::is_convertible_v<Key, simple_str<K>>)
4312 auto emplace(Key&& key, ValArgs&&... args) {
4313 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
4314 if (!it.second) {
4315 it.first->second = T(std::forward<ValArgs>(args)...);
4316 }
4317 return it;
4318 }
4319
4320 auto& operator[](const InStore& key) {
4321 return try_emplace(key).first->second;
4322 }
4323
4324 template<typename Key>
4325 requires(std::is_convertible_v<Key, simple_str<K>>)
4326 auto& operator[](Key&& key) {
4327 return try_emplace(std::forward<Key>(key)).first->second;
4328 }
4329
4330 decltype(auto) at(const InStore& key) {
4331 return hash_t::at(key);
4332 }
4333 decltype(auto) at(const InStore& key) const {
4334 return hash_t::at(key);
4335 }
4336
4337 decltype(auto) at(simple_str<K> key) {
4338 return hash_t::at(toStoreType(key));
4339 }
4340 decltype(auto) at(simple_str<K> key) const {
4341 return hash_t::at(toStoreType(key));
4342 }
4343
4344 auto find(const InStore& key) const {
4345 return hash_t::find(key);
4346 }
4347
4348 auto find(simple_str<K> key) const {
4349 return find(toStoreType(key));
4350 }
4351
4352 auto find(const InStore& key) {
4353 return hash_t::find(key);
4354 }
4355
4356 auto find(simple_str<K> key) {
4357 return find(toStoreType(key));
4358 }
4359
4360 auto erase(typename hash_t::const_iterator it) {
4361 if (it != hash_t::end()) {
4362 ((sstring<K>*)it->first.node)->~sstring();
4363 }
4364 return hash_t::erase(it);
4365 }
4366
4367 auto erase(const InStore& key) {
4368 auto it = hash_t::find(key);
4369 if (it != hash_t::end()) {
4370 ((sstring<K>*)it->first.node)->~sstring();
4371 hash_t::erase(it);
4372 return 1;
4373 }
4374 return 0;
4375 }
4376
4377 auto erase(simple_str<K> key) {
4378 return erase(toStoreType(key));
4379 }
4380
4381 bool lookup(simple_str<K> txt, T& val) const {
4382 auto it = find(txt);
4383 if (it != hash_t::end()) {
4384 val = it->second;
4385 return true;
4386 }
4387 return false;
4388 }
4389
4390 void clear() {
4391 for (auto& k: *this)
4392 ((sstring<K>*)k.first.node)->~sstring();
4393 hash_t::clear();
4394 }
4395 bool contains(const InStore& key) const {
4396 return hash_t::find(key) != this->end();
4397 }
4398
4399 bool contains(simple_str<K> key) const {
4400 return find(toStoreType(key)) != this->end();
4401 }
4402};
4403
4404template<typename K>
4405struct str_exact {
4406 struct eql {
4407 bool operator()(const StoreType<K, str_exact>& _Left, const StoreType<K, str_exact>& _Right) const {
4408 return _Left.hash == _Right.hash && _Left.str == _Right.str;
4409 }
4410 };
4411 struct hash { // hash functor for basic_string
4412 size_t operator()(simple_str<K> _Keyval) const {
4413 return fnv_hash(_Keyval.symbols(), _Keyval.length());
4414 }
4415 size_t operator()(const StoreType<K, str_exact>& _Keyval) const {
4416 return _Keyval.hash;
4417 }
4418 };
4419};
4420
4421template<typename K>
4422struct str_eqlia {
4423 struct eql {
4424 bool operator()(const StoreType<K, str_eqlia>& _Left, const StoreType<K, str_eqlia>& _Right) const {
4425 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
4426 }
4427 };
4428 struct hash {
4429 size_t operator()(simple_str<K> _Keyval) const {
4430 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
4431 }
4432 size_t operator()(const StoreType<K, str_eqlia>& _Keyval) const {
4433 return _Keyval.hash;
4434 }
4435 };
4436};
4437
4438template<typename K>
4439struct str_eqliu {
4440 struct eql {
4441 bool operator()(const StoreType<K, str_eqliu>& _Left, const StoreType<K, str_eqliu>& _Right) const {
4442 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
4443 }
4444 };
4445 struct hash {
4446 size_t operator()(simple_str<K> _Keyval) const {
4447 using bc = to_base_char_t<K>;
4448 return unicode_traits<bc>::hashiu((const bc*)_Keyval.symbols(), _Keyval.length());
4449 }
4450 size_t operator()(const StoreType<K, str_eqliu>& _Keyval) const {
4451 return _Keyval.hash;
4452 }
4453 };
4454};
4455
4470template<typename K>
4471class chunked_string_builder : expr_to_std_string<chunked_string_builder<K>> {
4472 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
4473 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
4474 K* write{}; // Текущая позиция записи | Current write position
4475 size_t len{}; // Общая длина | Total length
4476 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
4477 size_t align{1024};
4478
4479public:
4480 using my_type = chunked_string_builder<K>;
4481 using symb_type = K;
4482 chunked_string_builder() = default;
4483 chunked_string_builder(size_t a) : align(a){};
4484 chunked_string_builder(const my_type&) = delete;
4485 chunked_string_builder(my_type&& other) noexcept
4486 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
4487 other.len = other.remain = 0;
4488 other.write = nullptr;
4489 }
4490 my_type& operator=(my_type other) noexcept {
4491 chunks.swap(other.chunks);
4492 write = other.write;
4493 len = other.len;
4494 remain = other.remain;
4495 align = other.align;
4496 other.len = other.remain = 0;
4497 other.write = nullptr;
4498 return *this;
4499 }
4500
4503 if (data.len) {
4504 len += data.len;
4505 if (data.len <= remain) {
4506 // Добавляемые данные влезают в выделенный блок, просто скопируем их
4507 // The added data fits into the selected block, just copy it
4508 ch_traits<K>::copy(write, data.str, data.len);
4509 write += data.len; // Сдвинем позицию записи | Let's move the recording position
4510 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4511 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
4512 } else {
4513 // Не влезают | They don't fit
4514 if (remain) {
4515 // Сначала запишем сколько влезет
4516 // First, write down as much as we can
4517 ch_traits<K>::copy(write, data.str, remain);
4518 data.len -= remain;
4519 data.str += remain;
4520 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4521 }
4522 // Выделим новый блок и впишем в него данные
4523 // Рассчитаем размер блока, кратного заданному выравниванию
4524 // Select a new block and write data into it
4525 // Calculate the block size that is a multiple of the given alignment
4526 size_t blockSize = (data.len + align - 1) / align * align;
4527 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
4528 write = chunks.back().first.get();
4529 ch_traits<K>::copy(write, data.str, data.len);
4530 write += data.len;
4531 remain = blockSize - data.len;
4532 }
4533 }
4534 return *this;
4535 }
4536
4537 my_type& operator<<(const StrExprForType<K> auto& expr) {
4538 size_t l = expr.length();
4539 if (l) {
4540 if (l < remain) {
4541 write = expr.place(write);
4542 chunks.back().second += l;
4543 len += l;
4544 remain -= l;
4545 } else if (!remain) {
4546 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
4547 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
4548 write = expr.place(chunks.back().first.get());
4549 len += l;
4550 remain = blockSize - l;
4551 } else {
4552 auto store = std::make_unique<K[]>(l);
4553 expr.place(store.get());
4554 return operator<<({store.get(), l});
4555 }
4556 }
4557 return *this;
4558 }
4559
4560 template<typename T>
4561 my_type& operator<<(T data)
4562 requires std::is_same_v<T, K>
4563 {
4564 return operator<<(expr_char<K>(data));
4565 }
4566
4567 constexpr size_t length() const noexcept {
4568 return len;
4569 }
4570
4571 void reset() {
4572 if (chunks.empty()) {
4573 return;
4574 }
4575 if (chunks.size() > 1) {
4576 remain = 0;
4577 chunks.resize(1);
4578 }
4579 remain += chunks[0].second;
4580 chunks[0].second = 0;
4581 len = 0;
4582 write = chunks[0].first.get();
4583 }
4584
4585 constexpr K* place(K* p) const noexcept {
4586 for (const auto& block: chunks) {
4587 ch_traits<K>::copy(p, block.first.get(), block.second);
4588 p += block.second;
4589 }
4590 return p;
4591 }
4600 template<typename Op>
4601 void out(const Op& o) const {
4602 for (const auto& block: chunks)
4603 o(block.first.get(), block.second);
4604 }
4605
4609 bool is_continuous() const {
4610 if (chunks.size()) {
4611 const K* ptr = chunks.front().first.get();
4612 for (const auto& chunk: chunks) {
4613 if (chunk.first.get() != ptr)
4614 return false;
4615 ptr += chunk.second;
4616 }
4617 }
4618 return true;
4619 }
4620
4626 const K* begin() const {
4627 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
4628 }
4629
4633 void clear() {
4634 chunks.clear();
4635 write = nullptr;
4636 len = 0;
4637 remain = 0;
4638 }
4639
4644 typename decltype(chunks)::const_iterator it, end;
4645 size_t writedFromCurrentChunk;
4650 bool is_end() {
4651 return it == end;
4652 }
4653
4663 size_t store(K* buffer, size_t size) {
4664 size_t writed = 0;
4665 while (size && !is_end()) {
4666 size_t remain = it->second - writedFromCurrentChunk;
4667 size_t write = std::min(size, remain);
4668 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
4669 writed += write;
4670 remain -= write;
4671 size -= write;
4672 if (!remain) {
4673 ++it;
4674 writedFromCurrentChunk = 0;
4675 } else
4676 writedFromCurrentChunk += write;
4677 }
4678 return writed;
4679 }
4680 };
4681
4688 return {chunks.begin(), chunks.end(), 0};
4689 }
4690
4696 const auto& data() const {
4697 return chunks;
4698 }
4699};
4700
4701using stringa = sstring<u8s>;
4702using stringb = sstring<ubs>;
4703using stringw = sstring<wchar_t>;
4704using stringu = sstring<u16s>;
4705using stringuu = sstring<u32s>;
4706static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
4707
4712template<typename T, typename A = allocator_hashstrmap<u8s, str_exact, T>>
4718template<typename T, typename A = allocator_hashstrmap<u8s, str_eqlia, T>>
4724template<typename T, typename A = allocator_hashstrmap<u8s, str_eqliu, T>>
4726
4731template<typename T, typename A = allocator_hashstrmap<ubs, str_exact, T>>
4737template<typename T, typename A = allocator_hashstrmap<ubs, str_eqlia, T>>
4743template<typename T, typename A = allocator_hashstrmap<ubs, str_eqliu, T>>
4745
4750template<typename T, typename A = allocator_hashstrmap<wchar_t, str_exact, T>>
4752
4757template<typename T, typename A = allocator_hashstrmap<wchar_t, str_eqlia, T>>
4759
4764template<typename T, typename A = allocator_hashstrmap<wchar_t, str_eqliu, T>>
4766
4771template<typename T, typename A = allocator_hashstrmap<u16s, str_exact, T>>
4777template<typename T, typename A = allocator_hashstrmap<u16s, str_eqlia, T>>
4783template<typename T, typename A = allocator_hashstrmap<u16s, str_eqliu, T>>
4785
4790template<typename T, typename A = allocator_hashstrmap<u32s, str_exact, T>>
4796template<typename T, typename A = allocator_hashstrmap<u32s, str_eqlia, T>>
4802template<typename T, typename A = allocator_hashstrmap<u32s, str_eqliu, T>>
4804
4805inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
4806
4807inline namespace literals {
4808
4819SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
4820 return simple_str_nt<u8s>{ptr, l};
4821}
4822
4832SS_CONSTEVAL simple_str_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
4833 return simple_str_nt<ubs>{ptr, l};
4834}
4835
4845SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
4846 return simple_str_nt<uws>{ptr, l};
4847}
4848
4858SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
4859 return simple_str_nt<u16s>{ptr, l};
4860}
4861
4872SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
4873 return simple_str_nt<u32s>{ptr, l};
4874}
4875
4876template<typename K> using HashKey = StoreType<K, str_exact>;
4877template<typename K> using HashKeyIA = StoreType<K, str_eqlia>;
4878template<typename K> using HashKeyIU = StoreType<K, str_eqliu>;
4879
4890consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
4891 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4892}
4893
4904consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
4905 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4906}
4907
4918inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
4919 return HashKeyIU<u8s>{{ptr, l}, str_eqliu<u8s>::hash{}(simple_str<u8s>{ptr, l})};
4920}
4921
4932consteval HashKey<ubs> operator""_h(const ubs* ptr, size_t l) {
4933 return HashKey<ubs>{{ptr, l}, fnv_hash_compile(ptr, l)};
4934}
4935
4946consteval HashKeyIA<ubs> operator""_ia(const ubs* ptr, size_t l) {
4947 return HashKeyIA<ubs>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4948}
4949
4960inline HashKeyIU<ubs> operator""_iu(const ubs* ptr, size_t l) {
4961 return HashKeyIU<ubs>{{ptr, l}, str_eqliu<u8s>::hash{}(simple_str<u8s>{(const u8s*)ptr, l})};
4962}
4963
4974consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
4975 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4976}
4977
4988consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
4989 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4990}
4991
5002inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
5003 return HashKeyIU<u16s>{{ptr, l}, str_eqliu<u16s>::hash{}(simple_str<u16s>{ptr, l})};
5004}
5005
5016consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
5017 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
5018}
5019
5030consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
5031 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
5032}
5033
5044inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
5045 return HashKeyIU<u32s>{{ptr, l}, str_eqliu<u32s>::hash{}(simple_str<u32s>{ptr, l})};
5046}
5047
5058consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
5059 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
5060}
5061
5072consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
5073 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
5074}
5075
5086inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
5087 return HashKeyIU<uws>{{ptr, l}, str_eqliu<uws>::hash{}(simple_str<uws>{ptr, l})};
5088}
5089} // namespace literals
5090
5101inline std::ostream& operator<<(std::ostream& stream, ssa text) {
5102 return stream << std::string_view{text.symbols(), text.length()};
5103}
5104
5115inline std::wostream& operator<<(std::wostream& stream, ssw text) {
5116 return stream << std::wstring_view{text.symbols(), text.length()};
5117}
5118
5129inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
5130 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5131}
5132
5143inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
5144 return stream << std::string_view{text.symbols(), text.length()};
5145}
5146
5157inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
5158 return stream << std::wstring_view{text.symbols(), text.length()};
5159}
5160
5171inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
5172 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5173}
5174
5185template<size_t N, bool S, simstr::Allocatable A>
5186inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
5187 return stream << std::string_view{text.symbols(), text.length()};
5188}
5189
5200template<size_t N, bool S, simstr::Allocatable A>
5201inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
5202 return stream << std::wstring_view{text.symbols(), text.length()};
5203}
5204
5215template<size_t N, bool S, simstr::Allocatable A>
5216inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
5217 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5218}
5219
5220} // namespace simstr
5221
5226template<typename K>
5227struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5228 // Define format() by calling the base class implementation with the wrapped value
5229 template<typename FormatContext>
5230 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
5231 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5232 }
5233};
5234
5239template<typename K>
5240struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5241 // Define format() by calling the base class implementation with the wrapped value
5242 template<typename FormatContext>
5243 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
5244 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5245 }
5246};
5247
5252template<typename K>
5253struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5254 // Define format() by calling the base class implementation with the wrapped value
5255 template<typename FormatContext>
5256 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
5257 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5258 }
5259};
5260
5265template<typename K, size_t N, bool S, typename A>
5266struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
5267 // Define format() by calling the base class implementation with the wrapped value
5268 template<typename FormatContext>
5269 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
5270 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5271 }
5272};
5273
5278template<>
5279struct std::formatter<simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5280 // Define format() by calling the base class implementation with the wrapped value
5281 template<typename FormatContext>
5282 auto format(simstr::simple_str<char8_t> t, FormatContext& fc) const {
5283 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5284 }
5285};
5286
5291template<>
5292struct std::formatter<simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5293 // Define format() by calling the base class implementation with the wrapped value
5294 template<typename FormatContext>
5295 auto format(simstr::simple_str_nt<char8_t> t, FormatContext& fc) const {
5296 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5297 }
5298};
5299
5304template<>
5305struct std::formatter<simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5306 // Define format() by calling the base class implementation with the wrapped value
5307 template<typename FormatContext>
5308 auto format(const simstr::sstring<char8_t>& t, FormatContext& fc) const {
5309 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5310 }
5311};
5312
5317template<size_t N, bool S, typename A>
5318struct std::formatter<simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
5319 // Define format() by calling the base class implementation with the wrapped value
5320 template<typename FormatContext>
5321 auto format(const simstr::lstring<char8_t, N, S, A>& t, FormatContext& fc) const {
5322 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5323 }
5324};
5325
5330template<>
5331struct std::formatter<simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5332 // Define format() by calling the base class implementation with the wrapped value
5333 template<typename FormatContext>
5334 auto format(simstr::simple_str<simstr::wchar_type> t, FormatContext& fc) const {
5335 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5336 }
5337};
5338
5343template<>
5344struct std::formatter<simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5345 // Define format() by calling the base class implementation with the wrapped value
5346 template<typename FormatContext>
5347 auto format(simstr::simple_str_nt<simstr::wchar_type> t, FormatContext& fc) const {
5348 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5349 }
5350};
5351
5356template<>
5357struct std::formatter<simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5358 // Define format() by calling the base class implementation with the wrapped value
5359 template<typename FormatContext>
5360 auto format(const simstr::sstring<simstr::wchar_type>& t, FormatContext& fc) const {
5361 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5362 }
5363};
5364
5369template<size_t N, bool S, typename A>
5370struct std::formatter<simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5371 // Define format() by calling the base class implementation with the wrapped value
5372 template<typename FormatContext>
5373 auto format(const simstr::lstring<simstr::wchar_type, N, S, A>& t, FormatContext& fc) const {
5374 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5375 }
5376};
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:3234
my_type & operator<<(simple_str< K > data)
Adding a piece of data.
Definition sstring.h:4502
portion_store get_portion() const
Get a portion_store through which data can be sequentially retrieved into an external buffer.
Definition sstring.h:4687
constexpr size_t length() const noexcept
Length of the saved text.
Definition sstring.h:4567
my_type & operator<<(T data)
Adding a symbol.
Definition sstring.h:4561
void reset()
Resets the contents, but does not delete the first buffer in order to avoid allocation later.
Definition sstring.h:4571
void clear()
Clear the object, freeing all allocated buffers.
Definition sstring.h:4633
my_type & operator<<(const StrExprForType< K > auto &expr)
Adding a string expression.
Definition sstring.h:4537
bool is_continuous() const
Checks whether all text is located in one contiguous chunk in memory.
Definition sstring.h:4609
void out(const Op &o) const
Applies a functor to each stored buffer.
Definition sstring.h:4601
const auto & data() const
Get internal data buffers.
Definition sstring.h:4696
const K * begin() const
Get a pointer to the beginning of the first buffer. It makes sense to apply only if is_continuous tru...
Definition sstring.h:4626
Container for more efficient searching by string keys.
Definition sstring.h:4222
The mutable, owning string class. Contains an internal buffer for text of a given size.
Definition sstring.h:2717
constexpr void define_size()
Determine the length of the string. Searches for the character 0 in the string buffer to its capacity...
Definition sstring.h:3264
constexpr lstring(T &&value, Args &&... args)
String literal constructor.
Definition sstring.h:2990
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3135
constexpr void reset()
Makes the string empty and frees the external buffer, if there was one.
Definition sstring.h:3299
@ LocalCapacity
Definition sstring.h:2725
constexpr bool is_local() const noexcept
Find out whether a local or external buffer is used for characters.
Definition sstring.h:3255
my_type & operator=(my_type &&other) noexcept
Assignment operator by moving from a string of the same type.
Definition sstring.h:3078
constexpr size_t length() const noexcept
String length.
Definition sstring.h:3158
constexpr lstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:2857
constexpr lstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:2927
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:2872
constexpr lstring(const my_type &other)
Copy from another string of the same type.
Definition sstring.h:2946
constexpr lstring(const my_type &other, Args &&... args)
Copy from another string of the same type, but with a different allocator.
Definition sstring.h:2973
my_type & operator=(const StrExprForType< K > auto &expr)
String expression appending operator.
Definition sstring.h:3148
constexpr K * reserve_no_preserve(size_t newSize)
Definition sstring.h:3193
my_type & operator=(const my_type &other)
Copy assignment operator from a string of the same type.
Definition sstring.h:3061
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3170
lstring(const Op &op, Args &&... args)
A fill constructor using a functor (see str_mutable::fill).
Definition sstring.h:3032
constexpr K * set_size(size_t newSize)
Sets the size of the current string, allocating space if necessary.
Definition sstring.h:3238
constexpr lstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:2887
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:2906
constexpr const K * symbols() const noexcept
Pointer to constant characters.
Definition sstring.h:3162
constexpr void clear()
Makes a string empty without changing the string buffer.
Definition sstring.h:3295
my_type & operator=(simple_str< K > other)
Assignment operator from simple_str.
Definition sstring.h:3123
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3174
constexpr void shrink_to_fit()
Reduces the size of the external buffer to the smallest possible size to hold the string....
Definition sstring.h:3281
constexpr lstring(my_type &&other) noexcept
Constructor for moving from a string of the same type.
Definition sstring.h:3005
constexpr K * reserve(size_t newSize)
Allocate a buffer large enough to hold newSize characters plus a terminating null.
Definition sstring.h:3215
constexpr K * str() noexcept
Pointer to a string buffer.
Definition sstring.h:3166
constexpr size_t capacity() const noexcept
Current row buffer capacity.
Definition sstring.h:3178
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Create an empty object.
Definition sstring.h:2842
Immutable owning string class.
Definition sstring.h:3410
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Assignment operator to another string of type lstring.
Definition sstring.h:3754
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3806
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3802
sstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:3515
sstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:3545
constexpr const K * symbols() const noexcept
Pointer to characters in the string.
Definition sstring.h:3794
constexpr sstring(const my_type &other) noexcept
String copy constructor.
Definition sstring.h:3601
static my_type format(const FmtString< to_std_char_t< K >, T... > &fmtString, T &&... args)
Get a string formatted with std::format.
Definition sstring.h:3836
constexpr my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3742
sstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:3530
constexpr my_type & operator=(my_type other) noexcept
Assignment operator to another string of the same type.
Definition sstring.h:3718
static my_type printf(const K *pattern, T &&... args)
Get a string formatted with std::sprintf.
Definition sstring.h:3822
constexpr sstring(lstring< K, N, true, Allocator > &&src)
A move constructor from lstring with an sstring-compatible external buffer.
Definition sstring.h:3628
constexpr size_t length() const noexcept
string length.
Definition sstring.h:3798
constexpr sstring(my_type &&other) noexcept
Move constructor.
Definition sstring.h:3612
static my_type vformat(simple_str< K > fmtString, T &&... args)
Get a string formatted with std::vformat.
Definition sstring.h:3850
sstring(simple_str< O > init)
Initialization from a string source with a different character type. Converts via UTF.
Definition sstring.h:3693
constexpr my_type & operator=(simple_str< K > other)
Assignment operator to another string of a different type.
Definition sstring.h:3730
constexpr my_type & make_empty() noexcept
Make the string empty.
Definition sstring.h:3788
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
String expression assignment operator.
Definition sstring.h:3779
sstring(T &&s, Args &&... args)
Initialize from a string literal.
Definition sstring.h:3680
constexpr my_type & operator=(lstring< K, N, true, Allocator > &&other)
Assignment operator to a movable string of type lstring with a compatible buffer.
Definition sstring.h:3766
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:3564
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Constructor for the empty string.
Definition sstring.h:3500
constexpr ~sstring()
String destructor.
Definition sstring.h:3592
sstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:3585
A class with additional constant string algorithms.
Definition sstring.h:193
bool starts_with_iu(str_piece prefix) const noexcept
Whether the string starts with the given substring, case-insensitive Unicode characters of the first ...
Definition sstring.h:266
int compare_iu(str_piece text) const noexcept
Compare strings character by character without taking into account the case of Unicode characters of ...
Definition sstring.h:230
bool less_iu(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:252
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Whether the string ends with the specified substring, case-insensitive Unicode characters of the firs...
Definition sstring.h:281
R upperred() const
Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:293
constexpr std::optional< R > strip_prefix_iu(str_piece prefix) const
Get a string without a prefix if it starts with it, insensitive to Unicode characters up to 0xFFFF.
Definition sstring.h:389
constexpr R trimmed_prefix_iu(str_piece prefix, size_t max_count=0) const
Returns a string with the beginning of the string removed if it begins with the specified prefix,...
Definition sstring.h:427
R lowered() const
Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:305
std::optional< double > to_double() const noexcept
Convert string to double.
Definition sstring.h:315
constexpr std::optional< R > strip_suffix_iu(str_piece suffix) const
Get a string without a suffix if it ends with one in case-insensitive Unicode characters up to 0xFFFF...
Definition sstring.h:408
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition sstring.h:373
bool equal_iu(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of the Unicode chara...
Definition sstring.h:241
constexpr R trimmed_suffix_iu(str_piece suffix) const
Returns a string with the end of the string removed if it ends with the specified suffix of case-inse...
Definition sstring.h:450
void as_number(double &t) const
Convert string to double.
Definition sstring.h:356
Base class for working with mutable strings.
Definition sstring.h:1465
Impl & insert(size_t to, const A &expr)
Insert a string expression at the specified position.
Definition sstring.h:2005
Impl & operator<<=(const Op &fillFunction)
Fills a string with the fill method after the end of the string.
Definition sstring.h:2279
Impl & append(const A &expr)
Add a string expression to the end of the string.
Definition sstring.h:1889
Impl & operator<<(const Op &fillFunction)
Calls the passed functor, passing a reference to itself.
Definition sstring.h:2292
Impl & trim(str_piece pattern)
Remove characters included in the passed string at the beginning and end of the string.
Definition sstring.h:1699
Impl & upper_only_ascii()
Convert ASCII characters to uppercase.
Definition sstring.h:1763
Impl & lower_only_ascii()
Convert ASCII characters to lowercase.
Definition sstring.h:1778
Impl & trim_left()
Remove whitespace at the beginning of a string.
Definition sstring.h:1601
Impl & append_printf(const K *format, T &&... args)
Appends sprintf formatted output to the end of the string.
Definition sstring.h:2381
Impl & trim_right_with_wpaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the end of a string.
Definition sstring.h:1688
Impl & trim_left(str_piece pattern)
Remove characters included in the passed string at the beginning of the string.
Definition sstring.h:1710
Impl & trim_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning and end of th...
Definition sstring.h:1662
Impl & prepend(str_piece other)
Add another string to the beginning of the string.
Definition sstring.h:2029
Impl & append_formatted(const FmtString< fmt_type, T... > &format, T &&... args)
Appends std::format-formatted output to the end of the string.
Definition sstring.h:2519
my_type & format(const FmtString< fmt_type, T... > &pattern, T &&... args)
Definition sstring.h:2503
Impl & printf(const K *format, T &&... args)
Formats a string using sprintf.
Definition sstring.h:2365
Impl & with(const Op &fillFunction, Args &&... args)
Call a functor with a string and passed arguments.
Definition sstring.h:2601
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line, writing no more than the specified numb...
Definition sstring.h:2587
Impl & operator<<(const Op &fillFunction)
Fills a string with the fill method from position zero.
Definition sstring.h:2266
Impl & vformat(str_piece format, T &&... args)
Formats a string using std::vformat.
Definition sstring.h:2535
Impl & change(size_t from, size_t len, const A &expr)
Replace a piece of string with a string expression.
Definition sstring.h:1978
Impl & fill(size_t from, const Op &fillFunction)
Fill a string buffer using a functor.
Definition sstring.h:2238
Impl & change(size_t from, size_t len, str_piece other)
Replace a piece of string with another string.
Definition sstring.h:1962
Impl & remove(size_t from, size_t len)
Remove part of a string.
Definition sstring.h:2018
Impl & trim_left_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning of a string.
Definition sstring.h:1675
Impl & trim_left(T &&pattern)
Remove characters included in a string literal at the beginning of the string.
Definition sstring.h:1636
Impl & trim_right(T &&pattern)
Remove characters included in a string literal at the end of the string.
Definition sstring.h:1649
Impl & trim_right_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace at the end of the string.
Definition sstring.h:1754
Impl & append_in(size_t pos, str_piece other)
Add another string starting at the given position.
Definition sstring.h:1929
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Appends std::vformat formatted output starting at the specified position.
Definition sstring.h:2468
Impl & trim_left_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace, at the beginning of the strin...
Definition sstring.h:1743
Impl & insert(size_t to, str_piece other)
Insert a string at the specified position.
Definition sstring.h:1991
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Formats a string using std::vformat up to the specified size.
Definition sstring.h:2569
Impl & printf_from(size_t from, const K *format, T &&... args)
Appends sprintf formatted output starting at the specified position.
Definition sstring.h:2311
Impl & operator+=(str_piece other)
Add another string to the end of the string.
Definition sstring.h:1900
Impl & trim_right()
Remove whitespace from the end of a string.
Definition sstring.h:1610
Impl & append(str_piece other)
Add another string to the end of the string.
Definition sstring.h:1877
Impl & trim_right(str_piece pattern)
Remove characters included in the passed string from the end of the string.
Definition sstring.h:1721
Impl & trim_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace characters,...
Definition sstring.h:1732
K * str() noexcept
Get a pointer to the string buffer.
Definition sstring.h:1574
Impl & prepend(const A &expr)
Add a string expression to the beginning of a string.
Definition sstring.h:2041
Impl & trim()
Remove whitespace from the beginning and end of a string.
Definition sstring.h:1592
Impl & lower()
Convert first plane characters (<0xFFFF) to lowercase Unicode.
Definition sstring.h:1812
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Replace occurrences of a substring with another string.
Definition sstring.h:2058
Impl & upper()
Convert first plane characters (<0xFFFF) to uppercase Unicode.
Definition sstring.h:1797
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Copy the source string, replacing occurrences of substrings with another string.
Definition sstring.h:2176
Impl & append_vformatted(str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the string.
Definition sstring.h:2551
Impl & append_in(size_t pos, const A &expr)
Add a string expression starting at the given position.
Definition sstring.h:1947
my_type & format_from(size_t from, const FmtString< fmt_type, T... > &format, T &&... args)
Definition sstring.h:2439
Impl & trim(T &&pattern)
Remove characters included in a string literal at the beginning and end of the string.
Definition sstring.h:1623
Impl & operator+=(const A &expr)
Add a string expression to the end of the string.
Definition sstring.h:1912
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:3422
constexpr size_t size() const
Definition strexpr.h:3363
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "str_src".
Definition strexpr.h:3448
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:4227
constexpr str_storable(Args &&... args)
Create an empty object.
Definition sstring.h:1028
void init_replaced(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0)
Initialization from string source with replacement.
Definition sstring.h:1115
static my_type upperred_from(const From &f, Args &&... args)
Create a copy of the passed string in uppercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1309
constexpr void init_from_str_other(s_str other)
Initialization from another string object.
Definition sstring.h:1036
constexpr allocator_t & allocator()
Get the allocator.
Definition sstring.h:1005
constexpr void init_symb_repeat(size_t count, K pad)
Character repetition initialization.
Definition sstring.h:1072
constexpr void init_str_expr(const A &expr)
Initialization from a string expression.
Definition sstring.h:1093
static my_type upperred_only_ascii_from(const From &f, Args &&... args)
Create a string copy of the passed in uppercase ASCII characters.
Definition sstring.h:1279
static my_type lowered_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1326
constexpr void init_str_repeat(size_t repeat, s_str pattern)
String repetition initialization.
Definition sstring.h:1052
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase ASCII characters.
Definition sstring.h:1292
static my_type join(const T &strings, s_str delimiter, bool tail=false, bool skip_empty=false, Args &&... args)
Concatenate strings from the container into one string.
Definition sstring.h:1228
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Create a copy of the passed string with substrings replaced.
Definition sstring.h:1347
constexpr s_str_nt to_nts(size_t from=0) const
Get simple_str_nt starting at the given character.
Definition sstring.h:1172
Concept of a memory management type.
Definition sstring.h:1377
Concept of a memory management type.
Definition sstring.h:1357
Concept of a memory management type.
Definition sstring.h:1367
The concept of a string expression compatible with a given character type.
Definition strexpr.h:525
A type concept that cannot modify a stored string.
Definition sstring.h:953
A type concept that can modify a stored string.
Definition sstring.h:946
A type concept that can store a string.
Definition sstring.h:936
Small namespace for standard string methods.
Definition strexpr.h:1600
Library namespace.
Definition sstring.cpp:12
hashStrMap< u16s, T, str_eqlia, A > hashStrMapUIA
Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4778
hashStrMap< ubs, T, str_exact, A > hashStrMapB
Type of hash dictionary for char8_t strings, case sensitive search.
Definition sstring.h:4732
hashStrMap< wchar_t, T, str_eqliu, A > hashStrMapWIU
Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:4765
hashStrMap< u16s, T, str_exact, A > hashStrMapU
Hash dictionary type for char16_t strings, case sensitive search.
Definition sstring.h:4772
hashStrMap< u8s, T, str_exact, A > hashStrMapA
Type of hash dictionary for char strings, case sensitive search.
Definition sstring.h:4713
hashStrMap< u32s, T, str_exact, A > hashStrMapUU
Hash dictionary type for char32_t strings, case sensitive search.
Definition sstring.h:4791
hashStrMap< ubs, T, str_eqliu, A > hashStrMapBIU
Hash dictionary type for char8_t strings, case-insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:4744
hashStrMap< wchar_t, T, str_eqlia, A > hashStrMapWIA
Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4758
hashStrMap< u32s, T, str_eqliu, A > hashStrMapUUIU
Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4803
expr_utf< From, To > e_utf(simple_str< From > from)
Returns a string expression that converts a string of one character type to another type,...
Definition sstring.h:839
hashStrMap< ubs, T, str_eqlia, A > hashStrMapBIA
Type of hash dictionary for char8_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4738
hashStrMap< u8s, T, str_eqliu, A > hashStrMapAIU
Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
Definition sstring.h:4725
hashStrMap< wchar_t, T, str_exact, A > hashStrMapW
Hash dictionary type for wchar_t strings, case sensitive search.
Definition sstring.h:4751
hashStrMap< u32s, T, str_eqlia, A > hashStrMapUUIA
Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4797
hashStrMap< u8s, T, str_eqlia, A > hashStrMapAIA
Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4719
hashStrMap< u16s, T, str_eqliu, A > hashStrMapUIU
Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4784
Some methods for working with standard strings.
Definition strexpr.h:7505
An object that allows you to sequentially copy content into a buffer of a given size.
Definition sstring.h:4643
bool is_end()
Check that the data has not yet run out.
Definition sstring.h:4650
size_t store(K *buffer, size_t size)
Save the next portion of data to the buffer.
Definition sstring.h:4663
Generate a string based on the original one, replacing all ASCII uppercase letters (A-Z) with lowerca...
Definition strexpr.h:6945
Generate a string based on the original one, replacing all ASCII lowercase letters (a-z) with upperca...
Definition strexpr.h:6922
Definition sstring.h:923
Generates a string based on the original one, replacing all lowercase letters of the first Unicode pl...
Definition sstring.h:900
Base class for converting string expressions to standard strings.
Definition strexpr.h:659
String expression to convert strings to different UTF types.
Definition sstring.h:808
A class that claims to refer to a null-terminated string.
Definition sstring.h:617
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition sstring.h:680
constexpr simple_str_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:669
constexpr simple_str_nt(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:651
constexpr simple_str_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:657
constexpr simple_str_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition sstring.h:642
The simplest immutable non-owning string class.
Definition sstring.h:481
constexpr simple_str(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:497
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition sstring.h:561
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition sstring.h:585
constexpr size_t length() const noexcept
Get the length of the string.
Definition sstring.h:518
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition sstring.h:525
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a string by the specified number of characters.
Definition sstring.h:572
constexpr simple_str(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition sstring.h:513
constexpr simple_str(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:502
constexpr simple_str(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:508
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition sstring.h:532
constexpr bool is_same(simple_str< K > other) const noexcept
Check if two objects point to the same string.
Definition sstring.h:541
constexpr bool is_part_of(simple_str< K > other) const noexcept
Check if a string is part of another string.
Definition sstring.h:550
The simplest class of an immutable non-owning string.
Definition strexpr.h:5057
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition strexpr.h:5104