simstr 1.6.1
Yet another strings library
 
Загрузка...
Поиск...
Не найдено
sstring.h
1/*
2 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
3 * ver. 1.6.1
4 * Классы для работы со строками
5* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6* ver. 1.6.1
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
52#include <format>
53#include <unordered_map>
54#include <atomic>
55#include <memory>
56#include <string.h>
57#include <iostream>
58#include <cmath>
59
60#ifdef _WIN32
61#include <stdio.h>
62#endif
63
64#ifdef _MSC_VER
65// warning C4201 : nonstandard extension used : nameless struct / union
66#pragma warning(disable : 4201)
67#endif
68
69namespace simstr {
70
71template<typename T>
72struct unicode_traits {}; // NOLINT
73
74template<>
75struct unicode_traits<u8s> {
76 // Эти операции с utf-8 могут изменить длину строки
77 // Поэтому их специализации отличаются
78 // В функцию помимо текста и адреса буфера для записи передается размер буфера
79 // Возвращает длину получающейся строки.
80 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
81 // обработанные символы, для повторного возобновления работы,
82 // а для оставшихся символов считается нужный размер буфера.
83 // These utf-8 operations can change the length of the string
84 // Therefore their specializations are different
85 // In addition to the text and address of the buffer for writing, the buffer size is passed to the function
86 // Returns the length of the resulting string.
87 // If the resulting string does not fit into the allocated buffer, pointers are set to the last
88 // processed characters to resume work again,
89 // and for the remaining characters the required buffer size is calculated.
90 static SIMSTR_API size_t upper(const u8s*& src, size_t lenStr, u8s*& dest, size_t lenBuf);
91 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
92
93 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
94
95 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
96 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
97};
98
99template<>
100struct unicode_traits<u16s> {
101 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
102 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
103
104 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
105 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
106 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
107};
108
109template<>
110struct unicode_traits<u32s> {
111 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
112 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
113
114 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
115 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
116 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
117};
118
119template<>
120struct unicode_traits<wchar_t> {
121 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
122 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
123 }
124 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
125 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
126 }
127
128 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
129 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
130 }
131 static size_t hashia(const wchar_t* src, size_t s) {
132 return unicode_traits<wchar_type>::hashia(to_w(src), s);
133 }
134 static size_t hashiu(const wchar_t* src, size_t s) {
135 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
136 }
137};
138
139
140#if defined(_MSC_VER) && _MSC_VER <= 1933
141template<typename K, typename... Args>
142using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
143#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
144template<typename K, typename... Args>
145using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
146#else
147template<typename K, typename... Args>
148using FmtString = std::basic_string_view<K>;
149#endif
150
151template<typename K>
152SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
153
166template<typename K, typename StrRef, typename Impl, bool Mutable>
167class str_algs : public str_src_algs<K, StrRef, Impl, Mutable> {
168 constexpr const Impl& d() const noexcept {
169 return *static_cast<const Impl*>(this);
170 }
171 constexpr size_t _len() const noexcept {
172 return d().length();
173 }
174 constexpr const K* _str() const noexcept {
175 return d().symbols();
176 }
177 constexpr bool _is_empty() const noexcept {
178 return d().is_empty();
179 }
180
181public:
182 using symb_type = K;
183 using str_piece = StrRef;
184 using traits = ch_traits<K>;
185 using uni = unicode_traits<K>;
186 using uns_type = std::make_unsigned_t<K>;
187 using my_type = Impl;
188 using base = str_src_algs<K, StrRef, Impl, Mutable>;
189 str_algs() = default;
190
191 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
192 if (!len)
193 return _is_empty() ? 0 : 1;
194 return uni::compareiu(_str(), _len(), text, len);
195 }
204 int compare_iu(str_piece text) const noexcept { // NOLINT
205 return compare_iu(text.symbols(), text.length());
206 }
207
215 bool equal_iu(str_piece text) const noexcept { // NOLINT
216 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
217 }
218
226 bool less_iu(str_piece text) const noexcept { // NOLINT
227 return compare_iu(text.symbols(), text.length()) < 0;
228 }
229 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
230 // Does this string begin with the specified substring, insensitive to unicode case
231 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
232 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
233 }
240 bool starts_with_iu(str_piece prefix) const noexcept {
241 return starts_with_iu(prefix.symbols(), prefix.length());
242 }
243 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
244 // Whether the string ends with the specified substring, case insensitive UNICODE
245 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
246 size_t myLen = _len();
247 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
248 }
255 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
256 return ends_with_iu(suffix.symbols(), suffix.length());
257 }
258
266 template<typename R = my_type>
267 R upperred() const {
268 return R::upperred_from(d());
269 }
270
278 template<typename R = my_type>
279 R lowered() const {
280 return R::lowered_from(d());
281 }
282
288 template<bool SkipWS = true, bool AllowPlus = true>
289 std::optional<double> to_double() const noexcept {
290 size_t len = _len();
291 const K* ptr = _str();
292 if constexpr (SkipWS) {
293 while (len && uns_type(*ptr) <= ' ') {
294 len--;
295 ptr++;
296 }
297 }
298 if constexpr (AllowPlus) {
299 if (len && *ptr == K('+')) {
300 ptr++;
301 len--;
302 }
303 }
304 if (!len) {
305 return {};
306 }
307 #ifdef __linux__
308 if constexpr(sizeof(K) == 1) {
309 double d{};
310 if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
311 return d;
312 }
313 return {};
314 }
315 #endif
316 if constexpr (sizeof(K) == 1) {
317 return impl_to_double((const char*)ptr, (const char*)ptr + len);
318 } else if constexpr (sizeof(K) == 2) {
319 return impl_to_double((const char16_t*)ptr, (const char16_t*)ptr + len);
320 } else {
321 return impl_to_double((const char32_t*)ptr, (const char32_t*)ptr + len);
322 }
323 }
324
330 void as_number(double& t) const {
331 auto res = to_double();
332 t = res ? *res : std::nan("0");
333 }
334
346 template<ToIntNumber T>
347 constexpr void as_number(T& t) const {
349 }
350};
351
352/*
353* Базовая структура с информацией о строке.
354* Это структура для не владеющих строк.
355* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
356* в результате размер класса не увеличивается
357* Basic structure with string information.
358* This is the structure for non-owning strings.
359* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
360* as a result the class size does not increase
361*/
362
373template<typename K>
374struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
375 using symb_type = K;
376 using my_type = simple_str<K>;
377
378 const symb_type* str;
379 size_t len;
380
381 constexpr simple_str() = default;
382
383 constexpr simple_str(str_src<K> src) : str(src.str), len(src.len){}
384
389 template<typename T, size_t N = const_lit_for<K, T>::Count>
390 constexpr simple_str(T&& v) noexcept : str(v), len(N - 1) {}
395 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
400 template<typename A>
401 constexpr simple_str(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
406 constexpr simple_str(const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
411 constexpr size_t length() const noexcept {
412 return len;
413 }
414
418 constexpr const symb_type* symbols() const noexcept {
419 return str;
420 }
421
425 constexpr bool is_empty() const noexcept {
426 return len == 0;
427 }
428
434 constexpr bool is_same(simple_str<K> other) const noexcept {
435 return str == other.str && len == other.len;
436 }
437
443 constexpr bool is_part_of(simple_str<K> other) const noexcept {
444 return str >= other.str && str + len <= other.str + other.len;
445 }
446
454 constexpr K operator[](size_t idx) const {
455 return str[idx];
456 }
457
465 constexpr my_type& remove_prefix(size_t delta) {
466 str += delta;
467 len -= delta;
468 return *this;
469 }
470
478 constexpr my_type& remove_suffix(size_t delta) {
479 len -= delta;
480 return *this;
481 }
482};
483
484template<typename K>
485struct simple_str_selector {
486 using type = simple_str<K>;
487};
488
509template<typename K>
510struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
511 using symb_type = K;
512 using my_type = simple_str_nt<K>;
513 using base = simple_str<K>;
514
515 constexpr static const K empty_string[1] = {0};
516
517 simple_str_nt() = default;
534 template<typename T> requires is_one_of_type<std::remove_cvref_t<T>, const K*, K*>::value
535 constexpr explicit simple_str_nt(T&& p) noexcept {
536 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
537 base::str = base::len ? p : empty_string;
538 }
539
543 template<typename T, size_t N = const_lit_for<K, T>::Count>
544 constexpr simple_str_nt(T&& v) noexcept : base(std::forward<T>(v)) {}
545
550 constexpr simple_str_nt(const K* p, size_t l) noexcept : base(p, l) {}
551
552 template<StrType<K> T>
553 constexpr simple_str_nt(T&& t) {
554 base::str = t.symbols();
555 base::len = t.length();
556 }
561 template<typename A>
562 constexpr simple_str_nt(const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
563
564 static const my_type empty_str;
573 constexpr my_type to_nts(size_t from) {
574 if (from > base::len) {
575 from = base::len;
576 }
577 return {base::str + from, base::len - from};
578 }
579};
580
581template<typename K>
582inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
583
584template<typename K>
585using Splitter = SplitterBase<K, simple_str<K>>;
586
587using ssa = simple_str<u8s>;
588using ssb = simple_str<ubs>;
589using ssw = simple_str<wchar_t>;
590using ssu = simple_str<u16s>;
591using ssuu = simple_str<u32s>;
592using stra = simple_str_nt<u8s>;
593using strb = simple_str_nt<ubs>;
594using strw = simple_str_nt<wchar_t>;
595using stru = simple_str_nt<u16s>;
596using struu = simple_str_nt<u32s>;
597
598template<typename Src, typename Dest>
599struct utf_convert_selector;
600
601template<>
602struct utf_convert_selector<u8s, u16s> {
603 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
604 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
605};
606
607template<>
608struct utf_convert_selector<u8s, u32s> {
609 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
610 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
611};
612
613template<>
614struct utf_convert_selector<u8s, wchar_t> {
615 static size_t need_len(const u8s* src, size_t srcLen) {
616 return utf_convert_selector<u8s, wchar_type>::need_len(src, srcLen);
617 }
618 static size_t convert(const u8s* src, size_t srcLen, wchar_t* dest) {
619 return utf_convert_selector<u8s, wchar_type>::convert(src, srcLen, to_w(dest));
620 }
621};
622
623template<>
624struct utf_convert_selector<u16s, u8s> {
625 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
626 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
627};
628
629template<>
630struct utf_convert_selector<u16s, u32s> {
631 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
632 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
633};
634
635template<>
636struct utf_convert_selector<u16s, u16s> {
637 // При конвертации char16_t в wchar_t под windows будет вызываться эта реализация
638 // When converting char16_t to wchar_t under windows this implementation will be called
639 static size_t need_len(const u16s* src, size_t srcLen) {
640 return srcLen;
641 }
642 static size_t convert(const u16s* src, size_t srcLen, u16s* dest) {
643 ch_traits<u16s>::copy(dest, src, srcLen + 1);
644 return srcLen;
645 }
646};
647
648template<>
649struct utf_convert_selector<u32s, u32s> {
650 // При конвертации char32_t в wchar_t под linux будет вызываться эта реализация
651 // When converting char32_t to wchar_t under Linux, this implementation will be called
652 static size_t need_len(const u32s* src, size_t srcLen) {
653 return srcLen;
654 }
655 static size_t convert(const u32s* src, size_t srcLen, u32s* dest) {
656 ch_traits<u32s>::copy(dest, src, srcLen + 1);
657 return srcLen;
658 }
659};
660
661template<>
662struct utf_convert_selector<u16s, wchar_t> {
663 static size_t need_len(const u16s* src, size_t srcLen) {
664 return utf_convert_selector<u16s, wchar_type>::need_len(src, srcLen);
665 }
666 static size_t convert(const u16s* src, size_t srcLen, wchar_t* dest) {
667 return utf_convert_selector<u16s, wchar_type>::convert(src, srcLen, to_w(dest));
668 }
669};
670
671template<>
672struct utf_convert_selector<u32s, u8s> {
673 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
674 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
675};
676
677template<>
678struct utf_convert_selector<u32s, u16s> {
679 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
680 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
681};
682
683template<>
684struct utf_convert_selector<u32s, wchar_t> {
685 static size_t need_len(const u32s* src, size_t srcLen) {
686 return utf_convert_selector<u32s, wchar_type>::need_len(src, srcLen);
687 }
688 static size_t convert(const u32s* src, size_t srcLen, wchar_t* dest) {
689 return utf_convert_selector<u32s, wchar_type>::convert(src, srcLen, to_w(dest));
690 }
691};
692
693template<>
694struct utf_convert_selector<wchar_t, u8s> {
695 static size_t need_len(const wchar_t* src, size_t srcLen) {
696 return utf_convert_selector<wchar_type, u8s>::need_len(to_w(src), srcLen);
697 }
698 static size_t convert(const wchar_t* src, size_t srcLen, u8s* dest) {
699 return utf_convert_selector<wchar_type, u8s>::convert(to_w(src), srcLen, dest);
700 }
701};
702
703template<>
704struct utf_convert_selector<wchar_t, u16s> {
705 static size_t need_len(const wchar_t* src, size_t srcLen) {
706 return utf_convert_selector<wchar_type, u16s>::need_len(to_w(src), srcLen);
707 }
708 static size_t convert(const wchar_t* src, size_t srcLen, u16s* dest) {
709 return utf_convert_selector<wchar_type, u16s>::convert(to_w(src), srcLen, dest);
710 }
711};
712
713template<>
714struct utf_convert_selector<wchar_t, u32s> {
715 static size_t need_len(const wchar_t* src, size_t srcLen) {
716 return utf_convert_selector<wchar_type, u32s>::need_len(to_w(src), srcLen);
717 }
718 static size_t convert(const wchar_t* src, size_t srcLen, u32s* dest) {
719 return utf_convert_selector<wchar_type, u32s>::convert(to_w(src), srcLen, dest);
720 }
721};
722
723template<>
724struct utf_convert_selector<u8s, ubs> {
725 static size_t need_len(const u8s* src, size_t srcLen) {
726 return srcLen;
727 }
728 static size_t convert(const u8s* src, size_t srcLen, ubs* dest) {
729 ch_traits<u8s>::copy((u8s*)dest, src, srcLen);
730 return srcLen;
731 }
732};
733
734template<>
735struct utf_convert_selector<ubs, u8s> {
736 static size_t need_len(const ubs* src, size_t srcLen) {
737 return srcLen;
738 }
739 static size_t convert(const ubs* src, size_t srcLen, u8s* dest) {
740 ch_traits<u8s>::copy(dest, (const u8s*)src, srcLen);
741 return srcLen;
742 }
743};
744
759template<typename K, typename Impl>
760class from_utf_convertible {
761protected:
762 from_utf_convertible() = default;
763 using my_type = Impl;
764 /*
765 Эти методы должен реализовать класс-наследник.
766 вызывается только при создании объекта
767 init(size_t size)
768 set_size(size_t size)
769 */
770 template<typename O>
771 requires(!std::is_same_v<O, K>)
772 void init_from_utf_convertible(simple_str<O> init) {
773 using worker = utf_convert_selector<O, K>;
774 Impl* d = static_cast<Impl*>(this);
775 size_t len = init.length();
776 if (!len)
777 d->create_empty();
778 else {
779 size_t need = worker::need_len(init.symbols(), len);
780 K* str = d->init(need);
781 str[need] = 0;
782 worker::convert(init.symbols(), len, str);
783 }
784 }
785};
786
795template<typename From, typename To> requires (!std::is_same_v<From, To>)
796struct expr_utf : expr_to_std_string<expr_utf<From, To>> {
797 using symb_type = To;
798 using worker = utf_convert_selector<From, To>;
799
800 simple_str<From> source_;
801
802 constexpr expr_utf(simple_str<From> source) : source_(source){}
803
804 size_t length() const noexcept {
805 return worker::need_len(source_.symbols(), source_.length());
806 }
807 To* place(To* ptr) const noexcept {
808 return ptr + worker::convert(source_.symbols(), source_.length(), ptr);
809 }
810};
811
824template<typename To, typename From> requires (!std::is_same_v<From, To>)
826 return {from};
827}
828
833template<typename A, typename K>
834concept storable_str = requires {
835 A::is_str_storable == true;
836 std::is_same_v<typename A::symb_type, K>;
837};
838
843template<typename A, typename K>
844concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
845
850template<typename A, typename K>
852
895template<typename K, typename Impl, typename Allocator>
896class str_storable : protected Allocator {
897public:
898 using my_type = Impl;
899 using traits = ch_traits<K>;
900 using allocator_t = Allocator;
901 using s_str = simple_str<K>;
902 using s_str_nt = simple_str_nt<K>;
903
904protected:
909 constexpr allocator_t& allocator() {
910 return *static_cast<Allocator*>(this);
911 }
912 constexpr const allocator_t& allocator() const {
913 return *static_cast<const Allocator*>(this);
914 }
915
916 using uni = unicode_traits<K>;
917
918 constexpr Impl& d() noexcept {
919 return *static_cast<Impl*>(this);
920 }
921 constexpr const Impl& d() const noexcept {
922 return *static_cast<const Impl*>(this);
923 }
924
931 template<typename... Args>
932 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
933
940 constexpr void init_from_str_other(s_str other) {
941 if (other.length()) {
942 K* ptr = d().init(other.length());
943 traits::copy(ptr, other.symbols(), other.length());
944 ptr[other.length()] = 0;
945 } else
946 d().create_empty();
947 }
948
956 constexpr void init_str_repeat(size_t repeat, s_str pattern) {
957 size_t l = pattern.length(), allLen = l * repeat;
958 if (allLen) {
959 K* ptr = d().init(allLen);
960 for (size_t i = 0; i < repeat; i++) {
961 traits::copy(ptr, pattern.symbols(), l);
962 ptr += l;
963 }
964 *ptr = 0;
965 } else
966 d().create_empty();
967 }
968
976 constexpr void init_symb_repeat(size_t count, K pad) {
977 if (count) {
978 K* str = d().init(count);
979 traits::assign(str, count, pad);
980 str[count] = 0;
981 } else
982 d().create_empty();
983 }
984
996 template<StrExprForType<K> A>
997 constexpr void init_str_expr(const A& expr) {
998 size_t len = expr.length();
999 if (len)
1000 *expr.place((typename A::symb_type*)d().init(len)) = 0;
1001 else
1002 d().create_empty();
1003 }
1004
1018 template<StrType<K> From>
1019 void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
1020 auto findes = f.find_all(pattern, offset, maxCount);
1021 if (!findes.size()) {
1022 new (this) my_type{f};
1023 return;
1024 }
1025 size_t srcLen = f.length();
1026 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
1027
1028 if (!newSize) {
1029 new (this) my_type{};
1030 return;
1031 }
1032
1033 K* ptr = d().init(newSize);
1034 const K* src = f.symbols();
1035 size_t from = 0;
1036 for (const auto& s: findes) {
1037 size_t copyLen = s - from;
1038 if (copyLen) {
1039 traits::copy(ptr, src + from, copyLen);
1040 ptr += copyLen;
1041 }
1042 if (repl.len) {
1043 traits::copy(ptr, repl.str, repl.len);
1044 ptr += repl.len;
1045 }
1046 from = s + pattern.len;
1047 }
1048 srcLen -= from;
1049 if (srcLen) {
1050 traits::copy(ptr, src + from, srcLen);
1051 ptr += srcLen;
1052 }
1053 *ptr = 0;
1054 }
1055
1056 template<StrType<K> From, typename Op1, typename... Args>
1057 requires std::is_constructible_v<allocator_t, Args...>
1058 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
1059 my_type result{std::forward<Args>(args)...};
1060 size_t len = f.length();
1061 if (len) {
1062 const K* source = f.symbols();
1063 K* destination = result.init(len);
1064 for (size_t l = 0; l < len; l++) {
1065 destination[l] = opMakeNeedCase(source[l]);
1066 }
1067 }
1068 return result;
1069 }
1070 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
1071 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
1072 // GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
1073 // So let's add a dummy template parameter to make the specialization for u8s right in the class.
1074 template<typename T, bool Dummy = true>
1075 struct ChangeCase {
1076 template<typename From, typename Op1, typename... Args>
1077 requires std::is_constructible_v<allocator_t, Args...>
1078 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1079 my_type result{std::forward<Args>(args)...};
1080 size_t len = f.length();
1081 if (len) {
1082 opChangeCase(f.symbols(), len, result.init(len));
1083 }
1084 return result;
1085 }
1086 };
1087 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
1088 // For utf8 we will make a separate specification, since changing the register may change the length of the string
1089 template<bool Dummy>
1090 struct ChangeCase<u8s, Dummy> {
1091 template<typename From, typename Op1, typename... Args>
1092 requires std::is_constructible_v<allocator_t, Args...>
1093 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
1094 my_type result{std::forward<Args>(args)...};
1095 ;
1096 size_t len = f.length();
1097 if (len) {
1098 const K* ptr = f.symbols();
1099 K* pWrite = result.init(len);
1100
1101 const u8s* source = ptr;
1102 u8s* dest = pWrite;
1103 size_t newLen = opChangeCase(source, len, dest, len);
1104 if (newLen < len) {
1105 // Строка просто укоротилась
1106 // The string was simply shortened
1107 result.set_size(newLen);
1108 } else if (newLen > len) {
1109 // Строка не влезла в буфер.
1110 // The line did not fit into the buffer.
1111 size_t readed = static_cast<size_t>(source - ptr);
1112 size_t writed = static_cast<size_t>(dest - pWrite);
1113 pWrite = result.set_size(newLen);
1114 dest = pWrite + writed;
1115 opChangeCase(source, len - readed, dest, newLen - writed);
1116 }
1117 pWrite[newLen] = 0;
1118 }
1119 return result;
1120 }
1121 };
1122
1123public:
1124
1125 inline static constexpr bool is_str_storable = true;
1132 constexpr operator const K*() const noexcept {
1133 return d().symbols();
1134 }
1135
1143 constexpr s_str_nt to_nts(size_t from = 0) const {
1144 size_t len = d().length();
1145 if (from >= len) {
1146 from = len;
1147 }
1148 return {d().symbols() + from, len - from};
1149 }
1150
1156 constexpr operator s_str_nt() const {
1157 return {d().symbols(), d().length()};
1158 }
1159
1197 template<typename T, typename... Args>
1198 requires std::is_constructible_v<allocator_t, Args...>
1199 static my_type join(const T& strings, s_str delimiter, bool tail = false, bool skip_empty = false, Args&&... args) {
1200 my_type result(std::forward<Args>(args)...);
1201 if (strings.size()) {
1202 if (strings.size() == 1 && (!delimiter.length() || !tail)) {
1203 result = strings.front();
1204 } else {
1205 size_t commonLen = 0;
1206 for (const auto& t: strings) {
1207 size_t len = t.length();
1208 if (len > 0 || !skip_empty) {
1209 if (commonLen > 0) {
1210 commonLen += delimiter.len;
1211 }
1212 commonLen += len;
1213 }
1214 }
1215 commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
1216 if (commonLen) {
1217 K* ptr = result.init(commonLen);
1218 K* write = ptr;
1219 for (const auto& t: strings) {
1220 size_t copyLen = t.length();
1221 if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
1222 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1223 write += delimiter.len;
1224 }
1225 ch_traits<K>::copy(write, t.symbols(), copyLen);
1226 write += copyLen;
1227 }
1228 if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
1229 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1230 write += delimiter.len;
1231 }
1232 *write = 0;
1233 } else {
1234 result.create_empty();
1235 }
1236 }
1237 }
1238 return result;
1239 }
1240
1248 template<StrType<K> From, typename... Args>
1249 requires std::is_constructible_v<allocator_t, Args...>
1250 static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
1251 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
1252 }
1253
1261 template<StrType<K> From, typename... Args>
1262 requires std::is_constructible_v<allocator_t, Args...>
1263 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
1264 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
1265 }
1266
1278 template<StrType<K> From, typename... Args>
1279 requires std::is_constructible_v<allocator_t, Args...>
1280 static my_type upperred_from(const From& f, Args&&... args) {
1281 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
1282 }
1283
1295 template<StrType<K> From, typename... Args>
1296 requires std::is_constructible_v<allocator_t, Args...>
1297 static my_type lowered_from(const From& f, Args&&... args) {
1298 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
1299 }
1300
1316 template<StrType<K> From, typename... Args>
1317 requires std::is_constructible_v<allocator_t, Args...>
1318 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
1319 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
1320 }
1321};
1322
1327template<typename A>
1328concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
1329 { a.allocate(size) } -> std::same_as<void*>;
1330 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
1331};
1332
1333struct printf_selector {
1334 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
1335 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
1336 if constexpr (sizeof(K) == 1) {
1337 #ifndef _WIN32
1338 return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
1339 #else
1340 // Поддерживает позиционные параметры
1341 // Supports positional parameters
1342 return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1343 #endif
1344 } else {
1345 #ifndef _WIN32
1346 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1347 #else
1348 // Поддерживает позиционные параметры
1349 // Supports positional parameters
1350 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1351 #endif
1352 }
1353 }
1354 template<typename K> requires (is_one_of_std_char_v<K>)
1355 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
1356 if constexpr (std::is_same_v<K, u8s>) {
1357 #ifndef _WIN32
1358 return std::vsnprintf(buffer, count, format, args);
1359 #else
1360 // Поддерживает позиционные параметры
1361 // Supports positional parameters
1362 return _vsprintf_p(buffer, count, format, args);
1363 #endif
1364 } else {
1365 #ifndef _WIN32
1366 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
1367 #else
1368 // Поддерживает позиционные параметры
1369 // Supports positional parameters
1370 return _vswprintf_p(buffer, count, format, args);
1371 #endif
1372 }
1373 }
1374};
1375
1376inline size_t grow2(size_t ret, size_t currentCapacity) {
1377 return ret <= currentCapacity ? ret : ret * 2;
1378}
1379
1380template<typename K>
1381struct to_std_char_type : std::type_identity<K>{};
1382
1383template<>
1384struct to_std_char_type<char8_t>{
1385 using type = char;
1386};
1387
1388template<>
1389struct to_std_char_type<char16_t>{
1390 using type = std::conditional_t<sizeof(char16_t) == sizeof(wchar_t), wchar_t, void>;
1391};
1392
1393template<>
1394struct to_std_char_type<char32_t>{
1395 using type = std::conditional_t<sizeof(char32_t) == sizeof(wchar_t), wchar_t, void>;
1396};
1397
1436template<typename K, typename Impl>
1438public:
1439 using my_type = Impl;
1440
1441private:
1442 Impl& d() {
1443 return *static_cast<Impl*>(this);
1444 }
1445 const Impl& d() const {
1446 return *static_cast<const Impl*>(this);
1447 }
1448 size_t _len() const noexcept {
1449 return d().length();
1450 }
1451 const K* _str() const noexcept {
1452 return d().symbols();
1453 }
1454 using str_piece = simple_str<K>;
1455 using symb_type = K;
1456 using traits = ch_traits<K>;
1457 using uni = unicode_traits<K>;
1458 using uns_type = std::make_unsigned_t<K>;
1459
1460 template<typename Op>
1461 Impl& make_trim_op(const Op& op) {
1462 str_piece me = d(), pos = op(me);
1463 if (me.length() != pos.length()) {
1464 if (me.symbols() != pos.symbols())
1465 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
1466 d().set_size(pos.length());
1467 }
1468 return d();
1469 }
1470
1471 template<auto Op>
1472 Impl& commonChangeCase() {
1473 size_t len = _len();
1474 if (len)
1475 Op(_str(), len, str());
1476 return d();
1477 }
1478 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
1479 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
1480 // GCC still does not allow full specialization of a nested class within a class,
1481 // only partial. Resources additive unused parameter template.
1482 template<typename T, bool Dummy = true>
1483 struct CaseTraits {
1484 static Impl& upper(Impl& obj) {
1485 return obj.template commonChangeCase<unicode_traits<K>::upper>();
1486 }
1487 static Impl& lower(Impl& obj) {
1488 return obj.template commonChangeCase<unicode_traits<K>::lower>();
1489 }
1490 };
1491
1492 template<auto Op>
1493 Impl& utf8CaseChange() {
1494 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1495 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1496 size_t len = _len();
1497 if (len) {
1498 u8s* writePos = str();
1499 const u8s *startData = writePos, *readPos = writePos;
1500 size_t newLen = Op(readPos, len, writePos, len);
1501 if (newLen < len) {
1502 // Строка просто укоротилась
1503 // The string was simply shortened
1504 d().set_size(newLen);
1505 } else if (newLen > len) {
1506 // Строка не влезла в буфер.
1507 // The line did not fit into the buffer.
1508 size_t readed = static_cast<size_t>(readPos - startData);
1509 size_t writed = static_cast<size_t>(writePos - startData);
1510 d().set_size(newLen);
1511 startData = str(); // при изменении размера могло изменится | may change when resizing
1512 readPos = startData + readed;
1513 writePos = const_cast<u8s*>(startData) + writed;
1514 Op(readPos, len - readed, writePos, newLen - writed);
1515 }
1516 }
1517 return d();
1518 }
1519 template<bool Dummy>
1520 struct CaseTraits<u8s, Dummy> {
1521 static Impl& upper(Impl& obj) {
1522 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
1523 }
1524 static Impl& lower(Impl& obj) {
1525 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
1526 }
1527 };
1528
1529 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
1530 Impl& makeTrim(T&& pattern) {
1531 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
1532 }
1533
1534 template<TrimSides S, bool withSpaces>
1535 Impl& makeTrim(str_piece pattern) {
1536 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
1537 }
1538
1539public:
1546 K* str() noexcept {
1547 return d().str();
1548 }
1549
1555 explicit operator K*() noexcept {
1556 return str();
1557 }
1558
1564 Impl& trim() {
1565 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
1566 }
1567
1573 Impl& trim_left() {
1574 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
1575 }
1576
1582 Impl& trim_right() {
1583 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
1584 }
1585
1593 template<typename T, size_t N = const_lit_for<K, T>::Count>
1594 requires is_const_pattern<N>
1595 Impl& trim(T&& pattern) {
1596 return makeTrim<TrimSides::TrimAll, false>(pattern);
1597 }
1598
1606 template<typename T, size_t N = const_lit_for<K, T>::Count>
1607 requires is_const_pattern<N>
1608 Impl& trim_left(T&& pattern) {
1609 return makeTrim<TrimSides::TrimLeft, false>(pattern);
1610 }
1611
1619 template<typename T, size_t N = const_lit_for<K, T>::Count>
1620 requires is_const_pattern<N>
1621 Impl& trim_right(T&& pattern) {
1622 return makeTrim<TrimSides::TrimRight, false>(pattern);
1623 }
1624
1632 template<typename T, size_t N = const_lit_for<K, T>::Count>
1633 requires is_const_pattern<N>
1634 Impl& trim_with_spaces(T&& pattern) {
1635 return makeTrim<TrimSides::TrimAll, true>(pattern);
1636 }
1637
1645 template<typename T, size_t N = const_lit_for<K, T>::Count>
1646 requires is_const_pattern<N>
1647 Impl& trim_left_with_spaces(T&& pattern) {
1648 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1649 }
1650
1658 template<typename T, size_t N = const_lit_for<K, T>::Count>
1659 requires is_const_pattern<N>
1660 Impl& trim_right_with_wpaces(T&& pattern) {
1661 return makeTrim<TrimSides::TrimRight, true>(pattern);
1662 }
1663
1671 Impl& trim(str_piece pattern) {
1672 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
1673 }
1674
1682 Impl& trim_left(str_piece pattern) {
1683 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
1684 }
1685
1693 Impl& trim_right(str_piece pattern) {
1694 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
1695 }
1696
1704 Impl& trim_with_spaces(str_piece pattern) {
1705 return makeTrim<TrimSides::TrimAll, true>(pattern);
1706 }
1707
1715 Impl& trim_left_with_spaces(str_piece pattern) {
1716 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1717 }
1718
1726 Impl& trim_right_with_spaces(str_piece pattern) {
1727 return makeTrim<TrimSides::TrimRight, true>(pattern);
1728 }
1729
1736 K* ptr = str();
1737 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1738 K s = *ptr;
1739 if (isAsciiLower(s))
1740 *ptr = s & ~0x20;
1741 }
1742 return d();
1743 }
1744
1751 K* ptr = str();
1752 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
1753 K s = *ptr;
1754 if (isAsciiUpper(s))
1755 *ptr = s | 0x20;
1756 }
1757 return d();
1758 }
1759
1769 Impl& upper() {
1770 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1771 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1772 return CaseTraits<K>::upper(d());
1773 }
1774
1784 Impl& lower() {
1785 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
1786 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
1787 return CaseTraits<K>::lower(d());
1788 }
1789
1790private:
1791 template<typename T>
1792 Impl& changeImpl(size_t from, size_t len, T expr) {
1793 size_t myLen = _len();
1794 if (from > myLen) {
1795 from = myLen;
1796 }
1797 if (from + len > myLen) {
1798 len = myLen - from;
1799 }
1800 K* buffer = str();
1801 size_t otherLen = expr.length();
1802 if (len == otherLen) {
1803 expr.place(buffer + from);
1804 } else {
1805 size_t tailLen = myLen - from - len;
1806 if (len > otherLen) {
1807 expr.place(buffer + from);
1808 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1809 d().set_size(myLen - (len - otherLen));
1810 } else {
1811 buffer = d().set_size(myLen + otherLen - len);
1812 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1813 expr.place(buffer + from);
1814 }
1815 }
1816 return d();
1817 }
1818
1819 template<typename T>
1820 Impl& appendImpl(T expr) {
1821 if (size_t len = expr.length(); len) {
1822 size_t size = _len();
1823 expr.place(d().set_size(size + len) + size);
1824 }
1825 return d();
1826 }
1827
1828 template<typename T>
1829 Impl& appendFromImpl(size_t pos, T expr) {
1830 if (pos > _len())
1831 pos = _len();
1832 if (size_t len = expr.length())
1833 expr.place(d().set_size(pos + len) + pos);
1834 else
1835 d().set_size(pos);
1836 return d();
1837 }
1838
1839public:
1840 inline static constexpr bool is_str_mutable = true;
1849 Impl& append(str_piece other) {
1850 return appendImpl<str_piece>(other);
1851 }
1852
1860 template<StrExprForType<K> A>
1861 Impl& append(const A& expr) {
1862 return appendImpl<const A&>(expr);
1863 }
1864
1872 Impl& operator+=(str_piece other) {
1873 return appendImpl<str_piece>(other);
1874 }
1875
1883 template<StrExprForType<K> A>
1884 Impl& operator+=(const A& expr) {
1885 return appendImpl<const A&>(expr);
1886 }
1887
1901 Impl& append_in(size_t pos, str_piece other) {
1902 return appendFromImpl<str_piece>(pos, other);
1903 }
1904
1918 template<StrExprForType<K> A>
1919 Impl& append_in(size_t pos, const A& expr) {
1920 return appendFromImpl<const A&>(pos, expr);
1921 }
1922
1934 Impl& change(size_t from, size_t len, str_piece other) {
1935 return changeImpl<str_piece>(from, len, other);
1936 }
1937
1949 template<StrExprForType<K> A>
1950 Impl& change(size_t from, size_t len, const A& expr) {
1951 return changeImpl<const A&>(from, len, expr);
1952 }
1953
1963 Impl& insert(size_t to, str_piece other) {
1964 return changeImpl<str_piece>(to, 0, other);
1965 }
1966
1976 template<StrExprForType<K> A>
1977 Impl& insert(size_t to, const A& expr) {
1978 return changeImpl<const A&>(to, 0, expr);
1979 }
1980
1990 Impl& remove(size_t from, size_t len) {
1991 return changeImpl<const empty_expr<K>&>(from, len, {});
1992 }
1993
2001 Impl& prepend(str_piece other) {
2002 return changeImpl<str_piece>(0, 0, other);
2003 }
2004
2012 template<StrExprForType<K> A>
2013 Impl& prepend(const A& expr) {
2014 return changeImpl<const A&>(0, 0, expr);
2015 }
2016
2030 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2031 offset = d().find(pattern, offset);
2032 if (offset == str::npos) {
2033 return d();
2034 }
2035 if (!maxCount)
2036 maxCount--;
2037 size_t replLength = repl.length(), patternLength = pattern.length();
2038
2039 if (patternLength == replLength) {
2040 // Заменяем inplace на подстроку такой же длины
2041 // Replace inplace with a substring of the same length
2042 K* ptr = str();
2043 while (maxCount--) {
2044 traits::copy(ptr + offset, repl.symbols(), replLength);
2045 offset = d().find(pattern, offset + replLength);// replLength == patternLength
2046 if (offset == str::npos)
2047 break;
2048 }
2049 } else if (patternLength > replLength) {
2050 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
2051 // Replace with a shorter piece, the length of the text will decrease, go from left to right
2052 K* ptr = str();
2053 traits::copy(ptr + offset, repl.symbols(), replLength);
2054 size_t posWrite = offset + replLength;
2055 offset += patternLength;
2056
2057 while (--maxCount) {
2058 size_t idx = d().find(pattern, offset);
2059 if (idx == str::npos)
2060 break;
2061 size_t lenOfPiece = idx - offset;
2062 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
2063 posWrite += lenOfPiece;
2064 traits::copy(ptr + posWrite, repl.symbols(), replLength);
2065 posWrite += replLength;
2066 offset = idx + patternLength;
2067 }
2068 size_t tailLen = _len() - offset;
2069 traits::move(ptr + posWrite, ptr + offset, tailLen);
2070 d().set_size(posWrite + tailLen);
2071 } else {
2072 struct replace_grow_helper {
2073 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
2074 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
2075 my_type& source;
2076 const str_piece pattern;
2077 const str_piece repl;
2078 size_t maxCount;
2079 const size_t delta;
2080 size_t all_delta{};
2081 K* reserve_for_copy{};
2082 size_t end_of_piece{};
2083 size_t total_length{};
2084
2085 void replace(size_t offset) {
2086 size_t found[16] = {offset};
2087 maxCount--;
2088 offset += pattern.length();
2089 all_delta += delta;
2090 size_t idx = 1;
2091 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
2092 found[idx] = source.find(pattern, offset);
2093 if (found[idx] == str::npos) {
2094 break;
2095 }
2096 offset = found[idx] + pattern.length();
2097 all_delta += delta;
2098 }
2099 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
2100 replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail
2101 }
2102 // Теперь делаем свои замены
2103 // Now we make our replacements
2104 if (!reserve_for_copy) {
2105 // Только начинаем
2106 // Just getting started
2107 end_of_piece = source.length();
2108 total_length = end_of_piece + all_delta;
2109 reserve_for_copy = source.alloc_for_copy(total_length);
2110 }
2111 K* dst_start = reserve_for_copy;
2112 const K* src_start = source.symbols();
2113 while(idx-- > 0) {
2114 size_t pos = found[idx] + pattern.length();
2115 size_t lenOfPiece = end_of_piece - pos;
2116 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
2117 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
2118 all_delta -= delta;
2119 end_of_piece = found[idx];
2120 }
2121 if (!all_delta && reserve_for_copy != src_start) {
2122 ch_traits<K>::copy(dst_start, src_start, found[0]);
2123 }
2124 }
2125 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
2126 helper.replace(offset);
2127 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
2128 }
2129 return d();
2130 }
2131
2147 template<StrType<K> From>
2148 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
2149 if (pattern.length() >= repl.length()) {
2150 K* dst = d().reserve_no_preserve(f.length());
2151 const K* src = f.symbols();
2152 size_t delta = 0;
2153 if (maxCount == 0) {
2154 maxCount--;
2155 }
2156 size_t src_length = f.length(), start = 0;
2157 while (maxCount--) {
2158 offset = f.find(pattern, offset);
2159 if (offset == str::npos) {
2160 break;
2161 }
2162 size_t piece_len = offset - start;
2163 if (piece_len) {
2164 ch_traits<K>::copy(dst, src + start, piece_len);
2165 dst += piece_len;
2166 }
2167 if (repl.length()) {
2168 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
2169 dst += repl.length();
2170 }
2171 delta += pattern.length() - repl.length();
2172 offset += pattern.length();
2173 start = offset;
2174 }
2175 if (start < src_length) {
2176 ch_traits<K>::copy(dst, src + start, src_length - start);
2177 }
2178 d().set_size(src_length - delta);
2179 } else {
2180 d() = f;
2181 replace(pattern, repl, offset, maxCount);
2182 }
2183 return d();
2184 }
2185
2209 template<typename Op>
2210 Impl& fill(size_t from, const Op& fillFunction) {
2211 size_t size = _len();
2212 if (from > size)
2213 from = size;
2214 size_t capacity = d().capacity();
2215 K* ptr = str();
2216 capacity -= from;
2217 for (;;) {
2218 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
2219 if (capacity >= needSize) {
2220 d().set_size(from + needSize);
2221 break;
2222 }
2223 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
2224 capacity = d().capacity() - from;
2225 }
2226 return d();
2227 }
2228
2236 template<typename Op>
2237 requires std::is_invocable_v<Op, K*, size_t>
2238 Impl& operator<<(const Op& fillFunction) {
2239 return fill(0, fillFunction);
2240 }
2241
2249 template<typename Op>
2250 requires std::is_invocable_v<Op, K*, size_t>
2251 Impl& operator<<=(const Op& fillFunction) {
2252 return fill(_len(), fillFunction);
2253 }
2254
2262 template<typename Op>
2263 requires std::is_invocable_v<Op, my_type&>
2264 Impl& operator<<(const Op& fillFunction) {
2265 fillFunction(d());
2266 return d();
2267 }
2268
2282 template<typename... T> requires (is_one_of_std_char_v<K>)
2283 Impl& printf_from(size_t from, const K* format, T&&... args) {
2284 size_t size = _len();
2285 if (from > size)
2286 from = size;
2287 size_t capacity = d().capacity();
2288 K* ptr = str();
2289 capacity -= from;
2290
2291 int result = 0;
2292 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
2293 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
2294 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
2295 // 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
2296 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
2297 // Therefore, for them you need to stupidly increase the buffer at random until it fits
2298 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
2299 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2300 if (result > (int)capacity) {
2301 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
2302 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
2303 }
2304 } else {
2305 for (;;) {
2306 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
2307 if (result < 0) {
2308 // Не хватило буфера или ошибка конвертации.
2309 // Попробуем увеличить буфер в два раза
2310 // Not enough buffer or conversion error.
2311 // Let's try to double the buffer
2312 capacity *= 2;
2313 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
2314 } else
2315 break;
2316 }
2317 }
2318 if (result < 0)
2319 d().set_size(static_cast<size_t>(traits::length(_str())));
2320 else
2321 d().set_size(from + result);
2322 return d();
2323 }
2324
2336 template<typename... T> requires (is_one_of_std_char_v<K>)
2337 Impl& printf(const K* format, T&&... args) {
2338 return printf_from(0, format, std::forward<T>(args)...);
2339 }
2340
2352 template<typename... T> requires (is_one_of_std_char_v<K>)
2353 Impl& append_printf(const K* format, T&&... args) {
2354 return printf_from(_len(), format, std::forward<T>(args)...);
2355 }
2356
2357 struct writer {
2358 my_type* store;
2359 K* ptr;
2360 const K* end;
2361 size_t max_write;
2362 size_t writed = 0;
2363 inline static K pad;
2364 K& operator*() const {
2365 return *ptr;
2366 }
2367 writer& operator++() {
2368 if (writed < max_write) {
2369 ++ptr;
2370 if (ptr == end) {
2371 size_t l = ptr - store->begin();
2372 store->set_size(l);
2373 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
2374 end = store->end();
2375 }
2376 } else {
2377 ptr = &pad;
2378 }
2379 return *this;
2380 }
2381 writer operator++(int) {
2382 auto ret = *this;
2383 operator++();
2384 return ret;
2385 }
2386
2387 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
2388 writer() = default;
2389 writer(const writer&) = delete;
2390 writer& operator=(const writer&) noexcept = delete;
2391 writer(writer&&) noexcept = default;
2392 writer& operator=(writer&&) noexcept = default;
2393 using difference_type = int;
2394 };
2395 using fmt_type = typename to_std_char_type<K>::type;
2410 template<typename... T> requires (is_one_of_std_char_v<K>)
2411 Impl& format_from(size_t from, const FmtString<fmt_type, T...>& format, T&&... args) {
2412 size_t size = _len();
2413 if (from > size)
2414 from = size;
2415 size_t capacity = d().capacity();
2416 K* ptr = str();
2417
2418 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
2419 std::forward<decltype(format)>(format), std::forward<T>(args)...);
2420 d().set_size(result.ptr - _str());
2421 return d();
2422 }
2423
2439 template<typename... T> requires (is_one_of_std_char_v<K>)
2440 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
2441 size_t size = _len();
2442 if (from > size)
2443 from = size;
2444 size_t capacity = d().capacity();
2445 K* ptr = str();
2446
2447 if constexpr (std::is_same_v<K, u8s>) {
2448 auto result = std::vformat_to(
2449 writer{d(), ptr + from, ptr + capacity, max_write},
2450 std::basic_string_view<K>{format.symbols(), format.length()},
2451 std::make_format_args(args...));
2452 d().set_size(result.ptr - _str());
2453 } else {
2454 auto result = std::vformat_to(
2455 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
2456 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
2457 std::make_wformat_args(std::forward<T>(args)...));
2458 d().set_size(result.ptr - _str());
2459 }
2460 return d();
2461 }
2462
2474 template<typename... T> requires (is_one_of_std_char_v<K>)
2475 Impl& format(const FmtString<fmt_type, T...>& pattern, T&&... args) {
2476 return format_from(0, pattern, std::forward<T>(args)...);
2477 }
2478
2490 template<typename... T> requires (is_one_of_std_char_v<K>)
2491 Impl& append_formatted(const FmtString<fmt_type, T...>& format, T&&... args) {
2492 return format_from(_len(), format, std::forward<T>(args)...);
2493 }
2494
2506 template<typename... T> requires (is_one_of_std_char_v<K>)
2507 Impl& vformat(str_piece format, T&&... args) {
2508 return vformat_from(0, -1, format, std::forward<T>(args)...);
2509 }
2510
2522 template<typename... T> requires (is_one_of_std_char_v<K>)
2523 Impl& append_vformatted(str_piece format, T&&... args) {
2524 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
2525 }
2526
2540 template<typename... T> requires (is_one_of_std_char_v<K>)
2541 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
2542 return vformat_from(0, max_write, format, std::forward<T>(args)...);
2543 }
2544
2558 template<typename... T> requires (is_one_of_std_char_v<K>)
2559 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
2560 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
2561 }
2562
2572 template<typename Op, typename... Args>
2573 Impl& with(const Op& fillFunction, Args&&... args) {
2574 fillFunction(d(), std::forward<Args>(args)...);
2575 return d();
2576 }
2577};
2578
2579template<typename K>
2580struct SharedStringData {
2581 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
2582
2583 SharedStringData() {
2584 ref_ = 1;
2585 }
2586 K* str() const {
2587 return (K*)(this + 1);
2588 }
2589 void incr() {
2590 ref_.fetch_add(1, std::memory_order_relaxed);
2591 }
2592 void decr(Allocatorable auto& allocator) {
2593 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2594 if (val == 1) {
2595 allocator.deallocate(this);
2596 }
2597 }
2598 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
2599 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
2600 return new (allocator.allocate(size)) SharedStringData();
2601 }
2602 static SharedStringData<K>* from_str(const K* p) {
2603 return (SharedStringData<K>*)p - 1;
2604 }
2605 K* place(K* p, size_t len) {
2606 ch_traits<K>::copy(p, str(), len);
2607 return p + len;
2608 }
2609};
2610
2611// Дефолтный аллокатор для строк, может работать статически
2612// Default allocator for strings, can work statically
2613class string_common_allocator {
2614public:
2615 void* allocate(size_t bytes) {
2616 return new char[bytes];
2617 }
2618 void deallocate(void* address) noexcept {
2619 delete [] static_cast<char*>(address);
2620 }
2621};
2622
2623string_common_allocator default_string_allocator_selector(...);
2624// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
2625// объявите функцию
2626// ваш_тип_аллокатора default_string_allocator_selector(int);
2627// If you want to set your default allocator for strings, before including sstring.h
2628// declare a function
2629// your_allocator_type default_string_allocator_selector(int);
2630using allocator_string = decltype(default_string_allocator_selector(int(0)));
2631
2632template<typename K, Allocatorable Allocator>
2633class sstring;
2634
2635/*
2636* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
2637* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
2638* Укажем ему явно.
2639* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
2640* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
2641* Let's tell him explicitly.
2642*/
2643
2664template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
2665class decl_empty_bases lstring :
2666 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
2667 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
2668 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
2669 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
2670 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
2671public:
2672 using symb_type = K;
2674 using allocator_t = Allocator;
2675
2676 enum : size_t {
2678 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
2679 };
2680
2681protected:
2682 enum : size_t {
2683 extra = forShared ? sizeof(SharedStringData<K>) : 0,
2684 };
2685
2686 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
2687 using base_storable = str_storable<K, my_type, Allocator>;
2688 using base_mutable = str_mutable<K, my_type>;
2689 using base_utf = from_utf_convertible<K, my_type>;
2690 using traits = ch_traits<K>;
2691 using s_str = base_storable::s_str;
2692
2693 friend base_storable;
2694 friend base_mutable;
2695 friend base_utf;
2696 friend class sstring<K, Allocator>;
2697
2698 K* data_;
2699 size_t size_;
2700
2701 union {
2702 size_t capacity_;
2703 K local_[LocalCapacity + 1];
2704 };
2705
2706 constexpr void create_empty() {
2707 data_ = local_;
2708 size_ = 0;
2709 local_[0] = 0;
2710 }
2711 constexpr static size_t calc_capacity(size_t s) {
2712 const int al = alignof(std::max_align_t) < 16 ? 16 : alignof(std::max_align_t);
2713 size_t real_need = (s + 1) * sizeof(K) + extra;
2714 size_t aligned_alloced = (real_need + al - 1) / al * al;
2715 return (aligned_alloced - extra) / sizeof(K) - 1;
2716 }
2717
2718 constexpr K* init(size_t s) {
2719 size_ = s;
2720 if (size_ > LocalCapacity) {
2721 s = calc_capacity(s);
2722 data_ = alloc_place(s);
2723 capacity_ = s;
2724 } else {
2725 data_ = local_;
2726 }
2727 return str();
2728 }
2729 // Методы для себя | Methods for yourself
2730 constexpr bool is_alloced() const noexcept {
2731 return data_ != local_;
2732 }
2733
2734 constexpr void dealloc() {
2735 if (is_alloced()) {
2736 base_storable::allocator().deallocate(to_real_address(data_));
2737 data_ = local_;
2738 }
2739 }
2740
2741 constexpr static K* to_real_address(void* ptr) {
2742 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
2743 }
2744 constexpr static K* from_real_address(void* ptr) {
2745 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
2746 }
2747
2748 constexpr K* alloc_place(size_t newSize) {
2749 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
2750 }
2751 // Вызывается при replace, когда меняют на более длинную замену
2752 // Called on replace when changing to a longer replacement
2753 constexpr K* alloc_for_copy(size_t newSize) {
2754 if (capacity() >= newSize) {
2755 // Замена войдёт в текущий буфер
2756 // Replacement will go into the current buffer
2757 return data_;
2758 }
2759 return alloc_place(calc_capacity(newSize));
2760 }
2761 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
2762 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
2763 constexpr void set_from_copy(K* ptr, size_t newSize) {
2764 if (ptr != data_) {
2765 // Да, копировали в новый буфер
2766 // Yes, copied to a new buffer
2767 dealloc();
2768 data_ = ptr;
2769 capacity_ = calc_capacity(newSize);
2770 }
2771 size_ = newSize;
2772 data_[newSize] = 0;
2773 }
2774
2775public:
2782 template<typename... Args>
2783 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
2784 constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2785 : base_storable(std::forward<Args>(args)...) {
2786 create_empty();
2787 }
2788
2797 template<typename... Args>
2798 requires std::is_constructible_v<allocator_t, Args...>
2799 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2801 }
2802
2812 template<typename... Args>
2813 requires std::is_constructible_v<allocator_t, Args...>
2814 constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2815 base_storable::init_str_repeat(repeat, pattern);
2816 }
2817
2827 template<typename... Args>
2828 requires std::is_constructible_v<allocator_t, Args...>
2829 constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2831 }
2832
2846 template<typename... Args>
2847 requires std::is_constructible_v<allocator_t, Args...>
2848 constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2850 }
2851
2867 template<StrType<K> From, typename... Args>
2868 requires std::is_constructible_v<allocator_t, Args...>
2869 constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
2870 : base_storable(std::forward<Args>(args)...) {
2871 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
2872 }
2873
2874 constexpr lstring() {
2875 create_empty();
2876 }
2877
2878 constexpr ~lstring() {
2879 dealloc();
2880 }
2881
2888 constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
2889 if (other.size_) {
2890 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2891 }
2892 }
2893
2901 template<typename... Args>
2902 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
2903 constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2904 if (other.size_) {
2905 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2906 }
2907 }
2908
2916 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
2917 requires std::is_constructible_v<allocator_t, Args...>
2918 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2919 if constexpr (I > 1) {
2920 K* ptr = init(I - 1);
2921 traits::copy(ptr, value, I - 1);
2922 ptr[I - 1] = 0;
2923 } else
2924 create_empty();
2925 }
2926
2932 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
2933 if (other.size_) {
2934 size_ = other.size_;
2935 if (other.is_alloced()) {
2936 data_ = other.data_;
2937 capacity_ = other.capacity_;
2938 } else {
2939 data_ = local_;
2940 traits::copy(local_, other.local_, size_ + 1);
2941 }
2942 other.data_ = other.local_;
2943 other.size_ = 0;
2944 other.local_[0] = 0;
2945 }
2946 }
2947
2955 template<typename Op, typename... Args>
2956 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
2957 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2958 create_empty();
2959 this->operator<<(op);
2960 }
2961 template<typename O>
2962 requires(!std::is_same_v<O, K>)
2963 lstring(simple_str<O> init) {
2964 this->init_from_utf_convertible(init);
2965 }
2966
2967 template<typename O, typename I, bool M>
2968 requires(!std::is_same_v<O, K>)
2969 lstring(const str_algs<O, simple_str<O>, I, M>& init) {
2970 this->init_from_utf_convertible(init.to_str());
2971 }
2972
2973 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
2974 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
2975 // 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
2976 // Therefore, we implement the copy and move assignment separately
2977
2986 my_type& operator=(const my_type& other) {
2987 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
2988 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
2989 if (&other != this) {
2990 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
2991 size_ = other.size_;
2992 }
2993 return *this;
2994 }
2995
3003 my_type& operator=(my_type&& other) noexcept {
3004 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3005 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
3006 if (&other != this) {
3007 dealloc();
3008 if (other.is_alloced()) {
3009 data_ = other.data_;
3010 capacity_ = other.capacity_;
3011 } else {
3012 traits::copy(data_, other.local_, other.size_ + 1);
3013 }
3014 base_storable::allocator() = std::move(other.allocator());
3015 size_ = other.size_;
3016 other.create_empty();
3017 }
3018 return *this;
3019 }
3020
3021 my_type& assign(const K* other, size_t len) {
3022 if (len) {
3023 bool isIntersect = other >= data_ && other + len <= data_ + size_;
3024 if (isIntersect) {
3025 // Особый случай, нам пытаются присвоить кусок нашей же строки.
3026 // Просто переместим текст в буфере, и установим новый размер
3027 // A special case, they are trying to assign us a piece of our own string.
3028 // Just move the text in the buffer and set a new size
3029 if (other > data_) {
3030 traits::move(data_, other, len);
3031 }
3032 } else {
3033 traits::copy(reserve_no_preserve(len), other, len);
3034 }
3035 }
3036 size_ = len;
3037 data_[size_] = 0;
3038 return *this;
3039 }
3048 my_type& operator=(simple_str<K> other) {
3049 return assign(other.str, other.len);
3050 }
3051
3059 template<typename T, size_t S = const_lit_for<K, T>::Count>
3060 my_type& operator=(T&& other) {
3061 return assign(other, S - 1);
3062 }
3063
3073 my_type& operator=(const StrExprForType<K> auto& expr) {
3074 size_t newLen = expr.length();
3075 if (newLen) {
3076 expr.place(reserve_no_preserve(newLen));
3077 }
3078 size_ = newLen;
3079 data_[size_] = 0;
3080 return *this;
3081 }
3082
3083 constexpr size_t length() const noexcept {
3084 return size_;
3085 }
3086
3087 constexpr const K* symbols() const noexcept {
3088 return data_;
3089 }
3090
3091 constexpr K* str() noexcept {
3092 return data_;
3093 }
3094
3095 constexpr bool is_empty() const noexcept {
3096 return size_ == 0;
3097 }
3098
3099 constexpr bool empty() const noexcept {
3100 return size_ == 0;
3101 }
3102
3103 constexpr size_t capacity() const noexcept {
3104 return is_alloced() ? capacity_ : LocalCapacity;
3105 }
3106
3118 constexpr K* reserve_no_preserve(size_t newSize) {
3119 if (newSize > capacity()) {
3120 newSize = calc_capacity(newSize);
3121 K* newData = alloc_place(newSize);
3122 dealloc();
3123 data_ = newData;
3124 capacity_ = newSize;
3125 }
3126 return data_;
3127 }
3128
3140 constexpr K* reserve(size_t newSize) {
3141 if (newSize > capacity()) {
3142 newSize = calc_capacity(newSize);
3143 K* newData = alloc_place(newSize);
3144 traits::copy(newData, data_, size_);
3145 dealloc();
3146 data_ = newData;
3147 capacity_ = newSize;
3148 }
3149 return data_;
3150 }
3151
3163 constexpr K* set_size(size_t newSize) {
3164 size_t cap = capacity();
3165 if (newSize > cap) {
3166 size_t needPlace = newSize;
3167 if (needPlace < (cap + 1) * 2) {
3168 needPlace = (cap + 1) * 2 - 1;
3169 }
3170 reserve(needPlace);
3171 }
3172 size_ = newSize;
3173 data_[newSize] = 0;
3174 return data_;
3175 }
3176
3180 constexpr bool is_local() const noexcept {
3181 return !is_alloced();
3182 }
3183
3189 constexpr void define_size() {
3190 size_t cap = capacity();
3191 for (size_t i = 0; i < cap; i++) {
3192 if (data_[i] == 0) {
3193 size_ = i;
3194 return;
3195 }
3196 }
3197 size_ = cap;
3198 data_[size_] = 0;
3199 }
3200
3206 constexpr void shrink_to_fit() {
3207 size_t need_capacity = calc_capacity(size_);
3208 if (is_alloced() && capacity_ > need_capacity) {
3209 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
3210 traits::copy(newData, data_, size_ + 1);
3211 base_storable::allocator().deallocate(to_real_address(data_));
3212 data_ = newData;
3213
3214 if (size_ > LocalCapacity) {
3215 capacity_ = need_capacity;
3216 }
3217 }
3218 }
3219
3220 constexpr void clear() {
3221 set_size(0);
3222 }
3223
3224 constexpr void reset() {
3225 dealloc();
3226 local_[0] = 0;
3227 size_ = 0;
3228 }
3229};
3230
3231template<size_t N = 15>
3232using lstringa = lstring<u8s, N>;
3233template<size_t N = 15>
3234using lstringb = lstring<ubs, N>;
3235template<size_t N = 15>
3236using lstringw = lstring<wchar_t, N>;
3237template<size_t N = 15>
3238using lstringu = lstring<u16s, N>;
3239template<size_t N = 15>
3240using lstringuu = lstring<u32s, N>;
3241
3242template<size_t N = 15>
3243using lstringsa = lstring<u8s, N, true>;
3244template<size_t N = 15>
3245using lstringsb = lstring<ubs, N, true>;
3246template<size_t N = 15>
3247using lstringsw = lstring<wchar_t, N, true>;
3248template<size_t N = 15>
3249using lstringsu = lstring<u16s, N, true>;
3250template<size_t N = 15>
3251using lstringsuu = lstring<u32s, N, true>;
3252
3253
3254template<typename T, typename K = typename const_lit<T>::symb_type>
3255auto getLiteralType(T&&) {
3256 return K{};
3257};
3258
3259template<size_t Arch, size_t L>
3260inline constexpr const size_t _local_count = 0;
3261
3262template<>
3263inline constexpr const size_t _local_count<8, 1> = 23;
3264template<>
3265inline constexpr const size_t _local_count<8, 2> = 15;
3266template<>
3267inline constexpr const size_t _local_count<8, 4> = 7;
3268template<>
3269inline constexpr const size_t _local_count<4, 1> = 15;
3270template<>
3271inline constexpr const size_t _local_count<4, 2> = 11;
3272template<>
3273inline constexpr const size_t _local_count<4, 4> = 5;
3274
3275template<typename T>
3276constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
3277
3330template<typename K, Allocatorable Allocator = allocator_string>
3331class decl_empty_bases sstring :
3332 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
3333 public str_storable<K, sstring<K, Allocator>, Allocator>,
3334 public null_terminated<K, sstring<K, Allocator>>,
3335 public from_utf_convertible<K, sstring<K, Allocator>> {
3336public:
3337 using symb_type = K;
3338 using uns_type = std::make_unsigned_t<K>;
3339 using my_type = sstring<K, Allocator>;
3340 using allocator_t = Allocator;
3341
3342 enum { LocalCount = local_count<K> };
3343
3344protected:
3345 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
3346 using base_storable = str_storable<K, my_type, Allocator>;
3347 using base_utf = from_utf_convertible<K, my_type>;
3348 using traits = ch_traits<K>;
3349 using uni = unicode_traits<K>;
3350 using s_str = base_storable::s_str;
3351
3352 friend base_storable;
3353 friend base_utf;
3354
3355 enum Types { Local, Constant, Shared };
3356
3357 union {
3358 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
3359 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
3360 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
3361 // и в итоге после символов строки получается 0, как и надо!
3362 // When we have a short string, it lies in the object itself, and in localRemain
3363 // writes how many more characters can be entered. When a line takes up everything
3364 // possible location, then localRemain becomes 0, type in this case is also 0,
3365 // and as a result, after the characters of the line we get 0, as it should!
3366 struct {
3367 K buf_[LocalCount]; // Локальный буфер строки | Local line buffer
3368 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
3369 uns_type type_ : 2;
3370 };
3371 struct {
3372 union {
3373 // Указатель на конcтантную строку | Pointer to a constant string
3374 const K* cstr_;
3375 // Указатель на строку, перед которой лежит SharedStringData
3376 // Pointer to the string preceded by SharedStringData
3377 const K* sstr_;
3378 };
3379 size_t bigLen_; // Длина не локальной строки | Non-local string length
3380 };
3381 };
3382
3383 constexpr void create_empty() {
3384 type_ = Local;
3385 localRemain_ = LocalCount;
3386 buf_[0] = 0;
3387 }
3388 constexpr 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
3447 sstring() {
3448 create_empty();
3449 }
3450
3457 template<typename... Args>
3458 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
3459 sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
3460 : base_storable(std::forward<Args>(args)...) {
3461 create_empty();
3462 }
3463
3472 template<typename... Args>
3473 requires std::is_constructible_v<allocator_t, Args...>
3474 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3476 }
3477
3487 template<typename... Args>
3488 requires std::is_constructible_v<allocator_t, Args...>
3489 sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3490 base_storable::init_str_repeat(repeat, pattern);
3491 }
3492
3502 template<typename... Args>
3503 requires std::is_constructible_v<allocator_t, Args...>
3504 sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3506 }
3507
3521 template<typename... Args>
3522 requires std::is_constructible_v<allocator_t, Args...>
3523 constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3525 }
3526
3542 template<StrType<K> From, typename... Args>
3543 requires std::is_constructible_v<allocator_t, Args...>
3544 sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
3545 : base_storable(std::forward<Args>(args)...) {
3546 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3547 }
3548
3549 static const sstring<K> empty_str;
3551 constexpr ~sstring() {
3552 if (type_ == Shared) {
3553 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3554 }
3555 }
3556
3562 constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
3563 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3564 if (type_ == Shared)
3565 SharedStringData<K>::from_str(sstr_)->incr();
3566 }
3567
3573 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3574 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
3575 other.create_empty();
3576 }
3577
3588 template<size_t N>
3589 constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
3590 size_t size = src.length();
3591 if (size) {
3592 if (src.is_alloced()) {
3593 // Там динамический буфер, выделенный с запасом для SharedStringData.
3594 // There is a dynamic buffer allocated with a reserve for SharedStringData.
3595 K* str = src.str();
3596 if (size > LocalCount) {
3597 // Просто присвоим его себе.
3598 // Let's just assign it to ourselves.
3599 sstr_ = str;
3600 bigLen_ = size;
3601 type_ = Shared;
3602 localRemain_ = 0;
3603 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
3604 } else {
3605 // Скопируем локально
3606 // Copy locally
3607 type_ = Local;
3608 localRemain_ = LocalCount - size;
3609 traits::copy(buf_, str, size + 1);
3610 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
3611 // Let's free that buffer; a local string's buffer is not shared with others
3612 src.dealloc();
3613 }
3614 } else {
3615 // Копируем из локального буфера
3616 // Copy from local buffer
3617 K* str = init(src.size_);
3618 traits::copy(str, src.symbols(), size + 1);
3619 }
3620 src.create_empty();
3621 } else
3622 create_empty();
3623 }
3624
3635 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
3636 requires std::is_constructible_v<allocator_t, Args...>
3637 sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3638 type_ = Constant;
3639 localRemain_ = 0;
3640 cstr_ = s;
3641 bigLen_ = N - 1;
3642 }
3643
3649 template<typename O> requires(!std::is_same_v<O, K>)
3651 this->init_from_utf_convertible(init);
3652 }
3653
3654 template<typename O, typename I, bool M> requires(!std::is_same_v<O, K>)
3655 sstring(const str_algs<O, simple_str<O>, I, M>& init) {
3656 this->init_from_utf_convertible(init.to_str());
3657 }
3658
3659 constexpr void swap(my_type&& other) noexcept {
3660 char buf[sizeof(buf_) + sizeof(K)];
3661 memcpy(buf, buf_, sizeof(buf));
3662 memcpy(buf_, other.buf_, sizeof(buf));
3663 memcpy(other.buf_, buf, sizeof(buf));
3664
3665 std::swap(base_storable::allocator(), other.allocator());
3666 }
3675 constexpr my_type& operator=(my_type other) noexcept {
3676 swap(std::move(other));
3677 return *this;
3678 }
3679
3687 constexpr my_type& operator=(simple_str<K> other) {
3688 return operator=(my_type{other, base_storable::allocator()});
3689 }
3690
3698 template<typename T, size_t N = const_lit_for<K, T>::Count>
3699 constexpr my_type& operator=(T&& other) {
3700 return operator=(my_type{other, base_storable::allocator()});
3701 }
3702
3710 template<size_t N, bool forShared, typename A>
3711 constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
3712 return operator=(my_type{other.to_str(), base_storable::allocator()});
3713 }
3714
3722 template<size_t N>
3723 constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
3724 return operator=(my_type{std::move(other)});
3725 }
3726
3736 constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
3737 return operator=(my_type{expr, base_storable::allocator()});
3738 }
3739
3745 constexpr my_type& make_empty() noexcept {
3746 if (type_ == Shared)
3747 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
3748 create_empty();
3749 return *this;
3750 }
3751
3752 constexpr const K* symbols() const noexcept {
3753 return type_ == Local ? buf_ : cstr_;
3754 }
3755
3756 constexpr size_t length() const noexcept {
3757 return type_ == Local ? LocalCount - localRemain_ : bigLen_;
3758 }
3759
3760 constexpr bool is_empty() const noexcept {
3761 return length() == 0;
3762 }
3763
3764 constexpr bool empty() const noexcept {
3765 return is_empty();
3766 }
3767
3779 template<typename... T>
3780 static my_type printf(const K* pattern, T&&... args) {
3781 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
3782 }
3783
3793 template<typename... T>
3794 static my_type format(const FmtString<typename to_std_char_type<K>::type, T...>& fmtString, T&&... args) {
3795 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
3796 }
3797
3807 template<typename... T>
3808 static my_type vformat(simple_str<K> fmtString, T&&... args) {
3809 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
3810 }
3811};
3812
3813template<typename K, Allocatorable Allocator>
3814inline const sstring<K> sstring<K, Allocator>::empty_str{};
3815
3816struct no_alloc{};
3817
3818template<typename K, size_t N>
3819class decl_empty_bases cestring :
3820 public str_algs<K, simple_str<K>, cestring<K, N>, true>,
3821 public str_storable<K, cestring<K, N>, no_alloc>,
3822 public null_terminated<K, lstring<K, N>>
3823 //, public from_utf_convertible<K, lstring<K, N, forShared, Allocator>>
3824{
3825 using symb_type = K;
3826 using my_type = cestring<K, N>;
3827
3828 enum : size_t {
3830 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
3831 };
3832
3833protected:
3834
3835 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
3836 using base_storable = str_storable<K, my_type, no_alloc>;
3837 //using base_utf = from_utf_convertible<K, my_type>;
3838 using traits = ch_traits<K>;
3839 using s_str = base_storable::s_str;
3840
3841 friend base_storable;
3842 //friend base_utf;
3843 const K* cstr_{};
3844 size_t size_{};
3845 bool is_cstr_{};
3846 K local_[LocalCapacity + 1]{};
3847
3848 constexpr void create_empty() {
3849 is_cstr_ = false;
3850 size_ = 0;
3851 local_[0] = 0;
3852 }
3853
3854 constexpr K* init(size_t s) {
3855 size_ = s;
3856 if (size_ > LocalCapacity) {
3857 throw std::bad_alloc{};
3858 }
3859 is_cstr_ = false;
3860 return local_;
3861 }
3862public:
3864 constexpr size_t length() const noexcept {
3865 return size_;
3866 }
3868 constexpr const K* symbols() const noexcept {
3869 return is_cstr_ ? cstr_ : local_;
3870 }
3872 constexpr bool is_empty() const noexcept {
3873 return size_ == 0;
3874 }
3876 constexpr bool empty() const noexcept {
3877 return size_ == 0;
3878 }
3880 constexpr size_t capacity() const noexcept {
3881 return LocalCapacity;
3882 }
3887 constexpr cestring() noexcept = default;
3888
3895 constexpr cestring(s_str other) : base_storable() {
3896 base_storable::init_from_str_other(other);
3897 }
3906 constexpr cestring(size_t repeat, s_str pattern) : base_storable() {
3907 base_storable::init_str_repeat(repeat, pattern);
3908 }
3917 constexpr cestring(size_t count, K pad) : base_storable() {
3918 base_storable::init_symb_repeat(count, pad);
3919 }
3932 constexpr cestring(const StrExprForType<K> auto& expr) : base_storable() {
3933 base_storable::init_str_expr(expr);
3934 }
3949 template<StrType<K> From>
3950 constexpr cestring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0)
3951 : base_storable() {
3952 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
3953 }
3954
3956 constexpr ~cestring() {}
3957
3966 template<typename T, size_t M = const_lit_for<K, T>::Count>
3967 constexpr cestring(T&& s) : base_storable(), cstr_(s), size_(M - 1), is_cstr_(true), local_{0} {}
3968};
3969
3970template<typename K>
3971consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<ubs> sb, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
3972 if constexpr (std::is_same_v<K, u8s>)
3973 return s8;
3974 if constexpr (std::is_same_v<K, ubs>)
3975 return sb;
3976 if constexpr (std::is_same_v<K, uws>)
3977 return sw;
3978 if constexpr (std::is_same_v<K, u16s>)
3979 return s16;
3980 if constexpr (std::is_same_v<K, u32s>)
3981 return s32;
3982}
3983
3984#define uni_string(K, p) select_str<K>(p, u8##p, L##p, u##p, U##p)
3985
3986template<typename K, typename H>
3987struct StoreType {
3988 simple_str<K> str;
3989 size_t hash;
3990 char node[sizeof(sstring<K>)];
3991
3992 const simple_str_nt<K>& to_nt() const noexcept {
3993 return static_cast<const simple_str_nt<K>&>(str);
3994 }
3995 const sstring<K>& to_str() const noexcept {
3996 return *reinterpret_cast<const sstring<K>*>(node);
3997 }
3998};
3999
4000template<bool Wide>
4001struct fnv_const {
4002 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
4003 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
4004};
4005
4006template<>
4007struct fnv_const<false> {
4008 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
4009 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
4010};
4011
4012using fnv = fnv_const<sizeof(size_t) == 8>;
4013
4014template<typename K>
4015inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
4016 size_t h = fnv::basis;
4017 for (size_t i = 0; i < l; i++) {
4018 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
4019 }
4020 return h;
4021};
4022
4023template<typename K>
4024inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
4025 size_t h = fnv::basis;
4026 for (size_t i = 0; i < l; i++) {
4027 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
4028 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
4029 }
4030 return h;
4031};
4032
4033template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
4034inline constexpr size_t fnv_hash(T&& value) {
4035 size_t h = fnv::basis;
4036 for (size_t i = 0; i < N - 1; i++) {
4037 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
4038 }
4039 return h;
4040};
4041
4042template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
4043inline constexpr size_t fnv_hash_ia(T&& value) {
4044 size_t h = fnv::basis;
4045 for (size_t i = 0; i < N - 1; i++) {
4046 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
4047 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
4048 }
4049 return h;
4050};
4051
4052template<typename K>
4053inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
4054 return fnv_hash(ptr, l);
4055};
4056
4057template<typename K>
4058inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
4059 return fnv_hash_ia(ptr, l);
4060};
4061
4062static_assert(std::is_trivially_copyable_v<StoreType<u8s, int>>, "Store type must be trivially copyable");
4063
4064template<typename K>
4065struct streql;
4066template<typename K>
4067struct strhash;
4068
4163template<typename K, typename T, typename H = strhash<K>, typename E = streql<K>>
4164class hashStrMap : public std::unordered_map<StoreType<K, H>, T, H, E> {
4165protected:
4166 using InStore = StoreType<K, H>;
4167
4168public:
4169 using my_type = hashStrMap<K, T, H, E>;
4170 using hash_t = std::unordered_map<InStore, T, H, E>;
4171 using hasher = H;
4172
4173 hashStrMap() = default;
4174 hashStrMap(const my_type& other) : hash_t(other) {
4175 for (const auto& [k, v] : *this) {
4176 InStore& stored = const_cast<InStore&>(k);
4177 sstring<K> tmp = *(sstring<K>*)stored.node;
4178 new (stored.node) sstring<K>(std::move(tmp));
4179 stored.str.str = stored.to_str().symbols();
4180 }
4181 }
4182 ~hashStrMap() {
4183 for (auto& k: *this)
4184 ((sstring<K>*)k.first.node)->~sstring();
4185 }
4186
4187 hashStrMap(my_type&& o) = default;
4188
4189 my_type& operator=(const my_type& other) {
4190 hash_t::operator=(other);
4191 for (const auto& [k, v] : *this) {
4192 InStore& stored = const_cast<InStore&>(k);
4193 sstring<K> tmp = *(sstring<K>*)stored.node;
4194 new (stored.node) sstring<K>(std::move(tmp));
4195 stored.str.str = stored.to_str().symbols();
4196 }
4197 return *this;
4198 };
4199 my_type& operator=(my_type&&) = default;
4200
4201 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
4202 for (const auto& e: init)
4203 emplace(e.first, e.second);
4204 }
4205
4206 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
4207
4208 hashStrMap(init_str&& init) {
4209 for (const auto& e: init)
4210 emplace(e.first, e.second);
4211 }
4212
4213 // При входе хэш должен быть уже посчитан
4214 // When entering, the hash must already be calculated
4215 template<typename... ValArgs>
4216 auto try_emplace(const InStore& key, ValArgs&&... args) {
4217 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
4218 if (it.second) {
4219 InStore& stored = const_cast<InStore&>(it.first->first);
4220 new (stored.node) sstring<K>(key.str);
4221 stored.str.str = stored.to_str().symbols();
4222 }
4223 return it;
4224 }
4225
4226 static InStore toStoreType(simple_str<K> key) {
4227 return {key, H{}(key)};
4228 }
4229
4230 template<typename Key, typename... ValArgs>
4231 requires(std::is_convertible_v<Key, simple_str<K>>)
4232 auto try_emplace(Key&& key, ValArgs&&... args) {
4233 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
4234 if (it.second) {
4235 InStore& stored = const_cast<InStore&>(it.first->first);
4236 new (stored.node) sstring<K>(std::forward<Key>(key));
4237 stored.str.str = stored.to_str().symbols();
4238 }
4239 return it;
4240 }
4241
4242 template<typename... ValArgs>
4243 auto emplace(const InStore& key, ValArgs&&... args) {
4244 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
4245 if (!it.second) {
4246 it.first->second = T(std::forward<ValArgs>(args)...);
4247 }
4248 return it;
4249 }
4250
4251 template<typename Key, typename... ValArgs>
4252 requires(std::is_convertible_v<Key, simple_str<K>>)
4253 auto emplace(Key&& key, ValArgs&&... args) {
4254 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
4255 if (!it.second) {
4256 it.first->second = T(std::forward<ValArgs>(args)...);
4257 }
4258 return it;
4259 }
4260
4261 auto& operator[](const InStore& key) {
4262 return try_emplace(key).first->second;
4263 }
4264
4265 template<typename Key>
4266 requires(std::is_convertible_v<Key, simple_str<K>>)
4267 auto& operator[](Key&& key) {
4268 return try_emplace(std::forward<Key>(key)).first->second;
4269 }
4270
4271 decltype(auto) at(const InStore& key) {
4272 return hash_t::at(key);
4273 }
4274 decltype(auto) at(const InStore& key) const {
4275 return hash_t::at(key);
4276 }
4277
4278 decltype(auto) at(simple_str<K> key) {
4279 return hash_t::at(toStoreType(key));
4280 }
4281 decltype(auto) at(simple_str<K> key) const {
4282 return hash_t::at(toStoreType(key));
4283 }
4284
4285 auto find(const InStore& key) const {
4286 return hash_t::find(key);
4287 }
4288
4289 auto find(simple_str<K> key) const {
4290 return find(toStoreType(key));
4291 }
4292
4293 auto find(const InStore& key) {
4294 return hash_t::find(key);
4295 }
4296
4297 auto find(simple_str<K> key) {
4298 return find(toStoreType(key));
4299 }
4300
4301 auto erase(typename hash_t::const_iterator it) {
4302 if (it != hash_t::end()) {
4303 ((sstring<K>*)it->first.node)->~sstring();
4304 }
4305 return hash_t::erase(it);
4306 }
4307
4308 auto erase(const InStore& key) {
4309 auto it = hash_t::find(key);
4310 if (it != hash_t::end()) {
4311 ((sstring<K>*)it->first.node)->~sstring();
4312 hash_t::erase(it);
4313 return 1;
4314 }
4315 return 0;
4316 }
4317
4318 auto erase(simple_str<K> key) {
4319 return erase(toStoreType(key));
4320 }
4321
4322 bool lookup(simple_str<K> txt, T& val) const {
4323 auto it = find(txt);
4324 if (it != hash_t::end()) {
4325 val = it->second;
4326 return true;
4327 }
4328 return false;
4329 }
4330
4331 void clear() {
4332 for (auto& k: *this)
4333 ((sstring<K>*)k.first.node)->~sstring();
4334 hash_t::clear();
4335 }
4336 bool contains(const InStore& key) const {
4337 return hash_t::find(key) != this->end();
4338 }
4339
4340 bool contains(simple_str<K> key) const {
4341 return find(toStoreType(key)) != this->end();
4342 }
4343};
4344
4345template<typename K>
4346struct streql {
4347 template<typename H>
4348 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4349 return _Left.hash == _Right.hash && _Left.str == _Right.str;
4350 }
4351};
4352
4353template<typename K>
4354struct strhash { // hash functor for basic_string
4355 size_t operator()(simple_str<K> _Keyval) const {
4356 return fnv_hash(_Keyval.symbols(), _Keyval.length());
4357 }
4358 size_t operator()(const StoreType<K, strhash<K>>& _Keyval) const {
4359 return _Keyval.hash;
4360 }
4361};
4362
4363template<typename K>
4364struct streqlia {
4365 template<typename H>
4366 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4367 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
4368 }
4369};
4370
4371template<typename K>
4372struct strhashia {
4373 size_t operator()(simple_str<K> _Keyval) const {
4374 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
4375 }
4376 size_t operator()(const StoreType<K, strhashia<K>>& _Keyval) const {
4377 return _Keyval.hash;
4378 }
4379};
4380
4381template<typename K>
4382struct streqliu {
4383 template<typename H>
4384 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
4385 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
4386 }
4387};
4388
4389template<typename K>
4390struct strhashiu {
4391 size_t operator()(simple_str<K> _Keyval) const {
4392 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
4393 }
4394 size_t operator()(const StoreType<K, strhashiu<K>>& _Keyval) const {
4395 return _Keyval.hash;
4396 }
4397};
4398
4413template<typename K>
4414class chunked_string_builder : expr_to_std_string<chunked_string_builder<K>> {
4415 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
4416 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
4417 K* write{}; // Текущая позиция записи | Current write position
4418 size_t len{}; // Общая длина | Total length
4419 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
4420 size_t align{1024};
4421
4422public:
4423 using my_type = chunked_string_builder<K>;
4424 using symb_type = K;
4425 chunked_string_builder() = default;
4426 chunked_string_builder(size_t a) : align(a){};
4427 chunked_string_builder(const my_type&) = delete;
4428 chunked_string_builder(my_type&& other) noexcept
4429 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
4430 other.len = other.remain = 0;
4431 other.write = nullptr;
4432 }
4433 my_type& operator=(my_type other) noexcept {
4434 chunks.swap(other.chunks);
4435 write = other.write;
4436 len = other.len;
4437 remain = other.remain;
4438 align = other.align;
4439 other.len = other.remain = 0;
4440 other.write = nullptr;
4441 return *this;
4442 }
4443
4446 if (data.len) {
4447 len += data.len;
4448 if (data.len <= remain) {
4449 // Добавляемые данные влезают в выделенный блок, просто скопируем их
4450 // The added data fits into the selected block, just copy it
4451 ch_traits<K>::copy(write, data.str, data.len);
4452 write += data.len; // Сдвинем позицию записи | Let's move the recording position
4453 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4454 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
4455 } else {
4456 // Не влезают | They don't fit
4457 if (remain) {
4458 // Сначала запишем сколько влезет
4459 // First, write down as much as we can
4460 ch_traits<K>::copy(write, data.str, remain);
4461 data.len -= remain;
4462 data.str += remain;
4463 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
4464 }
4465 // Выделим новый блок и впишем в него данные
4466 // Рассчитаем размер блока, кратного заданному выравниванию
4467 // Select a new block and write data into it
4468 // Calculate the block size that is a multiple of the given alignment
4469 size_t blockSize = (data.len + align - 1) / align * align;
4470 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
4471 write = chunks.back().first.get();
4472 ch_traits<K>::copy(write, data.str, data.len);
4473 write += data.len;
4474 remain = blockSize - data.len;
4475 }
4476 }
4477 return *this;
4478 }
4479
4480 my_type& operator<<(const StrExprForType<K> auto& expr) {
4481 size_t l = expr.length();
4482 if (l) {
4483 if (l < remain) {
4484 write = expr.place(write);
4485 chunks.back().second += l;
4486 len += l;
4487 remain -= l;
4488 } else if (!remain) {
4489 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
4490 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
4491 write = expr.place(chunks.back().first.get());
4492 len += l;
4493 remain = blockSize - l;
4494 } else {
4495 auto store = std::make_unique<K[]>(l);
4496 expr.place(store.get());
4497 return operator<<({store.get(), l});
4498 }
4499 }
4500 return *this;
4501 }
4502
4503 template<typename T>
4504 my_type& operator<<(T data)
4505 requires std::is_same_v<T, K>
4506 {
4507 return operator<<(expr_char<K>(data));
4508 }
4509
4510 constexpr size_t length() const noexcept {
4511 return len;
4512 }
4513
4514 void reset() {
4515 if (chunks.empty()) {
4516 return;
4517 }
4518 if (chunks.size() > 1) {
4519 remain = 0;
4520 chunks.resize(1);
4521 }
4522 remain += chunks[0].second;
4523 chunks[0].second = 0;
4524 len = 0;
4525 write = chunks[0].first.get();
4526 }
4527
4528 constexpr K* place(K* p) const noexcept {
4529 for (const auto& block: chunks) {
4530 ch_traits<K>::copy(p, block.first.get(), block.second);
4531 p += block.second;
4532 }
4533 return p;
4534 }
4543 template<typename Op>
4544 void out(const Op& o) const {
4545 for (const auto& block: chunks)
4546 o(block.first.get(), block.second);
4547 }
4548
4552 bool is_continuous() const {
4553 if (chunks.size()) {
4554 const K* ptr = chunks.front().first.get();
4555 for (const auto& chunk: chunks) {
4556 if (chunk.first.get() != ptr)
4557 return false;
4558 ptr += chunk.second;
4559 }
4560 }
4561 return true;
4562 }
4563
4569 const K* begin() const {
4570 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
4571 }
4572
4576 void clear() {
4577 chunks.clear();
4578 write = nullptr;
4579 len = 0;
4580 remain = 0;
4581 }
4582
4587 typename decltype(chunks)::const_iterator it, end;
4588 size_t writedFromCurrentChunk;
4593 bool is_end() {
4594 return it == end;
4595 }
4596
4606 size_t store(K* buffer, size_t size) {
4607 size_t writed = 0;
4608 while (size && !is_end()) {
4609 size_t remain = it->second - writedFromCurrentChunk;
4610 size_t write = std::min(size, remain);
4611 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
4612 writed += write;
4613 remain -= write;
4614 size -= write;
4615 if (!remain) {
4616 ++it;
4617 writedFromCurrentChunk = 0;
4618 } else
4619 writedFromCurrentChunk += write;
4620 }
4621 return writed;
4622 }
4623 };
4624
4631 return {chunks.begin(), chunks.end(), 0};
4632 }
4633
4639 const auto& data() const {
4640 return chunks;
4641 }
4642};
4643
4644using stringa = sstring<u8s>;
4645using stringb = sstring<ubs>;
4646using stringw = sstring<wchar_t>;
4647using stringu = sstring<u16s>;
4648using stringuu = sstring<u32s>;
4649static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
4650
4655template<typename T>
4661template<typename T>
4667template<typename T>
4669
4674template<typename T>
4676
4681template<typename T>
4683
4688template<typename T>
4690
4695template<typename T>
4697template<typename T>
4707template<typename T>
4709
4714template<typename T>
4720template<typename T>
4726template<typename T>
4728
4729inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
4730
4731inline namespace literals {
4732
4743SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
4744 return simple_str_nt<u8s>{ptr, l};
4745}
4746
4756SS_CONSTEVAL simple_str_nt<ubs> operator""_ss(const ubs* ptr, size_t l) {
4757 return simple_str_nt<ubs>{ptr, l};
4758}
4759
4769SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
4770 return simple_str_nt<uws>{ptr, l};
4771}
4772
4782SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
4783 return simple_str_nt<u16s>{ptr, l};
4784}
4785
4796SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
4797 return simple_str_nt<u32s>{ptr, l};
4798}
4799
4800template<typename K> using HashKey = StoreType<K, strhash<K>>;
4801template<typename K> using HashKeyIA = StoreType<K, strhashia<K>>;
4802template<typename K> using HashKeyIU = StoreType<K, strhashiu<K>>;
4803
4814consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
4815 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4816}
4817
4828consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
4829 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4830}
4831
4842inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
4843 return HashKeyIU<u8s>{{ptr, l}, strhashiu<u8s>{}(simple_str<u8s>{ptr, l})};
4844}
4845
4856consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
4857 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4858}
4859
4870consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
4871 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4872}
4873
4884inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
4885 return HashKeyIU<u16s>{{ptr, l}, strhashiu<u16s>{}(simple_str<u16s>{ptr, l})};
4886}
4887
4898consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
4899 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4900}
4901
4912consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
4913 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4914}
4915
4926inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
4927 return HashKeyIU<u32s>{{ptr, l}, strhashiu<u32s>{}(simple_str<u32s>{ptr, l})};
4928}
4929
4940consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
4941 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
4942}
4943
4954consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
4955 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4956}
4957
4968inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
4969 return HashKeyIU<uws>{{ptr, l}, strhashiu<uws>{}(simple_str<uws>{ptr, l})};
4970}
4971} // namespace literals
4972
4983inline std::ostream& operator<<(std::ostream& stream, ssa text) {
4984 return stream << std::string_view{text.symbols(), text.length()};
4985}
4986
4997inline std::wostream& operator<<(std::wostream& stream, ssw text) {
4998 return stream << std::wstring_view{text.symbols(), text.length()};
4999}
5000
5011inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
5012 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5013}
5014
5025inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
5026 return stream << std::string_view{text.symbols(), text.length()};
5027}
5028
5039inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
5040 return stream << std::wstring_view{text.symbols(), text.length()};
5041}
5042
5053inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
5054 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5055}
5056
5067template<size_t N, bool S, simstr::Allocatorable A>
5068inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
5069 return stream << std::string_view{text.symbols(), text.length()};
5070}
5071
5082template<size_t N, bool S, simstr::Allocatorable A>
5083inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
5084 return stream << std::wstring_view{text.symbols(), text.length()};
5085}
5086
5097template<size_t N, bool S, simstr::Allocatorable A>
5098inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
5099 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
5100}
5101
5102} // namespace simstr
5103
5108template<typename K>
5109struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5110 // Define format() by calling the base class implementation with the wrapped value
5111 template<typename FormatContext>
5112 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
5113 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5114 }
5115};
5116
5121template<typename K>
5122struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5123 // Define format() by calling the base class implementation with the wrapped value
5124 template<typename FormatContext>
5125 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
5126 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
5127 }
5128};
5129
5134template<typename K>
5135struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
5136 // Define format() by calling the base class implementation with the wrapped value
5137 template<typename FormatContext>
5138 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
5139 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5140 }
5141};
5142
5147template<typename K, size_t N, bool S, typename A>
5148struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
5149 // Define format() by calling the base class implementation with the wrapped value
5150 template<typename FormatContext>
5151 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
5152 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
5153 }
5154};
5155
5160template<>
5161struct std::formatter<simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5162 // Define format() by calling the base class implementation with the wrapped value
5163 template<typename FormatContext>
5164 auto format(simstr::simple_str<char8_t> t, FormatContext& fc) const {
5165 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5166 }
5167};
5168
5173template<>
5174struct std::formatter<simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5175 // Define format() by calling the base class implementation with the wrapped value
5176 template<typename FormatContext>
5177 auto format(simstr::simple_str_nt<char8_t> t, FormatContext& fc) const {
5178 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.str, t.len}, fc);
5179 }
5180};
5181
5186template<>
5187struct std::formatter<simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5188 // Define format() by calling the base class implementation with the wrapped value
5189 template<typename FormatContext>
5190 auto format(const simstr::sstring<char8_t>& t, FormatContext& fc) const {
5191 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5192 }
5193};
5194
5199template<size_t N, bool S, typename A>
5200struct std::formatter<simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
5201 // Define format() by calling the base class implementation with the wrapped value
5202 template<typename FormatContext>
5203 auto format(const simstr::lstring<char8_t, N, S, A>& t, FormatContext& fc) const {
5204 return std::formatter<std::basic_string_view<char>, char>::format({(const char*)t.symbols(), t.length()}, fc);
5205 }
5206};
5207
5212template<>
5213struct std::formatter<simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5214 // Define format() by calling the base class implementation with the wrapped value
5215 template<typename FormatContext>
5216 auto format(simstr::simple_str<simstr::wchar_type> t, FormatContext& fc) const {
5217 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5218 }
5219};
5220
5225template<>
5226struct std::formatter<simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5227 // Define format() by calling the base class implementation with the wrapped value
5228 template<typename FormatContext>
5229 auto format(simstr::simple_str_nt<simstr::wchar_type> t, FormatContext& fc) const {
5230 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.str, t.len}, fc);
5231 }
5232};
5233
5238template<>
5239struct std::formatter<simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5240 // Define format() by calling the base class implementation with the wrapped value
5241 template<typename FormatContext>
5242 auto format(const simstr::sstring<simstr::wchar_type>& t, FormatContext& fc) const {
5243 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5244 }
5245};
5246
5251template<size_t N, bool S, typename A>
5252struct std::formatter<simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5253 // Define format() by calling the base class implementation with the wrapped value
5254 template<typename FormatContext>
5255 auto format(const simstr::lstring<simstr::wchar_type, N, S, A>& t, FormatContext& fc) const {
5256 return std::formatter<std::basic_string_view<wchar_t>, wchar_t>::format({(const wchar_t*)t.symbols(), t.length()}, fc);
5257 }
5258};
Класс для последовательного получения подстрок по заданному разделителю.
Определения strexpr.h:2466
my_type & operator<<(simple_str< K > data)
Добавление порции данных.
Определения sstring.h:4445
portion_store get_portion() const
Получить portion_store, через который можно последовательно извлекать данные во внешний буфер.
Определения sstring.h:4630
constexpr size_t length() const noexcept
Длина сохранённого текста.
Определения sstring.h:4510
my_type & operator<<(T data)
Добавление символа.
Определения sstring.h:4504
void reset()
Сбрасывает содержимое, но при этом не удаляет первый буфер, чтобы потом избежать аллокации.
Определения sstring.h:4514
void clear()
Очистить объект, освободив все выделенные буфера.
Определения sstring.h:4576
my_type & operator<<(const StrExprForType< K > auto &expr)
Добавление строкового выражения.
Определения sstring.h:4480
bool is_continuous() const
Проверяет, расположен ли весь текст одним непрерывным куском в памяти.
Определения sstring.h:4552
void out(const Op &o) const
Применяет функтор к каждому сохранённому буферу.
Определения sstring.h:4544
const auto & data() const
Получить внутренние буфера с данными.
Определения sstring.h:4639
const K * begin() const
Получить указатель на начало первого буфера. Имеет смысл применять только если is_continuous true.
Определения sstring.h:4569
Контейнер для более эффективного поиска по строковым ключам.
Определения sstring.h:4164
Класс мутабельной, владеющей строки. Содержит внутренний буфер для строк заданного размера.
Определения sstring.h:2670
constexpr void define_size()
Определить длину строки. Ищет символ 0 в буфере строки до его ёмкости, после чего устаналивает длину ...
Определения sstring.h:3189
constexpr lstring(T &&value, Args &&... args)
Конструктор из строкового литерала.
Определения sstring.h:2918
my_type & operator=(T &&other)
Оператор присваивания строкового литерала.
Определения sstring.h:3060
constexpr void reset()
Делает строку пустой и освобождает внешний буфер, если он был.
Определения sstring.h:3224
@ LocalCapacity
Определения sstring.h:2678
constexpr bool is_local() const noexcept
Узнать, локальный или внешний буфер используется для символов.
Определения sstring.h:3180
my_type & operator=(my_type &&other) noexcept
Оператор присваивания перемещением из строки такого же типа.
Определения sstring.h:3003
constexpr size_t length() const noexcept
Длина строки.
Определения sstring.h:3083
constexpr lstring(s_str other, Args &&... args)
Конструктор из другого строкового объекта.
Определения sstring.h:2799
constexpr lstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Конструктор из строкового источника с заменой.
Определения sstring.h:2869
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
Конструктор повторения строки.
Определения sstring.h:2814
constexpr lstring(const my_type &other)
Копирование из другой строки такого же типа.
Определения sstring.h:2888
constexpr lstring(const my_type &other, Args &&... args)
Копирование из другой строки такого же типа, но с другим аллокатором.
Определения sstring.h:2903
my_type & operator=(const StrExprForType< K > auto &expr)
Оператор присваивания строкового выражения.
Определения sstring.h:3073
constexpr K * reserve_no_preserve(size_t newSize)
Определения sstring.h:3118
my_type & operator=(const my_type &other)
Оператор присваивания копией из строки такого же типа.
Определения sstring.h:2986
constexpr bool is_empty() const noexcept
Пустая ли строка.
Определения sstring.h:3095
lstring(const Op &op, Args &&... args)
Конструктор заполнения с помощью функтора (см. str_mutable::fill).
Определения sstring.h:2957
constexpr K * set_size(size_t newSize)
Устанавливает размер текущей строки, при необходимости выделяя место.
Определения sstring.h:3163
constexpr lstring(size_t count, K pad, Args &&... args)
Конструктор повторения символа.
Определения sstring.h:2829
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Конструктор из строкового выражения.
Определения sstring.h:2848
constexpr const K * symbols() const noexcept
Указатель на константные символы.
Определения sstring.h:3087
constexpr void clear()
Делает строку пустой, не меняя буфер строки.
Определения sstring.h:3220
my_type & operator=(simple_str< K > other)
Оператор присваивания из simple_str.
Определения sstring.h:3048
constexpr bool empty() const noexcept
Пустая ли строка, для совместимости с std::string.
Определения sstring.h:3099
constexpr void shrink_to_fit()
Уменьшает размер внешнего буфера до минимально возможного для хранения строки. Если строка уместится ...
Определения sstring.h:3206
constexpr lstring(my_type &&other) noexcept
Конструктор перемещения из строки такого же типа.
Определения sstring.h:2932
constexpr K * reserve(size_t newSize)
Выделить буфер, достаточный для размещения newSize символов плюс завершающий ноль.
Определения sstring.h:3140
constexpr K * str() noexcept
Указатель на буфер строки.
Определения sstring.h:3091
constexpr size_t capacity() const noexcept
Текущая ёмкость буфера строки.
Определения sstring.h:3103
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Создать пустой объект.
Определения sstring.h:2784
Класс иммутабельной владеющей строки.
Определения sstring.h:3335
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Оператор присвоения другой строки типа lstring.
Определения sstring.h:3711
constexpr bool empty() const noexcept
Пустая ли строка, для совместимости с std::string.
Определения sstring.h:3764
constexpr bool is_empty() const noexcept
Пустая ли строка.
Определения sstring.h:3760
sstring(s_str other, Args &&... args)
Конструктор из другого строкового объекта.
Определения sstring.h:3474
sstring(size_t count, K pad, Args &&... args)
Конструктор повторения символа.
Определения sstring.h:3504
constexpr const K * symbols() const noexcept
Указатель на символы строки.
Определения sstring.h:3752
constexpr sstring(const my_type &other) noexcept
Конструктор копирования строки.
Определения sstring.h:3562
constexpr my_type & operator=(T &&other)
Оператор присвоения строкового литерала.
Определения sstring.h:3699
sstring(size_t repeat, s_str pattern, Args &&... args)
Конструктор повторения строки.
Определения sstring.h:3489
constexpr my_type & operator=(my_type other) noexcept
Оператор присвоения другой строки того же типа.
Определения sstring.h:3675
static my_type printf(const K *pattern, T &&... args)
Получить строку, отформатированную с помощью std::sprintf.
Определения sstring.h:3780
constexpr sstring(lstring< K, N, true, Allocator > &&src)
Конструктор перемещения из lstring с совместимым с sstring внешним буфером.
Определения sstring.h:3589
constexpr size_t length() const noexcept
Длина строки.
Определения sstring.h:3756
constexpr sstring(my_type &&other) noexcept
Конструктор перемещения.
Определения sstring.h:3573
static my_type format(const FmtString< typename to_std_char_type< K >::type, T... > &fmtString, T &&... args)
Получить строку, отформатированную с помощью std::format.
Определения sstring.h:3794
static my_type vformat(simple_str< K > fmtString, T &&... args)
Получить строку, отформатированную с помощью std::vformat.
Определения sstring.h:3808
sstring(simple_str< O > init)
Инициализация из строкового источника с другим типом символов. Конвертирует через UTF.
Определения sstring.h:3650
constexpr my_type & operator=(simple_str< K > other)
Оператор присвоения другой строки другого типа.
Определения sstring.h:3687
constexpr my_type & make_empty() noexcept
Сделать строку пустой.
Определения sstring.h:3745
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
Оператор присвоения строкового выражения.
Определения sstring.h:3736
sstring(T &&s, Args &&... args)
Инициализация из строкового литерала.
Определения sstring.h:3637
constexpr my_type & operator=(lstring< K, N, true, Allocator > &&other)
Оператор присвоения перемещаемой строки типа lstring с совместимым буфером.
Определения sstring.h:3723
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Конструктор из строкового выражения.
Определения sstring.h:3523
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Конструктор пустой строки.
Определения sstring.h:3459
constexpr ~sstring()
Деструктор строки.
Определения sstring.h:3551
sstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Конструктор из строкового источника с заменой.
Определения sstring.h:3544
Класс с дополнительными константными строковыми алгоритмами.
Определения sstring.h:167
bool starts_with_iu(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки без учёта регистра Unicode символов первой плоскости (<0xFF...
Определения sstring.h:240
int compare_iu(str_piece text) const noexcept
Сравнение строк посимвольно без учёта регистра Unicode символов первой плоскости (<0xFFFF).
Определения sstring.h:204
bool less_iu(str_piece text) const noexcept
Меньше ли строка другой строки посимвольно без учёта регистра Unicode символов первой плоскости (<0xF...
Определения sstring.h:226
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой без учёта регистра Unicode символов первой плоскости (<0...
Определения sstring.h:255
R upperred() const
Получить копию строки в верхнем регистре Unicode символов первой плоскости (<0xFFFF).
Определения sstring.h:267
R lowered() const
Получить копию строки в нижнем регистре Unicode символов первой плоскости (<0xFFFF).
Определения sstring.h:279
std::optional< double > to_double() const noexcept
Преобразовать строку в double.
Определения sstring.h:289
constexpr void as_number(T &t) const
Преобразовать строку в целое число.
Определения sstring.h:347
bool equal_iu(str_piece text) const noexcept
Равна ли строка другой строке посимвольно без учёта регистра Unicode символов первой плоскости (<0xFF...
Определения sstring.h:215
void as_number(double &t) const
Преобразовать строку в double.
Определения sstring.h:330
Базовый класс работы с изменяемыми строками
Определения sstring.h:1437
Impl & insert(size_t to, const A &expr)
Вставить строковое выражение в указанную позицию.
Определения sstring.h:1977
Impl & operator<<=(const Op &fillFunction)
Заполняет строку методом fill после конца строки.
Определения sstring.h:2251
Impl & append(const A &expr)
Добавить строковое выражение в конец строки.
Определения sstring.h:1861
Impl & operator<<(const Op &fillFunction)
Вызывает переданный функтор, передав ссылку на себя.
Определения sstring.h:2264
Impl & trim(str_piece pattern)
Удалить символы, входящие в переданную строку, в начале и в конце строки.
Определения sstring.h:1671
Impl & upper_only_ascii()
Преобразовать в верхний регистр ASCII символы.
Определения sstring.h:1735
Impl & lower_only_ascii()
Преобразовать в нижний регистр ASCII символы.
Определения sstring.h:1750
Impl & trim_left()
Удалить пробельные символы в начале строки.
Определения sstring.h:1573
Impl & append_printf(const K *format, T &&... args)
Добавляет отформатированный с помощью sprintf вывод в конец строки.
Определения sstring.h:2353
Impl & trim_right_with_wpaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в конце строки.
Определения sstring.h:1660
Impl & trim_left(str_piece pattern)
Удалить символы, входящие в переданную строку, в начале строки.
Определения sstring.h:1682
Impl & trim_with_spaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале и в конце строки.
Определения sstring.h:1634
Impl & prepend(str_piece other)
Добавить другую строку в начало строки.
Определения sstring.h:2001
Impl & append_formatted(const FmtString< fmt_type, T... > &format, T &&... args)
Добавляет отформатированный с помощью std::format вывод в конец строки.
Определения sstring.h:2491
my_type & format(const FmtString< fmt_type, T... > &pattern, T &&... args)
Определения sstring.h:2475
Impl & printf(const K *format, T &&... args)
Форматирует строку помощью sprintf.
Определения sstring.h:2337
Impl & with(const Op &fillFunction, Args &&... args)
Вызов функтора со строкой и переданными аргументами.
Определения sstring.h:2573
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод в конец строки, записывая не более указанног...
Определения sstring.h:2559
Impl & operator<<(const Op &fillFunction)
Заполняет строку методом fill с нулевой позиции.
Определения sstring.h:2238
Impl & vformat(str_piece format, T &&... args)
Форматирует строку с помощью std::vformat.
Определения sstring.h:2507
Impl & change(size_t from, size_t len, const A &expr)
Заменить кусок строки на строковое выражение.
Определения sstring.h:1950
Impl & fill(size_t from, const Op &fillFunction)
Заполнение буфера строки с помощью функтора.
Определения sstring.h:2210
Impl & change(size_t from, size_t len, str_piece other)
Заменить кусок строки на другую строку.
Определения sstring.h:1934
Impl & remove(size_t from, size_t len)
Удалить часть строки.
Определения sstring.h:1990
Impl & trim_left_with_spaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале строки.
Определения sstring.h:1647
Impl & trim_left(T &&pattern)
Удалить символы, входящие в строковый литерал, в начале строки.
Определения sstring.h:1608
Impl & trim_right(T &&pattern)
Удалить символы, входящие в строковый литерал, в конце строки.
Определения sstring.h:1621
Impl & trim_right_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в конце строки.
Определения sstring.h:1726
Impl & append_in(size_t pos, str_piece other)
Добавить другую строку, начиная с заданной позиции.
Определения sstring.h:1901
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод, начиная с указанной позиции.
Определения sstring.h:2440
Impl & trim_left_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в начале строки.
Определения sstring.h:1715
Impl & insert(size_t to, str_piece other)
Вставить строку в указанную позицию.
Определения sstring.h:1963
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Форматирует строку с помощью std::vformat не более указанного размера.
Определения sstring.h:2541
Impl & printf_from(size_t from, const K *format, T &&... args)
Добавляет отформатированный с помощью sprintf вывод, начиная с указанной позиции.
Определения sstring.h:2283
Impl & operator+=(str_piece other)
Добавить другую строку в конец строки.
Определения sstring.h:1872
Impl & trim_right()
Удалить пробельные символы в конце строки.
Определения sstring.h:1582
Impl & append(str_piece other)
Добавить другую строку в конец строки.
Определения sstring.h:1849
Impl & trim_right(str_piece pattern)
Удалить символы, входящие в переданную строку, в конце строки.
Определения sstring.h:1693
Impl & trim_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в начале и в конце строки.
Определения sstring.h:1704
K * str() noexcept
Получить указатель на буфер строки.
Определения sstring.h:1546
Impl & prepend(const A &expr)
Добавить строковое выражение в начало строки.
Определения sstring.h:2013
Impl & trim()
Удалить пробельные символы в начале и в конце строки.
Определения sstring.h:1564
Impl & lower()
Преобразовать в нижний регистр Unicode символы первой плоскости (<0xFFFF).
Определения sstring.h:1784
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Заменить вхождения подстроки на другую строку.
Определения sstring.h:2030
Impl & upper()
Преобразовать в верхний регистр Unicode символы первой плоскости (<0xFFFF).
Определения sstring.h:1769
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Скопировать строку-источник, заменив вхождения подстрок на другую строку.
Определения sstring.h:2148
Impl & append_vformatted(str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод в конец строки.
Определения sstring.h:2523
Impl & append_in(size_t pos, const A &expr)
Добавить строковое выражение, начиная с заданной позиции.
Определения sstring.h:1919
my_type & format_from(size_t from, const FmtString< fmt_type, T... > &format, T &&... args)
Определения sstring.h:2411
Impl & trim(T &&pattern)
Удалить символы, входящие в строковый литерал, в начале и в конце строки.
Определения sstring.h:1595
Impl & operator+=(const A &expr)
Добавить строковое выражение в конец строки.
Определения sstring.h:1884
constexpr str_piece to_str() const noexcept
Преобразовать себя в "кусок строки", включающий всю строку.
Определения strexpr.h:2654
constexpr size_t size() const
Определения strexpr.h:2595
constexpr void as_number(T &t) const
Преобразовать строку в целое число.
Определения strexpr.h:3438
constexpr str_storable(Args &&... args)
Создать пустой объект.
Определения sstring.h:932
void init_replaced(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0)
Инициализация из строкового источника с заменой.
Определения sstring.h:1019
static my_type upperred_from(const From &f, Args &&... args)
Создать копию переданной строки в верхнем регистре символов Unicode первой плоскости (<0xFFFF).
Определения sstring.h:1280
constexpr void init_from_str_other(s_str other)
Инициализация из другого строкового объекта.
Определения sstring.h:940
constexpr allocator_t & allocator()
Получить аллокатор.
Определения sstring.h:909
constexpr void init_symb_repeat(size_t count, K pad)
Инициализация повторением символа.
Определения sstring.h:976
constexpr void init_str_expr(const A &expr)
Инициализация из строкового выражения.
Определения sstring.h:997
static my_type upperred_only_ascii_from(const From &f, Args &&... args)
Создать строку, копию переданной в верхнем регистре символов ASCII.
Определения sstring.h:1250
static my_type lowered_from(const From &f, Args &&... args)
Создать копию переданной строки в нижнем регистре символов Unicode первой плоскости (<0xFFFF).
Определения sstring.h:1297
constexpr void init_str_repeat(size_t repeat, s_str pattern)
Инициализация повторением строки.
Определения sstring.h:956
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Создать копию переданной строки в нижнем регистре символов ASCII.
Определения sstring.h:1263
static my_type join(const T &strings, s_str delimiter, bool tail=false, bool skip_empty=false, Args &&... args)
Конкатенация строк из контейнера в одну строку.
Определения sstring.h:1199
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Создать копию переданной строки с заменой подстрок.
Определения sstring.h:1318
constexpr s_str_nt to_nts(size_t from=0) const
Получить simple_str_nt, начиная с заданного символа.
Определения sstring.h:1143
Концепт типа, управляющего памятью
Определения sstring.h:1328
Концепт строкового выражения, совместимого с заданным типом символов.
Определения strexpr.h:488
Концепт типа, который не может модифицировать хранимую строку.
Определения sstring.h:851
Концепт типа, который может модифицировать хранимую строку.
Определения sstring.h:844
Концепт типа, который может сохранить строку.
Определения sstring.h:834
Небольшое пространство для методов работы со стандартными строками.
Определения strexpr.h:1569
Пространство имён для объектов библиотеки
Определения sstring.cpp:12
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Тип хеш-словаря для char16_t строк, регистрозависимый поиск.
Определения sstring.h:4696
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Тип хеш-словаря для char строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4662
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Тип хеш-словаря для char32_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4727
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Тип хеш-словаря для char32_t строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4721
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4689
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4682
expr_utf< From, To > e_utf(simple_str< From > from)
Возвращает строковое выражение, преобразующую строку из одного типа символов в другой тип,...
Определения sstring.h:825
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Тип хеш-словаря для char строк, регистрозависимый поиск.
Определения sstring.h:4656
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Тип хеш-словаря для char16_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4708
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Тип хеш-словаря для char16_t строк, регистронезависимый поиск для ASCII символов.
Определения sstring.h:4702
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Тип хеш-словаря для wchar_t строк, регистрозависимый поиск.
Определения sstring.h:4675
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Тип хеш-словаря для char строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:4668
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Тип хеш-словаря для char32_t строк, регистрозависимый поиск.
Определения sstring.h:4715
Объект, позволяющий последовательно копировать содержимое в буфер заданного размера.
Определения sstring.h:4586
bool is_end()
Проверить, что данные ещё не кончились.
Определения sstring.h:4593
size_t store(K *buffer, size_t size)
Сохранить очередную порцию данных в буфер.
Определения sstring.h:4606
Базовый класс для преобразования строковых выражений в стандартные строки
Определения strexpr.h:625
Строковое выражение для конвертации строк в разные виды UTF.
Определения sstring.h:796
Класс, заявляющий, что ссылается на нуль-терминированную строку.
Определения sstring.h:510
constexpr my_type to_nts(size_t from)
Получить нуль-терминированную строку, сдвинув начало на заданное количество символов.
Определения sstring.h:573
constexpr simple_str_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения sstring.h:562
constexpr simple_str_nt(T &&v) noexcept
Конструктор из строкового литерала.
Определения sstring.h:544
constexpr simple_str_nt(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения sstring.h:550
constexpr simple_str_nt(T &&p) noexcept
Явный конструктор из С-строки.
Определения sstring.h:535
Простейший класс иммутабельной не владеющей строки.
Определения sstring.h:374
constexpr simple_str(T &&v) noexcept
Конструктор из строкового литерала.
Определения sstring.h:390
constexpr K operator[](size_t idx) const
Получить символ из указанной позиции. Проверка границ не выполняется.
Определения sstring.h:454
constexpr my_type & remove_suffix(size_t delta)
Укорачивает строку на заданное количество символов.
Определения sstring.h:478
constexpr size_t length() const noexcept
Получить длину строки.
Определения sstring.h:411
constexpr const symb_type * symbols() const noexcept
Получить указатель на константный буфер с символами строки.
Определения sstring.h:418
constexpr my_type & remove_prefix(size_t delta)
Сдвигает начало строки на заданное количество символов.
Определения sstring.h:465
constexpr simple_str(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Конструктор из std::basic_string_view.
Определения sstring.h:406
constexpr simple_str(const K *p, size_t l) noexcept
Конструктор из указателя и длины.
Определения sstring.h:395
constexpr simple_str(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Конструктор из std::basic_string.
Определения sstring.h:401
constexpr bool is_empty() const noexcept
Проверить, не пуста ли строка.
Определения sstring.h:425
constexpr bool is_same(simple_str< K > other) const noexcept
Проверить, не указывают ли два объекта на одну строку.
Определения sstring.h:434
constexpr bool is_part_of(simple_str< K > other) const noexcept
Проверить, не является ли строка частью другой строки.
Определения sstring.h:443
Простейший класс иммутабельной не владеющей строки.
Определения strexpr.h:4047