simstr 1.2.4
Yet another strings library
 
Loading...
Searching...
No Matches
sstring.h
1/*
2 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
3 * ver. 1.2.4
4 * Классы для работы со строками
5* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6* ver. 1.2.4
7* Classes for working with strings
8 */
9
20#pragma once
21#include <optional>
22#ifndef __has_declspec_attribute
23#define __has_declspec_attribute(x) 0
24#endif
25
26#ifdef SIMSTR_IN_SHARED
27 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
28 #ifdef SIMSTR_EXPORT
29 #define SIMSTR_API __declspec(dllexport)
30 #else
31 #define SIMSTR_API __declspec(dllimport)
32 #endif
33 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
34 #define SIMSTR_API __attribute__((visibility("default")))
35 #else
36 #define SIMSTR_API
37 #endif
38#else
39 #define SIMSTR_API
40#endif
41const bool isWindowsOs = // NOLINT
42#ifdef _WIN32
43 true
44#else
45 false
46#endif
47 ;
48const bool isx64 = sizeof(void*) == 8; // NOLINT
49
50#ifdef _MSC_VER
51#define _no_unique_address msvc::no_unique_address
52#define decl_empty_bases __declspec(empty_bases)
53#else
54#define _no_unique_address no_unique_address
55#define decl_empty_bases
56#endif
57
58#if defined __has_builtin
59# if __has_builtin (__builtin_mul_overflow) && __has_builtin (__builtin_add_overflow)
60# define HAS_BUILTIN_OVERFLOW
61# endif
62#endif
63
64#include "strexpr.h"
65
66#include <cstddef>
67#include <string>
68#include <type_traits>
69#include <vector>
70#include <format>
71#include <unordered_map>
72#include <tuple>
73#include <limits>
74#include <cstdint>
75#include <atomic>
76#include <memory>
77#include <string.h>
78#include <iostream>
79#include <cmath>
80#include <charconv>
81
82#ifdef _WIN32
83#include <stdio.h>
84#endif
85
86#ifdef _MSC_VER
87// warning C4201 : nonstandard extension used : nameless struct / union
88#pragma warning(disable : 4201)
89#endif
90
91namespace simstr {
92
93template<typename T>
94struct unicode_traits {}; // NOLINT
95
96template<>
97struct unicode_traits<u8s> {
98 // Эти операции с utf-8 могут изменить длину строки
99 // Поэтому их специализации отличаются
100 // В функцию помимо текста и адреса буфера для записи передается размер буфера
101 // Возвращает длину получающейся строки.
102 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
103 // обработанные символы, для повторного возобновления работы,
104 // а для оставшихся символов считается нужный размер буфера.
105 // These utf-8 operations can change the length of the string
106 // Therefore their specializations are different
107 // In addition to the text and address of the buffer for writing, the buffer size is passed to the function
108 // Returns the length of the resulting string.
109 // If the resulting string does not fit into the allocated buffer, pointers are set to the last
110 // processed characters to resume work again,
111 // and for the remaining characters the required buffer size is calculated.
112 static SIMSTR_API size_t upper(const u8s*& src, size_t lenStr, u8s*& dest, size_t lenBuf);
113 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
114
115 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
116
117 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
118 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
119};
120
121template<>
122struct unicode_traits<u16s> {
123 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
124 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
125
126 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
127 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
128 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
129};
130
131template<>
132struct unicode_traits<u32s> {
133 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
134 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
135
136 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
137 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
138 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
139};
140
141template<>
142struct unicode_traits<wchar_t> {
143 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
144 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
145 }
146 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
147 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
148 }
149
150 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
151 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
152 }
153 static size_t hashia(const wchar_t* src, size_t s) {
154 return unicode_traits<wchar_type>::hashia(to_w(src), s);
155 }
156 static size_t hashiu(const wchar_t* src, size_t s) {
157 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
158 }
159};
160
161namespace str {
162constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
163} // namespace str
164
165template<typename K>
166struct ch_traits : std::char_traits<K>{};
167
168template<size_t N>
169concept is_const_pattern = N > 1 && N <= 17;
170
171template<typename K, size_t I>
172struct _ascii_mask { // NOLINT
173 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
174};
175
176template<typename K>
177struct _ascii_mask<K, 0> {
178 constexpr static const size_t value = 0;
179};
180
181template<typename K>
182struct ascii_mask { // NOLINT
183 using uns = std::make_unsigned_t<K>;
184 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
185 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
186};
187
188template<typename K>
189constexpr inline bool isAsciiUpper(K k) {
190 return k >= 'A' && k <= 'Z';
191}
192
193template<typename K>
194constexpr inline bool isAsciiLower(K k) {
195 return k >= 'a' && k <= 'z';
196}
197
198template<typename K>
199constexpr inline K makeAsciiLower(K k) {
200 return isAsciiUpper(k) ? k | 0x20 : k;
201}
202
203template<typename K>
204constexpr inline K makeAsciiUpper(K k) {
205 return isAsciiLower(k) ? k & ~0x20 : k;
206}
207
208enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
209template<TrimSides S, typename K, size_t N, bool withSpaces = false>
210struct trim_operator;
211
212template<typename K, size_t N, size_t L>
213struct expr_replaces;
214
215template<typename T>
216concept FromIntNumber =
217 is_one_of_type<std::remove_cv_t<T>, unsigned char, int, short, long, long long, unsigned, unsigned short, unsigned long, unsigned long long>::value;
218
219template<typename T>
220concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
221
222#if defined(_MSC_VER) && _MSC_VER <= 1933
223template<typename K, typename... Args>
224using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
225#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
226template<typename K, typename... Args>
227using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
228#else
229template<typename K, typename... Args>
230using FmtString = std::basic_string_view<K>;
231#endif
232
233template<typename K, bool I, typename T>
234struct need_sign { // NOLINT
235 bool sign;
236 need_sign(T& t) : sign(t < 0) {
237 if (sign && t != std::numeric_limits<T>::min())
238 t = -t;
239 }
240 void after(K*& ptr) {
241 if (sign)
242 *--ptr = '-';
243 }
244};
245
246template<typename K, typename T>
247struct need_sign<K, false, T> {
248 need_sign(T&) {}
249 void after(K*&) {}
250};
251
262
263template<bool CanNegate, bool CheckOverflow, typename T>
264struct result_type_selector { // NOLINT
265 using type = T;
266};
267
268template<typename T>
269struct result_type_selector<true, false, T> {
270 using type = std::make_unsigned_t<T>;
271};
272
273template<unsigned Base>
274constexpr unsigned digit_width() {
275 if (Base <=2) {
276 return 1;
277 }
278 if (Base <= 4) {
279 return 2;
280 }
281 if (Base <= 8) {
282 return 3;
283 }
284 if (Base <= 16) {
285 return 4;
286 }
287 if (Base <= 32) {
288 return 5;
289 }
290 return 6;
291}
292
293template<typename T, unsigned Base>
294constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
295
296template<typename T>
297struct convert_result {
298 T value;
300 size_t read;
301};
302
303struct int_convert { // NOLINT
304 inline static const uint8_t NUMBERS[] = {
305 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
306 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3,
307 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
308 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
309 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255,
310 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
311 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
312 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
313 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
314 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
315
316 template<typename K, unsigned Base>
317 static std::make_unsigned_t<K> toDigit(K s) {
318 auto us = static_cast<std::make_unsigned_t<K>>(s);
319 if constexpr (Base <= 10) {
320 return us - '0';
321 } else {
322 if constexpr (sizeof(K) == 1) {
323 return NUMBERS[us];
324 } else {
325 return us < 256 ? NUMBERS[us] : 255;
326 }
327 }
328 }
329
330 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
331 requires(Base != 0)
332 static convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
333 using u_type = std::make_unsigned_t<T>;
334 #ifndef HAS_BUILTIN_OVERFLOW
335 u_type maxMult = 0, maxAdd = 0;
336 if constexpr (CheckOverflow) {
337 maxMult = std::numeric_limits<u_type>::max() / Base;
338 maxAdd = std::numeric_limits<u_type>::max() % Base;
339 }
340 #endif
341 u_type number = 0;
342 unsigned maxDigits = max_overflow_digits<u_type, Base>;
344 const K* from = current;
345
346 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
347
348 if (no_need_check_o_f) {
349 for (;;) {
350 const u_type digit = toDigit<K, Base>(*current);
351 if (digit >= Base) {
352 break;
353 }
354 number = number * Base + digit;
355 if (++current == end) {
357 break;
358 }
359 }
360 } else {
361 for (;maxDigits; maxDigits--) {
362 const u_type digit = toDigit<K, Base>(*current);
363 if (digit >= Base) {
364 break;
365 }
366 number = number * Base + digit;
367 ++current;
368 }
369 if (!maxDigits) {
370 // Прошли все цифры, дальше надо с проверкой на overflow
371 // All numbers have passed, then we need to check for overflow
372 for (;;) {
373 const u_type digit = toDigit<K, Base>(*current);
374 if (digit >= Base) {
375 break;
376 }
377 #ifdef HAS_BUILTIN_OVERFLOW
378 if (__builtin_mul_overflow(number, Base, &number) ||
379 __builtin_add_overflow(number, digit, &number)) {
380 #else
381 if (number < maxMult || (number == maxMult && number < maxAdd)) {
382 number = number * Base + digit;
383 } else {
384 #endif
386 while(++current < end) {
387 if (toDigit<K, Base>(*current) >= Base) {
388 break;
389 }
390 }
391 break;
392 }
393 if (++current == end) {
395 break;
396 }
397 }
398 }
399 }
400 T result;
401 if constexpr (std::is_signed_v<T>) {
402 result = negate ? 0 - number : number;
403 if constexpr (CheckOverflow) {
404 if (error != IntConvertResult::Overflow) {
405 if (number > std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
407 }
408 }
409 }
410 } else {
411 result = number;
412 }
413 if (error == IntConvertResult::NotNumber && current > from) {
415 }
416 return {result, error, size_t(current - start)};
417 }
418public:
419 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
420 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
421 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
422 // If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10
423 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
424 requires(Base == -1 || (Base < 37 && Base != 1))
425 static convert_result<T> to_integer(const K* start, size_t len) noexcept {
426 const K *ptr = start, *end = ptr + len;
427 bool negate = false;
428 if constexpr (SkipWs) {
429 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
430 ptr++;
431 }
432 if (ptr != end) {
433 if constexpr (std::is_signed_v<T>) {
434 if constexpr (AllowSign) {
435 // Может быть число, +число или -число
436 // Can be a number, +number or -number
437 if (*ptr == '+') {
438 ptr++;
439 } else if (*ptr == '-') {
440 negate = true;
441 ptr++;
442 }
443 } else {
444 // Может быть число или -число
445 // Can be a number or -number
446 if (*ptr == '-') {
447 negate = true;
448 ptr++;
449 }
450 }
451 } else if constexpr (AllowSign) {
452 // Может быть число или +число
453 // Can be a number or +number
454 if (*ptr == '+') {
455 ptr++;
456 }
457 }
458 }
459 if (ptr != end) {
460 if constexpr (Base == 0 || Base == -1) {
461 if (*ptr == '0') {
462 ptr++;
463 if (ptr != end) {
464 if (*ptr == 'x' || *ptr == 'X') {
465 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
466 }
467 if constexpr (Base == -1) {
468 if (*ptr == 'b' || *ptr == 'B') {
469 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
470 }
471 if (*ptr == 'o' || *ptr == 'O') {
472 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
473 }
474 }
475 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
476 }
477 return {0, IntConvertResult::Success, size_t(ptr - start)};
478 }
479 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
480 } else
481 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
482 }
483 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
484 }
485};
486
487template<typename K>
488SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
489
490template<typename K>
491class Splitter;
492
493template<typename K, typename Impl, bool Mutable> class buffer_pointers;
494
503template<typename K, typename Impl>
504class buffer_pointers<K, Impl, false> {
505 const Impl& d() const { return *static_cast<const Impl*>(this); }
506public:
513 const K* c_str() const { return d().symbols(); }
520 const K* data() const { return d().symbols(); }
527 const K* begin() const { return d().symbols(); }
534 const K* end() const { return d().symbols() + d().length(); }
535};
536
537template<typename K, typename Impl>
538class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
539 Impl& d() { return *static_cast<Impl*>(this); }
540 using base = buffer_pointers<K, Impl, false>;
541public:
548 const K* data() const { return base::data(); }
555 const K* begin() const { return base::begin(); }
562 const K* end() const { return base::end(); }
569 K* data() { return d().str(); }
576 K* begin() { return d().str(); }
583 K* end() { return d().str() + d().length(); }
584};
585
610template<typename K, typename StrRef, typename Impl, bool Mutable>
611class str_algs : public buffer_pointers<K, Impl, Mutable> {
612 const Impl& d() const noexcept {
613 return *static_cast<const Impl*>(this);
614 }
615 size_t _len() const noexcept {
616 return d().length();
617 }
618 const K* _str() const noexcept {
619 return d().symbols();
620 }
621 bool _is_empty() const noexcept {
622 return d().is_empty();
623 }
624
625public:
626 using symb_type = K;
627 using str_piece = StrRef;
628 using traits = ch_traits<K>;
629 using uni = unicode_traits<K>;
630 using uns_type = std::make_unsigned_t<K>;
631 using my_type = Impl;
632 using base = str_algs<K, StrRef, Impl, Mutable>;
633 str_algs() = default;
634
647 constexpr K* place(K* ptr) const noexcept {
648 size_t myLen = _len();
649 if (myLen) {
650 traits::copy(ptr, _str(), myLen);
651 return ptr + myLen;
652 }
653 return ptr;
654 }
655
665 void copy_to(K* buffer, size_t bufSize) {
666 size_t tlen = std::min(_len(), bufSize - 1);
667 if (tlen)
668 traits::copy(buffer, _str(), tlen);
669 buffer[tlen] = 0;
670 }
671
677 size_t size() const {
678 return _len();
679 }
680
687 constexpr operator str_piece() const noexcept {
688 return str_piece{_str(), _len()};
689 }
690
696 str_piece to_str() const noexcept {
697 return {_str(), _len()};
698 }
699
705 template<typename=int> requires is_one_of_std_char_v<K>
706 std::basic_string_view<K> to_sv() const noexcept {
707 return {_str(), _len()};
708 }
709
715 template<typename=int> requires is_one_of_std_char_v<K>
716 std::basic_string<K> to_string() const noexcept {
717 return {_str(), _len()};
718 }
719
742 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
743 size_t myLen = _len(), idxStart = from >= 0 ? from : myLen > -from ? myLen + from : 0,
744 idxEnd = len > 0 ? idxStart + len : myLen > -len ? myLen + len : 0;
745 if (idxEnd > myLen)
746 idxEnd = myLen;
747 if (idxStart > idxEnd)
748 idxStart = idxEnd;
749 return str_piece{_str() + idxStart, idxEnd - idxStart};
750 }
751
761 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
762 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
763 if (idxEnd > myLen)
764 idxEnd = myLen;
765 if (idxStart > idxEnd)
766 idxStart = idxEnd;
767 return str_piece{_str() + idxStart, idxEnd - idxStart};
768 }
769
783 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
784 return str_piece{_str() + from, to - from};
785 }
786
790 bool operator!() const noexcept {
791 return _is_empty();
792 }
793
803 K at(ptrdiff_t idx) const {
804 return _str()[idx >= 0 ? idx : _len() + idx];
805 }
806 // Сравнение строк
807 // String comparison
808 constexpr int compare(const K* text, size_t len) const {
809 size_t myLen = _len();
810 int cmp = traits::compare(_str(), text, std::min(myLen, len));
811 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
812 }
821 constexpr int compare(str_piece o) const {
822 return compare(o.symbols(), o.length());
823 }
824
832 constexpr int strcmp(const K* text) const {
833 size_t myLen = _len(), idx = 0;
834 const K* ptr = _str();
835 for (; idx < myLen; idx++) {
836 uns_type s1 = (uns_type)text[idx];
837 if (!s1) {
838 return 1;
839 }
840 uns_type s2 = (uns_type)ptr[idx];
841 if (s1 < s2) {
842 return 1;
843 } else if (s1 > s2) {
844 return -1;
845 }
846 }
847 return text[idx] == 0 ? 0 : -1;
848 }
849
850 constexpr bool equal(const K* text, size_t len) const noexcept {
851 return len == _len() && traits::compare(_str(), text, len) == 0;
852 }
861 constexpr bool equal(str_piece other) const noexcept {
862 return equal(other.symbols(), other.length());
863 }
864
872 constexpr bool operator==(const base& other) const noexcept {
873 return equal(other._str(), other._len());
874 }
875
881 constexpr auto operator<=>(const base& other) const noexcept {
882 return compare(other._str(), other._len()) <=> 0;
883 }
884
890 template<typename T, size_t N = const_lit_for<K, T>::Count>
891 bool operator==(T&& other) const noexcept {
892 return N - 1 == _len() && traits::compare(_str(), other, N - 1) == 0;
893 }
894
900 template<typename T, size_t N = const_lit_for<K, T>::Count>
901 auto operator<=>(T&& other) const noexcept {
902 size_t myLen = _len();
903 int cmp = traits::compare(_str(), other, std::min(myLen, N - 1));
904 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
905 return res <=> 0;
906 }
907
908 // Сравнение ascii строк без учёта регистра
909 // Compare ascii strings without taking into account case
910 int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
911 if (!len)
912 return _is_empty() ? 0 : 1;
913 size_t myLen = _len(), checkLen = std::min(myLen, len);
914 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
915 while (checkLen--) {
916 uns_type s1 = *ptr1++, s2 = *ptr2++;
917 if (s1 == s2)
918 continue;
919 s1 = makeAsciiLower(s1);
920 s2 = makeAsciiLower(s2);
921 if (s1 > s2)
922 return 1;
923 else if (s1 < s2)
924 return -1;
925 }
926 return myLen == len ? 0 : myLen > len ? 1 : -1;
927 }
936 int compare_ia(str_piece text) const noexcept { // NOLINT
937 return compare_ia(text.symbols(), text.length());
938 }
939
948 bool equal_ia(str_piece text) const noexcept { // NOLINT
949 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
950 }
951
959 bool less_ia(str_piece text) const noexcept { // NOLINT
960 return compare_ia(text.symbols(), text.length()) < 0;
961 }
962
963 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
964 if (!len)
965 return _is_empty() ? 0 : 1;
966 return uni::compareiu(_str(), _len(), text, len);
967 }
976 int compare_iu(str_piece text) const noexcept { // NOLINT
977 return compare_iu(text.symbols(), text.length());
978 }
979
988 bool equal_iu(str_piece text) const noexcept { // NOLINT
989 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
990 }
991
999 bool less_iu(str_piece text) const noexcept { // NOLINT
1000 return compare_iu(text.symbols(), text.length()) < 0;
1001 }
1002
1003 size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
1004 size_t lenText = _len();
1005 // Образец, не вмещающийся в строку и пустой образец не находим
1006 // We don't look for an empty line or a line longer than the text.
1007 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
1008 return str::npos;
1009 lenPattern--;
1010 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
1011 pattern++;
1012 for (const K* fnd = text + offset;; ++fnd) {
1013 fnd = traits::find(fnd, last - fnd, first);
1014 if (!fnd)
1015 return str::npos;
1016 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
1017 return static_cast<size_t>(fnd - text);
1018 }
1019 }
1030 size_t find(str_piece pattern, size_t offset = 0) const noexcept {
1031 return find(pattern.symbols(), pattern.length(), offset);
1032 }
1033
1049 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
1050 size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
1051 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
1052 return fnd;
1053 }
1054 throw Exc(std::forward<Args>(args)...);
1055 }
1056
1066 size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
1067 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
1068 return fnd == str::npos ? fnd : fnd + pattern.length();
1069 }
1070
1080 size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
1081 auto fnd = find(pattern.symbols(), pattern.length(), offset);
1082 return fnd == str::npos ? _len() : fnd;
1083 }
1084
1094 size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
1095 auto fnd = find(pattern.symbols(), pattern.length(), offset);
1096 return fnd == str::npos ? _len() : fnd + pattern.length();
1097 }
1098
1099 size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
1100 if (lenPattern == 1)
1101 return find_last(pattern[0], offset);
1102 size_t lenText = std::min(_len(), offset);
1103 // Образец, не вмещающийся в строку и пустой образец не находим
1104 // We don't look for an empty line or a line longer than the text.
1105 if (!lenPattern || lenPattern > lenText)
1106 return str::npos;
1107
1108 lenPattern--;
1109 const K *text = _str() + lenPattern, last = pattern[lenPattern];
1110 lenText -= lenPattern;
1111 while(lenText) {
1112 if (text[--lenText] == last) {
1113 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
1114 return lenText;
1115 }
1116 }
1117 }
1118 return str::npos;
1119 }
1130 size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
1131 return find_last(pattern.symbols(), pattern.length(), offset);
1132 }
1133
1143 size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
1144 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
1145 return fnd == str::npos ? fnd : fnd + pattern.length();
1146 }
1147
1157 size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
1158 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
1159 return fnd == str::npos ? _len() : fnd;
1160 }
1161
1171 size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
1172 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
1173 return fnd == str::npos ? _len() : fnd + pattern.length();
1174 }
1175
1185 bool contains(str_piece pattern, size_t offset = 0) const noexcept {
1186 return find(pattern, offset) != str::npos;
1187 }
1188
1198 size_t find(K s, size_t offset = 0) const noexcept {
1199 size_t len = _len();
1200 if (offset < len) {
1201 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
1202 if (fnd)
1203 return static_cast<size_t>(fnd - str);
1204 }
1205 return str::npos;
1206 }
1207
1217 size_t find_or_all(K s, size_t offset = 0) const noexcept {
1218 size_t len = _len();
1219 if (offset < len) {
1220 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
1221 if (fnd)
1222 return static_cast<size_t>(fnd - str);
1223 }
1224 return len;
1225 }
1226
1227 template<typename Op>
1228 void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
1229 if (!maxCount)
1230 maxCount--;
1231 while (maxCount-- > 0) {
1232 size_t fnd = find(pattern, patternLen, offset);
1233 if (fnd == str::npos)
1234 break;
1235 op(fnd);
1236 offset = fnd + patternLen;
1237 }
1238 }
1251 template<typename Op>
1252 void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
1253 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
1254 }
1255
1256 std::vector<size_t> find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
1257 std::vector<size_t> result;
1258 for_all_finded([&](auto f) { result.push_back(f); }, pattern, patternLen, offset, maxCount);
1259 return result;
1260 }
1273 std::vector<size_t> find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
1274 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
1275 }
1276
1286 size_t find_last(K s, size_t offset = -1) const noexcept {
1287 size_t len = std::min(_len(), offset);
1288 const K *text = _str();
1289 while (len > 0) {
1290 if (text[--len] == s)
1291 return len;
1292 }
1293 return str::npos;
1294 }
1295
1305 size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
1306 return std::string_view{_str(), _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
1307 }
1308
1318 std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
1319 const K* text = _str();
1320 size_t fnd = std::string_view{text, _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
1321 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
1322 }
1323
1333 size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
1334 return std::string_view{_str(), _len()}.find_first_not_of(std::string_view{pattern.str, pattern.len}, offset);
1335 }
1336
1346 size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
1347 return std::string_view{_str(), _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
1348 }
1349
1359 std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
1360 const K* text = _str();
1361 size_t fnd = std::string_view{text, _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
1362 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
1363 }
1364
1374 size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
1375 return std::string_view{_str(), _len()}.find_last_not_of(std::string_view{pattern.str, pattern.len}, offset);
1376 }
1377
1387 my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
1388 return my_type{d()(from, len)};
1389 }
1390
1400 my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
1401 return my_type{d().mid(from, len)};
1402 }
1403
1431 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
1432 T as_int() const noexcept {
1433 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
1434 return err == IntConvertResult::Overflow ? 0 : res;
1435 }
1436
1464 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
1465 convert_result<T> to_int() const noexcept {
1466 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
1467 }
1468
1474 template<bool SkipWS = true, bool AllowPlus = true>
1475 std::optional<double> to_double() const noexcept {
1476 size_t len = _len();
1477 const K* ptr = _str();
1478 if constexpr (SkipWS) {
1479 while (len && uns_type(*ptr) <= ' ') {
1480 len--;
1481 ptr++;
1482 }
1483 }
1484 if constexpr (AllowPlus) {
1485 if (len && *ptr == K('+')) {
1486 ptr++;
1487 len--;
1488 }
1489 }
1490 if (!len) {
1491 return {};
1492 }
1493 #ifdef __linux__
1494 if constexpr(sizeof(K) == 1) {
1495 double d{};
1496 if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
1497 return d;
1498 }
1499 return {};
1500 }
1501 #endif
1502 return impl_to_double(ptr, ptr + len);
1503 }
1504
1510 template<bool SkipWS = true> requires (sizeof(K) == 1)
1511 std::optional<double> to_double_hex() const noexcept {
1512 size_t len = _len();
1513 const K* ptr = _str();
1514 if constexpr (SkipWS) {
1515 while (len && uns_type(*ptr) <= ' ') {
1516 len--;
1517 ptr++;
1518 }
1519 }
1520 if (len) {
1521 double d{};
1522 if (std::from_chars(ptr, ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
1523 return d;
1524 }
1525 }
1526 return {};
1527 }
1528
1536 template<ToIntNumber T>
1537 void as_number(T& t) const {
1538 t = as_int<T>();
1539 }
1540
1546 void as_number(double& t) const {
1547 auto res = to_double();
1548 t = res ? *res : std::nan("0");
1549 }
1550
1551 template<typename T, typename Op>
1552 T splitf(const K* delimeter, size_t lenDelimeter, const Op& beforeFunc, size_t offset) const {
1553 size_t mylen = _len();
1554 std::conditional_t<std::is_same_v<T, void>, char, T> results;
1555 str_piece me{_str(), mylen};
1556 for (int i = 0;; i++) {
1557 size_t beginOfDelim = find(delimeter, lenDelimeter, offset);
1558 if (beginOfDelim == str::npos) {
1559 str_piece last{me.symbols() + offset, me.length() - offset};
1560 if constexpr (std::is_invocable_v<Op, str_piece&>) {
1561 beforeFunc(last);
1562 }
1563 if constexpr (requires { results.emplace_back(last); }) {
1564 if (last.is_same(me)) {
1565 // Пробуем положить весь объект.
1566 // Try to put the entire object.
1567 results.emplace_back(d());
1568 } else {
1569 results.emplace_back(last);
1570 }
1571 } else if constexpr (requires { results.push_back(last); }) {
1572 if (last.is_same(me)) {
1573 // Пробуем положить весь объект.
1574 // Try to put the entire object.
1575 results.push_back(d());
1576 } else {
1577 results.push_back(last);
1578 }
1579 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
1580 if (i < std::size(results)) {
1581 if (last.is_same(me)) {
1582 // Пробуем положить весь объект.
1583 // Try to put the entire object.
1584 results[i] = d();
1585 } else
1586 results[i] = last;
1587 }
1588 }
1589 break;
1590 }
1591 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
1592 if constexpr (std::is_invocable_v<Op, str_piece&>) {
1593 beforeFunc(piece);
1594 }
1595 if constexpr (requires { results.emplace_back(piece); }) {
1596 results.emplace_back(piece);
1597 } else if constexpr (requires { results.push_back(piece); }) {
1598 results.push_back(piece);
1599 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
1600 if (i < std::size(results)) {
1601 results[i] = piece;
1602 if (i == results.size() - 1) {
1603 break;
1604 }
1605 }
1606 }
1607 offset = beginOfDelim + lenDelimeter;
1608 }
1609 if constexpr (!std::is_same_v<T, void>) {
1610 return results;
1611 }
1612 }
1643 template<typename T, typename Op>
1644 T splitf(str_piece delimeter, const Op& beforeFunc, size_t offset = 0) const {
1645 return splitf<T>(delimeter.symbols(), delimeter.length(), beforeFunc, offset);
1646 }
1647
1659 template<typename T>
1660 T split(str_piece delimeter, size_t offset = 0) const {
1661 return splitf<T>(delimeter.symbols(), delimeter.length(), 0, offset);
1662 }
1663
1673 Splitter<K> splitter(str_piece delimeter) const;
1674
1675 // Начинается ли эта строка с указанной подстроки
1676 // Does this string start with the specified substring
1677 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
1678 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
1679 }
1686 constexpr bool starts_with(str_piece prefix) const noexcept {
1687 return starts_with(prefix.symbols(), prefix.length());
1688 }
1689
1690 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
1691 size_t myLen = _len();
1692 if (myLen < len) {
1693 return false;
1694 }
1695 const K* ptr1 = _str();
1696 while (len--) {
1697 K s1 = *ptr1++, s2 = *prefix++;
1698 if (s1 == s2)
1699 continue;
1700 if (makeAsciiLower(s1) != makeAsciiLower(s2))
1701 return false;
1702 }
1703 return true;
1704 }
1711 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
1712 return starts_with_ia(prefix.symbols(), prefix.length());
1713 }
1714 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
1715 // Does this string begin with the specified substring, insensitive to unicode case
1716 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
1717 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
1718 }
1725 bool starts_with_iu(str_piece prefix) const noexcept {
1726 return starts_with_iu(prefix.symbols(), prefix.length());
1727 }
1728
1729 // Является ли эта строка началом указанной строки
1730 // Is this string the beginning of the specified string
1731 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
1732 size_t myLen = _len();
1733 if (myLen > len)
1734 return false;
1735 return !myLen || 0 == traits::compare(text, _str(), myLen);
1736 }
1743 constexpr bool prefix_in(str_piece text) const noexcept {
1744 return prefix_in(text.symbols(), text.length());
1745 }
1746 // Заканчивается ли строка указанной подстрокой
1747 // Does the string end with the specified substring
1748 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
1749 size_t myLen = _len();
1750 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
1751 }
1758 constexpr bool ends_with(str_piece suffix) const noexcept {
1759 return ends_with(suffix.symbols(), suffix.length());
1760 }
1761 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
1762 // Whether the string ends with the specified substring, case insensitive ASCII
1763 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
1764 size_t myLen = _len();
1765 if (myLen < len) {
1766 return false;
1767 }
1768 const K* ptr1 = _str() + myLen - len;
1769 while (len--) {
1770 K s1 = *ptr1++, s2 = *suffix++;
1771 if (s1 == s2)
1772 continue;
1773 if (makeAsciiLower(s1) != makeAsciiLower(s2))
1774 return false;
1775 }
1776 return true;
1777 }
1784 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
1785 return ends_with_ia(suffix.symbols(), suffix.length());
1786 }
1787 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
1788 // Whether the string ends with the specified substring, case insensitive UNICODE
1789 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
1790 size_t myLen = _len();
1791 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
1792 }
1799 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
1800 return ends_with_iu(suffix.symbols(), suffix.length());
1801 }
1802
1806 bool is_ascii() const noexcept {
1807 if (_is_empty())
1808 return true;
1809 const int sl = ascii_mask<K>::WIDTH;
1810 const size_t mask = ascii_mask<K>::VALUE;
1811 size_t len = _len();
1812 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
1813 if constexpr (sl > 1) {
1814 const size_t roundMask = sizeof(size_t) - 1;
1815 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
1816 if (*ptr++ > 127)
1817 return false;
1818 len--;
1819 }
1820 while (len >= sl) {
1821 if (*reinterpret_cast<const size_t*>(ptr) & mask)
1822 return false;
1823 ptr += sl;
1824 len -= sl;
1825 }
1826 }
1827 while (len--) {
1828 if (*ptr++ > 127)
1829 return false;
1830 }
1831 return true;
1832 }
1833
1841 template<typename R = my_type>
1843 return R::uppered_only_ascii_from(d());
1844 }
1845
1853 template<typename R = my_type>
1855 return R::lowered_only_ascii_from(d());
1856 }
1857
1865 template<typename R = my_type>
1866 R uppered() const {
1867 return R::uppered_from(d());
1868 }
1869
1877 template<typename R = my_type>
1878 R lowered() const {
1879 return R::lowered_from(d());
1880 }
1881
1897 template<typename R = my_type>
1898 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
1899 return R::replaced_from(d(), pattern, repl, offset, maxCount);
1900 }
1901
1912 template<typename T, size_t N = const_lit_for<K, T>::Count, typename M, size_t L = const_lit_for<K, M>::Count>
1913 expr_replaces<K, N - 1, L - 1> replace_init(T&& pattern, M&& repl) const {
1914 return expr_replaces<K, N - 1, L - 1>{d(), pattern, repl};
1915 }
1916
1917 template<StrType<K> From>
1918 static my_type make_trim_op(const From& from, const auto& opTrim) {
1919 str_piece sfrom = from, newPos = opTrim(sfrom);
1920 return newPos.is_same(sfrom) ? my_type{from} : my_type{newPos};
1921 }
1922 template<TrimSides S, StrType<K> From>
1923 static my_type trim_static(const From& from) {
1924 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
1925 }
1926
1927 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
1928 requires is_const_pattern<N>
1929 static my_type trim_static(const From& from, T&& pattern) {
1930 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
1931 }
1932
1933 template<TrimSides S, bool withSpaces, StrType<K> From>
1934 static my_type trim_static(const From& from, str_piece pattern) {
1935 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
1936 }
1945 template<typename R = str_piece>
1946 R trimmed() const {
1947 return R::template trim_static<TrimSides::TrimAll>(d());
1948 }
1949
1957 template<typename R = str_piece>
1958 R trimmed_left() const {
1959 return R::template trim_static<TrimSides::TrimLeft>(d());
1960 }
1961
1969 template<typename R = str_piece>
1970 R trimmed_right() const {
1971 return R::template trim_static<TrimSides::TrimRight>(d());
1972 }
1973
1983 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1984 requires is_const_pattern<N>
1985 R trimmed(T&& pattern) const {
1986 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
1987 }
1988
1998 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1999 requires is_const_pattern<N>
2000 R trimmed_left(T&& pattern) const {
2001 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
2002 }
2003
2013 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2014 requires is_const_pattern<N>
2015 R trimmed_right(T&& pattern) const {
2016 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
2017 }
2018 // Триминг по символам в литерале и пробелам
2019 // Trimming by characters in literal and spaces
2020
2035 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2036 requires is_const_pattern<N>
2037 R trimmed_with_spaces(T&& pattern) const {
2038 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
2039 }
2040
2054 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2055 requires is_const_pattern<N>
2056 R trimmed_left_with_spaces(T&& pattern) const {
2057 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
2058 }
2059
2073 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2074 requires is_const_pattern<N>
2075 R trimmed_right_with_spaces(T&& pattern) const {
2076 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
2077 }
2078 // Триминг по динамическому источнику
2079 // Trimming by dynamic source
2080
2091 template<typename R = str_piece>
2092 R trimmed(str_piece pattern) const {
2093 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
2094 }
2095
2105 template<typename R = str_piece>
2106 R trimmed_left(str_piece pattern) const {
2107 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
2108 }
2109
2119 template<typename R = str_piece>
2120 R trimmed_right(str_piece pattern) const {
2121 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
2122 }
2123
2137 template<typename R = str_piece>
2138 R trimmed_with_spaces(str_piece pattern) const {
2139 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
2140 }
2141
2155 template<typename R = str_piece>
2156 R trimmed_left_with_spaces(str_piece pattern) const {
2157 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
2158 }
2159
2173 template<typename R = str_piece>
2174 R trimmed_right_with_spaces(str_piece pattern) const {
2175 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
2176 }
2177};
2178
2179/*
2180* Базовая структура с информацией о строке.
2181* Это структура для не владеющих строк.
2182* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
2183* в результате размер класса не увеличивается
2184* Basic structure with string information.
2185* This is the structure for non-owning strings.
2186* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
2187* as a result the class size does not increase
2188*/
2189
2200template<typename K>
2201struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
2202 using symb_type = K;
2203 using my_type = simple_str<K>;
2204
2205 const symb_type* str;
2206 size_t len;
2207
2208 simple_str() = default;
2209
2214 template<typename T, size_t N = const_lit_for<K, T>::Count>
2215 constexpr simple_str(T&& v) noexcept : str(v), len(N - 1) {}
2220 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
2227 template<typename S>
2228 requires(std::is_same_v<S, std::string&> || std::is_same_v<S, const std::string&>
2229 || std::is_same_v<S, std::string_view&> || std::is_same_v<S, const std::string_view&>)
2230 constexpr simple_str(S&& s) noexcept : str(s.data()), len(s.length()) {}
2235 constexpr size_t length() const noexcept {
2236 return len;
2237 }
2238
2242 constexpr const symb_type* symbols() const noexcept {
2243 return str;
2244 }
2245
2249 constexpr bool is_empty() const noexcept {
2250 return len == 0;
2251 }
2252
2258 bool is_same(simple_str<K> other) const noexcept {
2259 return str == other.str && len == other.len;
2260 }
2261
2267 bool is_part_of(simple_str<K> other) const noexcept {
2268 return str >= other.str && str + len <= other.str + other.len;
2269 }
2270
2278 K operator[](size_t idx) const {
2279 return str[idx];
2280 }
2281
2289 my_type& remove_prefix(size_t delta) {
2290 str += delta;
2291 len -= delta;
2292 return *this;
2293 }
2294
2302 my_type& remove_suffix(size_t delta) {
2303 len -= delta;
2304 return *this;
2305 }
2306};
2307
2328template<typename K>
2329struct simple_str_nt : simple_str<K> {
2330 using symb_type = K;
2331 using my_type = simple_str_nt<K>;
2332 using base = simple_str<K>;
2333 using base::base;
2334
2335 constexpr static const K empty_string[1] = {0};
2336
2337 simple_str_nt() = default;
2354 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
2355 explicit simple_str_nt(T&& p) noexcept {
2356 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
2357 base::str = base::len ? p : empty_string;
2358 }
2359
2365 template<typename S>
2366 requires(std::is_same_v<S, std::string&> || std::is_same_v<S, const std::string&>
2367 || std::is_same_v<S, std::string_view&> || std::is_same_v<S, const std::string_view&>)
2368 constexpr simple_str_nt(S&& s) noexcept : base(s) {}
2369
2370 static const my_type empty_str;
2377 operator const K*() const noexcept {
2378 return base::str;
2379 }
2380
2388 my_type to_nts(size_t from) {
2389 if (from > base::len) {
2390 from = base::len;
2391 }
2392 return {base::str + from, base::len - from};
2393 }
2394};
2395
2396template<typename K>
2397inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
2398
2399using ssa = simple_str<u8s>;
2400using ssw = simple_str<wchar_t>;
2401using ssu = simple_str<u16s>;
2402using ssuu = simple_str<u32s>;
2403using stra = simple_str_nt<u8s>;
2404using strw = simple_str_nt<wchar_t>;
2405using stru = simple_str_nt<u16s>;
2406using struu = simple_str_nt<u32s>;
2407
2414template<typename K>
2415class Splitter {
2416 simple_str<K> text_;
2417 simple_str<K> delim_;
2418
2419public:
2420 Splitter(simple_str<K> text, simple_str<K> delim) : text_(text), delim_(delim) {}
2425 bool is_done() const {
2426 return text_.length() == str::npos;
2427 }
2428
2435 if (!text_.length()) {
2436 auto ret = text_;
2437 text_.str++;
2438 text_.len--;
2439 return ret;
2440 } else if (text_.length() == str::npos) {
2441 return {nullptr, 0};
2442 }
2443 size_t pos = text_.find(delim_), next = 0;
2444 if (pos == str::npos) {
2445 pos = text_.length();
2446 next = pos + 1;
2447 } else {
2448 next = pos + delim_.length();
2449 }
2450 simple_str<K> result{text_.str, pos};
2451 text_.str += next;
2452 text_.len -= next;
2453 return result;
2454 }
2455};
2456
2457template<typename K, typename StrRef, typename Impl, bool Mutable>
2459 return Splitter<K>{*this, delimeter};
2460}
2461
2462template<typename K, bool withSpaces>
2463struct CheckSpaceTrim {
2464 bool is_trim_spaces(K s) const {
2465 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
2466 }
2467};
2468template<typename K>
2469struct CheckSpaceTrim<K, false> {
2470 bool is_trim_spaces(K) const {
2471 return false;
2472 }
2473};
2474
2475template<typename K>
2476struct CheckSymbolsTrim {
2477 simple_str<K> symbols;
2478 bool is_trim_symbols(K s) const {
2479 return symbols.len != 0 && simple_str<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
2480 }
2481};
2482
2483template<typename K, size_t N>
2484struct CheckConstSymbolsTrim {
2485 const const_lit_to_array<K, N> symbols;
2486
2487 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
2488 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
2489
2490 bool is_trim_symbols(K s) const noexcept {
2491 return symbols.contain(s);
2492 }
2493};
2494
2495template<typename K>
2496struct CheckConstSymbolsTrim<K, 0> {
2497 bool is_trim_symbols(K) const {
2498 return false;
2499 }
2500};
2501
2502template<typename K, size_t N>
2503struct SymbSelector {
2504 using type = CheckConstSymbolsTrim<K, N>;
2505};
2506
2507template<typename K>
2508struct SymbSelector<K, 0> {
2509 using type = CheckSymbolsTrim<K>;
2510};
2511
2512template<typename K>
2513struct SymbSelector<K, static_cast<size_t>(-1)> {
2514 using type = CheckConstSymbolsTrim<K, 0>;
2515};
2516
2517template<TrimSides S, typename K, size_t N, bool withSpaces>
2518struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
2519 bool isTrim(K s) const {
2520 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
2521 }
2522 simple_str<K> operator()(simple_str<K> from) const {
2523 if constexpr ((S & TrimSides::TrimLeft) != 0) {
2524 while (from.len) {
2525 if (isTrim(*from.str)) {
2526 from.str++;
2527 from.len--;
2528 } else
2529 break;
2530 }
2531 }
2532 if constexpr ((S & TrimSides::TrimRight) != 0) {
2533 const K* back = from.str + from.len - 1;
2534 while (from.len) {
2535 if (isTrim(*back)) {
2536 back--;
2537 from.len--;
2538 } else
2539 break;
2540 }
2541 }
2542 return from;
2543 }
2544};
2545
2546template<TrimSides S, typename K>
2547using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
2548
2549using trim_w = SimpleTrim<TrimSides::TrimAll, u16s>;
2550using trim_a = SimpleTrim<TrimSides::TrimAll, u8s>;
2551using triml_w = SimpleTrim<TrimSides::TrimLeft, u16s>;
2552using triml_a = SimpleTrim<TrimSides::TrimLeft, u8s>;
2553using trimr_w = SimpleTrim<TrimSides::TrimRight, u16s>;
2554using trimr_a = SimpleTrim<TrimSides::TrimRight, u8s>;
2555
2556template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
2557 requires is_const_pattern<N>
2558inline auto trimOp(T&& pattern) {
2559 return trim_operator<S, K, N - 1, withSpaces>{pattern};
2560}
2561
2562template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
2563inline auto trimOp(simple_str<K> pattern) {
2564 return trim_operator<S, K, 0, withSpaces>{pattern};
2565}
2566
2567template<typename Src, typename Dest>
2568struct utf_convert_selector;
2569
2570template<>
2571struct utf_convert_selector<u8s, u16s> {
2572 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
2573 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
2574};
2575
2576template<>
2577struct utf_convert_selector<u8s, u32s> {
2578 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
2579 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
2580};
2581
2582template<>
2583struct utf_convert_selector<u8s, wchar_t> {
2584 static size_t need_len(const u8s* src, size_t srcLen) {
2585 return utf_convert_selector<u8s, wchar_type>::need_len(src, srcLen);
2586 }
2587 static size_t convert(const u8s* src, size_t srcLen, wchar_t* dest) {
2588 return utf_convert_selector<u8s, wchar_type>::convert(src, srcLen, to_w(dest));
2589 }
2590};
2591
2592template<>
2593struct utf_convert_selector<u16s, u8s> {
2594 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
2595 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
2596};
2597
2598template<>
2599struct utf_convert_selector<u16s, u32s> {
2600 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
2601 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
2602};
2603
2604template<>
2605struct utf_convert_selector<u16s, u16s> {
2606 // При конвертации char16_t в wchar_t под windows будет вызываться эта реализация
2607 // When converting char16_t to wchar_t under windows this implementation will be called
2608 static size_t need_len(const u16s* src, size_t srcLen) {
2609 return srcLen;
2610 }
2611 static size_t convert(const u16s* src, size_t srcLen, u16s* dest) {
2612 ch_traits<u16s>::copy(dest, src, srcLen + 1);
2613 return srcLen;
2614 }
2615};
2616
2617template<>
2618struct utf_convert_selector<u32s, u32s> {
2619 // При конвертации char32_t в wchar_t под linux будет вызываться эта реализация
2620 // When converting char32_t to wchar_t under Linux, this implementation will be called
2621 static size_t need_len(const u32s* src, size_t srcLen) {
2622 return srcLen;
2623 }
2624 static size_t convert(const u32s* src, size_t srcLen, u32s* dest) {
2625 ch_traits<u32s>::copy(dest, src, srcLen + 1);
2626 return srcLen;
2627 }
2628};
2629
2630template<>
2631struct utf_convert_selector<u16s, wchar_t> {
2632 static size_t need_len(const u16s* src, size_t srcLen) {
2633 return utf_convert_selector<u16s, wchar_type>::need_len(src, srcLen);
2634 }
2635 static size_t convert(const u16s* src, size_t srcLen, wchar_t* dest) {
2636 return utf_convert_selector<u16s, wchar_type>::convert(src, srcLen, to_w(dest));
2637 }
2638};
2639
2640template<>
2641struct utf_convert_selector<u32s, u8s> {
2642 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
2643 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
2644};
2645
2646template<>
2647struct utf_convert_selector<u32s, u16s> {
2648 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
2649 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
2650};
2651
2652template<>
2653struct utf_convert_selector<u32s, wchar_t> {
2654 static size_t need_len(const u32s* src, size_t srcLen) {
2655 return utf_convert_selector<u32s, wchar_type>::need_len(src, srcLen);
2656 }
2657 static size_t convert(const u32s* src, size_t srcLen, wchar_t* dest) {
2658 return utf_convert_selector<u32s, wchar_type>::convert(src, srcLen, to_w(dest));
2659 }
2660};
2661
2662template<>
2663struct utf_convert_selector<wchar_t, u8s> {
2664 static size_t need_len(const wchar_t* src, size_t srcLen) {
2665 return utf_convert_selector<wchar_type, u8s>::need_len(to_w(src), srcLen);
2666 }
2667 static size_t convert(const wchar_t* src, size_t srcLen, u8s* dest) {
2668 return utf_convert_selector<wchar_type, u8s>::convert(to_w(src), srcLen, dest);
2669 }
2670};
2671
2672template<>
2673struct utf_convert_selector<wchar_t, u16s> {
2674 static size_t need_len(const wchar_t* src, size_t srcLen) {
2675 return utf_convert_selector<wchar_type, u16s>::need_len(to_w(src), srcLen);
2676 }
2677 static size_t convert(const wchar_t* src, size_t srcLen, u16s* dest) {
2678 return utf_convert_selector<wchar_type, u16s>::convert(to_w(src), srcLen, dest);
2679 }
2680};
2681
2682template<>
2683struct utf_convert_selector<wchar_t, u32s> {
2684 static size_t need_len(const wchar_t* src, size_t srcLen) {
2685 return utf_convert_selector<wchar_type, u32s>::need_len(to_w(src), srcLen);
2686 }
2687 static size_t convert(const wchar_t* src, size_t srcLen, u32s* dest) {
2688 return utf_convert_selector<wchar_type, u32s>::convert(to_w(src), srcLen, dest);
2689 }
2690};
2691
2706template<typename K, typename Impl>
2707class from_utf_convertable {
2708protected:
2709 from_utf_convertable() = default;
2710 using my_type = Impl;
2711 /*
2712 Эти методы должен реализовать класс-наследник.
2713 вызывается только при создании объекта
2714 init(size_t size)
2715 set_size(size_t size)
2716 */
2717public:
2718 template<typename O>
2719 requires(!std::is_same_v<O, K>)
2720 from_utf_convertable(simple_str<O> init) {
2721 using worker = utf_convert_selector<O, K>;
2722 Impl* d = static_cast<Impl*>(this);
2723 size_t len = init.length();
2724 if (!len)
2725 d->create_empty();
2726 else {
2727 size_t need = worker::need_len(init.symbols(), len);
2728 K* str = d->init(need);
2729 str[need] = 0;
2730 worker::convert(init.symbols(), len, str);
2731 }
2732 }
2733 template<typename O, typename I, bool M>
2734 requires(!std::is_same_v<O, K>)
2735 from_utf_convertable(const str_algs<O, simple_str<O>, I, M>& init) : from_utf_convertable(init.to_str()) {}
2736};
2737
2746template<typename From, typename To> requires (!std::is_same_v<From, To>)
2747struct expr_utf {
2748 using symb_type = To;
2749 using worker = utf_convert_selector<From, To>;
2750
2751 simple_str<From> source_;
2752
2753 size_t length() const noexcept {
2754 return worker::need_len(source_.symbols(), source_.length());
2755 }
2756 To* place(To* ptr) const noexcept {
2757 return ptr + worker::convert(source_.symbols(), source_.length(), ptr);
2758 }
2759};
2760
2773template<typename To, typename From> requires (!std::is_same_v<From, To>)
2775 return expr_utf<From, To>{from};
2776}
2777
2782template<typename A, typename K>
2783concept storable_str = requires {
2784 A::is_str_storable == true;
2785 std::is_same_v<typename A::symb_type, K>;
2786};
2787
2792template<typename A, typename K>
2793concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
2794
2799template<typename A, typename K>
2801
2844template<typename K, typename Impl, typename Allocator>
2845class str_storable : protected Allocator {
2846public:
2847 using my_type = Impl;
2848 using traits = ch_traits<K>;
2849 using allocator_t = Allocator;
2850
2851protected:
2856 allocator_t& allocator() {
2857 return *static_cast<Allocator*>(this);
2858 }
2859 const allocator_t& allocator() const {
2860 return *static_cast<const Allocator*>(this);
2861 }
2862
2863 using uni = unicode_traits<K>;
2864
2865 Impl& d() noexcept {
2866 return *static_cast<Impl*>(this);
2867 }
2868 const Impl& d() const noexcept {
2869 return *static_cast<const Impl*>(this);
2870 }
2871 template<typename... Args>
2872 requires std::is_constructible_v<allocator_t, Args...>
2873 explicit constexpr str_storable(size_t size, Args&&... args) : Allocator(std::forward<Args>(args)...) {
2874 if (size)
2875 d().init(size);
2876 else
2877 d().create_empty();
2878 }
2879
2880 template<StrType<K> From, typename Op1, typename... Args>
2881 requires std::is_constructible_v<allocator_t, Args...>
2882 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
2883 my_type result{std::forward<Args>(args)...};
2884 size_t len = f.length();
2885 if (len) {
2886 const K* source = f.symbols();
2887 K* destination = result.init(len);
2888 for (size_t l = 0; l < len; l++) {
2889 destination[l] = opMakeNeedCase(source[l]);
2890 }
2891 }
2892 return result;
2893 }
2894 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
2895 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
2896 // GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
2897 // So let's add a dummy template parameter to make the specialization for u8s right in the class.
2898 template<typename T, bool Dummy = true>
2899 struct ChangeCase {
2900 template<typename From, typename Op1, typename... Args>
2901 requires std::is_constructible_v<allocator_t, Args...>
2902 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
2903 my_type result{std::forward<Args>(args)...};
2904 size_t len = f.length();
2905 if (len) {
2906 opChangeCase(f.symbols(), len, result.init(len));
2907 }
2908 return result;
2909 }
2910 };
2911 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
2912 // For utf8 we will make a separate specification, since changing the register may change the length of the string
2913 template<bool Dummy>
2914 struct ChangeCase<u8s, Dummy> {
2915 template<typename From, typename Op1, typename... Args>
2916 requires std::is_constructible_v<allocator_t, Args...>
2917 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
2918 my_type result{std::forward<Args>(args)...};
2919 ;
2920 size_t len = f.length();
2921 if (len) {
2922 const K* ptr = f.symbols();
2923 K* pWrite = result.init(len);
2924
2925 const u8s* source = ptr;
2926 u8s* dest = pWrite;
2927 size_t newLen = opChangeCase(source, len, dest, len);
2928 if (newLen < len) {
2929 // Строка просто укоротилась
2930 // The string was simply shortened
2931 result.set_size(newLen);
2932 } else if (newLen > len) {
2933 // Строка не влезла в буфер.
2934 // The line did not fit into the buffer.
2935 size_t readed = static_cast<size_t>(source - ptr);
2936 size_t writed = static_cast<size_t>(dest - pWrite);
2937 pWrite = result.set_size(newLen);
2938 dest = pWrite + writed;
2939 opChangeCase(source, len - readed, dest, newLen - writed);
2940 }
2941 pWrite[newLen] = 0;
2942 }
2943 return result;
2944 }
2945 };
2946
2947public:
2948 using s_str = simple_str<K>;
2949 using s_str_nt = simple_str_nt<K>;
2950
2951 inline static constexpr bool is_str_storable = true;
2952
2959 template<typename... Args>
2960 requires std::is_constructible_v<allocator_t, Args...>
2961 constexpr str_storable(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2962 : Allocator(std::forward<Args>(args)...) {
2963 d().create_empty();
2964 }
2965
2974 template<typename... Args>
2975 requires std::is_constructible_v<allocator_t, Args...>
2976 constexpr str_storable(s_str other, Args&&... args) : Allocator(std::forward<Args>(args)...) {
2977 if (other.length()) {
2978 K* ptr = d().init(other.length());
2979 traits::copy(ptr, other.symbols(), other.length());
2980 ptr[other.length()] = 0;
2981 } else
2982 d().create_empty();
2983 }
2984
2994 template<typename... Args>
2995 requires std::is_constructible_v<allocator_t, Args...>
2996 constexpr str_storable(size_t repeat, s_str pattern, Args&&... args) : Allocator(std::forward<Args>(args)...) {
2997 size_t l = pattern.length(), allLen = l * repeat;
2998 if (allLen) {
2999 K* ptr = d().init(allLen);
3000 for (size_t i = 0; i < repeat; i++) {
3001 traits::copy(ptr, pattern.symbols(), l);
3002 ptr += l;
3003 }
3004 *ptr = 0;
3005 } else
3006 d().create_empty();
3007 }
3008
3018 template<typename... Args>
3019 requires std::is_constructible_v<allocator_t, Args...>
3020 str_storable(size_t count, K pad, Args&&... args) : Allocator(std::forward<Args>(args)...) {
3021 if (count) {
3022 K* str = d().init(count);
3023 traits::assign(str, count, pad);
3024 str[count] = 0;
3025 } else
3026 d().create_empty();
3027 }
3028
3042 template<typename... Args>
3043 requires std::is_constructible_v<allocator_t, Args...>
3044 constexpr str_storable(const StrExprForType<K> auto& expr, Args&&... args) : Allocator(std::forward<Args>(args)...) {
3045 size_t len = expr.length();
3046 if (len)
3047 *expr.place(d().init(len)) = 0;
3048 else
3049 d().create_empty();
3050 }
3051
3067 template<StrType<K> From, typename... Args>
3068 requires std::is_constructible_v<allocator_t, Args...>
3069 str_storable(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
3070 : Allocator(std::forward<Args>(args)...) {
3071
3072 auto findes = f.find_all(pattern, offset, maxCount);
3073 if (!findes.size()) {
3074 new (this) my_type{f};
3075 return;
3076 }
3077 size_t srcLen = f.length();
3078 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
3079
3080 if (!newSize) {
3081 new (this) my_type{};
3082 return;
3083 }
3084
3085 K* ptr = d().init(newSize);
3086 const K* src = f.symbols();
3087 size_t from = 0;
3088 for (const auto& s: findes) {
3089 size_t copyLen = s - from;
3090 if (copyLen) {
3091 traits::copy(ptr, src + from, copyLen);
3092 ptr += copyLen;
3093 }
3094 if (repl.len) {
3095 traits::copy(ptr, repl.str, repl.len);
3096 ptr += repl.len;
3097 }
3098 from = s + pattern.len;
3099 }
3100 srcLen -= from;
3101 if (srcLen) {
3102 traits::copy(ptr, src + from, srcLen);
3103 ptr += srcLen;
3104 }
3105 *ptr = 0;
3106 }
3107
3113 operator const K*() const noexcept {
3114 return d().symbols();
3115 }
3116
3124 s_str_nt to_nts(size_t from = 0) const {
3125 size_t len = d().length();
3126 if (from >= len) {
3127 from = len;
3128 }
3129 return {d().symbols() + from, len - from};
3130 }
3131
3137 operator s_str_nt() const {
3138 return {d().symbols(), d().length()};
3139 }
3140
3178 template<typename T, typename... Args>
3179 requires std::is_constructible_v<allocator_t, Args...>
3180 static my_type join(const T& strings, s_str delimeter, bool tail = false, bool skip_empty = false, Args&&... args) {
3181 my_type result(std::forward<Args>(args)...);
3182 if (strings.size()) {
3183 if (strings.size() == 1 && (!delimeter.length() || !tail)) {
3184 result = strings.front();
3185 } else {
3186 size_t commonLen = 0;
3187 for (const auto& t: strings) {
3188 size_t len = t.length();
3189 if (len > 0 || !skip_empty) {
3190 if (commonLen > 0) {
3191 commonLen += delimeter.len;
3192 }
3193 commonLen += len;
3194 }
3195 }
3196 commonLen += (tail && delimeter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimeter.len : 0);
3197 if (commonLen) {
3198 K* ptr = result.init(commonLen);
3199 K* write = ptr;
3200 for (const auto& t: strings) {
3201 size_t copyLen = t.length();
3202 if (delimeter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
3203 ch_traits<K>::copy(write, delimeter.str, delimeter.len);
3204 write += delimeter.len;
3205 }
3206 ch_traits<K>::copy(write, t.symbols(), copyLen);
3207 write += copyLen;
3208 }
3209 if (delimeter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
3210 ch_traits<K>::copy(write, delimeter.str, delimeter.len);
3211 write += delimeter.len;
3212 }
3213 *write = 0;
3214 } else {
3215 result.create_empty();
3216 }
3217 }
3218 }
3219 return result;
3220 }
3221
3229 template<StrType<K> From, typename... Args>
3230 requires std::is_constructible_v<allocator_t, Args...>
3231 static my_type uppered_only_ascii_from(const From& f, Args&&... args) {
3232 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
3233 }
3234
3242 template<StrType<K> From, typename... Args>
3243 requires std::is_constructible_v<allocator_t, Args...>
3244 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
3245 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
3246 }
3247
3259 template<StrType<K> From, typename... Args>
3260 requires std::is_constructible_v<allocator_t, Args...>
3261 static my_type uppered_from(const From& f, Args&&... args) {
3262 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
3263 }
3264
3276 template<StrType<K> From, typename... Args>
3277 requires std::is_constructible_v<allocator_t, Args...>
3278 static my_type lowered_from(const From& f, Args&&... args) {
3279 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
3280 }
3281
3297 template<StrType<K> From, typename... Args>
3298 requires std::is_constructible_v<allocator_t, Args...>
3299 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
3300 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
3301 }
3302};
3303
3308template<typename A>
3309concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
3310 { a.allocate(size) } -> std::same_as<void*>;
3311 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
3312};
3313
3314struct printf_selector {
3315 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
3316 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
3317 if constexpr (std::is_same_v<K, u8s>) {
3318 #ifndef _WIN32
3319 return std::snprintf(buffer, count, format, std::forward<T>(args)...);
3320 #else
3321 // Поддерживает позиционные параметры
3322 // Supports positional parameters
3323 return _sprintf_p(buffer, count, format, args...);
3324 #endif
3325 } else {
3326 #ifndef _WIN32
3327 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
3328 #else
3329 // Поддерживает позиционные параметры
3330 // Supports positional parameters
3331 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
3332 #endif
3333 }
3334 }
3335 template<typename K> requires (is_one_of_std_char_v<K>)
3336 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
3337 if constexpr (std::is_same_v<K, u8s>) {
3338 #ifndef _WIN32
3339 return std::vsnprintf(buffer, count, format, args);
3340 #else
3341 // Поддерживает позиционные параметры
3342 // Supports positional parameters
3343 return _vsprintf_p(buffer, count, format, args);
3344 #endif
3345 } else {
3346 #ifndef _WIN32
3347 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
3348 #else
3349 // Поддерживает позиционные параметры
3350 // Supports positional parameters
3351 return _vswprintf_p(buffer, count, format, args);
3352 #endif
3353 }
3354 }
3355};
3356
3357inline size_t grow2(size_t ret, size_t currentCapacity) {
3358 return ret <= currentCapacity ? ret : ret * 2;
3359}
3360
3399template<typename K, typename Impl>
3401public:
3402 using my_type = Impl;
3403
3404private:
3405 Impl& d() {
3406 return *static_cast<Impl*>(this);
3407 }
3408 const Impl& d() const {
3409 return *static_cast<const Impl*>(this);
3410 }
3411 size_t _len() const noexcept {
3412 return d().length();
3413 }
3414 const K* _str() const noexcept {
3415 return d().symbols();
3416 }
3417 using str_piece = simple_str<K>;
3418 using symb_type = K;
3419 using traits = ch_traits<K>;
3420 using uni = unicode_traits<K>;
3421 using uns_type = std::make_unsigned_t<K>;
3422
3423 template<typename Op>
3424 Impl& make_trim_op(const Op& op) {
3425 str_piece me = static_cast<str_piece>(d()), pos = op(me);
3426 if (me.length() != pos.length()) {
3427 if (me.symbols() != pos.symbols())
3428 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
3429 d().set_size(pos.length());
3430 }
3431 return d();
3432 }
3433
3434 template<auto Op>
3435 Impl& commonChangeCase() {
3436 size_t len = _len();
3437 if (len)
3438 Op(_str(), len, str());
3439 return d();
3440 }
3441 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
3442 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
3443 // GCC still does not allow full specialization of a nested class within a class,
3444 // only partial. Resources additive unused parameter template.
3445 template<typename T, bool Dummy = true>
3446 struct CaseTraits {
3447 static Impl& upper(Impl& obj) {
3448 return obj.template commonChangeCase<unicode_traits<K>::upper>();
3449 }
3450 static Impl& lower(Impl& obj) {
3451 return obj.template commonChangeCase<unicode_traits<K>::lower>();
3452 }
3453 };
3454
3455 template<auto Op>
3456 Impl& utf8CaseChange() {
3457 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3458 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
3459 size_t len = _len();
3460 if (len) {
3461 u8s* writePos = str();
3462 const u8s *startData = writePos, *readPos = writePos;
3463 size_t newLen = Op(readPos, len, writePos, len);
3464 if (newLen < len) {
3465 // Строка просто укоротилась
3466 // The string was simply shortened
3467 d().set_size(newLen);
3468 } else if (newLen > len) {
3469 // Строка не влезла в буфер.
3470 // The line did not fit into the buffer.
3471 size_t readed = static_cast<size_t>(readPos - startData);
3472 size_t writed = static_cast<size_t>(writePos - startData);
3473 d().set_size(newLen);
3474 startData = str(); // при изменении размера могло изменится | may change when resizing
3475 readPos = startData + readed;
3476 writePos = const_cast<u8s*>(startData) + writed;
3477 Op(readPos, len - readed, writePos, newLen - writed);
3478 }
3479 }
3480 return d();
3481 }
3482 template<bool Dummy>
3483 struct CaseTraits<u8s, Dummy> {
3484 static Impl& upper(Impl& obj) {
3485 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
3486 }
3487 static Impl& lower(Impl& obj) {
3488 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
3489 }
3490 };
3491
3492 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
3493 Impl& makeTrim(T&& pattern) {
3494 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
3495 }
3496
3497 template<TrimSides S, bool withSpaces>
3498 Impl& makeTrim(str_piece pattern) {
3499 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
3500 }
3501
3502public:
3509 K* str() noexcept {
3510 return d().str();
3511 }
3512
3518 explicit operator K*() noexcept {
3519 return str();
3520 }
3521
3527 Impl& trim() {
3528 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
3529 }
3530
3536 Impl& trim_left() {
3537 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
3538 }
3539
3545 Impl& trim_right() {
3546 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
3547 }
3548
3556 template<typename T, size_t N = const_lit_for<K, T>::Count>
3557 requires is_const_pattern<N>
3558 Impl& trim(T&& pattern) {
3559 return makeTrim<TrimSides::TrimAll, false>(pattern);
3560 }
3561
3569 template<typename T, size_t N = const_lit_for<K, T>::Count>
3570 requires is_const_pattern<N>
3571 Impl& trim_left(T&& pattern) {
3572 return makeTrim<TrimSides::TrimLeft, false>(pattern);
3573 }
3574
3582 template<typename T, size_t N = const_lit_for<K, T>::Count>
3583 requires is_const_pattern<N>
3584 Impl& trim_right(T&& pattern) {
3585 return makeTrim<TrimSides::TrimRight, false>(pattern);
3586 }
3587
3595 template<typename T, size_t N = const_lit_for<K, T>::Count>
3596 requires is_const_pattern<N>
3597 Impl& trim_with_spaces(T&& pattern) {
3598 return makeTrim<TrimSides::TrimAll, true>(pattern);
3599 }
3600
3608 template<typename T, size_t N = const_lit_for<K, T>::Count>
3609 requires is_const_pattern<N>
3610 Impl& trim_left_with_spaces(T&& pattern) {
3611 return makeTrim<TrimSides::TrimLeft, true>(pattern);
3612 }
3613
3621 template<typename T, size_t N = const_lit_for<K, T>::Count>
3622 requires is_const_pattern<N>
3623 Impl& trim_right_with_wpaces(T&& pattern) {
3624 return makeTrim<TrimSides::TrimRight, true>(pattern);
3625 }
3626
3634 Impl& trim(str_piece pattern) {
3635 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
3636 }
3637
3645 Impl& trim_left(str_piece pattern) {
3646 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
3647 }
3648
3656 Impl& trim_right(str_piece pattern) {
3657 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
3658 }
3659
3667 Impl& trim_with_spaces(str_piece pattern) {
3668 return makeTrim<TrimSides::TrimAll, true>(pattern);
3669 }
3670
3678 Impl& trim_left_with_spaces(str_piece pattern) {
3679 return makeTrim<TrimSides::TrimLeft, true>(pattern);
3680 }
3681
3689 Impl& trim_right_with_spaces(str_piece pattern) {
3690 return makeTrim<TrimSides::TrimRight, true>(pattern);
3691 }
3692
3699 K* ptr = str();
3700 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
3701 K s = *ptr;
3702 if (isAsciiLower(s))
3703 *ptr = s & ~0x20;
3704 }
3705 return d();
3706 }
3707
3714 K* ptr = str();
3715 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
3716 K s = *ptr;
3717 if (isAsciiUpper(s))
3718 *ptr = s | 0x20;
3719 }
3720 return d();
3721 }
3722
3732 Impl& upper() {
3733 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3734 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
3735 return CaseTraits<K>::upper(d());
3736 }
3737
3747 Impl& lower() {
3748 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3749 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
3750 return CaseTraits<K>::lower(d());
3751 }
3752
3753private:
3754 template<typename T>
3755 Impl& changeImpl(size_t from, size_t len, T expr) {
3756 size_t myLen = _len();
3757 if (from > myLen) {
3758 from = myLen;
3759 }
3760 if (from + len > myLen) {
3761 len = myLen - from;
3762 }
3763 K* buffer = str();
3764 size_t otherLen = expr.length();
3765 if (len == otherLen) {
3766 expr.place(buffer + from);
3767 } else {
3768 size_t tailLen = myLen - from - len;
3769 if (len > otherLen) {
3770 expr.place(buffer + from);
3771 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
3772 d().set_size(myLen - (len - otherLen));
3773 } else {
3774 buffer = d().set_size(myLen + otherLen - len);
3775 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
3776 expr.place(buffer + from);
3777 }
3778 }
3779 return d();
3780 }
3781
3782 template<typename T>
3783 Impl& appendImpl(T expr) {
3784 if (size_t len = expr.length(); len) {
3785 size_t size = _len();
3786 expr.place(d().set_size(size + len) + size);
3787 }
3788 return d();
3789 }
3790
3791 template<typename T>
3792 Impl& appendFromImpl(size_t pos, T expr) {
3793 if (pos > _len())
3794 pos = _len();
3795 if (size_t len = expr.length())
3796 expr.place(d().set_size(pos + len) + pos);
3797 else
3798 d().set_size(pos);
3799 return d();
3800 }
3801
3802public:
3803 inline static constexpr bool is_str_mutable = true;
3812 Impl& append(str_piece other) {
3813 return appendImpl<str_piece>(other);
3814 }
3815
3823 template<StrExprForType<K> A>
3824 Impl& append(const A& expr) {
3825 return appendImpl<const A&>(expr);
3826 }
3827
3835 Impl& operator+=(str_piece other) {
3836 return appendImpl<str_piece>(other);
3837 }
3838
3846 template<StrExprForType<K> A>
3847 Impl& operator+=(const A& expr) {
3848 return appendImpl<const A&>(expr);
3849 }
3850
3864 Impl& append_in(size_t pos, str_piece other) {
3865 return appendFromImpl<str_piece>(pos, other);
3866 }
3867
3881 template<StrExprForType<K> A>
3882 Impl& append_in(size_t pos, const A& expr) {
3883 return appendFromImpl<const A&>(pos, expr);
3884 }
3885
3897 Impl& change(size_t from, size_t len, str_piece other) {
3898 return changeImpl<str_piece>(from, len, other);
3899 }
3900
3912 template<StrExprForType<K> A>
3913 Impl& change(size_t from, size_t len, const A& expr) {
3914 return changeImpl<const A&>(from, len, expr);
3915 }
3916
3926 Impl& insert(size_t to, str_piece other) {
3927 return changeImpl<str_piece>(to, 0, other);
3928 }
3929
3939 template<StrExprForType<K> A>
3940 Impl& insert(size_t to, const A& expr) {
3941 return changeImpl<const A&>(to, 0, expr);
3942 }
3943
3953 Impl& remove(size_t from, size_t len) {
3954 return changeImpl<const empty_expr<K>&>(from, len, {});
3955 }
3956
3964 Impl& prepend(str_piece other) {
3965 return changeImpl<str_piece>(0, 0, other);
3966 }
3967
3975 template<StrExprForType<K> A>
3976 Impl& prepend(const A& expr) {
3977 return changeImpl<const A&>(0, 0, expr);
3978 }
3979
3993 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
3994 offset = d().find(pattern, offset);
3995 if (offset == str::npos) {
3996 return d();
3997 }
3998 if (!maxCount)
3999 maxCount--;
4000 size_t replLength = repl.length(), patternLength = pattern.length();
4001
4002 if (patternLength == replLength) {
4003 // Заменяем inplace на подстроку такой же длины
4004 // Replace inplace with a substring of the same length
4005 K* ptr = str();
4006 for (size_t i = 0; i < maxCount; i++) {
4007 traits::copy(ptr + offset, repl.symbols(), replLength);
4008 offset = d().find(pattern, offset + replLength);// replLength == patternLength
4009 if (offset == str::npos)
4010 break;
4011 }
4012 } else if (patternLength > replLength) {
4013 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
4014 // Replace with a shorter piece, the length of the text will decrease, go from left to right
4015 K* ptr = str();
4016 traits::copy(ptr + offset, repl.symbols(), replLength);
4017 size_t posWrite = offset + replLength;
4018 maxCount--;
4019 offset += patternLength;
4020
4021 for (size_t i = 0; i < maxCount; i++) {
4022 size_t idx = d().find(pattern, offset);
4023 if (idx == str::npos)
4024 break;
4025 size_t lenOfPiece = idx - offset;
4026 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
4027 posWrite += lenOfPiece;
4028 traits::copy(ptr + posWrite, repl.symbols(), replLength);
4029 posWrite += replLength;
4030 offset = idx + patternLength;
4031 }
4032 size_t tailLen = _len() - offset;
4033 traits::move(ptr + posWrite, ptr + offset, tailLen);
4034 d().set_size(posWrite + tailLen);
4035 } else {
4036 struct replace_grow_helper {
4037 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
4038 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
4039 my_type& source;
4040 const str_piece pattern;
4041 const str_piece repl;
4042 size_t maxCount;
4043 const size_t delta;
4044 size_t all_delta{};
4045 K* reserve_for_copy{};
4046 size_t end_of_piece{};
4047 size_t total_length{};
4048
4049 void replace(size_t offset) {
4050 size_t finded[16] = {source.find(pattern, offset)};
4051 if (finded[0] == str::npos) {
4052 return;
4053 }
4054 maxCount--;
4055 offset = finded[0] + pattern.length();
4056 all_delta += delta;
4057 size_t idx = 1;
4058 for (size_t end = std::min(maxCount, std::size(finded)); idx < end; idx++, maxCount--) {
4059 finded[idx] = source.find(pattern, offset);
4060 if (finded[idx] == str::npos) {
4061 break;
4062 }
4063 offset = finded[idx] + pattern.length();
4064 all_delta += delta;
4065 }
4066 bool needMore = maxCount > 0 && idx == std::size(finded) && offset < source.length() - pattern.length();
4067 if (needMore) {
4068 replace(offset); // здесь произведутся замены в оставшемся хвосте | replacements will be made here in the remaining tail
4069 }
4070 // Теперь делаем свои замены
4071 // Now we make our replacements
4072 if (!reserve_for_copy) {
4073 // Только начинаем
4074 // Just getting started
4075 end_of_piece = source.length();
4076 total_length = end_of_piece + all_delta;
4077 reserve_for_copy = source.alloc_for_copy(total_length);
4078 }
4079 K* dst_start = reserve_for_copy;
4080 const K* src_start = source.symbols();
4081 while(idx-- > 0) {
4082 size_t pos = finded[idx] + pattern.length();
4083 size_t lenOfPiece = end_of_piece - pos;
4084 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
4085 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
4086 all_delta -= delta;
4087 end_of_piece = finded[idx];
4088 }
4089 if (!all_delta && reserve_for_copy != src_start) {
4090 ch_traits<K>::copy(dst_start, src_start, finded[0]);
4091 }
4092 }
4093 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
4094 helper.replace(offset);
4095 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
4096 }
4097 return d();
4098 }
4099
4115 template<StrType<K> From>
4116 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
4117 if (pattern.length() >= repl.length()) {
4118 K* dst = d().reserve_no_preserve(f.length());
4119 const K* src = f.symbols();
4120 size_t delta = 0;
4121 if (maxCount == 0) {
4122 maxCount--;
4123 }
4124 size_t src_length = f.length(), start = 0;
4125 while (maxCount--) {
4126 offset = f.find(pattern, offset);
4127 if (offset == str::npos) {
4128 break;
4129 }
4130 size_t piece_len = offset - start;
4131 if (piece_len) {
4132 ch_traits<K>::copy(dst, src + start, piece_len);
4133 dst += piece_len;
4134 }
4135 if (repl.length()) {
4136 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
4137 dst += repl.length();
4138 }
4139 delta += pattern.length() - repl.length();
4140 offset += pattern.length();
4141 start = offset;
4142 }
4143 if (start < src_length) {
4144 ch_traits<K>::copy(dst, src + start, src_length - start);
4145 }
4146 d().set_size(src_length - delta);
4147 } else {
4148 d() = f;
4149 replace(pattern, repl, offset, maxCount);
4150 }
4151 return d();
4152 }
4153
4177 template<typename Op>
4178 Impl& fill(size_t from, const Op& fillFunction) {
4179 size_t size = _len();
4180 if (from > size)
4181 from = size;
4182 size_t capacity = d().capacity();
4183 K* ptr = str();
4184 capacity -= from;
4185 for (;;) {
4186 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
4187 if (capacity >= needSize) {
4188 d().set_size(from + needSize);
4189 break;
4190 }
4191 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
4192 capacity = d().capacity() - from;
4193 }
4194 return d();
4195 }
4196
4204 template<typename Op>
4205 requires std::is_invocable_v<Op, K*, size_t>
4206 Impl& operator<<(const Op& fillFunction) {
4207 return fill(0, fillFunction);
4208 }
4209
4217 template<typename Op>
4218 requires std::is_invocable_v<Op, K*, size_t>
4219 Impl& operator<<=(const Op& fillFunction) {
4220 return fill(_len(), fillFunction);
4221 }
4222
4230 template<typename Op>
4231 requires std::is_invocable_v<Op, my_type&>
4232 Impl& operator<<(const Op& fillFunction) {
4233 fillFunction(d());
4234 return d();
4235 }
4236
4250 template<typename... T> requires (is_one_of_std_char_v<K>)
4251 Impl& printf_from(size_t from, const K* format, T&&... args) {
4252 size_t size = _len();
4253 if (from > size)
4254 from = size;
4255 size_t capacity = d().capacity();
4256 K* ptr = str();
4257 capacity -= from;
4258
4259 int result = 0;
4260 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
4261 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
4262 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
4263 // 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
4264 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
4265 // Therefore, for them you need to stupidly increase the buffer at random until it fits
4266 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
4267 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
4268 if (result > (int)capacity) {
4269 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
4270 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
4271 }
4272 } else {
4273 for (;;) {
4274 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
4275 if (result < 0) {
4276 // Не хватило буфера или ошибка конвертации.
4277 // Попробуем увеличить буфер в два раза
4278 // Not enough buffer or conversion error.
4279 // Let's try to double the buffer
4280 capacity *= 2;
4281 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
4282 } else
4283 break;
4284 }
4285 }
4286 if (result < 0)
4287 d().set_size(static_cast<size_t>(traits::length(_str())));
4288 else
4289 d().set_size(from + result);
4290 return d();
4291 }
4292
4304 template<typename... T> requires (is_one_of_std_char_v<K>)
4305 Impl& printf(const K* format, T&&... args) {
4306 return printf_from(0, format, std::forward<T>(args)...);
4307 }
4308
4320 template<typename... T> requires (is_one_of_std_char_v<K>)
4321 Impl& append_printf(const K* format, T&&... args) {
4322 return printf_from(_len(), format, std::forward<T>(args)...);
4323 }
4324
4325 struct writer {
4326 my_type* store;
4327 K* ptr;
4328 const K* end;
4329 size_t max_write;
4330 size_t writed = 0;
4331 inline static K pad;
4332 K& operator*() const {
4333 return *ptr;
4334 }
4335 writer& operator++() {
4336 if (writed < max_write) {
4337 ++ptr;
4338 if (ptr == end) {
4339 size_t l = ptr - store->begin();
4340 store->set_size(l);
4341 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
4342 end = store->end();
4343 }
4344 } else {
4345 ptr = &pad;
4346 }
4347 return *this;
4348 }
4349 writer operator++(int) {
4350 auto ret = *this;
4351 operator++();
4352 return ret;
4353 }
4354
4355 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
4356 writer() = default;
4357 writer(const writer&) = delete;
4358 writer& operator=(const writer&) noexcept = delete;
4359 writer(writer&&) noexcept = default;
4360 writer& operator=(writer&&) noexcept = default;
4361 using difference_type = int;
4362 };
4377 template<typename... T> requires (is_one_of_std_char_v<K>)
4378 Impl& format_from(size_t from, const FmtString<K, T...>& format, T&&... args) {
4379 size_t size = _len();
4380 if (from > size)
4381 from = size;
4382 size_t capacity = d().capacity();
4383 K* ptr = str();
4384
4385 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
4386 std::forward<decltype(format)>(format), std::forward<T>(args)...);
4387 d().set_size(result.ptr - _str());
4388 return d();
4389 }
4390
4406 template<typename... T> requires (is_one_of_std_char_v<K>)
4407 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
4408 size_t size = _len();
4409 if (from > size)
4410 from = size;
4411 size_t capacity = d().capacity();
4412 K* ptr = str();
4413
4414 if constexpr (std::is_same_v<K, u8s>) {
4415 auto result = std::vformat_to(
4416 writer{d(), ptr + from, ptr + capacity, max_write},
4417 std::basic_string_view<K>{format.symbols(), format.length()},
4418 std::make_format_args(args...));
4419 d().set_size(result.ptr - _str());
4420 } else {
4421 auto result = std::vformat_to(
4422 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
4423 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
4424 std::make_wformat_args(std::forward<T>(args)...));
4425 d().set_size(result.ptr - _str());
4426 }
4427 return d();
4428 }
4429
4441 template<typename... T> requires (is_one_of_std_char_v<K>)
4442 Impl& format(const FmtString<K, T...>& pattern, T&&... args) {
4443 return format_from(0, pattern, std::forward<T>(args)...);
4444 }
4445
4457 template<typename... T> requires (is_one_of_std_char_v<K>)
4458 Impl& append_formatted(const FmtString<K, T...>& format, T&&... args) {
4459 return format_from(_len(), format, std::forward<T>(args)...);
4460 }
4461
4473 template<typename... T> requires (is_one_of_std_char_v<K>)
4474 Impl& vformat(str_piece format, T&&... args) {
4475 return vformat_from(0, -1, format, std::forward<T>(args)...);
4476 }
4477
4489 template<typename... T> requires (is_one_of_std_char_v<K>)
4490 Impl& append_vformatted(str_piece format, T&&... args) {
4491 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
4492 }
4493
4507 template<typename... T> requires (is_one_of_std_char_v<K>)
4508 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
4509 return vformat_from(0, max_write, format, std::forward<T>(args)...);
4510 }
4511
4525 template<typename... T> requires (is_one_of_std_char_v<K>)
4526 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
4527 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
4528 }
4529
4539 template<typename Op, typename... Args>
4540 Impl& with(const Op& fillFunction, Args&&... args) {
4541 fillFunction(d(), std::forward<Args>(args)...);
4542 return d();
4543 }
4544};
4545
4546template<typename K>
4547struct SharedStringData {
4548 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
4549
4550 SharedStringData() {
4551 ref_ = 1;
4552 }
4553 K* str() const {
4554 return (K*)(this + 1);
4555 }
4556 void incr() {
4557 ref_.fetch_add(1, std::memory_order_relaxed);
4558 }
4559 void decr(Allocatorable auto& allocator) {
4560 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
4561 if (val == 1) {
4562 allocator.deallocate(this);
4563 }
4564 }
4565 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
4566 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
4567 return new (allocator.allocate(size)) SharedStringData();
4568 }
4569 static SharedStringData<K>* from_str(const K* p) {
4570 return (SharedStringData<K>*)p - 1;
4571 }
4572 K* place(K* p, size_t len) {
4573 ch_traits<K>::copy(p, str(), len);
4574 return p + len;
4575 }
4576};
4577
4578// Дефолтный аллокатор для строк, может работать статически
4579// Default allocator for strings, can work statically
4580class string_common_allocator {
4581public:
4582 void* allocate(size_t bytes) {
4583 return new char[bytes];
4584 }
4585 void deallocate(void* address) noexcept {
4586 delete [] static_cast<char*>(address);
4587 }
4588};
4589
4590string_common_allocator default_string_allocator_selector(...);
4591// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
4592// объявите функцию
4593// ваш_тип_аллокатора default_string_allocator_selector(int);
4594// If you want to set your default allocator for strings, before including sstring.h
4595// declare a function
4596// your_allocator_type default_string_allocator_selector(int);
4597using allocator_string = decltype(default_string_allocator_selector(int(0)));
4598
4599template<typename K, Allocatorable Allocator>
4600class sstring;
4601
4602/*
4603* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
4604* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
4605* Укажем ему явно.
4606* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
4607* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
4608* Let's tell him explicitly.
4609*/
4610
4631template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
4632class decl_empty_bases lstring :
4633 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
4634 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
4635 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
4636 public from_utf_convertable<K, lstring<K, N, forShared, Allocator>> {
4637public:
4638 using symb_type = K;
4639 using my_type = lstring<K, N, forShared, Allocator>;
4640 using allocator_t = Allocator;
4641
4642 enum : size_t {
4644 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
4645 };
4646
4647protected:
4648 enum : size_t {
4649 extra = forShared ? sizeof(SharedStringData<K>) : 0,
4650 };
4651
4652 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
4653 using base_storable = str_storable<K, my_type, Allocator>;
4654 using base_mutable = str_mutable<K, my_type>;
4655 using base_utf = from_utf_convertable<K, my_type>;
4656 using traits = ch_traits<K>;
4657
4658 friend base_storable;
4659 friend base_mutable;
4660 friend base_utf;
4661 friend class sstring<K, Allocator>;
4662
4663 K* data_;
4664 // Поле не должно инициализироваться, так как может устанавливаться в базовых конструкторах
4665 // The field should not be initialized, as it can be set in base constructors
4666 size_t size_;
4667
4668 union {
4669 // Поле не должно инициализироваться, так как может устанавливаться в базовых конструкторах
4670 // The field should not be initialized, as it can be set in base constructors
4671 size_t capacity_;
4672 K local_[LocalCapacity + 1];
4673 };
4674
4675 void create_empty() {
4676 data_ = local_;
4677 size_ = 0;
4678 local_[0] = 0;
4679 }
4680 static size_t calc_capacity(size_t s) {
4681 size_t real_need = (s + 1) * sizeof(K) + extra;
4682 size_t aligned_alloced = (real_need + alignof(std::max_align_t) - 1) / alignof(std::max_align_t) * alignof(std::max_align_t);
4683 return (aligned_alloced - extra) / sizeof(K) - 1;
4684 }
4685
4686 K* init(size_t s) {
4687 size_ = s;
4688 if (size_ > LocalCapacity) {
4689 s = calc_capacity(s);
4690 data_ = alloc_place(s);
4691 capacity_ = s;
4692 } else {
4693 data_ = local_;
4694 }
4695 return str();
4696 }
4697 // Методы для себя | Methods for yourself
4698 bool is_alloced() const noexcept {
4699 return data_ != local_;
4700 }
4701
4702 void dealloc() {
4703 if (is_alloced()) {
4704 base_storable::allocator().deallocate(to_real_address(data_));
4705 data_ = local_;
4706 }
4707 }
4708
4709 static K* to_real_address(void* ptr) {
4710 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
4711 }
4712 static K* from_real_address(void* ptr) {
4713 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
4714 }
4715
4716 K* alloc_place(size_t newSize) {
4717 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
4718 }
4719 // Вызывается при replace, когда меняют на более длинную замену
4720 // Called on replace when changing to a longer replacement
4721 K* alloc_for_copy(size_t newSize) {
4722 if (capacity() >= newSize) {
4723 // Замена войдёт в текущий буфер
4724 // Replacement will go into the current buffer
4725 return data_;
4726 }
4727 return alloc_place(calc_capacity(newSize));
4728 }
4729 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
4730 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
4731 void set_from_copy(K* ptr, size_t newSize) {
4732 if (ptr != data_) {
4733 // Да, копировали в новый буфер
4734 // Yes, copied to a new buffer
4735 dealloc();
4736 data_ = ptr;
4737 capacity_ = calc_capacity(newSize);
4738 }
4739 size_ = newSize;
4740 data_[newSize] = 0;
4741 }
4742
4743public:
4744 using base_storable::base_storable;
4745 using base_utf::base_utf;
4746
4747 lstring() = default;
4748
4749 ~lstring() {
4750 dealloc();
4751 }
4752
4759 lstring(const my_type& other) : base_storable(other.allocator()) {
4760 if (other.size_) {
4761 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
4762 }
4763 }
4764
4772 template<typename... Args>
4773 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
4774 lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4775 if (other.size_) {
4776 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
4777 }
4778 }
4779
4788 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
4789 requires std::is_constructible_v<allocator_t, Args...>
4790 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4791 if constexpr (I > 1) {
4792 K* ptr = init(I - 1);
4793 traits::copy(ptr, value, I - 1);
4794 ptr[I - 1] = 0;
4795 } else
4796 create_empty();
4797 }
4798
4804 lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
4805 if (other.size_) {
4806 size_ = other.size_;
4807 if (other.is_alloced()) {
4808 data_ = other.data_;
4809 capacity_ = other.capacity_;
4810 } else {
4811 data_ = local_;
4812 traits::copy(local_, other.local_, size_ + 1);
4813 }
4814 other.data_ = other.local_;
4815 other.size_ = 0;
4816 other.local_[0] = 0;
4817 }
4818 }
4819
4827 template<typename Op, typename... Args>
4828 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
4829 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4830 this->operator<<(op);
4831 }
4832
4833 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
4834 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
4835 // 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
4836 // Therefore, we implement the copy and move assignment separately
4837
4846 my_type& operator=(const my_type& other) {
4847 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
4848 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
4849 if (&other != this) {
4850 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
4851 size_ = other.size_;
4852 }
4853 return *this;
4854 }
4855
4863 my_type& operator=(my_type&& other) noexcept {
4864 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
4865 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
4866 if (&other != this) {
4867 dealloc();
4868 if (other.is_alloced()) {
4869 data_ = other.data_;
4870 capacity_ = other.capacity_;
4871 } else {
4872 traits::copy(data_, other.local_, other.size_ + 1);
4873 }
4874 base_storable::allocator() = std::move(other.allocator());
4875 size_ = other.size_;
4876 other.create_empty();
4877 }
4878 return *this;
4879 }
4880
4881 my_type& assign(const K* other, size_t len) {
4882 if (len) {
4883 bool isIntersect = other >= data_ && other + len <= data_ + size_;
4884 if (isIntersect) {
4885 // Особый случай, нам пытаются присвоить кусок нашей же строки.
4886 // Просто переместим текст в буфере, и установим новый размер
4887 // A special case, they are trying to assign us a piece of our own string.
4888 // Just move the text in the buffer and set a new size
4889 if (other > data_) {
4890 traits::move(data_, other, len);
4891 }
4892 } else {
4893 traits::copy(reserve_no_preserve(len), other, len);
4894 }
4895 }
4896 size_ = len;
4897 data_[size_] = 0;
4898 return *this;
4899 }
4908 my_type& operator=(simple_str<K> other) {
4909 return assign(other.str, other.len);
4910 }
4911
4919 template<typename T, size_t S = const_lit_for<K, T>::Count>
4920 my_type& operator=(T&& other) {
4921 return assign(other, S - 1);
4922 }
4923
4933 my_type& operator=(const StrExprForType<K> auto& expr) {
4934 size_t newLen = expr.length();
4935 if (newLen) {
4936 expr.place(reserve_no_preserve(newLen));
4937 }
4938 size_ = newLen;
4939 data_[size_] = 0;
4940 return *this;
4941 }
4942
4943 size_t length() const noexcept {
4944 return size_;
4945 }
4946
4947 const K* symbols() const noexcept {
4948 return data_;
4949 }
4950
4951 K* str() noexcept {
4952 return data_;
4953 }
4954
4955 bool is_empty() const noexcept {
4956 return size_ == 0;
4957 }
4958
4959 bool empty() const noexcept {
4960 return size_ == 0;
4961 }
4962
4963 size_t capacity() const noexcept {
4964 return is_alloced() ? capacity_ : LocalCapacity;
4965 }
4966
4978 K* reserve_no_preserve(size_t newSize) {
4979 if (newSize > capacity()) {
4980 newSize = calc_capacity(newSize);
4981 K* newData = alloc_place(newSize);
4982 dealloc();
4983 data_ = newData;
4984 capacity_ = newSize;
4985 }
4986 return data_;
4987 }
4988
5000 K* reserve(size_t newSize) {
5001 if (newSize > capacity()) {
5002 newSize = calc_capacity(newSize);
5003 K* newData = alloc_place(newSize);
5004 traits::copy(newData, data_, size_);
5005 dealloc();
5006 data_ = newData;
5007 capacity_ = newSize;
5008 }
5009 return data_;
5010 }
5011
5023 K* set_size(size_t newSize) {
5024 size_t cap = capacity();
5025 if (newSize > cap) {
5026 size_t needPlace = newSize;
5027 if (needPlace < (cap + 1) * 2) {
5028 needPlace = (cap + 1) * 2 - 1;
5029 }
5030 reserve(needPlace);
5031 }
5032 size_ = newSize;
5033 data_[newSize] = 0;
5034 return data_;
5035 }
5036
5040 bool is_local() const noexcept {
5041 return !is_alloced();
5042 }
5043
5050 size_t cap = capacity();
5051 for (size_t i = 0; i < cap; i++) {
5052 if (data_[i] == 0) {
5053 size_ = i;
5054 return;
5055 }
5056 }
5057 size_ = cap;
5058 data_[size_] = 0;
5059 }
5060
5067 size_t need_capacity = calc_capacity(size_);
5068 if (is_alloced() && capacity_ > need_capacity) {
5069 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
5070 traits::copy(newData, data_, size_ + 1);
5071 base_storable::allocator().deallocate(to_real_address(data_));
5072 data_ = newData;
5073
5074 if (size_ > LocalCapacity) {
5075 capacity_ = need_capacity;
5076 }
5077 }
5078 }
5079
5080 void clear() {
5081 set_size(0);
5082 }
5083
5084 void reset() {
5085 dealloc();
5086 local_[0] = 0;
5087 size_ = 0;
5088 }
5089};
5090
5091template<size_t N = 15>
5092using lstringa = lstring<u8s, N>;
5093template<size_t N = 15>
5094using lstringw = lstring<wchar_t, N>;
5095template<size_t N = 15>
5096using lstringu = lstring<u16s, N>;
5097template<size_t N = 15>
5098using lstringuu = lstring<u32s, N>;
5099
5100template<size_t N = 15>
5101using lstringsa = lstring<u8s, N, true>;
5102template<size_t N = 15>
5103using lstringsw = lstring<wchar_t, N, true>;
5104template<size_t N = 15>
5105using lstringsu = lstring<u16s, N, true>;
5106template<size_t N = 15>
5107using lstringsuu = lstring<u32s, N, true>;
5108
5109
5110template<typename T, typename K = typename const_lit<T>::symb_type>
5111auto getLiteralType(T&&) {
5112 return K{};
5113};
5114
5115template<size_t Arch, size_t L>
5116inline constexpr const size_t _local_count = 0;
5117
5118template<>
5119inline constexpr const size_t _local_count<8, 1> = 23;
5120template<>
5121inline constexpr const size_t _local_count<8, 2> = 15;
5122template<>
5123inline constexpr const size_t _local_count<8, 4> = 7;
5124template<>
5125inline constexpr const size_t _local_count<4, 1> = 15;
5126template<>
5127inline constexpr const size_t _local_count<4, 2> = 11;
5128template<>
5129inline constexpr const size_t _local_count<4, 4> = 5;
5130
5131template<typename T>
5132constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
5133
5186template<typename K, Allocatorable Allocator = allocator_string>
5187class decl_empty_bases sstring :
5188 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
5189 public str_storable<K, sstring<K, Allocator>, Allocator>,
5190 public from_utf_convertable<K, sstring<K, Allocator>> {
5191public:
5192 using symb_type = K;
5193 using uns_type = std::make_unsigned_t<K>;
5194 using my_type = sstring<K, Allocator>;
5195 using allocator_t = Allocator;
5196
5197 enum { LocalCount = local_count<K> };
5198
5199protected:
5200 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
5201 using base_storable = str_storable<K, my_type, Allocator>;
5202 using base_utf = from_utf_convertable<K, my_type>;
5203 using traits = ch_traits<K>;
5204 using uni = unicode_traits<K>;
5205
5206 friend base_storable;
5207 friend base_utf;
5208
5209 enum Types { Local, Constant, Shared };
5210
5211 union {
5212 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
5213 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
5214 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
5215 // и в итоге после символов строки получается 0, как и надо!
5216 // When we have a short string, it lies in the object itself, and in localRemain
5217 // writes how many more characters can be entered. When a line takes up everything
5218 // possible location, then localRemain becomes 0, type in this case is also 0,
5219 // and as a result, after the characters of the line we get 0, as it should!
5220 struct {
5221 K buf_[LocalCount]; // Локальный буфер строки | Local line buffer
5222 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
5223 uns_type type_ : 2;
5224 };
5225 struct {
5226 union {
5227 // Указатель на конcтантную строку | Pointer to a constant string
5228 const K* cstr_;
5229 // Указатель на строку, перед которой лежит SharedStringData
5230 // Pointer to the string preceded by SharedStringData
5231 const K* sstr_;
5232 };
5233 size_t bigLen_; // Длина не локальной строки | Non-local string length
5234 };
5235 };
5236
5237 void create_empty() {
5238 type_ = Local;
5239 localRemain_ = LocalCount;
5240 buf_[0] = 0;
5241 }
5242 K* init(size_t s) {
5243 if (s > LocalCount) {
5244 type_ = Shared;
5245 localRemain_ = 0;
5246 bigLen_ = s;
5247 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
5248 return (K*)sstr_;
5249 } else {
5250 type_ = Local;
5251 localRemain_ = LocalCount - s;
5252 return buf_;
5253 }
5254 }
5255
5256 K* set_size(size_t newSize) {
5257 // Вызывается при создании строки при необходимости изменить размер.
5258 // Других ссылок на shared buffer нет.
5259 // Called when a string is created and needs to be resized.
5260 // There are no other references to the shared buffer.
5261 size_t size = length();
5262 if (newSize != size) {
5263 if (type_ == Constant) {
5264 bigLen_ = newSize;
5265 } else {
5266 if (newSize <= LocalCount) {
5267 if (type_ == Shared) {
5268 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
5269 traits::copy(buf_, sstr_, newSize);
5270 str_buf->decr(base_storable::allocator());
5271 }
5272 type_ = Local;
5273 localRemain_ = LocalCount - newSize;
5274 } else {
5275 if (type_ == Shared) {
5276 if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
5277 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
5278 traits::copy(newStr, sstr_, newSize);
5279 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
5280 sstr_ = newStr;
5281 }
5282 } else if (type_ == Local) {
5283 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
5284 if (size)
5285 traits::copy(newStr, buf_, size);
5286 sstr_ = newStr;
5287 type_ = Shared;
5288 localRemain_ = 0;
5289 }
5290 bigLen_ = newSize;
5291 }
5292 }
5293 }
5294 K* str = type_ == Local ? buf_ : (K*)sstr_;
5295 str[newSize] = 0;
5296 return str;
5297 }
5298
5299public:
5300 using base_storable::base_storable;
5301 using base_utf::base_utf;
5302
5303 sstring() = default;
5304
5311 template<typename... Args>
5312 requires(sizeof...(Args) > 0 && std::is_constructible_v<Allocator, Args...>)
5313 sstring(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
5314
5315 static const sstring<K> empty_str;
5318 if (type_ == Shared) {
5319 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
5320 }
5321 }
5322
5328 sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
5329 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
5330 if (type_ == Shared)
5331 SharedStringData<K>::from_str(sstr_)->incr();
5332 }
5333
5339 sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
5340 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
5341 other.create_empty();
5342 }
5343
5354 template<size_t N>
5355 sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
5356 size_t size = src.length();
5357 if (size) {
5358 if (src.is_alloced()) {
5359 // Там динамический буфер, выделенный с запасом для SharedStringData.
5360 // There is a dynamic buffer allocated with a reserve for SharedStringData.
5361 K* str = src.str();
5362 if (size > LocalCount) {
5363 // Просто присвоим его себе.
5364 // Let's just assign it to ourselves.
5365 sstr_ = str;
5366 bigLen_ = size;
5367 type_ = Shared;
5368 localRemain_ = 0;
5369 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
5370 } else {
5371 // Скопируем локально
5372 // Copy locally
5373 type_ = Local;
5374 localRemain_ = LocalCount - size;
5375 traits::copy(buf_, str, size + 1);
5376 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
5377 // Let's free that buffer; a local string's buffer is not shared with others
5378 src.dealloc();
5379 }
5380 } else {
5381 // Копируем из локального буфера
5382 // Copy from local buffer
5383 K* str = init(src.size_);
5384 traits::copy(str, src.symbols(), size + 1);
5385 }
5386 src.create_empty();
5387 } else
5388 create_empty();
5389 }
5390
5401 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
5402 requires std::is_constructible_v<allocator_t, Args...>
5403 sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
5404 type_ = Constant;
5405 localRemain_ = 0;
5406 cstr_ = s;
5407 bigLen_ = N - 1;
5408 }
5409
5410 void swap(my_type&& other) noexcept {
5411 char buf[sizeof(buf_) + sizeof(K)];
5412 memcpy(buf, buf_, sizeof(buf));
5413 memcpy(buf_, other.buf_, sizeof(buf));
5414 memcpy(other.buf_, buf, sizeof(buf));
5415
5416 std::swap(base_storable::allocator(), other.allocator());
5417 }
5426 my_type& operator=(my_type other) noexcept {
5427 swap(std::move(other));
5428 return *this;
5429 }
5430
5438 my_type& operator=(simple_str<K> other) {
5439 return operator=(my_type{other, base_storable::allocator()});
5440 }
5441
5449 template<typename T, size_t N = const_lit_for<K, T>::Count>
5450 my_type& operator=(T&& other) {
5451 return operator=(my_type{other, base_storable::allocator()});
5452 }
5453
5461 template<size_t N, bool forShared, typename A>
5463 return operator=(my_type{other.to_str(), base_storable::allocator()});
5464 }
5465
5473 template<size_t N>
5475 return operator=(my_type{std::move(other)});
5476 }
5477
5487 my_type& operator=(const StrExprForType<K> auto& expr) {
5488 return operator=(my_type{expr, base_storable::allocator()});
5489 }
5490
5496 my_type& make_empty() noexcept {
5497 if (type_ == Shared)
5498 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
5499 create_empty();
5500 return *this;
5501 }
5502
5503 const K* symbols() const noexcept {
5504 return type_ == Local ? buf_ : cstr_;
5505 }
5506
5507 size_t length() const noexcept {
5508 return type_ == Local ? LocalCount - localRemain_ : bigLen_;
5509 }
5510
5511 bool is_empty() const noexcept {
5512 return length() == 0;
5513 }
5514
5515 bool empty() const noexcept {
5516 return is_empty();
5517 }
5518
5530 template<typename... T>
5531 static my_type printf(const K* pattern, T&&... args) {
5532 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
5533 }
5534
5544 template<typename... T>
5545 static my_type format(const FmtString<K, T...>& fmtString, T&&... args) {
5546 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
5547 }
5548
5558 template<typename... T>
5559 static my_type vformat(simple_str<K> fmtString, T&&... args) {
5560 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
5561 }
5562};
5563
5564template<typename K, Allocatorable Allocator>
5565inline const sstring<K> sstring<K, Allocator>::empty_str{};
5566
5567template<size_t I>
5568struct digits_selector {
5569 using wider_type = uint16_t;
5570};
5571
5572template<>
5573struct digits_selector<2> {
5574 using wider_type = uint32_t;
5575};
5576
5577template<>
5578struct digits_selector<4> {
5579 using wider_type = uint64_t;
5580};
5581
5582template<typename K, typename T>
5583constexpr size_t fromInt(K* bufEnd, T val) {
5584 const char* twoDigit =
5585 "0001020304050607080910111213141516171819"
5586 "2021222324252627282930313233343536373839"
5587 "4041424344454647484950515253545556575859"
5588 "6061626364656667686970717273747576777879"
5589 "8081828384858687888990919293949596979899";
5590 if (val) {
5591 need_sign<K, std::is_signed_v<T>, T> sign(val);
5592 K* itr = bufEnd;
5593 // Когда у нас минимальное отрицательное число, оно не меняется и остается меньше нуля
5594 // When we have a minimum negative number, it does not change and remains less than zero
5595 if constexpr (std::is_signed_v<T>) {
5596 if (val < 0) {
5597 // Возьмем две последние цифры
5598 // Take the last two digits
5599 const char* ptr = twoDigit - (val % 100) * 2;
5600 *--itr = static_cast<K>(ptr[1]);
5601 *--itr = static_cast<K>(ptr[0]);
5602 val /= 100;
5603 val = -val;
5604 }
5605 }
5606 while (val >= 100) {
5607 const char* ptr = twoDigit + (val % 100) * 2;
5608 *--itr = static_cast<K>(ptr[1]);
5609 *--itr = static_cast<K>(ptr[0]);
5610 val /= 100;
5611 }
5612 if (val < 10) {
5613 *--itr = static_cast<K>('0' + val);
5614 } else {
5615 const char* ptr = twoDigit + val * 2;
5616 *--itr = static_cast<K>(ptr[1]);
5617 *--itr = static_cast<K>(ptr[0]);
5618 }
5619 sign.after(itr);
5620 return size_t(bufEnd - itr);
5621 }
5622 bufEnd[-1] = '0';
5623 return 1;
5624}
5625
5626template<typename K, typename T>
5627struct expr_num {
5628 using symb_type = K;
5629 using my_type = expr_num<K, T>;
5630
5631 enum { bufSize = 24 };
5632 mutable T value;
5633 mutable K buf[bufSize];
5634
5635 expr_num(T t) : value(t) {}
5636 expr_num(expr_num<K, T>&& t) : value(t.value) {}
5637
5638 size_t length() const noexcept {
5639 value = (T)fromInt(buf + bufSize, value);
5640 return (size_t)value;
5641 }
5642 K* place(K* ptr) const noexcept {
5643 ch_traits<K>::copy(ptr, buf + bufSize - (size_t)value, (size_t)value);
5644 return ptr + (size_t)value;
5645 }
5646};
5647
5659template<StrExpr A, FromIntNumber T>
5660inline constexpr auto operator + (const A& a, T s) {
5662}
5663
5675template<StrExpr A, FromIntNumber T>
5676inline constexpr auto operator + (T s, const A& a) {
5678}
5679
5695template<typename K, typename T>
5696inline constexpr auto e_num(T t) {
5697 return expr_num<K, T>{t};
5698}
5699
5700template<typename K>
5701consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
5702 if constexpr (std::is_same_v<K, u8s>)
5703 return s8;
5704 if constexpr (std::is_same_v<K, uws>)
5705 return sw;
5706 if constexpr (std::is_same_v<K, u16s>)
5707 return s16;
5708 if constexpr (std::is_same_v<K, u32s>)
5709 return s32;
5710}
5711
5712#define uni_string(K, p) select_str<K>(p, L##p, u##p, U##p)
5713
5714template<typename K>
5715struct expr_real {
5716 using symb_type = K;
5717 mutable std::conditional_t<is_one_of_std_char_v<K>, K, u8s> buf[40];
5718 mutable size_t l;
5719 double v;
5720 expr_real(double d) : v(d) {}
5721 expr_real(float d) : v(d) {}
5722
5723 size_t length() const noexcept {
5724 if constexpr (is_one_of_std_char_v<K>) {
5725 printf_selector::snprintf(buf, 40, uni_string(K, "%.16g").str, v);
5726 l = (size_t)ch_traits<K>::length(buf);
5727 } else {
5728 l = std::snprintf(buf, sizeof(buf), "%.16g", v);
5729 }
5730 return l;
5731 }
5732 K* place(K* ptr) const noexcept {
5733 if constexpr (is_one_of_std_char_v<K>) {
5734 ch_traits<K>::copy(ptr, buf, l);
5735 } else {
5736 for (size_t i = 0; i < l; i++) {
5737 ptr[i] = buf[i];
5738 }
5739 }
5740 return ptr + l;
5741 }
5742};
5743
5755template<StrExpr A, typename R>
5756 requires(std::is_same_v<R, double> || std::is_same_v<R, float>)
5757inline constexpr auto operator+(const A& a, R s) {
5759}
5760
5772template<StrExpr A, typename R>
5773 requires(is_one_of_std_char_v<typename A::symb_type> && (std::is_same_v<R, double> || std::is_same_v<R, float>))
5774inline constexpr auto operator+(R s, const A& a) {
5776}
5777
5789template<typename K> requires(is_one_of_std_char_v<K>)
5790inline constexpr auto e_real(double t) {
5791 return expr_real<K>{t};
5792}
5793
5794/*
5795* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
5796* K - тип символов строки
5797* T - тип контейнера строк (vector, list)
5798* I - длина разделителя в символах
5799* tail - добавлять разделитель после последнего элемента контейнера.
5800* Если контейнер пустой, разделитель в любом случае не добавляется
5801* skip_empty - пропускать пустые строки без добавления разделителя
5802* To create string concatenations with vectors and lists joined by a constant delimiter
5803* K is the symbols
5804* T - type of string container (vector, list)
5805* I - length of separator in characters
5806* tail - add a separator after the last element of the container.
5807* If the container is empty, the separator is not added anyway
5808* skip_empty - skip empty lines without adding a separator
5809*/
5810template<typename K, typename T, size_t I, bool tail, bool skip_empty>
5811struct expr_join {
5812 using symb_type = K;
5813 using my_type = expr_join<K, T, I, tail, skip_empty>;
5814
5815 const T& s;
5816 const K* delim;
5817
5818 constexpr size_t length() const noexcept {
5819 size_t l = 0;
5820 for (const auto& t: s) {
5821 size_t len = t.length();
5822 if (len > 0 || !skip_empty) {
5823 if (I > 0 && l > 0) {
5824 l += I;
5825 }
5826 l += len;
5827 }
5828 }
5829 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
5830 }
5831 constexpr K* place(K* ptr) const noexcept {
5832 if (s.empty()) {
5833 return ptr;
5834 }
5835 K* write = ptr;
5836 for (const auto& t: s) {
5837 size_t copyLen = t.length();
5838 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
5839 ch_traits<K>::copy(write, delim, I);
5840 write += I;
5841 }
5842 ch_traits<K>::copy(write, t.symbols(), copyLen);
5843 write += copyLen;
5844 }
5845 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
5846 ch_traits<K>::copy(write, delim, I);
5847 write += I;
5848 }
5849 return write;
5850 }
5851};
5852
5866template<bool tail = false, bool skip_empty = false, typename L, typename K = typename const_lit<L>::symb_type, size_t I = const_lit<L>::Count, typename T>
5867inline constexpr auto e_join(const T& s, L&& d) {
5868 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
5869}
5870
5871template<typename K, size_t N, size_t L>
5872struct expr_replaces {
5873 using symb_type = K;
5874 using my_type = expr_replaces<K, N, L>;
5875 simple_str<K> what;
5876 const K* pattern;
5877 const K* repl;
5878 mutable size_t first_, last_;
5879
5880 constexpr expr_replaces(simple_str<K> w, const K* p, const K* r) : what(w), pattern(p), repl(r) {}
5881
5882 constexpr size_t length() const {
5883 size_t l = what.length();
5884 if constexpr (N == L) {
5885 return l;
5886 }
5887 first_ = what.find(pattern, N, 0);
5888 if (first_ != str::npos) {
5889 last_ = first_ + N;
5890 for (;;) {
5891 l += L - N;
5892 size_t next = what.find(pattern, N, last_);
5893 if (next == str::npos) {
5894 break;
5895 }
5896 last_ = next + N;
5897 }
5898 }
5899 return l;
5900 }
5901 constexpr K* place(K* ptr) const noexcept {
5902 if constexpr (N == L) {
5903 const K* from = what.symbols();
5904 for (size_t start = 0; start < what.length();) {
5905 size_t next = what.find(pattern, N, start);
5906 if (next == str::npos) {
5907 next = what.length();
5908 }
5909 size_t delta = next - start;
5910 ch_traits<K>::copy(ptr, from + start, delta);
5911 ptr += delta;
5912 ch_traits<K>::copy(ptr, repl, L);
5913 ptr += L;
5914 start = next + N;
5915 }
5916 return ptr;
5917 }
5918 if (first_ == str::npos) {
5919 return what.place(ptr);
5920 }
5921 const K* from = what.symbols();
5922 for (size_t start = 0, offset = first_; ;) {
5923 ch_traits<K>::copy(ptr, from + start, offset - start);
5924 ptr += offset - start;
5925 ch_traits<K>::copy(ptr, repl, L);
5926 ptr += L;
5927 start = offset + N;
5928 if (start >= last_) {
5929 size_t tail = what.length() - last_;
5930 ch_traits<K>::copy(ptr, from + last_, tail);
5931 ptr += tail;
5932 break;
5933 } else {
5934 offset = what.find(pattern, N, start);
5935 }
5936 }
5937 return ptr;
5938 }
5939};
5940
5954template<typename K, typename T, size_t N = const_lit_for<K, T>::Count, typename X, size_t L = const_lit_for<K, X>::Count>
5955 requires(N > 1)
5956inline constexpr auto e_repl(simple_str<K> w, T&& p, X&& r) {
5957 return expr_replaces<K, N - 1, L - 1>{w, p, r};
5958}
5959
5977template<typename K>
5979 using symb_type = K;
5980 using my_type = expr_replaced<K>;
5981 simple_str<K> what;
5982 const simple_str<K> pattern;
5983 const simple_str<K> repl;
5984 mutable size_t first_, last_;
5995 constexpr expr_replaced(simple_str<K> w, simple_str<K> p, simple_str<K> r) : what(w), pattern(p), repl(r) {}
5996
5997 constexpr size_t length() const {
5998 size_t l = what.length();
5999 if (pattern.length() == repl.length()) {
6000 return l;
6001 }
6002 first_ = what.find(pattern);
6003 if (first_ != str::npos) {
6004 last_ = first_ + pattern.length();
6005 for (;;) {
6006 l += repl.length() - pattern.length();
6007 size_t next = what.find(pattern, last_);
6008 if (next == str::npos) {
6009 break;
6010 }
6011 last_ = next + pattern.length();
6012 }
6013 }
6014 return l;
6015 }
6016 constexpr K* place(K* ptr) const noexcept {
6017 if (repl.length() == pattern.length()) {
6018 const K* from = what.symbols();
6019 for (size_t start = 0; start < what.length();) {
6020 size_t next = what.find(pattern, start);
6021 if (next == str::npos) {
6022 next = what.length();
6023 }
6024 size_t delta = next - start;
6025 ch_traits<K>::copy(ptr, from + start, delta);
6026 ptr += delta;
6027 ch_traits<K>::copy(ptr, repl.symbols(), repl.length());
6028 ptr += repl.length();
6029 start = next + pattern.length();
6030 }
6031 return ptr;
6032 }
6033 if (first_ == str::npos) {
6034 return what.place(ptr);
6035 }
6036 const K* from = what.symbols();
6037 for (size_t start = 0, offset = first_; ;) {
6038 ch_traits<K>::copy(ptr, from + start, offset - start);
6039 ptr += offset - start;
6040 ch_traits<K>::copy(ptr, repl.symbols(), repl.length());
6041 ptr += repl.length();
6042 start = offset + pattern.length();
6043 if (start >= last_) {
6044 size_t tail = what.length() - last_;
6045 ch_traits<K>::copy(ptr, from + last_, tail);
6046 ptr += tail;
6047 break;
6048 } else {
6049 offset = what.find(pattern, start);
6050 }
6051 }
6052 return ptr;
6053 }
6054};
6055
6056template<bool UseVectorForReplace>
6057struct replace_search_result_store {
6058 size_t count_{};
6059 std::pair<size_t, size_t> replaces_[16];
6060};
6061
6062template<>
6063struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
6064
6104template<typename K, bool UseVectorForReplace = false>
6106 using symb_type = K;
6107 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6108
6109 const simple_str<K> source_;
6110 const std::vector<std::pair<K, simple_str<K>>>& replaces_;
6111
6112 lstring<K, 32> pattern_;
6113
6114 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6115
6116 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
6144 constexpr expr_replace_symbols(simple_str<K> source, const std::vector<std::pair<K, simple_str<K>>>& repl )
6145 : source_(source), replaces_(repl)
6146 {
6147 size_t pattern_len = replaces_.size();
6148 K* pattern = pattern_.set_size(pattern_len);
6149
6150 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6151 *pattern++ = replaces_[idx].first;
6152 }
6153
6154 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6155 for (size_t idx = 0; idx < pattern_len; idx++) {
6156 uu8s s = static_cast<uu8s>(pattern_[idx]);
6157 if constexpr (sizeof(K) == 1) {
6158 bit_mask_[s >> 3] |= (1 << (s & 7));
6159 } else {
6160 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6161 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6162 } else {
6163 bit_mask_[s >> 3] |= (1 << (s & 7));
6164 }
6165 }
6166 }
6167 }
6168 }
6169
6170 size_t length() const {
6171 size_t l = source_.length();
6172 auto [fnd, num] = find_first_of(source_.str, source_.len);
6173 if (fnd == str::npos) {
6174 return l;
6175 }
6176 l += replaces_[num].second.len - 1;
6177 if constexpr (UseVectorForReplace) {
6178 search_results_.reserve((l >> 4) + 8);
6179 search_results_.emplace_back(fnd, num);
6180 for (size_t start = fnd + 1;;) {
6181 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6182 if (fnd == str::npos) {
6183 break;
6184 }
6185 search_results_.emplace_back(fnd, idx);
6186 start = fnd + 1;
6187 l += replaces_[idx].second.len - 1;
6188 }
6189 } else {
6190 const size_t max_store = std::size(search_results_.replaces_);
6191 search_results_.replaces_[0] = {fnd, num};
6192 search_results_.count_++;
6193 for (size_t start = fnd + 1;;) {
6194 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6195 if (found == str::npos) {
6196 break;
6197 }
6198 if (search_results_.count_ < max_store) {
6199 search_results_.replaces_[search_results_.count_] = {found, idx};
6200 }
6201 l += replaces_[idx].second.len - 1;
6202 search_results_.count_++;
6203 start = found + 1;
6204 }
6205 }
6206 return l;
6207 }
6208 K* place(K* ptr) const noexcept {
6209 size_t start = 0;
6210 const K* text = source_.str;
6211 if constexpr (UseVectorForReplace) {
6212 for (const auto& [pos, num] : search_results_) {
6213 size_t delta = pos - start;
6214 ch_traits<K>::copy(ptr, text + start, delta);
6215 ptr += delta;
6216 ptr = replaces_[num].second.place(ptr);
6217 start = pos + 1;
6218 }
6219 } else {
6220 const size_t max_store = std::size(search_results_.replaces_);
6221 size_t founded = search_results_.count_;
6222 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6223 const auto [pos, num] = search_results_.replaces_[idx];
6224 size_t delta = pos - start;
6225 ch_traits<K>::copy(ptr, text + start, delta);
6226 ptr += delta;
6227 ptr = replaces_[num].second.place(ptr);
6228 start = pos + 1;
6229 }
6230 if (founded > max_store) {
6231 founded -= max_store;
6232 while (founded--) {
6233 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6234 size_t delta = fnd - start;
6235 ch_traits<K>::copy(ptr, text + start, delta);
6236 ptr += delta;
6237 ptr = replaces_[idx].second.place(ptr);
6238 start = fnd + 1;
6239 }
6240 }
6241 }
6242 size_t tail = source_.len - start;
6243 ch_traits<K>::copy(ptr, text + start, tail);
6244 return ptr + tail;
6245 }
6246
6247protected:
6248 size_t index_of(K s) const {
6249 return pattern_.find(s);
6250 }
6251
6252 bool is_in_mask(uu8s s) const {
6253 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6254 }
6255 bool is_in_mask2(uu8s s) const {
6256 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6257 }
6258
6259 bool is_in_pattern(K s, size_t& idx) const {
6260 if constexpr (sizeof(K) == 1) {
6261 if (is_in_mask(s)) {
6262 idx = index_of(s);
6263 return true;
6264 }
6265 } else {
6266 if (std::make_unsigned_t<const K>(s) > 255) {
6267 if (is_in_mask2(s)) {
6268 return (idx = index_of(s)) != -1;
6269 }
6270 } else {
6271 if (is_in_mask(s)) {
6272 idx = index_of(s);
6273 return true;
6274 }
6275 }
6276 }
6277 return false;
6278 }
6279
6280 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6281 size_t pl = pattern_.length();
6282 if (pl >= BIT_SEARCH_TRESHHOLD) {
6283 size_t idx;
6284 while (offset < len) {
6285 if (is_in_pattern(text[offset], idx)) {
6286 return {offset, idx};
6287 }
6288 offset++;
6289 }
6290 } else {
6291 while (offset < len) {
6292 if (size_t idx = index_of(text[offset]); idx != -1) {
6293 return {offset, idx};
6294 }
6295 offset++;
6296 }
6297 }
6298 return {-1, -1};
6299 }
6300};
6301
6302// Строковое выражение для замены символов
6303// String expression to replace characters
6304template<typename K, size_t N, bool UseVectorForReplace>
6305struct expr_replace_const_symbols {
6306 using symb_type = K;
6307 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6308 const K pattern_[N];
6309 const simple_str<K> source_;
6310 const simple_str<K> replaces_[N];
6311
6312 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6313
6314 [[_no_unique_address]]
6315 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
6316
6317 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
6318 constexpr expr_replace_const_symbols(simple_str<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
6319
6320 size_t length() const {
6321 size_t l = source_.length();
6322 auto [fnd, num] = find_first_of(source_.str, source_.len);
6323 if (fnd == str::npos) {
6324 return l;
6325 }
6326 l += replaces_[num].len - 1;
6327 if constexpr (UseVectorForReplace) {
6328 search_results_.reserve((l >> 4) + 8);
6329 search_results_.emplace_back(fnd, num);
6330 for (size_t start = fnd + 1;;) {
6331 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6332 if (fnd == str::npos) {
6333 break;
6334 }
6335 search_results_.emplace_back(fnd, idx);
6336 start = fnd + 1;
6337 l += replaces_[idx].len - 1;
6338 }
6339 } else {
6340 const size_t max_store = std::size(search_results_.replaces_);
6341 search_results_.replaces_[0] = {fnd, num};
6342 search_results_.count_++;
6343 for (size_t start = fnd + 1;;) {
6344 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6345 if (found == str::npos) {
6346 break;
6347 }
6348 if (search_results_.count_ < max_store) {
6349 search_results_.replaces_[search_results_.count_] = {found, idx};
6350 }
6351 l += replaces_[idx].len - 1;
6352 search_results_.count_++;
6353 start = found + 1;
6354 }
6355 }
6356 return l;
6357 }
6358 K* place(K* ptr) const noexcept {
6359 size_t start = 0;
6360 const K* text = source_.str;
6361 if constexpr (UseVectorForReplace) {
6362 for (const auto& [pos, num] : search_results_) {
6363 size_t delta = pos - start;
6364 ch_traits<K>::copy(ptr, text + start, delta);
6365 ptr += delta;
6366 ptr = replaces_[num].place(ptr);
6367 start = pos + 1;
6368 }
6369 } else {
6370 const size_t max_store = std::size(search_results_.replaces_);
6371 size_t founded = search_results_.count_;
6372 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6373 const auto [pos, num] = search_results_.replaces_[idx];
6374 size_t delta = pos - start;
6375 ch_traits<K>::copy(ptr, text + start, delta);
6376 ptr += delta;
6377 ptr = replaces_[num].place(ptr);
6378 start = pos + 1;
6379 }
6380 if (founded > max_store) {
6381 founded -= max_store;
6382 while (founded--) {
6383 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6384 size_t delta = fnd - start;
6385 ch_traits<K>::copy(ptr, text + start, delta);
6386 ptr += delta;
6387 ptr = replaces_[idx].place(ptr);
6388 start = fnd + 1;
6389 }
6390 }
6391 }
6392 size_t tail = source_.len - start;
6393 ch_traits<K>::copy(ptr, text + start, tail);
6394 return ptr + tail;
6395 }
6396
6397protected:
6398 template<typename ... Repl>
6399 constexpr expr_replace_const_symbols(int, simple_str<K> source, K s, simple_str<K> r, Repl&&... repl) :
6400 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6401
6402 template<typename ... Repl> requires (sizeof...(Repl) == N)
6403 constexpr expr_replace_const_symbols(int, simple_str<K> source, Repl&&... repl) :
6404 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6405 {
6406 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6407 for (size_t idx = 0; idx < N; idx++) {
6408 uu8s s = static_cast<uu8s>(pattern_[idx]);
6409 if constexpr (sizeof(K) == 1) {
6410 bit_mask_[s >> 3] |= 1 << (s & 7);
6411 } else {
6412 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6413 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6414 } else {
6415 bit_mask_[s >> 3] |= 1 << (s & 7);
6416 }
6417 }
6418 }
6419 }
6420 }
6421
6422 template<size_t Idx>
6423 size_t index_of(K s) const {
6424 if constexpr (Idx < N) {
6425 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6426 }
6427 return -1;
6428 }
6429 bool is_in_mask(uu8s s) const {
6430 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6431 }
6432 bool is_in_mask2(uu8s s) const {
6433 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6434 }
6435
6436 bool is_in_pattern(K s, size_t& idx) const {
6437 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6438 if constexpr (sizeof(K) == 1) {
6439 if (is_in_mask(s)) {
6440 idx = index_of<0>(s);
6441 return true;
6442 }
6443 } else {
6444 if (std::make_unsigned_t<const K>(s) > 255) {
6445 if (is_in_mask2(s)) {
6446 return (idx = index_of<0>(s)) != -1;
6447 }
6448 } else {
6449 if (is_in_mask(s)) {
6450 idx = index_of<0>(s);
6451 return true;
6452 }
6453 }
6454 }
6455 }
6456 return false;
6457 }
6458 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6459 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6460 size_t idx;
6461 while (offset < len) {
6462 if (is_in_pattern(text[offset], idx)) {
6463 return {offset, idx};
6464 }
6465 offset++;
6466 }
6467 } else {
6468 while (offset < len) {
6469 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
6470 return {offset, idx};
6471 }
6472 offset++;
6473 }
6474 }
6475 return {-1, -1};
6476 }
6477};
6478
6518template<bool UseVector = false, typename K, typename ... Repl>
6519requires (sizeof...(Repl) % 2 == 0)
6520auto e_repl_const_symbols(simple_str<K> src, Repl&& ... other) {
6521 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(src, std::forward<Repl>(other)...);
6522}
6523
6524template<typename K, typename H>
6525struct StoreType {
6526 simple_str<K> str;
6527 size_t hash;
6528 char node[sizeof(sstring<K>)];
6529
6530 const simple_str_nt<K>& to_nt() const noexcept {
6531 return static_cast<const simple_str_nt<K>&>(str);
6532 }
6533 const sstring<K>& to_str() const noexcept {
6534 return *reinterpret_cast<const sstring<K>*>(node);
6535 }
6536};
6537
6538template<bool Wide>
6539struct fnv_const {
6540 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
6541 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
6542};
6543
6544template<>
6545struct fnv_const<false> {
6546 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
6547 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
6548};
6549
6550using fnv = fnv_const<sizeof(size_t) == 8>;
6551
6552template<typename K>
6553inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
6554 size_t h = fnv::basis;
6555 for (size_t i = 0; i < l; i++) {
6556 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
6557 }
6558 return h;
6559};
6560
6561template<typename K>
6562inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
6563 size_t h = fnv::basis;
6564 for (size_t i = 0; i < l; i++) {
6565 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
6566 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
6567 }
6568 return h;
6569};
6570
6571template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
6572inline constexpr size_t fnv_hash(T&& value) {
6573 size_t h = fnv::basis;
6574 for (size_t i = 0; i < N - 1; i++) {
6575 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
6576 }
6577 return h;
6578};
6579
6580template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
6581inline constexpr size_t fnv_hash_ia(T&& value) {
6582 size_t h = fnv::basis;
6583 for (size_t i = 0; i < N - 1; i++) {
6584 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
6585 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
6586 }
6587 return h;
6588};
6589
6590template<typename K>
6591inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
6592 return fnv_hash(ptr, l);
6593};
6594
6595template<typename K>
6596inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
6597 return fnv_hash_ia(ptr, l);
6598};
6599
6600static_assert(std::is_trivially_copyable_v<StoreType<u8s, int>>, "Store type must be trivially copyable");
6601
6602template<typename K>
6603struct streql;
6604template<typename K>
6605struct strhash;
6606
6701template<typename K, typename T, typename H = strhash<K>, typename E = streql<K>>
6702class hashStrMap : public std::unordered_map<StoreType<K, H>, T, H, E> {
6703protected:
6704 using InStore = StoreType<K, H>;
6705
6706public:
6707 using my_type = hashStrMap<K, T, H, E>;
6708 using hash_t = std::unordered_map<InStore, T, H, E>;
6709 using hasher = H;
6710
6711 hashStrMap() = default;
6712 hashStrMap(const my_type& other) : hash_t(other) {
6713 for (const auto& [k, v] : *this) {
6714 InStore& stored = const_cast<InStore&>(k);
6715 sstring<K> tmp = *(sstring<K>*)stored.node;
6716 new (stored.node) sstring<K>(std::move(tmp));
6717 stored.str.str = stored.to_str().symbols();
6718 }
6719 }
6720 ~hashStrMap() {
6721 for (auto& k: *this)
6722 ((sstring<K>*)k.first.node)->~sstring();
6723 }
6724
6725 hashStrMap(my_type&& o) = default;
6726
6727 my_type& operator=(const my_type& other) {
6728 hash_t::operator=(other);
6729 for (const auto& [k, v] : *this) {
6730 InStore& stored = const_cast<InStore&>(k);
6731 sstring<K> tmp = *(sstring<K>*)stored.node;
6732 new (stored.node) sstring<K>(std::move(tmp));
6733 stored.str.str = stored.to_str().symbols();
6734 }
6735 return *this;
6736 };
6737 my_type& operator=(my_type&&) = default;
6738
6739 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
6740 for (const auto& e: init)
6741 emplace(e.first, e.second);
6742 }
6743
6744 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
6745
6746 hashStrMap(init_str&& init) {
6747 for (const auto& e: init)
6748 emplace(e.first, e.second);
6749 }
6750
6751 // При входе хэш должен быть уже посчитан
6752 // When entering, the hash must already be calculated
6753 template<typename... ValArgs>
6754 auto try_emplace(const InStore& key, ValArgs&&... args) {
6755 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
6756 if (it.second) {
6757 InStore& stored = const_cast<InStore&>(it.first->first);
6758 new (stored.node) sstring<K>(key.str);
6759 stored.str.str = stored.to_str().symbols();
6760 }
6761 return it;
6762 }
6763
6764 static InStore toStoreType(simple_str<K> key) {
6765 return {key, H{}(key)};
6766 }
6767
6768 template<typename Key, typename... ValArgs>
6769 requires(std::is_convertible_v<Key, simple_str<K>>)
6770 auto try_emplace(Key&& key, ValArgs&&... args) {
6771 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
6772 if (it.second) {
6773 InStore& stored = const_cast<InStore&>(it.first->first);
6774 new (stored.node) sstring<K>(std::forward<Key>(key));
6775 stored.str.str = stored.to_str().symbols();
6776 }
6777 return it;
6778 }
6779
6780 template<typename... ValArgs>
6781 auto emplace(const InStore& key, ValArgs&&... args) {
6782 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
6783 if (!it.second) {
6784 it.first->second = T(std::forward<ValArgs>(args)...);
6785 }
6786 return it;
6787 }
6788
6789 template<typename Key, typename... ValArgs>
6790 requires(std::is_convertible_v<Key, simple_str<K>>)
6791 auto emplace(Key&& key, ValArgs&&... args) {
6792 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
6793 if (!it.second) {
6794 it.first->second = T(std::forward<ValArgs>(args)...);
6795 }
6796 return it;
6797 }
6798
6799 auto& operator[](const InStore& key) {
6800 return try_emplace(key).first->second;
6801 }
6802
6803 template<typename Key>
6804 requires(std::is_convertible_v<Key, simple_str<K>>)
6805 auto& operator[](Key&& key) {
6806 return try_emplace(std::forward<Key>(key)).first->second;
6807 }
6808
6809 decltype(auto) at(const InStore& key) {
6810 return hash_t::at(key);
6811 }
6812 decltype(auto) at(const InStore& key) const {
6813 return hash_t::at(key);
6814 }
6815
6816 decltype(auto) at(simple_str<K> key) {
6817 return hash_t::at(toStoreType(key));
6818 }
6819 decltype(auto) at(simple_str<K> key) const {
6820 return hash_t::at(toStoreType(key));
6821 }
6822
6823 auto find(const InStore& key) const {
6824 return hash_t::find(key);
6825 }
6826
6827 auto find(simple_str<K> key) const {
6828 return find(toStoreType(key));
6829 }
6830
6831 auto find(const InStore& key) {
6832 return hash_t::find(key);
6833 }
6834
6835 auto find(simple_str<K> key) {
6836 return find(toStoreType(key));
6837 }
6838
6839 auto erase(typename hash_t::const_iterator it) {
6840 if (it != hash_t::end()) {
6841 ((sstring<K>*)it->first.node)->~sstring();
6842 }
6843 return hash_t::erase(it);
6844 }
6845
6846 auto erase(const InStore& key) {
6847 auto it = hash_t::find(key);
6848 if (it != hash_t::end()) {
6849 ((sstring<K>*)it->first.node)->~sstring();
6850 hash_t::erase(it);
6851 return 1;
6852 }
6853 return 0;
6854 }
6855
6856 auto erase(simple_str<K> key) {
6857 return erase(toStoreType(key));
6858 }
6859
6860 bool lookup(simple_str<K> txt, T& val) const {
6861 auto it = find(txt);
6862 if (it != hash_t::end()) {
6863 val = it->second;
6864 return true;
6865 }
6866 return false;
6867 }
6868
6869 void clear() {
6870 for (auto& k: *this)
6871 ((sstring<K>*)k.first.node)->~sstring();
6872 hash_t::clear();
6873 }
6874 bool contains(const InStore& key) const {
6875 return hash_t::find(key) != this->end();
6876 }
6877
6878 bool contains(simple_str<K> key) const {
6879 return find(toStoreType(key)) != this->end();
6880 }
6881};
6882
6883template<typename K>
6884struct streql {
6885 template<typename H>
6886 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
6887 return _Left.hash == _Right.hash && _Left.str == _Right.str;
6888 }
6889};
6890
6891template<typename K>
6892struct strhash { // hash functor for basic_string
6893 size_t operator()(simple_str<K> _Keyval) const {
6894 return fnv_hash(_Keyval.symbols(), _Keyval.length());
6895 }
6896 size_t operator()(const StoreType<K, strhash<K>>& _Keyval) const {
6897 return _Keyval.hash;
6898 }
6899};
6900
6901template<typename K>
6902struct streqlia {
6903 template<typename H>
6904 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
6905 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
6906 }
6907};
6908
6909template<typename K>
6910struct strhashia {
6911 size_t operator()(simple_str<K> _Keyval) const {
6912 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
6913 }
6914 size_t operator()(const StoreType<K, strhashia<K>>& _Keyval) const {
6915 return _Keyval.hash;
6916 }
6917};
6918
6919template<typename K>
6920struct streqliu {
6921 template<typename H>
6922 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
6923 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
6924 }
6925};
6926
6927template<typename K>
6928struct strhashiu {
6929 size_t operator()(simple_str<K> _Keyval) const {
6930 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
6931 }
6932 size_t operator()(const StoreType<K, strhashiu<K>>& _Keyval) const {
6933 return _Keyval.hash;
6934 }
6935};
6936
6951template<typename K>
6952class chunked_string_builder {
6953 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
6954 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
6955 K* write{}; // Текущая позиция записи | Current write position
6956 size_t len{}; // Общая длина | Total length
6957 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
6958 size_t align{1024};
6959
6960public:
6961 using my_type = chunked_string_builder<K>;
6962 using symb_type = K;
6963 chunked_string_builder() = default;
6964 chunked_string_builder(size_t a) : align(a){};
6965 chunked_string_builder(const my_type&) = delete;
6966 chunked_string_builder(my_type&& other) noexcept
6967 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
6968 other.len = other.remain = 0;
6969 other.write = nullptr;
6970 }
6971 my_type& operator=(my_type other) noexcept {
6972 chunks.swap(other.chunks);
6973 write = other.write;
6974 len = other.len;
6975 remain = other.remain;
6976 align = other.align;
6977 other.len = other.remain = 0;
6978 other.write = nullptr;
6979 return *this;
6980 }
6981
6984 if (data.len) {
6985 len += data.len;
6986 if (data.len <= remain) {
6987 // Добавляемые данные влезают в выделенный блок, просто скопируем их
6988 // The added data fits into the selected block, just copy it
6989 ch_traits<K>::copy(write, data.str, data.len);
6990 write += data.len; // Сдвинем позицию записи | Let's move the recording position
6991 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
6992 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
6993 } else {
6994 // Не влезают | They don't fit
6995 if (remain) {
6996 // Сначала запишем сколько влезет
6997 // First, write down as much as we can
6998 ch_traits<K>::copy(write, data.str, remain);
6999 data.len -= remain;
7000 data.str += remain;
7001 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
7002 }
7003 // Выделим новый блок и впишем в него данные
7004 // Рассчитаем размер блока, кратного заданному выравниванию
7005 // Select a new block and write data into it
7006 // Calculate the block size that is a multiple of the given alignment
7007 size_t blockSize = (data.len + align - 1) / align * align;
7008 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
7009 write = chunks.back().first.get();
7010 ch_traits<K>::copy(write, data.str, data.len);
7011 write += data.len;
7012 remain = blockSize - data.len;
7013 }
7014 }
7015 return *this;
7016 }
7017
7018 my_type& operator<<(const StrExprForType<K> auto& expr) {
7019 size_t l = expr.length();
7020 if (l) {
7021 if (l < remain) {
7022 write = expr.place(write);
7023 chunks.back().second += l;
7024 len += l;
7025 remain -= l;
7026 } else if (!remain) {
7027 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
7028 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
7029 write = expr.place(chunks.back().first.get());
7030 len += l;
7031 remain = blockSize - l;
7032 } else {
7033 auto store = std::make_unique<K[]>(l);
7034 expr.place(store.get());
7035 return operator<<({store.get(), l});
7036 }
7037 }
7038 return *this;
7039 }
7040
7041 template<typename T>
7042 my_type& operator<<(T data)
7043 requires std::is_same_v<T, K>
7044 {
7045 return operator<<(expr_char<K>(data));
7046 }
7047
7048 constexpr size_t length() const noexcept {
7049 return len;
7050 }
7051
7052 void reset() {
7053 if (chunks.empty()) {
7054 return;
7055 }
7056 if (chunks.size() > 1) {
7057 remain = 0;
7058 chunks.resize(1);
7059 }
7060 remain += chunks[0].second;
7061 chunks[0].second = 0;
7062 len = 0;
7063 write = chunks[0].first.get();
7064 }
7065
7066 constexpr K* place(K* p) const noexcept {
7067 for (const auto& block: chunks) {
7068 ch_traits<K>::copy(p, block.first.get(), block.second);
7069 p += block.second;
7070 }
7071 return p;
7072 }
7081 template<typename Op>
7082 void out(const Op& o) const {
7083 for (const auto& block: chunks)
7084 o(block.first.get(), block.second);
7085 }
7086
7090 bool is_continuous() const {
7091 if (chunks.size()) {
7092 const K* ptr = chunks.front().first.get();
7093 for (const auto& chunk: chunks) {
7094 if (chunk.first.get() != ptr)
7095 return false;
7096 ptr += chunk.second;
7097 }
7098 }
7099 return true;
7100 }
7101
7107 const K* begin() const {
7108 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
7109 }
7110
7114 void clear() {
7115 chunks.clear();
7116 write = nullptr;
7117 len = 0;
7118 remain = 0;
7119 }
7120
7125 typename decltype(chunks)::const_iterator it, end;
7126 size_t writedFromCurrentChunk;
7131 bool is_end() {
7132 return it == end;
7133 }
7134
7144 size_t store(K* buffer, size_t size) {
7145 size_t writed = 0;
7146 while (size && !is_end()) {
7147 size_t remain = it->second - writedFromCurrentChunk;
7148 size_t write = std::min(size, remain);
7149 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
7150 writed += write;
7151 remain -= write;
7152 size -= write;
7153 if (!remain) {
7154 ++it;
7155 writedFromCurrentChunk = 0;
7156 } else
7157 writedFromCurrentChunk += write;
7158 }
7159 return writed;
7160 }
7161 };
7162
7169 return {chunks.begin(), chunks.end(), 0};
7170 }
7171
7177 const auto& data() const {
7178 return chunks;
7179 }
7180};
7181
7182using stringa = sstring<u8s>;
7183using stringw = sstring<wchar_t>;
7184using stringu = sstring<u16s>;
7185using stringuu = sstring<u32s>;
7186static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
7187
7192template<typename T>
7198template<typename T>
7204template<typename T>
7206
7211template<typename T>
7213
7218template<typename T>
7220
7225template<typename T>
7227
7232template<typename T>
7234template<typename T>
7244template<typename T>
7246
7251template<typename T>
7257template<typename T>
7263template<typename T>
7265
7266inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
7267
7268inline namespace literals {
7269
7270#ifdef _MSC_VER
7271/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
7272Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
7273Пишут, что баг исправлен, но видимо не до конца.
7274Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
7275/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
7276Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
7277They write that the bug has been fixed, but apparently not completely.
7278Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
7279#define SS_CONSTEVAL constexpr
7280#else
7281#define SS_CONSTEVAL consteval
7282#endif
7283
7294SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
7295 return simple_str_nt<u8s>{ptr, l};
7296}
7297
7307SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
7308 return simple_str_nt<uws>{ptr, l};
7309}
7310
7320SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
7321 return simple_str_nt<u16s>{ptr, l};
7322}
7323
7334SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
7335 return simple_str_nt<u32s>{ptr, l};
7336}
7337
7338template<typename K> using HashKey = StoreType<K, strhash<K>>;
7339template<typename K> using HashKeyIA = StoreType<K, strhashia<K>>;
7340template<typename K> using HashKeyIU = StoreType<K, strhashiu<K>>;
7341
7352consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
7353 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
7354}
7355
7366consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
7367 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7368}
7369
7380inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
7381 return HashKeyIU<u8s>{{ptr, l}, strhashiu<u8s>{}(simple_str<u8s>{ptr, l})};
7382}
7383
7394consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
7395 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
7396}
7397
7408consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
7409 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7410}
7411
7422inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
7423 return HashKeyIU<u16s>{{ptr, l}, strhashiu<u16s>{}(simple_str<u16s>{ptr, l})};
7424}
7425
7436consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
7437 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
7438}
7439
7450consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
7451 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7452}
7453
7464inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
7465 return HashKeyIU<u32s>{{ptr, l}, strhashiu<u32s>{}(simple_str<u32s>{ptr, l})};
7466}
7467
7478consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
7479 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
7480}
7481
7492consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
7493 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7494}
7495
7506inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
7507 return HashKeyIU<uws>{{ptr, l}, strhashiu<uws>{}(simple_str<uws>{ptr, l})};
7508}
7509} // namespace literals
7510
7521inline std::ostream& operator<<(std::ostream& stream, ssa text) {
7522 return stream << std::string_view{text.symbols(), text.length()};
7523}
7524
7535inline std::wostream& operator<<(std::wostream& stream, ssw text) {
7536 return stream << std::wstring_view{text.symbols(), text.length()};
7537}
7538
7549inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
7550 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
7551}
7552
7563inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
7564 return stream << std::string_view{text.symbols(), text.length()};
7565}
7566
7577inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
7578 return stream << std::wstring_view{text.symbols(), text.length()};
7579}
7580
7591inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
7592 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
7593}
7594
7605template<size_t N, bool S, simstr::Allocatorable A>
7606inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
7607 return stream << std::string_view{text.symbols(), text.length()};
7608}
7609
7620template<size_t N, bool S, simstr::Allocatorable A>
7621inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
7622 return stream << std::wstring_view{text.symbols(), text.length()};
7623}
7624
7635template<size_t N, bool S, simstr::Allocatorable A>
7636inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
7637 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
7638}
7639
7640} // namespace simstr
7641
7646template<typename K>
7647struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
7648 // Define format() by calling the base class implementation with the wrapped value
7649 template<typename FormatContext>
7650 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
7651 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
7652 }
7653};
7654
7659template<typename K>
7660struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
7661 // Define format() by calling the base class implementation with the wrapped value
7662 template<typename FormatContext>
7663 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
7664 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
7665 }
7666};
7667
7672template<typename K>
7673struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
7674 // Define format() by calling the base class implementation with the wrapped value
7675 template<typename FormatContext>
7676 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
7677 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
7678 }
7679};
7680
7685template<typename K, size_t N, bool S, typename A>
7686struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
7687 // Define format() by calling the base class implementation with the wrapped value
7688 template<typename FormatContext>
7689 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
7690 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
7691 }
7692};
Class for sequentially obtaining substrings by a given delimiter.
Definition sstring.h:2415
simple_str< K > next()
Get the next substring.
Definition sstring.h:2434
bool is_done() const
Find out if substrings are running out.
Definition sstring.h:2425
my_type & operator<<(simple_str< K > data)
Adding a piece of data.
Definition sstring.h:6983
portion_store get_portion() const
Get a portion_store through which data can be sequentially retrieved into an external buffer.
Definition sstring.h:7168
constexpr size_t length() const noexcept
Length of the saved text.
Definition sstring.h:7048
my_type & operator<<(T data)
Adding a symbol.
Definition sstring.h:7042
void reset()
Resets the contents, but does not delete the first buffer in order to avoid allocation later.
Definition sstring.h:7052
void clear()
Clear the object, freeing all allocated buffers.
Definition sstring.h:7114
my_type & operator<<(const StrExprForType< K > auto &expr)
Adding a string expression.
Definition sstring.h:7018
bool is_continuous() const
Checks whether all text is located in one contiguous chunk in memory.
Definition sstring.h:7090
void out(const Op &o) const
Applies a functor to each stored buffer.
Definition sstring.h:7082
const auto & data() const
Get internal data buffers.
Definition sstring.h:7177
const K * begin() const
Get a pointer to the beginning of the first buffer. It makes sense to apply only if is_continuous tru...
Definition sstring.h:7107
Container for more efficient searching by string keys.
Definition sstring.h:6702
The mutable, owning string class. Contains an internal buffer for text of a given size.
Definition sstring.h:4636
lstring(const my_type &other, Args &&... args)
Copy from another string of the same type, but with a different allocator.
Definition sstring.h:4774
lstring(const my_type &other)
Copy from another string of the same type.
Definition sstring.h:4759
constexpr lstring(T &&value, Args &&... args)
String literal constructor.
Definition sstring.h:4790
K * reserve_no_preserve(size_t newSize)
Definition sstring.h:4978
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:4920
@ LocalCapacity
Definition sstring.h:4644
my_type & operator=(my_type &&other) noexcept
Assignment operator by moving from a string of the same type.
Definition sstring.h:4863
void clear()
Makes a string empty without changing the string buffer.
Definition sstring.h:5080
bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:4955
void shrink_to_fit()
Reduces the size of the external buffer to the smallest possible size to hold the string....
Definition sstring.h:5066
size_t length() const noexcept
String length.
Definition sstring.h:4943
size_t capacity() const noexcept
Current row buffer capacity.
Definition sstring.h:4963
K * reserve(size_t newSize)
Allocate a buffer large enough to hold newSize characters plus a terminating null.
Definition sstring.h:5000
bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:4959
my_type & operator=(const StrExprForType< K > auto &expr)
String expression appending operator.
Definition sstring.h:4933
K * str() noexcept
Pointer to a string buffer.
Definition sstring.h:4951
my_type & operator=(const my_type &other)
Copy assignment operator from a string of the same type.
Definition sstring.h:4846
lstring(const Op &op, Args &&... args)
A fill constructor using a functor (see str_mutable::fill).
Definition sstring.h:4829
void reset()
Makes the string empty and frees the external buffer, if there was one.
Definition sstring.h:5084
const K * symbols() const noexcept
Pointer to constant characters.
Definition sstring.h:4947
lstring(my_type &&other) noexcept
Constructor for moving from a string of the same type.
Definition sstring.h:4804
K * set_size(size_t newSize)
Sets the size of the current string, allocating space if necessary.
Definition sstring.h:5023
my_type & operator=(simple_str< K > other)
Assignment operator from simple_str.
Definition sstring.h:4908
bool is_local() const noexcept
Find out whether a local or external buffer is used for characters.
Definition sstring.h:5040
void define_size()
Determine the length of the string. Searches for the character 0 in the string buffer to its capacity...
Definition sstring.h:5049
Immutable owning string class.
Definition sstring.h:5190
sstring(lstring< K, N, true, Allocator > &&src)
A move constructor from lstring with an sstring-compatible external buffer.
Definition sstring.h:5355
my_type & make_empty() noexcept
Make the string empty.
Definition sstring.h:5496
sstring(Args &&... args)
Constructor for the empty string.
Definition sstring.h:5313
bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:5515
my_type & operator=(my_type other) noexcept
Assignment operator to another string of the same type.
Definition sstring.h:5426
my_type & operator=(simple_str< K > other)
Assignment operator to another string of a different type.
Definition sstring.h:5438
sstring(const my_type &other) noexcept
String copy constructor.
Definition sstring.h:5328
~sstring()
String destructor.
Definition sstring.h:5317
static my_type format(const FmtString< K, T... > &fmtString, T &&... args)
Get a string formatted with std::format.
Definition sstring.h:5545
static my_type printf(const K *pattern, T &&... args)
Get a string formatted with std::sprintf.
Definition sstring.h:5531
my_type & operator=(lstring< K, N, true, Allocator > &&other)
Assignment operator to a movable string of type lstring with a compatible buffer.
Definition sstring.h:5474
my_type & operator=(const lstring< K, N, forShared, A > &other)
Assignment operator to another string of type lstring.
Definition sstring.h:5462
bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:5511
my_type & operator=(const StrExprForType< K > auto &expr)
String expression assignment operator.
Definition sstring.h:5487
static my_type vformat(simple_str< K > fmtString, T &&... args)
Get a string formatted with std::vformat.
Definition sstring.h:5559
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:5450
const K * symbols() const noexcept
Pointer to characters in the string.
Definition sstring.h:5503
sstring(T &&s, Args &&... args)
Initialize from a string literal.
Definition sstring.h:5403
size_t length() const noexcept
Line length.
Definition sstring.h:5507
sstring(my_type &&other) noexcept
Move constructor.
Definition sstring.h:5339
A class with basic constant string algorithms.
Definition sstring.h:611
bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition sstring.h:1806
T as_int() const noexcept
Convert a string to a number of the given type.
Definition sstring.h:1432
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition sstring.h:1686
size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string.
Definition sstring.h:1143
auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition sstring.h:901
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition sstring.h:1854
R trimmed() const
Get a string with whitespace removed on the left and right.
Definition sstring.h:1946
str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition sstring.h:696
size_t find_last(K s, size_t offset=-1) const noexcept
Find the last occurrence of a character in this string.
Definition sstring.h:1286
size_t find_or_all(K s, size_t offset=0) const noexcept
Find a character in this string or the end of a string.
Definition sstring.h:1217
R trimmed_left(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left.
Definition sstring.h:2000
size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition sstring.h:1346
size_t find(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string.
Definition sstring.h:1030
bool starts_with_iu(str_piece prefix) const noexcept
Whether the string starts with the given substring, case-insensitive Unicode characters of the first ...
Definition sstring.h:1725
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Get a copy of the string with occurrences of substrings replaced.
Definition sstring.h:1898
int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition sstring.h:936
expr_replaces< K, N - 1, L - 1 > replace_init(T &&pattern, M &&repl) const
Get a string expression that produces a string with replaced substrings given by string literals.
Definition sstring.h:1913
R uppered() const
Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1866
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition sstring.h:761
std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition sstring.h:1318
size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string, or the end of a string.
Definition sstring.h:1171
T splitf(str_piece delimeter, const Op &beforeFunc, size_t offset=0) const
Split a string into parts at a given delimiter, possibly applying a functor to each substring.
Definition sstring.h:1644
R trimmed_right_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition sstring.h:2174
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition sstring.h:1743
void as_number(T &t) const
Convert a string to an integer.
Definition sstring.h:1537
int compare_iu(str_piece text) const noexcept
Compare strings character by character without taking into account the case of Unicode characters of ...
Definition sstring.h:976
size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition sstring.h:1198
size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string.
Definition sstring.h:1130
bool less_iu(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:999
R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition sstring.h:2092
R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition sstring.h:2106
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Whether the string ends with the specified substring, case-insensitive Unicode characters of the firs...
Definition sstring.h:1799
std::basic_string< K > to_string() const noexcept
Convert to std::string.
Definition sstring.h:716
bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition sstring.h:1185
R uppered_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition sstring.h:1842
Splitter< K > splitter(str_piece delimeter) const
Retrieve a Splitter object by the given splitter, which allows sequential get substrings using the ne...
Definition sstring.h:2458
my_type str_mid(size_t from, size_t len=-1) const
Get part of a string with an object of the same type to which the method is applied,...
Definition sstring.h:1400
R trimmed_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition sstring.h:2037
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition sstring.h:1511
std::vector< size_t > find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Find all occurrences of a substring in this string.
Definition sstring.h:1273
R trimmed_right() const
Get a string with whitespace removed on the right.
Definition sstring.h:1970
R trimmed_right_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition sstring.h:2075
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition sstring.h:1758
R trimmed_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition sstring.h:2138
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "simple_str".
Definition sstring.h:742
bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition sstring.h:891
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition sstring.h:881
R lowered() const
Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1878
R trimmed_right(T &&pattern) const
Get a string with the characters specified by the string literal removed from the right.
Definition sstring.h:2015
convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition sstring.h:1465
size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string or the end of the string.
Definition sstring.h:1080
bool equal_ia(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of ASCII characters.
Definition sstring.h:948
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition sstring.h:647
std::optional< double > to_double() const noexcept
Convert string to double.
Definition sstring.h:1475
std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition sstring.h:1359
R trimmed_left_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition sstring.h:2156
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition sstring.h:665
R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition sstring.h:2120
R trimmed_left() const
Get a string with whitespace removed on the left.
Definition sstring.h:1958
std::basic_string_view< K > to_sv() const noexcept
Convert to std::string_view.
Definition sstring.h:706
size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the end of the first occurrence of a substring in this string, or the end of a string.
Definition sstring.h:1094
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition sstring.h:821
K at(ptrdiff_t idx) const
Get the character at the given position.
Definition sstring.h:803
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition sstring.h:832
size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string or the end of the string.
Definition sstring.h:1157
R trimmed_left_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition sstring.h:2056
void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Call a functor on all found occurrences of a substring in this string.
Definition sstring.h:1252
size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const noexcept
Find the beginning of the first occurrence of a substring in this string or throw an exception.
Definition sstring.h:1050
bool operator!() const noexcept
Check for emptiness.
Definition sstring.h:790
size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character not from the given character set.
Definition sstring.h:1374
size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character not from the given character set.
Definition sstring.h:1333
bool equal_iu(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of the Unicode chara...
Definition sstring.h:988
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Whether the string ends with the specified substring in a case-insensitive ASCII character.
Definition sstring.h:1784
my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Get a substring. Works similarly to operator(), only the result is the same type as the method applie...
Definition sstring.h:1387
T split(str_piece delimeter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition sstring.h:1660
size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Find the end of the occurrence of a substring in this string.
Definition sstring.h:1066
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition sstring.h:872
size_t size() const
The size of the string in characters.
Definition sstring.h:677
R trimmed(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left and right.
Definition sstring.h:1985
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition sstring.h:861
size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition sstring.h:1305
bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:959
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Get the substring simple_str from position from to position to (not including it).
Definition sstring.h:783
void as_number(double &t) const
Convert string to double.
Definition sstring.h:1546
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Whether the string begins with the given substring in a case-insensitive ASCII character.
Definition sstring.h:1711
Base class for working with mutable strings.
Definition sstring.h:3400
Impl & insert(size_t to, const A &expr)
Insert a string expression at the specified position.
Definition sstring.h:3940
Impl & operator<<=(const Op &fillFunction)
Fills a string with the fill method after the end of the string.
Definition sstring.h:4219
Impl & append(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:3824
Impl & operator<<(const Op &fillFunction)
Calls the passed functor, passing a reference to itself.
Definition sstring.h:4232
Impl & trim(str_piece pattern)
Remove characters included in the passed string at the beginning and end of the line.
Definition sstring.h:3634
Impl & upper_only_ascii()
Convert ASCII characters to uppercase.
Definition sstring.h:3698
Impl & lower_only_ascii()
Convert ASCII characters to lowercase.
Definition sstring.h:3713
Impl & trim_left()
Remove whitespace at the beginning of a line.
Definition sstring.h:3536
Impl & append_printf(const K *format, T &&... args)
Appends sprintf formatted output to the end of the line.
Definition sstring.h:4321
Impl & trim_right_with_wpaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the end of a string.
Definition sstring.h:3623
Impl & trim_left(str_piece pattern)
Remove characters included in the passed string at the beginning of the line.
Definition sstring.h:3645
Impl & trim_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning and end of th...
Definition sstring.h:3597
Impl & prepend(str_piece other)
Add another line to the beginning of the line.
Definition sstring.h:3964
Impl & printf(const K *format, T &&... args)
Formats a string using sprintf.
Definition sstring.h:4305
Impl & with(const Op &fillFunction, Args &&... args)
Call a functor with a string and passed arguments.
Definition sstring.h:4540
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line, writing no more than the specified numb...
Definition sstring.h:4526
Impl & operator<<(const Op &fillFunction)
Fills a string with the fill method from position zero.
Definition sstring.h:4206
Impl & vformat(str_piece format, T &&... args)
Formats a string using std::vformat.
Definition sstring.h:4474
Impl & change(size_t from, size_t len, const A &expr)
Replace a piece of string with a string expression.
Definition sstring.h:3913
Impl & fill(size_t from, const Op &fillFunction)
Fill a string buffer using a functor.
Definition sstring.h:4178
Impl & change(size_t from, size_t len, str_piece other)
Replace a piece of string with another string.
Definition sstring.h:3897
Impl & remove(size_t from, size_t len)
Remove part of a line.
Definition sstring.h:3953
Impl & trim_left_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning of a line.
Definition sstring.h:3610
Impl & trim_left(T &&pattern)
Remove characters included in a string literal at the beginning of the line.
Definition sstring.h:3571
Impl & trim_right(T &&pattern)
Remove characters included in a string literal at the end of the line.
Definition sstring.h:3584
Impl & trim_right_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace at the end of the string.
Definition sstring.h:3689
Impl & append_in(size_t pos, str_piece other)
Add another line starting at the given position.
Definition sstring.h:3864
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Appends std::vformat formatted output starting at the specified position.
Definition sstring.h:4407
Impl & trim_left_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace, at the beginning of the strin...
Definition sstring.h:3678
Impl & insert(size_t to, str_piece other)
Insert a line at the specified position.
Definition sstring.h:3926
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Formats a string using std::vformat up to the specified size.
Definition sstring.h:4508
my_type & format(const FmtString< K, T... > &pattern, T &&... args)
Definition sstring.h:4442
Impl & printf_from(size_t from, const K *format, T &&... args)
Appends sprintf formatted output starting at the specified position.
Definition sstring.h:4251
Impl & operator+=(str_piece other)
Add another line to the end of the line.
Definition sstring.h:3835
Impl & trim_right()
Remove whitespace from the end of a line.
Definition sstring.h:3545
Impl & append(str_piece other)
Add another line to the end of the line.
Definition sstring.h:3812
Impl & trim_right(str_piece pattern)
Remove characters included in the passed string from the end of the string.
Definition sstring.h:3656
Impl & trim_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace characters,...
Definition sstring.h:3667
K * str() noexcept
Get a pointer to the string buffer.
Definition sstring.h:3509
my_type & format_from(size_t from, const FmtString< K, T... > &format, T &&... args)
Definition sstring.h:4378
Impl & prepend(const A &expr)
Add a string expression to the beginning of a line.
Definition sstring.h:3976
Impl & trim()
Remove whitespace from the beginning and end of a line.
Definition sstring.h:3527
Impl & lower()
Convert first plane characters (<0xFFFF) to lowercase Unicode.
Definition sstring.h:3747
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Replace occurrences of a substring with another string.
Definition sstring.h:3993
Impl & upper()
Convert first plane characters (<0xFFFF) to uppercase Unicode.
Definition sstring.h:3732
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Copy the source string, replacing occurrences of substrings with another string.
Definition sstring.h:4116
Impl & append_vformatted(str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line.
Definition sstring.h:4490
Impl & append_formatted(const FmtString< K, T... > &format, T &&... args)
Appends std::format-formatted output to the end of the line.
Definition sstring.h:4458
Impl & append_in(size_t pos, const A &expr)
Add a string expression starting at the given position.
Definition sstring.h:3882
Impl & trim(T &&pattern)
Remove characters included in a string literal at the beginning and end of the line.
Definition sstring.h:3558
Impl & operator+=(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:3847
constexpr str_storable(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:2976
constexpr str_storable(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:2996
allocator_t & allocator()
Get the allocator.
Definition sstring.h:2856
static my_type uppered_from(const From &f, Args &&... args)
Create a copy of the passed string in uppercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:3261
constexpr str_storable(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:3044
static my_type lowered_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:3278
s_str_nt to_nts(size_t from=0) const
Get simple_str_nt starting at the given character.
Definition sstring.h:3124
static my_type uppered_only_ascii_from(const From &f, Args &&... args)
Create a string copy of the passed in uppercase ASCII characters.
Definition sstring.h:3231
constexpr str_storable(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Create an empty object.
Definition sstring.h:2961
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase ASCII characters.
Definition sstring.h:3244
static my_type join(const T &strings, s_str delimeter, bool tail=false, bool skip_empty=false, Args &&... args)
Concatenate strings from the container into one string.
Definition sstring.h:3180
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Create a copy of the passed string with substrings replaced.
Definition sstring.h:3299
str_storable(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:3020
str_storable(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:3069
Concept of a memory management type.
Definition sstring.h:3309
The concept of a string expression of a given character type.
Definition strexpr.h:339
A type concept that cannot modify a stored string.
Definition sstring.h:2800
A type concept that can modify a stored string.
Definition sstring.h:2793
A type concept that can store a string.
Definition sstring.h:2783
constexpr auto e_repl(simple_str< K > w, T &&p, X &&r)
Get a string expression that generates a string with all occurrences of a given substring replaced.
Definition sstring.h:5956
constexpr auto e_real(double t)
Convert a double number to a string expression.
Definition sstring.h:5790
constexpr auto e_num(T t)
Convert an integer to a string expression.
Definition sstring.h:5696
constexpr auto operator+(const A &a, T s)
Concatenation operator for string expression and integer.
Definition sstring.h:5660
auto e_repl_const_symbols(simple_str< K > src, Repl &&... other)
Returns a string expression that generates a string containing the given characters replaced with giv...
Definition sstring.h:6520
constexpr auto e_join(const T &s, L &&d)
Get a string expression concatenating the strings in the container into a single string with the give...
Definition sstring.h:5867
Library namespace.
Definition sstring.cpp:10
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Hash dictionary type for char16_t strings, case sensitive search.
Definition sstring.h:7233
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7199
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition sstring.h:256
@ Overflow
Переполнение, число не помещается в заданный тип
Definition sstring.h:259
@ Success
Успешно
Definition sstring.h:257
@ NotNumber
Вообще не число
Definition sstring.h:260
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition sstring.h:258
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:7264
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7258
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:7226
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7219
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Type of hash dictionary for char strings, case sensitive search.
Definition sstring.h:7193
auto e_utf(simple_str< From > from)
Returns a string expression that converts a string of one character type to another type,...
Definition sstring.h:2774
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:7245
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7239
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Hash dictionary type for wchar_t strings, case sensitive search.
Definition sstring.h:7212
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
Definition sstring.h:7205
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Hash dictionary type for char32_t strings, case sensitive search.
Definition sstring.h:7252
An object that allows you to sequentially copy content into a buffer of a given size.
Definition sstring.h:7124
bool is_end()
Check that the data has not yet run out.
Definition sstring.h:7131
size_t store(K *buffer, size_t size)
Save the next portion of data to the buffer.
Definition sstring.h:7144
constexpr expr_replace_symbols(simple_str< K > source, const std::vector< std::pair< K, simple_str< K > > > &repl)
Expression constructor.
Definition sstring.h:6144
constexpr expr_replaced(simple_str< K > w, simple_str< K > p, simple_str< K > r)
Constructor.
Definition sstring.h:5995
String expression to convert strings to different UTF types.
Definition sstring.h:2747
A class that claims to refer to a null-terminated string.
Definition sstring.h:2329
constexpr simple_str_nt(S &&s) noexcept
Constructor that allows you to initialize std::string and std::string_view objects provided that they...
Definition sstring.h:2368
my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition sstring.h:2388
simple_str_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition sstring.h:2355
The simplest immutable non-owning string class.
Definition sstring.h:2201
constexpr simple_str(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:2215
bool is_part_of(simple_str< K > other) const noexcept
Check if a string is part of another string.
Definition sstring.h:2267
my_type & remove_prefix(size_t delta)
Shifts the start of a line by the specified number of characters.
Definition sstring.h:2289
constexpr size_t length() const noexcept
Get the length of the string.
Definition sstring.h:2235
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition sstring.h:2242
constexpr simple_str(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:2220
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition sstring.h:2249
constexpr simple_str(S &&s) noexcept
Constructor that allows you to initialize std::string and std::string_view objects provided that they...
Definition sstring.h:2230
K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition sstring.h:2278
my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition sstring.h:2302
bool is_same(simple_str< K > other) const noexcept
Check if two objects point to the same string.
Definition sstring.h:2258
Concatenation of a reference to a string expression and the value of the string expression.
Definition strexpr.h:440