simstr 1.5.0
Yet another strings library
 
Loading...
Searching...
No Matches
sstring.h
1/*
2 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
3 * ver. 1.5.0
4 * Классы для работы со строками
5* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6* ver. 1.5.0
7* Classes for working with strings
8 */
9
20#pragma once
21
22#ifndef __has_declspec_attribute
23#define __has_declspec_attribute(x) 0
24#endif
25const bool isWindowsOs = // NOLINT
26#ifdef _WIN32
27 true
28#else
29 false
30#endif
31 ;
32
33#ifdef SIMSTR_IN_SHARED
34 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
35 #ifdef SIMSTR_EXPORT
36 #define SIMSTR_API __declspec(dllexport)
37 #else
38 #define SIMSTR_API __declspec(dllimport)
39 #endif
40 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
41 #define SIMSTR_API __attribute__((visibility("default")))
42 #else
43 #define SIMSTR_API
44 #endif
45#else
46 #define SIMSTR_API
47#endif
48
49#define IN_FULL_SIMSTR
50#include "strexpr.h"
51#undef simple_str
52
53#include <format>
54#include <unordered_map>
55#include <atomic>
56#include <memory>
57#include <string.h>
58#include <iostream>
59#include <cmath>
60
61#ifdef _WIN32
62#include <stdio.h>
63#endif
64
65#ifdef _MSC_VER
66// warning C4201 : nonstandard extension used : nameless struct / union
67#pragma warning(disable : 4201)
68#endif
69
70namespace simstr {
71
72template<typename T>
73struct unicode_traits {}; // NOLINT
74
75template<>
76struct unicode_traits<u8s> {
77 // Эти операции с utf-8 могут изменить длину строки
78 // Поэтому их специализации отличаются
79 // В функцию помимо текста и адреса буфера для записи передается размер буфера
80 // Возвращает длину получающейся строки.
81 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
82 // обработанные символы, для повторного возобновления работы,
83 // а для оставшихся символов считается нужный размер буфера.
84 // These utf-8 operations can change the length of the string
85 // Therefore their specializations are different
86 // In addition to the text and address of the buffer for writing, the buffer size is passed to the function
87 // Returns the length of the resulting string.
88 // If the resulting string does not fit into the allocated buffer, pointers are set to the last
89 // processed characters to resume work again,
90 // and for the remaining characters the required buffer size is calculated.
91 static SIMSTR_API size_t upper(const u8s*& src, size_t lenStr, u8s*& dest, size_t lenBuf);
92 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
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
140
141#if defined(_MSC_VER) && _MSC_VER <= 1933
142template<typename K, typename... Args>
143using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
144#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
145template<typename K, typename... Args>
146using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
147#else
148template<typename K, typename... Args>
149using FmtString = std::basic_string_view<K>;
150#endif
151
152template<typename K>
153SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
154
167template<typename K, typename StrRef, typename Impl, bool Mutable>
168class str_algs : public str_src_algs<K, StrRef, Impl, Mutable> {
169 constexpr const Impl& d() const noexcept {
170 return *static_cast<const Impl*>(this);
171 }
172 constexpr size_t _len() const noexcept {
173 return d().length();
174 }
175 constexpr const K* _str() const noexcept {
176 return d().symbols();
177 }
178 constexpr bool _is_empty() const noexcept {
179 return d().is_empty();
180 }
181
182public:
183 using symb_type = K;
184 using str_piece = StrRef;
185 using traits = ch_traits<K>;
186 using uni = unicode_traits<K>;
187 using uns_type = std::make_unsigned_t<K>;
188 using my_type = Impl;
189 using base = str_src_algs<K, StrRef, Impl, Mutable>;
190 str_algs() = default;
191
192 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
193 if (!len)
194 return _is_empty() ? 0 : 1;
195 return uni::compareiu(_str(), _len(), text, len);
196 }
205 int compare_iu(str_piece text) const noexcept { // NOLINT
206 return compare_iu(text.symbols(), text.length());
207 }
208
216 bool equal_iu(str_piece text) const noexcept { // NOLINT
217 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
218 }
219
227 bool less_iu(str_piece text) const noexcept { // NOLINT
228 return compare_iu(text.symbols(), text.length()) < 0;
229 }
230 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
231 // Does this string begin with the specified substring, insensitive to unicode case
232 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
233 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
234 }
241 bool starts_with_iu(str_piece prefix) const noexcept {
242 return starts_with_iu(prefix.symbols(), prefix.length());
243 }
244 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
245 // Whether the string ends with the specified substring, case insensitive UNICODE
246 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
247 size_t myLen = _len();
248 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
249 }
256 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
257 return ends_with_iu(suffix.symbols(), suffix.length());
258 }
259
267 template<typename R = my_type>
268 R upperred() const {
269 return R::upperred_from(d());
270 }
271
279 template<typename R = my_type>
280 R lowered() const {
281 return R::lowered_from(d());
282 }
283
289 template<bool SkipWS = true, bool AllowPlus = true>
290 std::optional<double> to_double() const noexcept {
291 size_t len = _len();
292 const K* ptr = _str();
293 if constexpr (SkipWS) {
294 while (len && uns_type(*ptr) <= ' ') {
295 len--;
296 ptr++;
297 }
298 }
299 if constexpr (AllowPlus) {
300 if (len && *ptr == K('+')) {
301 ptr++;
302 len--;
303 }
304 }
305 if (!len) {
306 return {};
307 }
308 #ifdef __linux__
309 if constexpr(sizeof(K) == 1) {
310 double d{};
311 if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
312 return d;
313 }
314 return {};
315 }
316 #endif
317 if constexpr (sizeof(K) == 1) {
318 return impl_to_double((const char*)ptr, (const char*)ptr + len);
319 } else if constexpr (sizeof(K) == 2) {
320 return impl_to_double((const char16_t*)ptr, (const char16_t*)ptr + len);
321 } else {
322 return impl_to_double((const char32_t*)ptr, (const char32_t*)ptr + len);
323 }
324 }
325
331 void as_number(double& t) const {
332 auto res = to_double();
333 t = res ? *res : std::nan("0");
334 }
335
347 template<ToIntNumber T>
348 constexpr void as_number(T& t) const {
350 }
351};
352
353/*
354* Базовая структура с информацией о строке.
355* Это структура для не владеющих строк.
356* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
357* в результате размер класса не увеличивается
358* Basic structure with string information.
359* This is the structure for non-owning strings.
360* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
361* as a result the class size does not increase
362*/
363
374template<typename K>
375struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
376 using symb_type = K;
377 using my_type = simple_str<K>;
378
379 const symb_type* str;
380 size_t len;
381
382 constexpr simple_str() = default;
383
384 constexpr simple_str(str_src<K> src) : str(src.str), len(src.len){}
385
390 template<typename T, size_t N = const_lit_for<K, T>::Count>
391 constexpr simple_str(T&& v) noexcept : str(v), len(N - 1) {}
396 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
401 template<typename A>
402 constexpr simple_str(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
407 constexpr simple_str(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
412 constexpr size_t length() const noexcept {
413 return len;
414 }
415
419 constexpr const symb_type* symbols() const noexcept {
420 return str;
421 }
422
426 constexpr bool is_empty() const noexcept {
427 return len == 0;
428 }
429
435 constexpr bool is_same(simple_str<K> other) const noexcept {
436 return str == other.str && len == other.len;
437 }
438
444 constexpr bool is_part_of(simple_str<K> other) const noexcept {
445 return str >= other.str && str + len <= other.str + other.len;
446 }
447
455 constexpr K operator[](size_t idx) const {
456 return str[idx];
457 }
458
466 constexpr my_type& remove_prefix(size_t delta) {
467 str += delta;
468 len -= delta;
469 return *this;
470 }
471
479 constexpr my_type& remove_suffix(size_t delta) {
480 len -= delta;
481 return *this;
482 }
483};
484
485template<typename K>
486struct simple_str_selector {
487 using type = simple_str<K>;
488};
489
510template<typename K>
511struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
512 using symb_type = K;
513 using my_type = simple_str_nt<K>;
514 using base = simple_str<K>;
515
516 constexpr static const K empty_string[1] = {0};
517
518 simple_str_nt() = default;
535 template<typename T> requires is_one_of_type<std::remove_cvref_t<T>, const K*, K*>::value
536 constexpr explicit simple_str_nt(T&& p) noexcept {
537 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
538 base::str = base::len ? p : empty_string;
539 }
540
544 template<typename T, size_t N = const_lit_for<K, T>::Count>
545 constexpr simple_str_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
546
551 constexpr simple_str_nt(const K* p, size_t l) noexcept : base(p, l) {}
552
553 template<StrType<K> T>
554 constexpr simple_str_nt(T&& t) {
555 base::str = t.symbols();
556 base::len = t.length();
557 }
562 template<typename A>
563 constexpr simple_str_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
564
565 static const my_type empty_str;
574 constexpr my_type to_nts(size_t from) {
575 if (from > base::len) {
576 from = base::len;
577 }
578 return {base::str + from, base::len - from};
579 }
580};
581
582template<typename K>
583inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
584
585template<typename K>
586using Splitter = SplitterBase<K, simple_str<K>>;
587
588using ssa = simple_str<u8s>;
589using ssb = simple_str<ubs>;
590using ssw = simple_str<wchar_t>;
591using ssu = simple_str<u16s>;
592using ssuu = simple_str<u32s>;
593using stra = simple_str_nt<u8s>;
594using strb = simple_str_nt<ubs>;
595using strw = simple_str_nt<wchar_t>;
596using stru = simple_str_nt<u16s>;
597using struu = simple_str_nt<u32s>;
598
599template<typename Src, typename Dest>
600struct utf_convert_selector;
601
602template<>
603struct utf_convert_selector<u8s, u16s> {
604 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
605 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
606};
607
608template<>
609struct utf_convert_selector<u8s, u32s> {
610 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
611 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
612};
613
614template<>
615struct utf_convert_selector<u8s, wchar_t> {
616 static size_t need_len(const u8s* src, size_t srcLen) {
617 return utf_convert_selector<u8s, wchar_type>::need_len(src, srcLen);
618 }
619 static size_t convert(const u8s* src, size_t srcLen, wchar_t* dest) {
620 return utf_convert_selector<u8s, wchar_type>::convert(src, srcLen, to_w(dest));
621 }
622};
623
624template<>
625struct utf_convert_selector<u16s, u8s> {
626 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
627 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
628};
629
630template<>
631struct utf_convert_selector<u16s, u32s> {
632 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
633 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
634};
635
636template<>
637struct utf_convert_selector<u16s, u16s> {
638 // При конвертации char16_t в wchar_t под windows будет вызываться эта реализация
639 // When converting char16_t to wchar_t under windows this implementation will be called
640 static size_t need_len(const u16s* src, size_t srcLen) {
641 return srcLen;
642 }
643 static size_t convert(const u16s* src, size_t srcLen, u16s* dest) {
644 ch_traits<u16s>::copy(dest, src, srcLen + 1);
645 return srcLen;
646 }
647};
648
649template<>
650struct utf_convert_selector<u32s, u32s> {
651 // При конвертации char32_t в wchar_t под linux будет вызываться эта реализация
652 // When converting char32_t to wchar_t under Linux, this implementation will be called
653 static size_t need_len(const u32s* src, size_t srcLen) {
654 return srcLen;
655 }
656 static size_t convert(const u32s* src, size_t srcLen, u32s* dest) {
657 ch_traits<u32s>::copy(dest, src, srcLen + 1);
658 return srcLen;
659 }
660};
661
662template<>
663struct utf_convert_selector<u16s, wchar_t> {
664 static size_t need_len(const u16s* src, size_t srcLen) {
665 return utf_convert_selector<u16s, wchar_type>::need_len(src, srcLen);
666 }
667 static size_t convert(const u16s* src, size_t srcLen, wchar_t* dest) {
668 return utf_convert_selector<u16s, wchar_type>::convert(src, srcLen, to_w(dest));
669 }
670};
671
672template<>
673struct utf_convert_selector<u32s, u8s> {
674 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
675 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
676};
677
678template<>
679struct utf_convert_selector<u32s, u16s> {
680 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
681 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
682};
683
684template<>
685struct utf_convert_selector<u32s, wchar_t> {
686 static size_t need_len(const u32s* src, size_t srcLen) {
687 return utf_convert_selector<u32s, wchar_type>::need_len(src, srcLen);
688 }
689 static size_t convert(const u32s* src, size_t srcLen, wchar_t* dest) {
690 return utf_convert_selector<u32s, wchar_type>::convert(src, srcLen, to_w(dest));
691 }
692};
693
694template<>
695struct utf_convert_selector<wchar_t, u8s> {
696 static size_t need_len(const wchar_t* src, size_t srcLen) {
697 return utf_convert_selector<wchar_type, u8s>::need_len(to_w(src), srcLen);
698 }
699 static size_t convert(const wchar_t* src, size_t srcLen, u8s* dest) {
700 return utf_convert_selector<wchar_type, u8s>::convert(to_w(src), srcLen, dest);
701 }
702};
703
704template<>
705struct utf_convert_selector<wchar_t, u16s> {
706 static size_t need_len(const wchar_t* src, size_t srcLen) {
707 return utf_convert_selector<wchar_type, u16s>::need_len(to_w(src), srcLen);
708 }
709 static size_t convert(const wchar_t* src, size_t srcLen, u16s* dest) {
710 return utf_convert_selector<wchar_type, u16s>::convert(to_w(src), srcLen, dest);
711 }
712};
713
714template<>
715struct utf_convert_selector<wchar_t, u32s> {
716 static size_t need_len(const wchar_t* src, size_t srcLen) {
717 return utf_convert_selector<wchar_type, u32s>::need_len(to_w(src), srcLen);
718 }
719 static size_t convert(const wchar_t* src, size_t srcLen, u32s* dest) {
720 return utf_convert_selector<wchar_type, u32s>::convert(to_w(src), srcLen, dest);
721 }
722};
723
724template<>
725struct utf_convert_selector<u8s, ubs> {
726 static size_t need_len(const u8s* src, size_t srcLen) {
727 return srcLen;
728 }
729 static size_t convert(const u8s* src, size_t srcLen, ubs* dest) {
730 ch_traits<u8s>::copy((u8s*)dest, src, srcLen);
731 return srcLen;
732 }
733};
734
735template<>
736struct utf_convert_selector<ubs, u8s> {
737 static size_t need_len(const ubs* src, size_t srcLen) {
738 return srcLen;
739 }
740 static size_t convert(const ubs* src, size_t srcLen, u8s* dest) {
741 ch_traits<u8s>::copy(dest, (const u8s*)src, srcLen);
742 return srcLen;
743 }
744};
745
760template<typename K, typename Impl>
761class from_utf_convertible {
762protected:
763 from_utf_convertible() = default;
764 using my_type = Impl;
765 /*
766 Эти методы должен реализовать класс-наследник.
767 вызывается только при создании объекта
768 init(size_t size)
769 set_size(size_t size)
770 */
771public:
772 template<typename O>
773 requires(!std::is_same_v<O, K>)
774 from_utf_convertible(simple_str<O> init) {
775 using worker = utf_convert_selector<O, K>;
776 Impl* d = static_cast<Impl*>(this);
777 size_t len = init.length();
778 if (!len)
779 d->create_empty();
780 else {
781 size_t need = worker::need_len(init.symbols(), len);
782 K* str = d->init(need);
783 str[need] = 0;
784 worker::convert(init.symbols(), len, str);
785 }
786 }
787 template<typename O, typename I, bool M>
788 requires(!std::is_same_v<O, K>)
789 from_utf_convertible(const str_algs<O, simple_str<O>, I, M>& init) : from_utf_convertible(init.to_str()) {}
790};
791
800template<typename From, typename To> requires (!std::is_same_v<From, To>)
801struct expr_utf : expr_to_std_string<expr_utf<From, To>> {
802 using symb_type = To;
803 using worker = utf_convert_selector<From, To>;
804
805 simple_str<From> source_;
806
807 constexpr expr_utf(simple_str<From> source) : source_(source){}
808
809 size_t length() const noexcept {
810 return worker::need_len(source_.symbols(), source_.length());
811 }
812 To* place(To* ptr) const noexcept {
813 return ptr + worker::convert(source_.symbols(), source_.length(), ptr);
814 }
815};
816
829template<typename To, typename From> requires (!std::is_same_v<From, To>)
831 return {from};
832}
833
838template<typename A, typename K>
839concept storable_str = requires {
840 A::is_str_storable == true;
841 std::is_same_v<typename A::symb_type, K>;
842};
843
848template<typename A, typename K>
849concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
850
855template<typename A, typename K>
857
900template<typename K, typename Impl, typename Allocator>
901class str_storable : protected Allocator {
902public:
903 using my_type = Impl;
904 using traits = ch_traits<K>;
905 using allocator_t = Allocator;
906 using s_str = simple_str<K>;
907 using s_str_nt = simple_str_nt<K>;
908
909protected:
914 constexpr allocator_t& allocator() {
915 return *static_cast<Allocator*>(this);
916 }
917 constexpr const allocator_t& allocator() const {
918 return *static_cast<const Allocator*>(this);
919 }
920
921 using uni = unicode_traits<K>;
922
923 constexpr Impl& d() noexcept {
924 return *static_cast<Impl*>(this);
925 }
926 constexpr const Impl& d() const noexcept {
927 return *static_cast<const Impl*>(this);
928 }
929
936 template<typename... Args>
937 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
938
945 constexpr void init_from_str_other(s_str other) {
946 if (other.length()) {
947 K* ptr = d().init(other.length());
948 traits::copy(ptr, other.symbols(), other.length());
949 ptr[other.length()] = 0;
950 } else
951 d().create_empty();
952 }
953
961 constexpr void init_str_repeat(size_t repeat, s_str pattern) {
962 size_t l = pattern.length(), allLen = l * repeat;
963 if (allLen) {
964 K* ptr = d().init(allLen);
965 for (size_t i = 0; i < repeat; i++) {
966 traits::copy(ptr, pattern.symbols(), l);
967 ptr += l;
968 }
969 *ptr = 0;
970 } else
971 d().create_empty();
972 }
973
981 constexpr void init_symb_repeat(size_t count, K pad) {
982 if (count) {
983 K* str = d().init(count);
984 traits::assign(str, count, pad);
985 str[count] = 0;
986 } else
987 d().create_empty();
988 }
989
1001 template<StrExprForType<K> A>
1002 constexpr void init_str_expr(const A& expr) {
1003 size_t len = expr.length();
1004 if (len)
1005 *expr.place((typename A::symb_type*)d().init(len)) = 0;
1006 else
1007 d().create_empty();
1008 }
1009
1023 template<StrType<K> From>
1024 void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
1025 auto findes = f.find_all(pattern, offset, maxCount);
1026 if (!findes.size()) {
1027 new (this) my_type{f};
1028 return;
1029 }
1030 size_t srcLen = f.length();
1031 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
1032
1033 if (!newSize) {
1034 new (this) my_type{};
1035 return;
1036 }
1037
1038 K* ptr = d().init(newSize);
1039 const K* src = f.symbols();
1040 size_t from = 0;
1041 for (const auto& s: findes) {
1042 size_t copyLen = s - from;
1043 if (copyLen) {
1044 traits::copy(ptr, src + from, copyLen);
1045 ptr += copyLen;
1046 }
1047 if (repl.len) {
1048 traits::copy(ptr, repl.str, repl.len);
1049 ptr += repl.len;
1050 }
1051 from = s + pattern.len;
1052 }
1053 srcLen -= from;
1054 if (srcLen) {
1055 traits::copy(ptr, src + from, srcLen);
1056 ptr += srcLen;
1057 }
1058 *ptr = 0;
1059 }
1060
1061 template<StrType<K> From, typename Op1, typename... Args>
1062 requires std::is_constructible_v<allocator_t, Args...>
1063 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
1064 my_type result{std::forward<Args>(args)...};
1065 size_t len = f.length();
1066 if (len) {
1067 const K* source = f.symbols();
1068 K* destination = result.init(len);
1069 for (size_t l = 0; l < len; l++) {
1070 destination[l] = opMakeNeedCase(source[l]);
1071 }
1072 }
1073 return result;
1074 }
1075 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
1076 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
1077 // GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
1078 // So let's add a dummy template parameter to make the specialization for u8s right in the class.
1079 template<typename T, bool Dummy = true>
1080 struct ChangeCase {
1081 template<typename From, typename Op1, typename... Args>
1082 requires std::is_constructible_v<allocator_t, Args...>
1083 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1084 my_type result{std::forward<Args>(args)...};
1085 size_t len = f.length();
1086 if (len) {
1087 opChangeCase(f.symbols(), len, result.init(len));
1088 }
1089 return result;
1090 }
1091 };
1092 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
1093 // For utf8 we will make a separate specification, since changing the register may change the length of the string
1094 template<bool Dummy>
1095 struct ChangeCase<u8s, Dummy> {
1096 template<typename From, typename Op1, typename... Args>
1097 requires std::is_constructible_v<allocator_t, Args...>
1098 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1099 my_type result{std::forward<Args>(args)...};
1100 ;
1101 size_t len = f.length();
1102 if (len) {
1103 const K* ptr = f.symbols();
1104 K* pWrite = result.init(len);
1105
1106 const u8s* source = ptr;
1107 u8s* dest = pWrite;
1108 size_t newLen = opChangeCase(source, len, dest, len);
1109 if (newLen < len) {
1110 // Строка просто укоротилась
1111 // The string was simply shortened
1112 result.set_size(newLen);
1113 } else if (newLen > len) {
1114 // Строка не влезла в буфер.
1115 // The line did not fit into the buffer.
1116 size_t readed = static_cast<size_t>(source - ptr);
1117 size_t writed = static_cast<size_t>(dest - pWrite);
1118 pWrite = result.set_size(newLen);
1119 dest = pWrite + writed;
1120 opChangeCase(source, len - readed, dest, newLen - writed);
1121 }
1122 pWrite[newLen] = 0;
1123 }
1124 return result;
1125 }
1126 };
1127
1128public:
1129
1130 inline static constexpr bool is_str_storable = true;
1137 constexpr operator const K*() const noexcept {
1138 return d().symbols();
1139 }
1140
1148 constexpr s_str_nt to_nts(size_t from = 0) const {
1149 size_t len = d().length();
1150 if (from >= len) {
1151 from = len;
1152 }
1153 return {d().symbols() + from, len - from};
1154 }
1155
1161 constexpr operator s_str_nt() const {
1162 return {d().symbols(), d().length()};
1163 }
1164
1202 template<typename T, typename... Args>
1203 requires std::is_constructible_v<allocator_t, Args...>
1204 static my_type join(const T& strings, s_str delimiter, bool tail = false, bool skip_empty = false, Args&&... args) {
1205 my_type result(std::forward<Args>(args)...);
1206 if (strings.size()) {
1207 if (strings.size() == 1 && (!delimiter.length() || !tail)) {
1208 result = strings.front();
1209 } else {
1210 size_t commonLen = 0;
1211 for (const auto& t: strings) {
1212 size_t len = t.length();
1213 if (len > 0 || !skip_empty) {
1214 if (commonLen > 0) {
1215 commonLen += delimiter.len;
1216 }
1217 commonLen += len;
1218 }
1219 }
1220 commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
1221 if (commonLen) {
1222 K* ptr = result.init(commonLen);
1223 K* write = ptr;
1224 for (const auto& t: strings) {
1225 size_t copyLen = t.length();
1226 if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
1227 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1228 write += delimiter.len;
1229 }
1230 ch_traits<K>::copy(write, t.symbols(), copyLen);
1231 write += copyLen;
1232 }
1233 if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
1234 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1235 write += delimiter.len;
1236 }
1237 *write = 0;
1238 } else {
1239 result.create_empty();
1240 }
1241 }
1242 }
1243 return result;
1244 }
1245
1253 template<StrType<K> From, typename... Args>
1254 requires std::is_constructible_v<allocator_t, Args...>
1255 static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
1256 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
1257 }
1258
1266 template<StrType<K> From, typename... Args>
1267 requires std::is_constructible_v<allocator_t, Args...>
1268 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
1269 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
1270 }
1271
1283 template<StrType<K> From, typename... Args>
1284 requires std::is_constructible_v<allocator_t, Args...>
1285 static my_type upperred_from(const From& f, Args&&... args) {
1286 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
1287 }
1288
1300 template<StrType<K> From, typename... Args>
1301 requires std::is_constructible_v<allocator_t, Args...>
1302 static my_type lowered_from(const From& f, Args&&... args) {
1303 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
1304 }
1305
1321 template<StrType<K> From, typename... Args>
1322 requires std::is_constructible_v<allocator_t, Args...>
1323 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
1324 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
1325 }
1326};
1327
1332template<typename A>
1333concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
1334 { a.allocate(size) } -> std::same_as<void*>;
1335 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
1336};
1337
1338struct printf_selector {
1339 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
1340 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
1341 if constexpr (sizeof(K) == 1) {
1342 #ifndef _WIN32
1343 return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
1344 #else
1345 // Поддерживает позиционные параметры
1346 // Supports positional parameters
1347 return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1348 #endif
1349 } else {
1350 #ifndef _WIN32
1351 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1352 #else
1353 // Поддерживает позиционные параметры
1354 // Supports positional parameters
1355 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1356 #endif
1357 }
1358 }
1359 template<typename K> requires (is_one_of_std_char_v<K>)
1360 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
1361 if constexpr (std::is_same_v<K, u8s>) {
1362 #ifndef _WIN32
1363 return std::vsnprintf(buffer, count, format, args);
1364 #else
1365 // Поддерживает позиционные параметры
1366 // Supports positional parameters
1367 return _vsprintf_p(buffer, count, format, args);
1368 #endif
1369 } else {
1370 #ifndef _WIN32
1371 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
1372 #else
1373 // Поддерживает позиционные параметры
1374 // Supports positional parameters
1375 return _vswprintf_p(buffer, count, format, args);
1376 #endif
1377 }
1378 }
1379};
1380
1381inline size_t grow2(size_t ret, size_t currentCapacity) {
1382 return ret <= currentCapacity ? ret : ret * 2;
1383}
1384
1385template<typename K>
1386struct to_std_char_type : std::type_identity<K>{};
1387
1388template<>
1389struct to_std_char_type<char8_t>{
1390 using type = char;
1391};
1392
1393template<>
1394struct to_std_char_type<char16_t>{
1395 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), wchar_t, void>;
1396};
1397
1398template<>
1399struct to_std_char_type<char32_t>{
1400 using type = std::conditional_t<sizeof(char32_t) == sizeof(wchar_t), wchar_t, void>;
1401};
1402
1441template<typename K, typename Impl>
1443public:
1444 using my_type = Impl;
1445
1446private:
1447 Impl& d() {
1448 return *static_cast<Impl*>(this);
1449 }
1450 const Impl& d() const {
1451 return *static_cast<const Impl*>(this);
1452 }
1453 size_t _len() const noexcept {
1454 return d().length();
1455 }
1456 const K* _str() const noexcept {
1457 return d().symbols();
1458 }
1459 using str_piece = simple_str<K>;
1460 using symb_type = K;
1461 using traits = ch_traits<K>;
1462 using uni = unicode_traits<K>;
1463 using uns_type = std::make_unsigned_t<K>;
1464
1465 template<typename Op>
1466 Impl& make_trim_op(const Op& op) {
1467 str_piece me = d(), pos = op(me);
1468 if (me.length() != pos.length()) {
1469 if (me.symbols() != pos.symbols())
1470 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
1471 d().set_size(pos.length());
1472 }
1473 return d();
1474 }
1475
1476 template<auto Op>
1477 Impl& commonChangeCase() {
1478 size_t len = _len();
1479 if (len)
1480 Op(_str(), len, str());
1481 return d();
1482 }
1483 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
1484 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
1485 // GCC still does not allow full specialization of a nested class within a class,
1486 // only partial. Resources additive unused parameter template.
1487 template<typename T, bool Dummy = true>
1488 struct CaseTraits {
1489 static Impl& upper(Impl& obj) {
1490 return obj.template commonChangeCase<unicode_traits<K>::upper>();
1491 }
1492 static Impl& lower(Impl& obj) {
1493 return obj.template commonChangeCase<unicode_traits<K>::lower>();
1494 }
1495 };
1496
1497 template<auto Op>
1498 Impl& utf8CaseChange() {
1499 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1500 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1501 size_t len = _len();
1502 if (len) {
1503 u8s* writePos = str();
1504 const u8s *startData = writePos, *readPos = writePos;
1505 size_t newLen = Op(readPos, len, writePos, len);
1506 if (newLen < len) {
1507 // Строка просто укоротилась
1508 // The string was simply shortened
1509 d().set_size(newLen);
1510 } else if (newLen > len) {
1511 // Строка не влезла в буфер.
1512 // The line did not fit into the buffer.
1513 size_t readed = static_cast<size_t>(readPos - startData);
1514 size_t writed = static_cast<size_t>(writePos - startData);
1515 d().set_size(newLen);
1516 startData = str(); // при изменении размера могло изменится | may change when resizing
1517 readPos = startData + readed;
1518 writePos = const_cast<u8s*>(startData) + writed;
1519 Op(readPos, len - readed, writePos, newLen - writed);
1520 }
1521 }
1522 return d();
1523 }
1524 template<bool Dummy>
1525 struct CaseTraits<u8s, Dummy> {
1526 static Impl& upper(Impl& obj) {
1527 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
1528 }
1529 static Impl& lower(Impl& obj) {
1530 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
1531 }
1532 };
1533
1534 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
1535 Impl& makeTrim(T&& pattern) {
1536 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
1537 }
1538
1539 template<TrimSides S, bool withSpaces>
1540 Impl& makeTrim(str_piece pattern) {
1541 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
1542 }
1543
1544public:
1551 K* str() noexcept {
1552 return d().str();
1553 }
1554
1560 explicit operator K*() noexcept {
1561 return str();
1562 }
1563
1569 Impl& trim() {
1570 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
1571 }
1572
1578 Impl& trim_left() {
1579 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
1580 }
1581
1587 Impl& trim_right() {
1588 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
1589 }
1590
1598 template<typename T, size_t N = const_lit_for<K, T>::Count>
1599 requires is_const_pattern<N>
1600 Impl& trim(T&& pattern) {
1601 return makeTrim<TrimSides::TrimAll, false>(pattern);
1602 }
1603
1611 template<typename T, size_t N = const_lit_for<K, T>::Count>
1612 requires is_const_pattern<N>
1613 Impl& trim_left(T&& pattern) {
1614 return makeTrim<TrimSides::TrimLeft, false>(pattern);
1615 }
1616
1624 template<typename T, size_t N = const_lit_for<K, T>::Count>
1625 requires is_const_pattern<N>
1626 Impl& trim_right(T&& pattern) {
1627 return makeTrim<TrimSides::TrimRight, false>(pattern);
1628 }
1629
1637 template<typename T, size_t N = const_lit_for<K, T>::Count>
1638 requires is_const_pattern<N>
1639 Impl& trim_with_spaces(T&& pattern) {
1640 return makeTrim<TrimSides::TrimAll, true>(pattern);
1641 }
1642
1650 template<typename T, size_t N = const_lit_for<K, T>::Count>
1651 requires is_const_pattern<N>
1652 Impl& trim_left_with_spaces(T&& pattern) {
1653 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1654 }
1655
1663 template<typename T, size_t N = const_lit_for<K, T>::Count>
1664 requires is_const_pattern<N>
1665 Impl& trim_right_with_wpaces(T&& pattern) {
1666 return makeTrim<TrimSides::TrimRight, true>(pattern);
1667 }
1668
1676 Impl& trim(str_piece pattern) {
1677 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
1678 }
1679
1687 Impl& trim_left(str_piece pattern) {
1688 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
1689 }
1690
1698 Impl& trim_right(str_piece pattern) {
1699 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
1700 }
1701
1709 Impl& trim_with_spaces(str_piece pattern) {
1710 return makeTrim<TrimSides::TrimAll, true>(pattern);
1711 }
1712
1720 Impl& trim_left_with_spaces(str_piece pattern) {
1721 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1722 }
1723
1731 Impl& trim_right_with_spaces(str_piece pattern) {
1732 return makeTrim<TrimSides::TrimRight, true>(pattern);
1733 }
1734
1741 K* ptr = str();
1742 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1743 K s = *ptr;
1744 if (isAsciiLower(s))
1745 *ptr = s & ~0x20;
1746 }
1747 return d();
1748 }
1749
1756 K* ptr = str();
1757 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1758 K s = *ptr;
1759 if (isAsciiUpper(s))
1760 *ptr = s | 0x20;
1761 }
1762 return d();
1763 }
1764
1774 Impl& upper() {
1775 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1776 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1777 return CaseTraits<K>::upper(d());
1778 }
1779
1789 Impl& lower() {
1790 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1791 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1792 return CaseTraits<K>::lower(d());
1793 }
1794
1795private:
1796 template<typename T>
1797 Impl& changeImpl(size_t from, size_t len, T expr) {
1798 size_t myLen = _len();
1799 if (from > myLen) {
1800 from = myLen;
1801 }
1802 if (from + len > myLen) {
1803 len = myLen - from;
1804 }
1805 K* buffer = str();
1806 size_t otherLen = expr.length();
1807 if (len == otherLen) {
1808 expr.place(buffer + from);
1809 } else {
1810 size_t tailLen = myLen - from - len;
1811 if (len > otherLen) {
1812 expr.place(buffer + from);
1813 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1814 d().set_size(myLen - (len - otherLen));
1815 } else {
1816 buffer = d().set_size(myLen + otherLen - len);
1817 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1818 expr.place(buffer + from);
1819 }
1820 }
1821 return d();
1822 }
1823
1824 template<typename T>
1825 Impl& appendImpl(T expr) {
1826 if (size_t len = expr.length(); len) {
1827 size_t size = _len();
1828 expr.place(d().set_size(size + len) + size);
1829 }
1830 return d();
1831 }
1832
1833 template<typename T>
1834 Impl& appendFromImpl(size_t pos, T expr) {
1835 if (pos > _len())
1836 pos = _len();
1837 if (size_t len = expr.length())
1838 expr.place(d().set_size(pos + len) + pos);
1839 else
1840 d().set_size(pos);
1841 return d();
1842 }
1843
1844public:
1845 inline static constexpr bool is_str_mutable = true;
1854 Impl& append(str_piece other) {
1855 return appendImpl<str_piece>(other);
1856 }
1857
1865 template<StrExprForType<K> A>
1866 Impl& append(const A& expr) {
1867 return appendImpl<const A&>(expr);
1868 }
1869
1877 Impl& operator+=(str_piece other) {
1878 return appendImpl<str_piece>(other);
1879 }
1880
1888 template<StrExprForType<K> A>
1889 Impl& operator+=(const A& expr) {
1890 return appendImpl<const A&>(expr);
1891 }
1892
1906 Impl& append_in(size_t pos, str_piece other) {
1907 return appendFromImpl<str_piece>(pos, other);
1908 }
1909
1923 template<StrExprForType<K> A>
1924 Impl& append_in(size_t pos, const A& expr) {
1925 return appendFromImpl<const A&>(pos, expr);
1926 }
1927
1939 Impl& change(size_t from, size_t len, str_piece other) {
1940 return changeImpl<str_piece>(from, len, other);
1941 }
1942
1954 template<StrExprForType<K> A>
1955 Impl& change(size_t from, size_t len, const A& expr) {
1956 return changeImpl<const A&>(from, len, expr);
1957 }
1958
1968 Impl& insert(size_t to, str_piece other) {
1969 return changeImpl<str_piece>(to, 0, other);
1970 }
1971
1981 template<StrExprForType<K> A>
1982 Impl& insert(size_t to, const A& expr) {
1983 return changeImpl<const A&>(to, 0, expr);
1984 }
1985
1995 Impl& remove(size_t from, size_t len) {
1996 return changeImpl<const empty_expr<K>&>(from, len, {});
1997 }
1998
2006 Impl& prepend(str_piece other) {
2007 return changeImpl<str_piece>(0, 0, other);
2008 }
2009
2017 template<StrExprForType<K> A>
2018 Impl& prepend(const A& expr) {
2019 return changeImpl<const A&>(0, 0, expr);
2020 }
2021
2035 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2036 offset = d().find(pattern, offset);
2037 if (offset == str::npos) {
2038 return d();
2039 }
2040 if (!maxCount)
2041 maxCount--;
2042 size_t replLength = repl.length(), patternLength = pattern.length();
2043
2044 if (patternLength == replLength) {
2045 // Заменяем inplace на подстроку такой же длины
2046 // Replace inplace with a substring of the same length
2047 K* ptr = str();
2048 while (maxCount--) {
2049 traits::copy(ptr + offset, repl.symbols(), replLength);
2050 offset = d().find(pattern, offset + replLength);// replLength == patternLength
2051 if (offset == str::npos)
2052 break;
2053 }
2054 } else if (patternLength > replLength) {
2055 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
2056 // Replace with a shorter piece, the length of the text will decrease, go from left to right
2057 K* ptr = str();
2058 traits::copy(ptr + offset, repl.symbols(), replLength);
2059 size_t posWrite = offset + replLength;
2060 offset += patternLength;
2061
2062 while (--maxCount) {
2063 size_t idx = d().find(pattern, offset);
2064 if (idx == str::npos)
2065 break;
2066 size_t lenOfPiece = idx - offset;
2067 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
2068 posWrite += lenOfPiece;
2069 traits::copy(ptr + posWrite, repl.symbols(), replLength);
2070 posWrite += replLength;
2071 offset = idx + patternLength;
2072 }
2073 size_t tailLen = _len() - offset;
2074 traits::move(ptr + posWrite, ptr + offset, tailLen);
2075 d().set_size(posWrite + tailLen);
2076 } else {
2077 struct replace_grow_helper {
2078 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
2079 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
2080 my_type& source;
2081 const str_piece pattern;
2082 const str_piece repl;
2083 size_t maxCount;
2084 const size_t delta;
2085 size_t all_delta{};
2086 K* reserve_for_copy{};
2087 size_t end_of_piece{};
2088 size_t total_length{};
2089
2090 void replace(size_t offset) {
2091 size_t found[16] = {offset};
2092 maxCount--;
2093 offset += pattern.length();
2094 all_delta += delta;
2095 size_t idx = 1;
2096 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
2097 found[idx] = source.find(pattern, offset);
2098 if (found[idx] == str::npos) {
2099 break;
2100 }
2101 offset = found[idx] + pattern.length();
2102 all_delta += delta;
2103 }
2104 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
2105 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
2106 }
2107 // Теперь делаем свои замены
2108 // Now we make our replacements
2109 if (!reserve_for_copy) {
2110 // Только начинаем
2111 // Just getting started
2112 end_of_piece = source.length();
2113 total_length = end_of_piece + all_delta;
2114 reserve_for_copy = source.alloc_for_copy(total_length);
2115 }
2116 K* dst_start = reserve_for_copy;
2117 const K* src_start = source.symbols();
2118 while(idx-- > 0) {
2119 size_t pos = found[idx] + pattern.length();
2120 size_t lenOfPiece = end_of_piece - pos;
2121 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
2122 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
2123 all_delta -= delta;
2124 end_of_piece = found[idx];
2125 }
2126 if (!all_delta && reserve_for_copy != src_start) {
2127 ch_traits<K>::copy(dst_start, src_start, found[0]);
2128 }
2129 }
2130 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
2131 helper.replace(offset);
2132 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
2133 }
2134 return d();
2135 }
2136
2152 template<StrType<K> From>
2153 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2154 if (pattern.length() >= repl.length()) {
2155 K* dst = d().reserve_no_preserve(f.length());
2156 const K* src = f.symbols();
2157 size_t delta = 0;
2158 if (maxCount == 0) {
2159 maxCount--;
2160 }
2161 size_t src_length = f.length(), start = 0;
2162 while (maxCount--) {
2163 offset = f.find(pattern, offset);
2164 if (offset == str::npos) {
2165 break;
2166 }
2167 size_t piece_len = offset - start;
2168 if (piece_len) {
2169 ch_traits<K>::copy(dst, src + start, piece_len);
2170 dst += piece_len;
2171 }
2172 if (repl.length()) {
2173 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
2174 dst += repl.length();
2175 }
2176 delta += pattern.length() - repl.length();
2177 offset += pattern.length();
2178 start = offset;
2179 }
2180 if (start < src_length) {
2181 ch_traits<K>::copy(dst, src + start, src_length - start);
2182 }
2183 d().set_size(src_length - delta);
2184 } else {
2185 d() = f;
2186 replace(pattern, repl, offset, maxCount);
2187 }
2188 return d();
2189 }
2190
2214 template<typename Op>
2215 Impl& fill(size_t from, const Op& fillFunction) {
2216 size_t size = _len();
2217 if (from > size)
2218 from = size;
2219 size_t capacity = d().capacity();
2220 K* ptr = str();
2221 capacity -= from;
2222 for (;;) {
2223 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
2224 if (capacity >= needSize) {
2225 d().set_size(from + needSize);
2226 break;
2227 }
2228 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
2229 capacity = d().capacity() - from;
2230 }
2231 return d();
2232 }
2233
2241 template<typename Op>
2242 requires std::is_invocable_v<Op, K*, size_t>
2243 Impl& operator<<(const Op& fillFunction) {
2244 return fill(0, fillFunction);
2245 }
2246
2254 template<typename Op>
2255 requires std::is_invocable_v<Op, K*, size_t>
2256 Impl& operator<<=(const Op& fillFunction) {
2257 return fill(_len(), fillFunction);
2258 }
2259
2267 template<typename Op>
2268 requires std::is_invocable_v<Op, my_type&>
2269 Impl& operator<<(const Op& fillFunction) {
2270 fillFunction(d());
2271 return d();
2272 }
2273
2287 template<typename... T> requires (is_one_of_std_char_v<K>)
2288 Impl& printf_from(size_t from, const K* format, T&&... args) {
2289 size_t size = _len();
2290 if (from > size)
2291 from = size;
2292 size_t capacity = d().capacity();
2293 K* ptr = str();
2294 capacity -= from;
2295
2296 int result = 0;
2297 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
2298 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
2299 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
2300 // 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
2301 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
2302 // Therefore, for them you need to stupidly increase the buffer at random until it fits
2303 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
2304 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2305 if (result > (int)capacity) {
2306 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
2307 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
2308 }
2309 } else {
2310 for (;;) {
2311 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2312 if (result < 0) {
2313 // Не хватило буфера или ошибка конвертации.
2314 // Попробуем увеличить буфер в два раза
2315 // Not enough buffer or conversion error.
2316 // Let's try to double the buffer
2317 capacity *= 2;
2318 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
2319 } else
2320 break;
2321 }
2322 }
2323 if (result < 0)
2324 d().set_size(static_cast<size_t>(traits::length(_str())));
2325 else
2326 d().set_size(from + result);
2327 return d();
2328 }
2329
2341 template<typename... T> requires (is_one_of_std_char_v<K>)
2342 Impl& printf(const K* format, T&&... args) {
2343 return printf_from(0, format, std::forward<T>(args)...);
2344 }
2345
2357 template<typename... T> requires (is_one_of_std_char_v<K>)
2358 Impl& append_printf(const K* format, T&&... args) {
2359 return printf_from(_len(), format, std::forward<T>(args)...);
2360 }
2361
2362 struct writer {
2363 my_type* store;
2364 K* ptr;
2365 const K* end;
2366 size_t max_write;
2367 size_t writed = 0;
2368 inline static K pad;
2369 K& operator*() const {
2370 return *ptr;
2371 }
2372 writer& operator++() {
2373 if (writed < max_write) {
2374 ++ptr;
2375 if (ptr == end) {
2376 size_t l = ptr - store->begin();
2377 store->set_size(l);
2378 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
2379 end = store->end();
2380 }
2381 } else {
2382 ptr = &pad;
2383 }
2384 return *this;
2385 }
2386 writer operator++(int) {
2387 auto ret = *this;
2388 operator++();
2389 return ret;
2390 }
2391
2392 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
2393 writer() = default;
2394 writer(const writer&) = delete;
2395 writer& operator=(const writer&) noexcept = delete;
2396 writer(writer&&) noexcept = default;
2397 writer& operator=(writer&&) noexcept = default;
2398 using difference_type = int;
2399 };
2400 using fmt_type = typename to_std_char_type<K>::type;
2415 template<typename... T> requires (is_one_of_std_char_v<K>)
2416 Impl& format_from(size_t from, const FmtString<fmt_type, T...>& format, T&&... args) {
2417 size_t size = _len();
2418 if (from > size)
2419 from = size;
2420 size_t capacity = d().capacity();
2421 K* ptr = str();
2422
2423 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
2424 std::forward<decltype(format)>(format), std::forward<T>(args)...);
2425 d().set_size(result.ptr - _str());
2426 return d();
2427 }
2428
2444 template<typename... T> requires (is_one_of_std_char_v<K>)
2445 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
2446 size_t size = _len();
2447 if (from > size)
2448 from = size;
2449 size_t capacity = d().capacity();
2450 K* ptr = str();
2451
2452 if constexpr (std::is_same_v<K, u8s>) {
2453 auto result = std::vformat_to(
2454 writer{d(), ptr + from, ptr + capacity, max_write},
2455 std::basic_string_view<K>{format.symbols(), format.length()},
2456 std::make_format_args(args...));
2457 d().set_size(result.ptr - _str());
2458 } else {
2459 auto result = std::vformat_to(
2460 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
2461 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
2462 std::make_wformat_args(std::forward<T>(args)...));
2463 d().set_size(result.ptr - _str());
2464 }
2465 return d();
2466 }
2467
2479 template<typename... T> requires (is_one_of_std_char_v<K>)
2480 Impl& format(const FmtString<fmt_type, T...>& pattern, T&&... args) {
2481 return format_from(0, pattern, std::forward<T>(args)...);
2482 }
2483
2495 template<typename... T> requires (is_one_of_std_char_v<K>)
2496 Impl& append_formatted(const FmtString<fmt_type, T...>& format, T&&... args) {
2497 return format_from(_len(), format, std::forward<T>(args)...);
2498 }
2499
2511 template<typename... T> requires (is_one_of_std_char_v<K>)
2512 Impl& vformat(str_piece format, T&&... args) {
2513 return vformat_from(0, -1, format, std::forward<T>(args)...);
2514 }
2515
2527 template<typename... T> requires (is_one_of_std_char_v<K>)
2528 Impl& append_vformatted(str_piece format, T&&... args) {
2529 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
2530 }
2531
2545 template<typename... T> requires (is_one_of_std_char_v<K>)
2546 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
2547 return vformat_from(0, max_write, format, std::forward<T>(args)...);
2548 }
2549
2563 template<typename... T> requires (is_one_of_std_char_v<K>)
2564 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
2565 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
2566 }
2567
2577 template<typename Op, typename... Args>
2578 Impl& with(const Op& fillFunction, Args&&... args) {
2579 fillFunction(d(), std::forward<Args>(args)...);
2580 return d();
2581 }
2582};
2583
2584template<typename K>
2585struct SharedStringData {
2586 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
2587
2588 SharedStringData() {
2589 ref_ = 1;
2590 }
2591 K* str() const {
2592 return (K*)(this + 1);
2593 }
2594 void incr() {
2595 ref_.fetch_add(1, std::memory_order_relaxed);
2596 }
2597 void decr(Allocatorable auto& allocator) {
2598 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2599 if (val == 1) {
2600 allocator.deallocate(this);
2601 }
2602 }
2603 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
2604 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
2605 return new (allocator.allocate(size)) SharedStringData();
2606 }
2607 static SharedStringData<K>* from_str(const K* p) {
2608 return (SharedStringData<K>*)p - 1;
2609 }
2610 K* place(K* p, size_t len) {
2611 ch_traits<K>::copy(p, str(), len);
2612 return p + len;
2613 }
2614};
2615
2616// Дефолтный аллокатор для строк, может работать статически
2617// Default allocator for strings, can work statically
2618class string_common_allocator {
2619public:
2620 void* allocate(size_t bytes) {
2621 return new char[bytes];
2622 }
2623 void deallocate(void* address) noexcept {
2624 delete [] static_cast<char*>(address);
2625 }
2626};
2627
2628string_common_allocator default_string_allocator_selector(...);
2629// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
2630// объявите функцию
2631// ваш_тип_аллокатора default_string_allocator_selector(int);
2632// If you want to set your default allocator for strings, before including sstring.h
2633// declare a function
2634// your_allocator_type default_string_allocator_selector(int);
2635using allocator_string = decltype(default_string_allocator_selector(int(0)));
2636
2637template<typename K, Allocatorable Allocator>
2638class sstring;
2639
2640/*
2641* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
2642* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
2643* Укажем ему явно.
2644* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
2645* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
2646* Let's tell him explicitly.
2647*/
2648
2669template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
2670class decl_empty_bases lstring :
2671 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
2672 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
2673 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
2674 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
2675 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
2676public:
2677 using symb_type = K;
2679 using allocator_t = Allocator;
2680
2681 enum : size_t {
2683 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
2684 };
2685
2686protected:
2687 enum : size_t {
2688 extra = forShared ? sizeof(SharedStringData<K>) : 0,
2689 };
2690
2691 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
2692 using base_storable = str_storable<K, my_type, Allocator>;
2693 using base_mutable = str_mutable<K, my_type>;
2694 using base_utf = from_utf_convertible<K, my_type>;
2695 using traits = ch_traits<K>;
2696 using s_str = base_storable::s_str;
2697
2698 friend base_storable;
2699 friend base_mutable;
2700 friend base_utf;
2701 friend class sstring<K, Allocator>;
2702
2703 K* data_;
2704 // Поле не должно инициализироваться, так как может устанавливаться в базовых конструкторах
2705 // The field should not be initialized, as it can be set in base constructors
2706 size_t size_;
2707
2708 union {
2709 size_t capacity_;
2710 K local_[LocalCapacity + 1];
2711 };
2712
2713 constexpr void create_empty() {
2714 data_ = local_;
2715 size_ = 0;
2716 local_[0] = 0;
2717 }
2718 constexpr static size_t calc_capacity(size_t s) {
2719 size_t real_need = (s + 1) * sizeof(K) + extra;
2720 size_t aligned_alloced = (real_need + alignof(std::max_align_t) - 1) / alignof(std::max_align_t) * alignof(std::max_align_t);
2721 return (aligned_alloced - extra) / sizeof(K) - 1;
2722 }
2723
2724 constexpr K* init(size_t s) {
2725 size_ = s;
2726 if (size_ > LocalCapacity) {
2727 s = calc_capacity(s);
2728 data_ = alloc_place(s);
2729 capacity_ = s;
2730 } else {
2731 data_ = local_;
2732 }
2733 return str();
2734 }
2735 // Методы для себя | Methods for yourself
2736 constexpr bool is_alloced() const noexcept {
2737 return data_ != local_;
2738 }
2739
2740 constexpr void dealloc() {
2741 if (is_alloced()) {
2742 base_storable::allocator().deallocate(to_real_address(data_));
2743 data_ = local_;
2744 }
2745 }
2746
2747 constexpr static K* to_real_address(void* ptr) {
2748 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
2749 }
2750 constexpr static K* from_real_address(void* ptr) {
2751 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
2752 }
2753
2754 constexpr K* alloc_place(size_t newSize) {
2755 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
2756 }
2757 // Вызывается при replace, когда меняют на более длинную замену
2758 // Called on replace when changing to a longer replacement
2759 constexpr K* alloc_for_copy(size_t newSize) {
2760 if (capacity() >= newSize) {
2761 // Замена войдёт в текущий буфер
2762 // Replacement will go into the current buffer
2763 return data_;
2764 }
2765 return alloc_place(calc_capacity(newSize));
2766 }
2767 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
2768 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
2769 constexpr void set_from_copy(K* ptr, size_t newSize) {
2770 if (ptr != data_) {
2771 // Да, копировали в новый буфер
2772 // Yes, copied to a new buffer
2773 dealloc();
2774 data_ = ptr;
2775 capacity_ = calc_capacity(newSize);
2776 }
2777 size_ = newSize;
2778 data_[newSize] = 0;
2779 }
2780
2781public:
2782 using base_utf::base_utf;
2783
2790 template<typename... Args>
2791 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
2792 constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2793 : base_storable(std::forward<Args>(args)...) {
2794 create_empty();
2795 }
2796
2805 template<typename... Args>
2806 requires std::is_constructible_v<allocator_t, Args...>
2807 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2809 }
2810
2820 template<typename... Args>
2821 requires std::is_constructible_v<allocator_t, Args...>
2822 constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2823 base_storable::init_str_repeat(repeat, pattern);
2824 }
2825
2835 template<typename... Args>
2836 requires std::is_constructible_v<allocator_t, Args...>
2837 constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2839 }
2840
2854 template<typename... Args>
2855 requires std::is_constructible_v<allocator_t, Args...>
2856 constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2858 }
2859
2875 template<StrType<K> From, typename... Args>
2876 requires std::is_constructible_v<allocator_t, Args...>
2877 constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
2878 : base_storable(std::forward<Args>(args)...) {
2879 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
2880 }
2881
2882 constexpr lstring() {
2883 create_empty();
2884 }
2885
2886 constexpr ~lstring() {
2887 dealloc();
2888 }
2889
2896 constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
2897 if (other.size_) {
2898 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2899 }
2900 }
2901
2909 template<typename... Args>
2910 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
2911 constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2912 if (other.size_) {
2913 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2914 }
2915 }
2916
2924 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
2925 requires std::is_constructible_v<allocator_t, Args...>
2926 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2927 if constexpr (I > 1) {
2928 K* ptr = init(I - 1);
2929 traits::copy(ptr, value, I - 1);
2930 ptr[I - 1] = 0;
2931 } else
2932 create_empty();
2933 }
2934
2940 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
2941 if (other.size_) {
2942 size_ = other.size_;
2943 if (other.is_alloced()) {
2944 data_ = other.data_;
2945 capacity_ = other.capacity_;
2946 } else {
2947 data_ = local_;
2948 traits::copy(local_, other.local_, size_ + 1);
2949 }
2950 other.data_ = other.local_;
2951 other.size_ = 0;
2952 other.local_[0] = 0;
2953 }
2954 }
2955
2963 template<typename Op, typename... Args>
2964 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
2965 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2966 create_empty();
2967 this->operator<<(op);
2968 }
2969
2970 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
2971 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
2972 // 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
2973 // Therefore, we implement the copy and move assignment separately
2974
2983 my_type& operator=(const my_type& other) {
2984 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
2985 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
2986 if (&other != this) {
2987 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
2988 size_ = other.size_;
2989 }
2990 return *this;
2991 }
2992
3000 my_type& operator=(my_type&& other) noexcept {
3001 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3002 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
3003 if (&other != this) {
3004 dealloc();
3005 if (other.is_alloced()) {
3006 data_ = other.data_;
3007 capacity_ = other.capacity_;
3008 } else {
3009 traits::copy(data_, other.local_, other.size_ + 1);
3010 }
3011 base_storable::allocator() = std::move(other.allocator());
3012 size_ = other.size_;
3013 other.create_empty();
3014 }
3015 return *this;
3016 }
3017
3018 my_type& assign(const K* other, size_t len) {
3019 if (len) {
3020 bool isIntersect = other >= data_ && other + len <= data_ + size_;
3021 if (isIntersect) {
3022 // Особый случай, нам пытаются присвоить кусок нашей же строки.
3023 // Просто переместим текст в буфере, и установим новый размер
3024 // A special case, they are trying to assign us a piece of our own string.
3025 // Just move the text in the buffer and set a new size
3026 if (other > data_) {
3027 traits::move(data_, other, len);
3028 }
3029 } else {
3030 traits::copy(reserve_no_preserve(len), other, len);
3031 }
3032 }
3033 size_ = len;
3034 data_[size_] = 0;
3035 return *this;
3036 }
3045 my_type& operator=(simple_str<K> other) {
3046 return assign(other.str, other.len);
3047 }
3048
3056 template<typename T, size_t S = const_lit_for<K, T>::Count>
3057 my_type& operator=(T&& other) {
3058 return assign(other, S - 1);
3059 }
3060
3070 my_type& operator=(const StrExprForType<K> auto& expr) {
3071 size_t newLen = expr.length();
3072 if (newLen) {
3073 expr.place(reserve_no_preserve(newLen));
3074 }
3075 size_ = newLen;
3076 data_[size_] = 0;
3077 return *this;
3078 }
3079
3080 constexpr size_t length() const noexcept {
3081 return size_;
3082 }
3083
3084 constexpr const K* symbols() const noexcept {
3085 return data_;
3086 }
3087
3088 constexpr K* str() noexcept {
3089 return data_;
3090 }
3091
3092 constexpr bool is_empty() const noexcept {
3093 return size_ == 0;
3094 }
3095
3096 constexpr bool empty() const noexcept {
3097 return size_ == 0;
3098 }
3099
3100 constexpr size_t capacity() const noexcept {
3101 return is_alloced() ? capacity_ : LocalCapacity;
3102 }
3103
3115 constexpr K* reserve_no_preserve(size_t newSize) {
3116 if (newSize > capacity()) {
3117 newSize = calc_capacity(newSize);
3118 K* newData = alloc_place(newSize);
3119 dealloc();
3120 data_ = newData;
3121 capacity_ = newSize;
3122 }
3123 return data_;
3124 }
3125
3137 constexpr K* reserve(size_t newSize) {
3138 if (newSize > capacity()) {
3139 newSize = calc_capacity(newSize);
3140 K* newData = alloc_place(newSize);
3141 traits::copy(newData, data_, size_);
3142 dealloc();
3143 data_ = newData;
3144 capacity_ = newSize;
3145 }
3146 return data_;
3147 }
3148
3160 constexpr K* set_size(size_t newSize) {
3161 size_t cap = capacity();
3162 if (newSize > cap) {
3163 size_t needPlace = newSize;
3164 if (needPlace < (cap + 1) * 2) {
3165 needPlace = (cap + 1) * 2 - 1;
3166 }
3167 reserve(needPlace);
3168 }
3169 size_ = newSize;
3170 data_[newSize] = 0;
3171 return data_;
3172 }
3173
3177 constexpr bool is_local() const noexcept {
3178 return !is_alloced();
3179 }
3180
3186 constexpr void define_size() {
3187 size_t cap = capacity();
3188 for (size_t i = 0; i < cap; i++) {
3189 if (data_[i] == 0) {
3190 size_ = i;
3191 return;
3192 }
3193 }
3194 size_ = cap;
3195 data_[size_] = 0;
3196 }
3197
3203 constexpr void shrink_to_fit() {
3204 size_t need_capacity = calc_capacity(size_);
3205 if (is_alloced() && capacity_ > need_capacity) {
3206 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
3207 traits::copy(newData, data_, size_ + 1);
3208 base_storable::allocator().deallocate(to_real_address(data_));
3209 data_ = newData;
3210
3211 if (size_ > LocalCapacity) {
3212 capacity_ = need_capacity;
3213 }
3214 }
3215 }
3216
3217 constexpr void clear() {
3218 set_size(0);
3219 }
3220
3221 constexpr void reset() {
3222 dealloc();
3223 local_[0] = 0;
3224 size_ = 0;
3225 }
3226};
3227
3228template<size_t N = 15>
3229using lstringa = lstring<u8s, N>;
3230template<size_t N = 15>
3231using lstringb = lstring<ubs, N>;
3232template<size_t N = 15>
3233using lstringw = lstring<wchar_t, N>;
3234template<size_t N = 15>
3235using lstringu = lstring<u16s, N>;
3236template<size_t N = 15>
3237using lstringuu = lstring<u32s, N>;
3238
3239template<size_t N = 15>
3240using lstringsa = lstring<u8s, N, true>;
3241template<size_t N = 15>
3242using lstringsb = lstring<ubs, N, true>;
3243template<size_t N = 15>
3244using lstringsw = lstring<wchar_t, N, true>;
3245template<size_t N = 15>
3246using lstringsu = lstring<u16s, N, true>;
3247template<size_t N = 15>
3248using lstringsuu = lstring<u32s, N, true>;
3249
3250
3251template<typename T, typename K = typename const_lit<T>::symb_type>
3252auto getLiteralType(T&&) {
3253 return K{};
3254};
3255
3256template<size_t Arch, size_t L>
3257inline constexpr const size_t _local_count = 0;
3258
3259template<>
3260inline constexpr const size_t _local_count<8, 1> = 23;
3261template<>
3262inline constexpr const size_t _local_count<8, 2> = 15;
3263template<>
3264inline constexpr const size_t _local_count<8, 4> = 7;
3265template<>
3266inline constexpr const size_t _local_count<4, 1> = 15;
3267template<>
3268inline constexpr const size_t _local_count<4, 2> = 11;
3269template<>
3270inline constexpr const size_t _local_count<4, 4> = 5;
3271
3272template<typename T>
3273constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
3274
3327template<typename K, Allocatorable Allocator = allocator_string>
3328class decl_empty_bases sstring :
3329 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
3330 public str_storable<K, sstring<K, Allocator>, Allocator>,
3331 public null_terminated<K, sstring<K, Allocator>>,
3332 public from_utf_convertible<K, sstring<K, Allocator>> {
3333public:
3334 using symb_type = K;
3335 using uns_type = std::make_unsigned_t<K>;
3336 using my_type = sstring<K, Allocator>;
3337 using allocator_t = Allocator;
3338
3339 enum { LocalCount = local_count<K> };
3340
3341protected:
3342 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
3343 using base_storable = str_storable<K, my_type, Allocator>;
3344 using base_utf = from_utf_convertible<K, my_type>;
3345 using traits = ch_traits<K>;
3346 using uni = unicode_traits<K>;
3347 using s_str = base_storable::s_str;
3348
3349 friend base_storable;
3350 friend base_utf;
3351
3352 enum Types { Local, Constant, Shared };
3353
3354 union {
3355 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
3356 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
3357 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
3358 // и в итоге после символов строки получается 0, как и надо!
3359 // When we have a short string, it lies in the object itself, and in localRemain
3360 // writes how many more characters can be entered. When a line takes up everything
3361 // possible location, then localRemain becomes 0, type in this case is also 0,
3362 // and as a result, after the characters of the line we get 0, as it should!
3363 struct {
3364 K buf_[LocalCount]; // Локальный буфер строки | Local line buffer
3365 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
3366 uns_type type_ : 2;
3367 };
3368 struct {
3369 union {
3370 // Указатель на конcтантную строку | Pointer to a constant string
3371 const K* cstr_;
3372 // Указатель на строку, перед которой лежит SharedStringData
3373 // Pointer to the string preceded by SharedStringData
3374 const K* sstr_;
3375 };
3376 size_t bigLen_; // Длина не локальной строки | Non-local string length
3377 uns_type pad_[LocalCount - (sizeof(const K*) + sizeof(size_t)) / sizeof(K)];
3378 uns_type blocalRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
3379 uns_type btype_ : 2;
3380 };
3381 };
3382
3383 constexpr void create_empty() {
3384 type_ = Local;
3385 localRemain_ = LocalCount;
3386 buf_[0] = 0;
3387 }
3388 K* init(size_t s) {
3389 if (s > LocalCount) {
3390 type_ = Shared;
3391 localRemain_ = 0;
3392 bigLen_ = s;
3393 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
3394 return (K*)sstr_;
3395 } else {
3396 type_ = Local;
3397 localRemain_ = LocalCount - s;
3398 return buf_;
3399 }
3400 }
3401
3402 K* set_size(size_t newSize) {
3403 // Вызывается при создании строки при необходимости изменить размер.
3404 // Других ссылок на shared buffer нет.
3405 // Called when a string is created and needs to be resized.
3406 // There are no other references to the shared buffer.
3407 size_t size = length();
3408 if (newSize != size) {
3409 if (type_ == Constant) {
3410 bigLen_ = newSize;
3411 } else {
3412 if (newSize <= LocalCount) {
3413 if (type_ == Shared) {
3414 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
3415 traits::copy(buf_, sstr_, newSize);
3416 str_buf->decr(base_storable::allocator());
3417 }
3418 type_ = Local;
3419 localRemain_ = LocalCount - newSize;
3420 } else {
3421 if (type_ == Shared) {
3422 if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
3423 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
3424 traits::copy(newStr, sstr_, newSize);
3425 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3426 sstr_ = newStr;
3427 }
3428 } else if (type_ == Local) {
3429 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
3430 if (size)
3431 traits::copy(newStr, buf_, size);
3432 sstr_ = newStr;
3433 type_ = Shared;
3434 localRemain_ = 0;
3435 }
3436 bigLen_ = newSize;
3437 }
3438 }
3439 }
3440 K* str = type_ == Local ? buf_ : (K*)sstr_;
3441 str[newSize] = 0;
3442 return str;
3443 }
3444
3445public:
3446 using base_utf::base_utf;
3447
3448 sstring() {
3449 create_empty();
3450 }
3451
3458 template<typename... Args>
3459 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
3460 sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
3461 : base_storable(std::forward<Args>(args)...) {
3462 create_empty();
3463 }
3464
3473 template<typename... Args>
3474 requires std::is_constructible_v<allocator_t, Args...>
3475 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...), buf_{0} {
3477 }
3478
3488 template<typename... Args>
3489 requires std::is_constructible_v<allocator_t, Args...>
3490 sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3491 base_storable::init_str_repeat(repeat, pattern);
3492 }
3493
3503 template<typename... Args>
3504 requires std::is_constructible_v<allocator_t, Args...>
3505 sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3507 }
3508
3522 template<typename... Args>
3523 requires std::is_constructible_v<allocator_t, Args...>
3524 constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3526 }
3527
3543 template<StrType<K> From, typename... Args>
3544 requires std::is_constructible_v<allocator_t, Args...>
3545 sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
3546 : base_storable(std::forward<Args>(args)...) {
3547 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3548 }
3549
3550 static const sstring<K> empty_str;
3552 constexpr ~sstring() {
3553 if (btype_ == Shared) {
3554 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3555 }
3556 }
3557
3563 constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
3564 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3565 if (type_ == Shared)
3566 SharedStringData<K>::from_str(sstr_)->incr();
3567 }
3568
3574 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3575 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3576 other.create_empty();
3577 }
3578
3589 template<size_t N>
3590 constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
3591 size_t size = src.length();
3592 if (size) {
3593 if (src.is_alloced()) {
3594 // Там динамический буфер, выделенный с запасом для SharedStringData.
3595 // There is a dynamic buffer allocated with a reserve for SharedStringData.
3596 K* str = src.str();
3597 if (size > LocalCount) {
3598 // Просто присвоим его себе.
3599 // Let's just assign it to ourselves.
3600 sstr_ = str;
3601 bigLen_ = size;
3602 type_ = Shared;
3603 localRemain_ = 0;
3604 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
3605 } else {
3606 // Скопируем локально
3607 // Copy locally
3608 type_ = Local;
3609 localRemain_ = LocalCount - size;
3610 traits::copy(buf_, str, size + 1);
3611 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
3612 // Let's free that buffer; a local string's buffer is not shared with others
3613 src.dealloc();
3614 }
3615 } else {
3616 // Копируем из локального буфера
3617 // Copy from local buffer
3618 K* str = init(src.size_);
3619 traits::copy(str, src.symbols(), size + 1);
3620 }
3621 src.create_empty();
3622 } else
3623 create_empty();
3624 }
3625
3636 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
3637 requires std::is_constructible_v<allocator_t, Args...>
3638 constexpr sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...)
3639 , btype_(Constant)
3640 , blocalRemain_(0)
3641 , cstr_(s)
3642 , bigLen_(N - 1)
3643 , pad_{}
3644 {
3645 }
3646
3647 constexpr void swap(my_type&& other) noexcept {
3648 char buf[sizeof(buf_) + sizeof(K)];
3649 memcpy(buf, buf_, sizeof(buf));
3650 memcpy(buf_, other.buf_, sizeof(buf));
3651 memcpy(other.buf_, buf, sizeof(buf));
3652
3653 std::swap(base_storable::allocator(), other.allocator());
3654 }
3663 constexpr my_type& operator=(my_type other) noexcept {
3664 swap(std::move(other));
3665 return *this;
3666 }
3667
3675 constexpr my_type& operator=(simple_str<K> other) {
3676 return operator=(my_type{other, base_storable::allocator()});
3677 }
3678
3686 template<typename T, size_t N = const_lit_for<K, T>::Count>
3687 constexpr my_type& operator=(T&& other) {
3688 return operator=(my_type{other, base_storable::allocator()});
3689 }
3690
3698 template<size_t N, bool forShared, typename A>
3699 constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
3700 return operator=(my_type{other.to_str(), base_storable::allocator()});
3701 }
3702
3710 template<size_t N>
3711 constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
3712 return operator=(my_type{std::move(other)});
3713 }
3714
3724 constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
3725 return operator=(my_type{expr, base_storable::allocator()});
3726 }
3727
3733 constexpr my_type& make_empty() noexcept {
3734 if (type_ == Shared)
3735 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3736 create_empty();
3737 return *this;
3738 }
3739
3740 constexpr const K* symbols() const noexcept {
3741 return btype_ == Local ? buf_ : cstr_;
3742 }
3743
3744 constexpr size_t length() const noexcept {
3745 return btype_ == Local ? LocalCount - blocalRemain_ : bigLen_;
3746 }
3747
3748 constexpr bool is_empty() const noexcept {
3749 return length() == 0;
3750 }
3751
3752 constexpr bool empty() const noexcept {
3753 return is_empty();
3754 }
3755
3767 template<typename... T>
3768 static my_type printf(const K* pattern, T&&... args) {
3769 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
3770 }
3771
3781 template<typename... T>
3782 static my_type format(const FmtString<typename to_std_char_type<K>::type, T...>& fmtString, T&&... args) {
3783 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
3784 }
3785
3795 template<typename... T>
3796 static my_type vformat(simple_str<K> fmtString, T&&... args) {
3797 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
3798 }
3799};
3800
3801template<typename K, Allocatorable Allocator>
3802inline const sstring<K> sstring<K, Allocator>::empty_str{};
3803
3804template<typename K>
3805consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
3806 if constexpr (std::is_same_v<K, u8s>)
3807 return s8;
3808 if constexpr (std::is_same_v<K, uws>)
3809 return sw;
3810 if constexpr (std::is_same_v<K, u16s>)
3811 return s16;
3812 if constexpr (std::is_same_v<K, u32s>)
3813 return s32;
3814}
3815
3816#define uni_string(K, p) select_str<K>(p, L##p, u##p, U##p)
3817
3818template<typename K, typename H>
3819struct StoreType {
3820 simple_str<K> str;
3821 size_t hash;
3822 char node[sizeof(sstring<K>)];
3823
3824 const simple_str_nt<K>& to_nt() const noexcept {
3825 return static_cast<const simple_str_nt<K>&>(str);
3826 }
3827 const sstring<K>& to_str() const noexcept {
3828 return *reinterpret_cast<const sstring<K>*>(node);
3829 }
3830};
3831
3832template<bool Wide>
3833struct fnv_const {
3834 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
3835 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
3836};
3837
3838template<>
3839struct fnv_const<false> {
3840 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
3841 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
3842};
3843
3844using fnv = fnv_const<sizeof(size_t) == 8>;
3845
3846template<typename K>
3847inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
3848 size_t h = fnv::basis;
3849 for (size_t i = 0; i < l; i++) {
3850 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
3851 }
3852 return h;
3853};
3854
3855template<typename K>
3856inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
3857 size_t h = fnv::basis;
3858 for (size_t i = 0; i < l; i++) {
3859 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
3860 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
3861 }
3862 return h;
3863};
3864
3865template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
3866inline constexpr size_t fnv_hash(T&& value) {
3867 size_t h = fnv::basis;
3868 for (size_t i = 0; i < N - 1; i++) {
3869 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
3870 }
3871 return h;
3872};
3873
3874template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
3875inline constexpr size_t fnv_hash_ia(T&& value) {
3876 size_t h = fnv::basis;
3877 for (size_t i = 0; i < N - 1; i++) {
3878 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
3879 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
3880 }
3881 return h;
3882};
3883
3884template<typename K>
3885inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
3886 return fnv_hash(ptr, l);
3887};
3888
3889template<typename K>
3890inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
3891 return fnv_hash_ia(ptr, l);
3892};
3893
3894static_assert(std::is_trivially_copyable_v<StoreType<u8s, int>>, "Store type must be trivially copyable");
3895
3896template<typename K>
3897struct streql;
3898template<typename K>
3899struct strhash;
3900
3995template<typename K, typename T, typename H = strhash<K>, typename E = streql<K>>
3996class hashStrMap : public std::unordered_map<StoreType<K, H>, T, H, E> {
3997protected:
3998 using InStore = StoreType<K, H>;
3999
4000public:
4001 using my_type = hashStrMap<K, T, H, E>;
4002 using hash_t = std::unordered_map<InStore, T, H, E>;
4003 using hasher = H;
4004
4005 hashStrMap() = default;
4006 hashStrMap(const my_type& other) : hash_t(other) {
4007 for (const auto& [k, v] : *this) {
4008 InStore& stored = const_cast<InStore&>(k);
4009 sstring<K> tmp = *(sstring<K>*)stored.node;
4010 new (stored.node) sstring<K>(std::move(tmp));
4011 stored.str.str = stored.to_str().symbols();
4012 }
4013 }
4014 ~hashStrMap() {
4015 for (auto& k: *this)
4016 ((sstring<K>*)k.first.node)->~sstring();
4017 }
4018
4019 hashStrMap(my_type&& o) = default;
4020
4021 my_type& operator=(const my_type& other) {
4022 hash_t::operator=(other);
4023 for (const auto& [k, v] : *this) {
4024 InStore& stored = const_cast<InStore&>(k);
4025 sstring<K> tmp = *(sstring<K>*)stored.node;
4026 new (stored.node) sstring<K>(std::move(tmp));
4027 stored.str.str = stored.to_str().symbols();
4028 }
4029 return *this;
4030 };
4031 my_type& operator=(my_type&&) = default;
4032
4033 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
4034 for (const auto& e: init)
4035 emplace(e.first, e.second);
4036 }
4037
4038 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
4039
4040 hashStrMap(init_str&& init) {
4041 for (const auto& e: init)
4042 emplace(e.first, e.second);
4043 }
4044
4045 // При входе хэш должен быть уже посчитан
4046 // When entering, the hash must already be calculated
4047 template<typename... ValArgs>
4048 auto try_emplace(const InStore& key, ValArgs&&... args) {
4049 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
4050 if (it.second) {
4051 InStore& stored = const_cast<InStore&>(it.first->first);
4052 new (stored.node) sstring<K>(key.str);
4053 stored.str.str = stored.to_str().symbols();
4054 }
4055 return it;
4056 }
4057
4058 static InStore toStoreType(simple_str<K> key) {
4059 return {key, H{}(key)};
4060 }
4061
4062 template<typename Key, typename... ValArgs>
4063 requires(std::is_convertible_v<Key, simple_str<K>>)
4064 auto try_emplace(Key&& key, ValArgs&&... args) {
4065 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
4066 if (it.second) {
4067 InStore& stored = const_cast<InStore&>(it.first->first);
4068 new (stored.node) sstring<K>(std::forward<Key>(key));
4069 stored.str.str = stored.to_str().symbols();
4070 }
4071 return it;
4072 }
4073
4074 template<typename... ValArgs>
4075 auto emplace(const InStore& key, ValArgs&&... args) {
4076 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
4077 if (!it.second) {
4078 it.first->second = T(std::forward<ValArgs>(args)...);
4079 }
4080 return it;
4081 }
4082
4083 template<typename Key, typename... ValArgs>
4084 requires(std::is_convertible_v<Key, simple_str<K>>)
4085 auto emplace(Key&& key, ValArgs&&... args) {
4086 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
4087 if (!it.second) {
4088 it.first->second = T(std::forward<ValArgs>(args)...);
4089 }
4090 return it;
4091 }
4092
4093 auto& operator[](const InStore& key) {
4094 return try_emplace(key).first->second;
4095 }
4096
4097 template<typename Key>
4098 requires(std::is_convertible_v<Key, simple_str<K>>)
4099 auto& operator[](Key&& key) {
4100 return try_emplace(std::forward<Key>(key)).first->second;
4101 }
4102
4103 decltype(auto) at(const InStore& key) {
4104 return hash_t::at(key);
4105 }
4106 decltype(auto) at(const InStore& key) const {
4107 return hash_t::at(key);
4108 }
4109
4110 decltype(auto) at(simple_str<K> key) {
4111 return hash_t::at(toStoreType(key));
4112 }
4113 decltype(auto) at(simple_str<K> key) const {
4114 return hash_t::at(toStoreType(key));
4115 }
4116
4117 auto find(const InStore& key) const {
4118 return hash_t::find(key);
4119 }
4120
4121 auto find(simple_str<K> key) const {
4122 return find(toStoreType(key));
4123 }
4124
4125 auto find(const InStore& key) {
4126 return hash_t::find(key);
4127 }
4128
4129 auto find(simple_str<K> key) {
4130 return find(toStoreType(key));
4131 }
4132
4133 auto erase(typename hash_t::const_iterator it) {
4134 if (it != hash_t::end()) {
4135 ((sstring<K>*)it->first.node)->~sstring();
4136 }
4137 return hash_t::erase(it);
4138 }
4139
4140 auto erase(const InStore& key) {
4141 auto it = hash_t::find(key);
4142 if (it != hash_t::end()) {
4143 ((sstring<K>*)it->first.node)->~sstring();
4144 hash_t::erase(it);
4145 return 1;
4146 }
4147 return 0;
4148 }
4149
4150 auto erase(simple_str<K> key) {
4151 return erase(toStoreType(key));
4152 }
4153
4154 bool lookup(simple_str<K> txt, T& val) const {
4155 auto it = find(txt);
4156 if (it != hash_t::end()) {
4157 val = it->second;
4158 return true;
4159 }
4160 return false;
4161 }
4162
4163 void clear() {
4164 for (auto& k: *this)
4165 ((sstring<K>*)k.first.node)->~sstring();
4166 hash_t::clear();
4167 }
4168 bool contains(const InStore& key) const {
4169 return hash_t::find(key) != this->end();
4170 }
4171
4172 bool contains(simple_str<K> key) const {
4173 return find(toStoreType(key)) != this->end();
4174 }
4175};
4176
4177template<typename K>
4178struct streql {
4179 template<typename H>
4180 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4181 return _Left.hash == _Right.hash && _Left.str == _Right.str;
4182 }
4183};
4184
4185template<typename K>
4186struct strhash { // hash functor for basic_string
4187 size_t operator()(simple_str<K> _Keyval) const {
4188 return fnv_hash(_Keyval.symbols(), _Keyval.length());
4189 }
4190 size_t operator()(const StoreType<K, strhash<K>>& _Keyval) const {
4191 return _Keyval.hash;
4192 }
4193};
4194
4195template<typename K>
4196struct streqlia {
4197 template<typename H>
4198 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4199 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
4200 }
4201};
4202
4203template<typename K>
4204struct strhashia {
4205 size_t operator()(simple_str<K> _Keyval) const {
4206 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
4207 }
4208 size_t operator()(const StoreType<K, strhashia<K>>& _Keyval) const {
4209 return _Keyval.hash;
4210 }
4211};
4212
4213template<typename K>
4214struct streqliu {
4215 template<typename H>
4216 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4217 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
4218 }
4219};
4220
4221template<typename K>
4222struct strhashiu {
4223 size_t operator()(simple_str<K> _Keyval) const {
4224 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
4225 }
4226 size_t operator()(const StoreType<K, strhashiu<K>>& _Keyval) const {
4227 return _Keyval.hash;
4228 }
4229};
4230
4245template<typename K>
4246class chunked_string_builder : expr_to_std_string<chunked_string_builder<K>> {
4247 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
4248 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
4249 K* write{}; // Текущая позиция записи | Current write position
4250 size_t len{}; // Общая длина | Total length
4251 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
4252 size_t align{1024};
4253
4254public:
4255 using my_type = chunked_string_builder<K>;
4256 using symb_type = K;
4257 chunked_string_builder() = default;
4258 chunked_string_builder(size_t a) : align(a){};
4259 chunked_string_builder(const my_type&) = delete;
4260 chunked_string_builder(my_type&& other) noexcept
4261 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
4262 other.len = other.remain = 0;
4263 other.write = nullptr;
4264 }
4265 my_type& operator=(my_type other) noexcept {
4266 chunks.swap(other.chunks);
4267 write = other.write;
4268 len = other.len;
4269 remain = other.remain;
4270 align = other.align;
4271 other.len = other.remain = 0;
4272 other.write = nullptr;
4273 return *this;
4274 }
4275
4278 if (data.len) {
4279 len += data.len;
4280 if (data.len <= remain) {
4281 // Добавляемые данные влезают в выделенный блок, просто скопируем их
4282 // The added data fits into the selected block, just copy it
4283 ch_traits<K>::copy(write, data.str, data.len);
4284 write += data.len; // Сдвинем позицию записи | Let's move the recording position
4285 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4286 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
4287 } else {
4288 // Не влезают | They don't fit
4289 if (remain) {
4290 // Сначала запишем сколько влезет
4291 // First, write down as much as we can
4292 ch_traits<K>::copy(write, data.str, remain);
4293 data.len -= remain;
4294 data.str += remain;
4295 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4296 }
4297 // Выделим новый блок и впишем в него данные
4298 // Рассчитаем размер блока, кратного заданному выравниванию
4299 // Select a new block and write data into it
4300 // Calculate the block size that is a multiple of the given alignment
4301 size_t blockSize = (data.len + align - 1) / align * align;
4302 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
4303 write = chunks.back().first.get();
4304 ch_traits<K>::copy(write, data.str, data.len);
4305 write += data.len;
4306 remain = blockSize - data.len;
4307 }
4308 }
4309 return *this;
4310 }
4311
4312 my_type& operator<<(const StrExprForType<K> auto& expr) {
4313 size_t l = expr.length();
4314 if (l) {
4315 if (l < remain) {
4316 write = expr.place(write);
4317 chunks.back().second += l;
4318 len += l;
4319 remain -= l;
4320 } else if (!remain) {
4321 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
4322 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
4323 write = expr.place(chunks.back().first.get());
4324 len += l;
4325 remain = blockSize - l;
4326 } else {
4327 auto store = std::make_unique<K[]>(l);
4328 expr.place(store.get());
4329 return operator<<({store.get(), l});
4330 }
4331 }
4332 return *this;
4333 }
4334
4335 template<typename T>
4336 my_type& operator<<(T data)
4337 requires std::is_same_v<T, K>
4338 {
4339 return operator<<(expr_char<K>(data));
4340 }
4341
4342 constexpr size_t length() const noexcept {
4343 return len;
4344 }
4345
4346 void reset() {
4347 if (chunks.empty()) {
4348 return;
4349 }
4350 if (chunks.size() > 1) {
4351 remain = 0;
4352 chunks.resize(1);
4353 }
4354 remain += chunks[0].second;
4355 chunks[0].second = 0;
4356 len = 0;
4357 write = chunks[0].first.get();
4358 }
4359
4360 constexpr K* place(K* p) const noexcept {
4361 for (const auto& block: chunks) {
4362 ch_traits<K>::copy(p, block.first.get(), block.second);
4363 p += block.second;
4364 }
4365 return p;
4366 }
4375 template<typename Op>
4376 void out(const Op& o) const {
4377 for (const auto& block: chunks)
4378 o(block.first.get(), block.second);
4379 }
4380
4384 bool is_continuous() const {
4385 if (chunks.size()) {
4386 const K* ptr = chunks.front().first.get();
4387 for (const auto& chunk: chunks) {
4388 if (chunk.first.get() != ptr)
4389 return false;
4390 ptr += chunk.second;
4391 }
4392 }
4393 return true;
4394 }
4395
4401 const K* begin() const {
4402 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
4403 }
4404
4408 void clear() {
4409 chunks.clear();
4410 write = nullptr;
4411 len = 0;
4412 remain = 0;
4413 }
4414
4419 typename decltype(chunks)::const_iterator it, end;
4420 size_t writedFromCurrentChunk;
4425 bool is_end() {
4426 return it == end;
4427 }
4428
4438 size_t store(K* buffer, size_t size) {
4439 size_t writed = 0;
4440 while (size && !is_end()) {
4441 size_t remain = it->second - writedFromCurrentChunk;
4442 size_t write = std::min(size, remain);
4443 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
4444 writed += write;
4445 remain -= write;
4446 size -= write;
4447 if (!remain) {
4448 ++it;
4449 writedFromCurrentChunk = 0;
4450 } else
4451 writedFromCurrentChunk += write;
4452 }
4453 return writed;
4454 }
4455 };
4456
4463 return {chunks.begin(), chunks.end(), 0};
4464 }
4465
4471 const auto& data() const {
4472 return chunks;
4473 }
4474};
4475
4476using stringa = sstring<u8s>;
4477using stringb = sstring<ubs>;
4478using stringw = sstring<wchar_t>;
4479using stringu = sstring<u16s>;
4480using stringuu = sstring<u32s>;
4481static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
4482
4487template<typename T>
4493template<typename T>
4499template<typename T>
4501
4506template<typename T>
4508
4513template<typename T>
4515
4520template<typename T>
4522
4527template<typename T>
4529template<typename T>
4539template<typename T>
4541
4546template<typename T>
4552template<typename T>
4558template<typename T>
4560
4561inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
4562
4563inline namespace literals {
4564
4575SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
4576 return simple_str_nt<u8s>{ptr, l};
4577}
4578
4588SS_CONSTEVAL simple_str_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
4589 return simple_str_nt<ubs>{ptr, l};
4590}
4591
4601SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
4602 return simple_str_nt<uws>{ptr, l};
4603}
4604
4614SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
4615 return simple_str_nt<u16s>{ptr, l};
4616}
4617
4628SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
4629 return simple_str_nt<u32s>{ptr, l};
4630}
4631
4632template<typename K> using HashKey = StoreType<K, strhash<K>>;
4633template<typename K> using HashKeyIA = StoreType<K, strhashia<K>>;
4634template<typename K> using HashKeyIU = StoreType<K, strhashiu<K>>;
4635
4646consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
4647 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4648}
4649
4660consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
4661 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4662}
4663
4674inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
4675 return HashKeyIU<u8s>{{ptr, l}, strhashiu<u8s>{}(simple_str<u8s>{ptr, l})};
4676}
4677
4688consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
4689 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4690}
4691
4702consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
4703 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4704}
4705
4716inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
4717 return HashKeyIU<u16s>{{ptr, l}, strhashiu<u16s>{}(simple_str<u16s>{ptr, l})};
4718}
4719
4730consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
4731 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4732}
4733
4744consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
4745 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4746}
4747
4758inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
4759 return HashKeyIU<u32s>{{ptr, l}, strhashiu<u32s>{}(simple_str<u32s>{ptr, l})};
4760}
4761
4772consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
4773 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
4774}
4775
4786consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
4787 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4788}
4789
4800inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
4801 return HashKeyIU<uws>{{ptr, l}, strhashiu<uws>{}(simple_str<uws>{ptr, l})};
4802}
4803} // namespace literals
4804
4815inline std::ostream& operator<<(std::ostream& stream, ssa text) {
4816 return stream << std::string_view{text.symbols(), text.length()};
4817}
4818
4829inline std::wostream& operator<<(std::wostream& stream, ssw text) {
4830 return stream << std::wstring_view{text.symbols(), text.length()};
4831}
4832
4843inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
4844 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4845}
4846
4857inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
4858 return stream << std::string_view{text.symbols(), text.length()};
4859}
4860
4871inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
4872 return stream << std::wstring_view{text.symbols(), text.length()};
4873}
4874
4885inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
4886 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4887}
4888
4899template<size_t N, bool S, simstr::Allocatorable A>
4900inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
4901 return stream << std::string_view{text.symbols(), text.length()};
4902}
4903
4914template<size_t N, bool S, simstr::Allocatorable A>
4915inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
4916 return stream << std::wstring_view{text.symbols(), text.length()};
4917}
4918
4929template<size_t N, bool S, simstr::Allocatorable A>
4930inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
4931 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4932}
4933
4934} // namespace simstr
4935
4940template<typename K>
4941struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
4942 // Define format() by calling the base class implementation with the wrapped value
4943 template<typename FormatContext>
4944 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
4945 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
4946 }
4947};
4948
4953template<typename K>
4954struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
4955 // Define format() by calling the base class implementation with the wrapped value
4956 template<typename FormatContext>
4957 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
4958 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
4959 }
4960};
4961
4966template<typename K>
4967struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
4968 // Define format() by calling the base class implementation with the wrapped value
4969 template<typename FormatContext>
4970 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
4971 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
4972 }
4973};
4974
4979template<typename K, size_t N, bool S, typename A>
4980struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
4981 // Define format() by calling the base class implementation with the wrapped value
4982 template<typename FormatContext>
4983 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
4984 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
4985 }
4986};
4987
4992template<>
4993struct std::formatter<simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
4994 // Define format() by calling the base class implementation with the wrapped value
4995 template<typename FormatContext>
4996 auto format(simstr::simple_str<char8_t> t, FormatContext& fc) const {
4997 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
4998 }
4999};
5000
5005template<>
5006struct std::formatter<simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5007 // Define format() by calling the base class implementation with the wrapped value
5008 template<typename FormatContext>
5009 auto format(simstr::simple_str_nt<char8_t> t, FormatContext& fc) const {
5010 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5011 }
5012};
5013
5018template<>
5019struct std::formatter<simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5020 // Define format() by calling the base class implementation with the wrapped value
5021 template<typename FormatContext>
5022 auto format(const simstr::sstring<char8_t>& t, FormatContext& fc) const {
5023 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5024 }
5025};
5026
5031template<size_t N, bool S, typename A>
5032struct std::formatter<simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
5033 // Define format() by calling the base class implementation with the wrapped value
5034 template<typename FormatContext>
5035 auto format(const simstr::lstring<char8_t, N, S, A>& t, FormatContext& fc) const {
5036 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5037 }
5038};
5039
5044template<>
5045struct std::formatter<simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5046 // Define format() by calling the base class implementation with the wrapped value
5047 template<typename FormatContext>
5048 auto format(simstr::simple_str<simstr::wchar_type> t, FormatContext& fc) const {
5049 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5050 }
5051};
5052
5057template<>
5058struct std::formatter<simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5059 // Define format() by calling the base class implementation with the wrapped value
5060 template<typename FormatContext>
5061 auto format(simstr::simple_str_nt<simstr::wchar_type> t, FormatContext& fc) const {
5062 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5063 }
5064};
5065
5070template<>
5071struct std::formatter<simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5072 // Define format() by calling the base class implementation with the wrapped value
5073 template<typename FormatContext>
5074 auto format(const simstr::sstring<simstr::wchar_type>& t, FormatContext& fc) const {
5075 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5076 }
5077};
5078
5083template<size_t N, bool S, typename A>
5084struct std::formatter<simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5085 // Define format() by calling the base class implementation with the wrapped value
5086 template<typename FormatContext>
5087 auto format(const simstr::lstring<simstr::wchar_type, N, S, A>& t, FormatContext& fc) const {
5088 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5089 }
5090};
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:2351
my_type & operator<<(simple_str< K > data)
Adding a piece of data.
Definition sstring.h:4277
portion_store get_portion() const
Get a portion_store through which data can be sequentially retrieved into an external buffer.
Definition sstring.h:4462
constexpr size_t length() const noexcept
Length of the saved text.
Definition sstring.h:4342
my_type & operator<<(T data)
Adding a symbol.
Definition sstring.h:4336
void reset()
Resets the contents, but does not delete the first buffer in order to avoid allocation later.
Definition sstring.h:4346
void clear()
Clear the object, freeing all allocated buffers.
Definition sstring.h:4408
my_type & operator<<(const StrExprForType< K > auto &expr)
Adding a string expression.
Definition sstring.h:4312
bool is_continuous() const
Checks whether all text is located in one contiguous chunk in memory.
Definition sstring.h:4384
void out(const Op &o) const
Applies a functor to each stored buffer.
Definition sstring.h:4376
const auto & data() const
Get internal data buffers.
Definition sstring.h:4471
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:4401
Container for more efficient searching by string keys.
Definition sstring.h:3996
The mutable, owning string class. Contains an internal buffer for text of a given size.
Definition sstring.h:2675
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:3186
constexpr lstring(T &&value, Args &&... args)
String literal constructor.
Definition sstring.h:2926
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3057
constexpr void reset()
Makes the string empty and frees the external buffer, if there was one.
Definition sstring.h:3221
@ LocalCapacity
Definition sstring.h:2683
constexpr bool is_local() const noexcept
Find out whether a local or external buffer is used for characters.
Definition sstring.h:3177
my_type & operator=(my_type &&other) noexcept
Assignment operator by moving from a string of the same type.
Definition sstring.h:3000
constexpr size_t length() const noexcept
String length.
Definition sstring.h:3080
constexpr lstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:2807
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:2877
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:2822
constexpr lstring(const my_type &other)
Copy from another string of the same type.
Definition sstring.h:2896
constexpr lstring(const my_type &other, Args &&... args)
Copy from another string of the same type, but with a different allocator.
Definition sstring.h:2911
my_type & operator=(const StrExprForType< K > auto &expr)
String expression appending operator.
Definition sstring.h:3070
constexpr K * reserve_no_preserve(size_t newSize)
Definition sstring.h:3115
my_type & operator=(const my_type &other)
Copy assignment operator from a string of the same type.
Definition sstring.h:2983
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3092
lstring(const Op &op, Args &&... args)
A fill constructor using a functor (see str_mutable::fill).
Definition sstring.h:2965
constexpr K * set_size(size_t newSize)
Sets the size of the current string, allocating space if necessary.
Definition sstring.h:3160
constexpr lstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:2837
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:2856
constexpr const K * symbols() const noexcept
Pointer to constant characters.
Definition sstring.h:3084
constexpr void clear()
Makes a string empty without changing the string buffer.
Definition sstring.h:3217
my_type & operator=(simple_str< K > other)
Assignment operator from simple_str.
Definition sstring.h:3045
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3096
constexpr void shrink_to_fit()
Reduces the size of the external buffer to the smallest possible size to hold the string....
Definition sstring.h:3203
constexpr lstring(my_type &&other) noexcept
Constructor for moving from a string of the same type.
Definition sstring.h:2940
constexpr K * reserve(size_t newSize)
Allocate a buffer large enough to hold newSize characters plus a terminating null.
Definition sstring.h:3137
constexpr K * str() noexcept
Pointer to a string buffer.
Definition sstring.h:3088
constexpr size_t capacity() const noexcept
Current row buffer capacity.
Definition sstring.h:3100
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Create an empty object.
Definition sstring.h:2792
Immutable owning string class.
Definition sstring.h:3332
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Assignment operator to another string of type lstring.
Definition sstring.h:3699
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3752
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3748
sstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:3475
sstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:3505
constexpr const K * symbols() const noexcept
Pointer to characters in the string.
Definition sstring.h:3740
constexpr sstring(const my_type &other) noexcept
String copy constructor.
Definition sstring.h:3563
constexpr my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3687
sstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:3490
constexpr my_type & operator=(my_type other) noexcept
Assignment operator to another string of the same type.
Definition sstring.h:3663
static my_type printf(const K *pattern, T &&... args)
Get a string formatted with std::sprintf.
Definition sstring.h:3768
constexpr sstring(T &&s, Args &&... args)
Initialize from a string literal.
Definition sstring.h:3638
constexpr sstring(lstring< K, N, true, Allocator > &&src)
A move constructor from lstring with an sstring-compatible external buffer.
Definition sstring.h:3590
constexpr size_t length() const noexcept
Line length.
Definition sstring.h:3744
constexpr sstring(my_type &&other) noexcept
Move constructor.
Definition sstring.h:3574
static my_type format(const FmtString< typename to_std_char_type< K >::type, T... > &fmtString, T &&... args)
Get a string formatted with std::format.
Definition sstring.h:3782
static my_type vformat(simple_str< K > fmtString, T &&... args)
Get a string formatted with std::vformat.
Definition sstring.h:3796
constexpr my_type & operator=(simple_str< K > other)
Assignment operator to another string of a different type.
Definition sstring.h:3675
constexpr my_type & make_empty() noexcept
Make the string empty.
Definition sstring.h:3733
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
String expression assignment operator.
Definition sstring.h:3724
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:3711
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:3524
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Constructor for the empty string.
Definition sstring.h:3460
constexpr ~sstring()
String destructor.
Definition sstring.h:3552
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:3545
A class with additional constant string algorithms.
Definition sstring.h:168
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:241
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:205
bool less_iu(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:227
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:256
R upperred() const
Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:268
R lowered() const
Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:280
std::optional< double > to_double() const noexcept
Convert string to double.
Definition sstring.h:290
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition sstring.h:348
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:216
void as_number(double &t) const
Convert string to double.
Definition sstring.h:331
Base class for working with mutable strings.
Definition sstring.h:1442
Impl & insert(size_t to, const A &expr)
Insert a string expression at the specified position.
Definition sstring.h:1982
Impl & operator<<=(const Op &fillFunction)
Fills a string with the fill method after the end of the string.
Definition sstring.h:2256
Impl & append(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:1866
Impl & operator<<(const Op &fillFunction)
Calls the passed functor, passing a reference to itself.
Definition sstring.h:2269
Impl & trim(str_piece pattern)
Remove characters included in the passed string at the beginning and end of the line.
Definition sstring.h:1676
Impl & upper_only_ascii()
Convert ASCII characters to uppercase.
Definition sstring.h:1740
Impl & lower_only_ascii()
Convert ASCII characters to lowercase.
Definition sstring.h:1755
Impl & trim_left()
Remove whitespace at the beginning of a line.
Definition sstring.h:1578
Impl & append_printf(const K *format, T &&... args)
Appends sprintf formatted output to the end of the line.
Definition sstring.h:2358
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:1665
Impl & trim_left(str_piece pattern)
Remove characters included in the passed string at the beginning of the line.
Definition sstring.h:1687
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:1639
Impl & prepend(str_piece other)
Add another line to the beginning of the line.
Definition sstring.h:2006
Impl & append_formatted(const FmtString< fmt_type, T... > &format, T &&... args)
Appends std::format-formatted output to the end of the line.
Definition sstring.h:2496
my_type & format(const FmtString< fmt_type, T... > &pattern, T &&... args)
Definition sstring.h:2480
Impl & printf(const K *format, T &&... args)
Formats a string using sprintf.
Definition sstring.h:2342
Impl & with(const Op &fillFunction, Args &&... args)
Call a functor with a string and passed arguments.
Definition sstring.h:2578
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:2564
Impl & operator<<(const Op &fillFunction)
Fills a string with the fill method from position zero.
Definition sstring.h:2243
Impl & vformat(str_piece format, T &&... args)
Formats a string using std::vformat.
Definition sstring.h:2512
Impl & change(size_t from, size_t len, const A &expr)
Replace a piece of string with a string expression.
Definition sstring.h:1955
Impl & fill(size_t from, const Op &fillFunction)
Fill a string buffer using a functor.
Definition sstring.h:2215
Impl & change(size_t from, size_t len, str_piece other)
Replace a piece of string with another string.
Definition sstring.h:1939
Impl & remove(size_t from, size_t len)
Remove part of a line.
Definition sstring.h:1995
Impl & trim_left_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning of a line.
Definition sstring.h:1652
Impl & trim_left(T &&pattern)
Remove characters included in a string literal at the beginning of the line.
Definition sstring.h:1613
Impl & trim_right(T &&pattern)
Remove characters included in a string literal at the end of the line.
Definition sstring.h:1626
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:1731
Impl & append_in(size_t pos, str_piece other)
Add another line starting at the given position.
Definition sstring.h:1906
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:2445
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:1720
Impl & insert(size_t to, str_piece other)
Insert a line at the specified position.
Definition sstring.h:1968
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:2546
Impl & printf_from(size_t from, const K *format, T &&... args)
Appends sprintf formatted output starting at the specified position.
Definition sstring.h:2288
Impl & operator+=(str_piece other)
Add another line to the end of the line.
Definition sstring.h:1877
Impl & trim_right()
Remove whitespace from the end of a line.
Definition sstring.h:1587
Impl & append(str_piece other)
Add another line to the end of the line.
Definition sstring.h:1854
Impl & trim_right(str_piece pattern)
Remove characters included in the passed string from the end of the string.
Definition sstring.h:1698
Impl & trim_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace characters,...
Definition sstring.h:1709
K * str() noexcept
Get a pointer to the string buffer.
Definition sstring.h:1551
Impl & prepend(const A &expr)
Add a string expression to the beginning of a line.
Definition sstring.h:2018
Impl & trim()
Remove whitespace from the beginning and end of a line.
Definition sstring.h:1569
Impl & lower()
Convert first plane characters (<0xFFFF) to lowercase Unicode.
Definition sstring.h:1789
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:2035
Impl & upper()
Convert first plane characters (<0xFFFF) to uppercase Unicode.
Definition sstring.h:1774
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:2153
Impl & append_vformatted(str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line.
Definition sstring.h:2528
Impl & append_in(size_t pos, const A &expr)
Add a string expression starting at the given position.
Definition sstring.h:1924
my_type & format_from(size_t from, const FmtString< fmt_type, T... > &format, T &&... args)
Definition sstring.h:2416
Impl & trim(T &&pattern)
Remove characters included in a string literal at the beginning and end of the line.
Definition sstring.h:1600
Impl & operator+=(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:1889
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:2539
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:3323
constexpr str_storable(Args &&... args)
Create an empty object.
Definition sstring.h:937
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:1024
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:1285
constexpr void init_from_str_other(s_str other)
Initialization from another string object.
Definition sstring.h:945
constexpr allocator_t & allocator()
Get the allocator.
Definition sstring.h:914
constexpr void init_symb_repeat(size_t count, K pad)
Character repetition initialization.
Definition sstring.h:981
constexpr void init_str_expr(const A &expr)
Initialization from a string expression.
Definition sstring.h:1002
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:1255
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:1302
constexpr void init_str_repeat(size_t repeat, s_str pattern)
String repetition initialization.
Definition sstring.h:961
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:1268
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:1204
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:1323
constexpr s_str_nt to_nts(size_t from=0) const
Get simple_str_nt starting at the given character.
Definition sstring.h:1148
Concept of a memory management type.
Definition sstring.h:1333
The concept of a string expression compatible with a given character type.
Definition strexpr.h:418
A type concept that cannot modify a stored string.
Definition sstring.h:856
A type concept that can modify a stored string.
Definition sstring.h:849
A type concept that can store a string.
Definition sstring.h:839
Small namespace for standard string methods.
Definition strexpr.h:1393
Library namespace.
Definition sstring.cpp:12
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Hash dictionary type for char16_t strings, case sensitive search.
Definition sstring.h:4528
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4494
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4559
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4553
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:4521
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4514
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:830
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Type of hash dictionary for char strings, case sensitive search.
Definition sstring.h:4488
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4540
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4534
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Hash dictionary type for wchar_t strings, case sensitive search.
Definition sstring.h:4507
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
Definition sstring.h:4500
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Hash dictionary type for char32_t strings, case sensitive search.
Definition sstring.h:4547
An object that allows you to sequentially copy content into a buffer of a given size.
Definition sstring.h:4418
bool is_end()
Check that the data has not yet run out.
Definition sstring.h:4425
size_t store(K *buffer, size_t size)
Save the next portion of data to the buffer.
Definition sstring.h:4438
Base class for converting string expressions to standard strings.
Definition strexpr.h:468
String expression to convert strings to different UTF types.
Definition sstring.h:801
A class that claims to refer to a null-terminated string.
Definition sstring.h:511
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:574
constexpr simple_str_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:563
constexpr simple_str_nt(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:545
constexpr simple_str_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:551
constexpr simple_str_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition sstring.h:536
The simplest immutable non-owning string class.
Definition sstring.h:375
constexpr simple_str(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:391
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition sstring.h:455
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition sstring.h:479
constexpr size_t length() const noexcept
Get the length of the string.
Definition sstring.h:412
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition sstring.h:419
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a line by the specified number of characters.
Definition sstring.h:466
constexpr simple_str(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition sstring.h:407
constexpr simple_str(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:396
constexpr simple_str(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:402
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition sstring.h:426
constexpr bool is_same(simple_str< K > other) const noexcept
Check if two objects point to the same string.
Definition sstring.h:435
constexpr bool is_part_of(simple_str< K > other) const noexcept
Check if a string is part of another string.
Definition sstring.h:444
The simplest class of an immutable non-owning string.
Definition strexpr.h:3924