simstr 1.0
Yet another strings library
 
Загрузка...
Поиск...
Не найдено
sstring.h
1/*
2 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
3 * ver. 1.0
4 * Классы для работы со строками
5 */
6
13#pragma once
14#ifndef __has_declspec_attribute
15#define __has_declspec_attribute(x) 0
16#endif
17
18#ifdef SIMSTR_SHARED
19 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
20 #ifdef SIMSTR_EXPORT
21 #define SIMSTR_API __declspec(dllexport)
22 #else
23 #define SIMSTR_API __declspec(dllimport)
24 #endif
25 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
26 #define SIMSTR_API __attribute__((visibility("default")))
27 #else
28 #define SIMSTR_API
29 #endif
30#else
31 #define SIMSTR_API
32#endif
33const bool isWindowsOs = // NOLINT
34#ifdef _WIN32
35 true
36#else
37 false
38#endif
39 ;
40const bool isx64 = sizeof(void*) == 8; // NOLINT
41
42#ifdef _MSC_VER
43#define _no_unique_address msvc::no_unique_address
44#else
45#define _no_unique_address no_unique_address
46#endif
47
48#if defined __has_builtin
49# if __has_builtin (__builtin_mul_overflow) && __has_builtin (__builtin_add_overflow)
50# define HAS_BUILTIN_OVERFLOW
51# endif
52#endif
53
54#include "strexpr.h"
55
56#include <cstddef>
57#include <string>
58#include <type_traits>
59#include <vector>
60#include <format>
61#include <unordered_map>
62#include <tuple>
63#include <limits>
64#include <cstdint>
65#include <atomic>
66#include <memory>
67#include <string.h>
68#include <iostream>
69
70#ifdef _WIN32
71#include <stdio.h>
72#endif
73
74#ifdef _MSC_VER
75// warning C4201 : nonstandard extension used : nameless struct / union
76#pragma warning(disable : 4201)
77#endif
78
79namespace simstr {
80
81template<typename T>
82struct unicode_traits {}; // NOLINT
83
84template<>
85struct unicode_traits<u8s> {
86 // Эти операции с utf-8 могут изменить длину строки
87 // Поэтому их специализации отличаются
88 // В функцию помимо текста и адреса буфера для записи передается размер буфера
89 // Возвращает длину получающейся строки.
90 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
91 // обработанные символы, для повторного возобновления работы,
92 // а для оставшихся символов считается нужный размер буфера.
93 static SIMSTR_API size_t upper(const u8s*& src, size_t lenStr, u8s*& dest, size_t lenBuf);
94 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
95
96 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
97
98 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
99 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
100};
101
102template<>
103struct unicode_traits<u16s> {
104 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
105 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
106
107 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
108 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
109 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
110};
111
112template<>
113struct unicode_traits<u32s> {
114 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
115 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
116
117 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
118 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
119 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
120};
121
122template<>
123struct unicode_traits<wchar_t> {
124 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
125 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
126 }
127 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
128 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
129 }
130
131 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
132 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
133 }
134 static size_t hashia(const wchar_t* src, size_t s) {
135 return unicode_traits<wchar_type>::hashia(to_w(src), s);
136 }
137 static size_t hashiu(const wchar_t* src, size_t s) {
138 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
139 }
140};
141
142namespace str {
143constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
144} // namespace str
145
146template<typename K>
147struct ch_traits : std::char_traits<K>{};
148
149template<size_t N>
150concept is_const_pattern = N > 1 && N <= 17;
151
152template<typename K, size_t I>
153struct _ascii_mask { // NOLINT
154 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
155};
156
157template<typename K>
158struct _ascii_mask<K, 0> {
159 constexpr static const size_t value = 0;
160};
161
162template<typename K>
163struct ascii_mask { // NOLINT
164 using uns = std::make_unsigned_t<K>;
165 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
166 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
167};
168
169template<typename K>
170constexpr inline bool isAsciiUpper(K k) {
171 return k >= 'A' && k <= 'Z';
172}
173
174template<typename K>
175constexpr inline bool isAsciiLower(K k) {
176 return k >= 'a' && k <= 'z';
177}
178
179template<typename K>
180constexpr inline K makeAsciiLower(K k) {
181 return isAsciiUpper(k) ? k | 0x20 : k;
182}
183
184template<typename K>
185constexpr inline K makeAsciiUpper(K k) {
186 return isAsciiLower(k) ? k & ~0x20 : k;
187}
188
189enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
190template<TrimSides S, typename K, size_t N, bool withSpaces = false>
191struct trim_operator;
192
193template<typename K, size_t N, size_t L>
194struct expr_replaces;
195
196template<typename T>
197concept FromIntNumber =
198 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;
199
200template<typename T>
201concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
202
203#if defined(_MSC_VER) && _MSC_VER <= 1933
204template<typename K, typename... Args>
205using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
206#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
207template<typename K, typename... Args>
208using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
209#else
210template<typename K, typename... Args>
211using FmtString = std::basic_string_view<K>;
212#endif
213
214template<typename K, bool I, typename T>
215struct need_sign { // NOLINT
216 bool sign;
217 need_sign(T& t) : sign(t < 0) {
218 if (sign && t != std::numeric_limits<T>::min())
219 t = -t;
220 }
221 void after(K*& ptr) {
222 if (sign)
223 *--ptr = '-';
224 }
225};
226
227template<typename K, typename T>
228struct need_sign<K, false, T> {
229 need_sign(T&) {}
230 void after(K*&) {}
231};
232
242
243template<bool CanNegate, bool CheckOverflow, typename T>
244struct result_type_selector { // NOLINT
245 using type = T;
246};
247
248template<typename T>
249struct result_type_selector<true, false, T> {
250 using type = std::make_unsigned_t<T>;
251};
252
253template<unsigned Base>
254constexpr unsigned digit_width() {
255 if (Base <=2) {
256 return 1;
257 }
258 if (Base <= 4) {
259 return 2;
260 }
261 if (Base <= 8) {
262 return 3;
263 }
264 if (Base <= 16) {
265 return 4;
266 }
267 if (Base <= 32) {
268 return 5;
269 }
270 return 6;
271}
272
273template<typename T, unsigned Base>
274constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
275
276struct int_convert { // NOLINT
277private:
278 inline static const uint8_t NUMBERS[] = {
279 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,
280 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,
281 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,
282 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,
283 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,
284 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,
285 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,
286 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,
287 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,
288 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
289
290 template<typename K, unsigned Base>
291 static uint8_t toDigit(K s) {
292 auto us = static_cast<std::make_unsigned_t<K>>(s);
293 if constexpr (Base <= 10) {
294 return us - '0';
295 } else {
296 if constexpr (sizeof(K) == 1) {
297 return NUMBERS[us];
298 } else {
299 return us < 256 ? NUMBERS[us] : 255;
300 }
301 }
302 }
303
304 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
305 requires(Base != 0)
306 static std::tuple<T, IntConvertResult, size_t> parse(const K* start, const K* current, const K* end, bool negate) {
307 using u_type = std::make_unsigned_t<T>;
308 #ifndef HAS_BUILTIN_OVERFLOW
309 u_type maxMult = 0, maxAdd = 0;
310 if constexpr (CheckOverflow) {
311 maxMult = std::numeric_limits<u_type>::max() / Base;
312 maxAdd = std::numeric_limits<u_type>::max() % Base;
313 }
314 #endif
315 u_type number = 0;
316 unsigned maxDigits = max_overflow_digits<u_type, Base>;
318 const K* from = current;
319
320 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
321
322 if (no_need_check_o_f) {
323 for (;;) {
324 const unsigned char digit = toDigit<K, Base>(*current);
325 if (digit >= Base) {
326 break;
327 }
328 number = number * Base + digit;
329 if (++current == end) {
331 break;
332 }
333 }
334 } else {
335 for (;maxDigits; maxDigits--) {
336 const unsigned char digit = toDigit<K, Base>(*current);
337 if (digit >= Base) {
338 break;
339 }
340 number = number * Base + digit;
341 ++current;
342 }
343 if (!maxDigits) {
344 // Прошли все цифры, дальше надо с проверкой на overflow
345 for (;;) {
346 const unsigned char digit = toDigit<K, Base>(*current);
347 if (digit >= Base) {
348 break;
349 }
350 #ifdef HAS_BUILTIN_OVERFLOW
351 if (__builtin_mul_overflow(number, Base, &number) ||
352 __builtin_add_overflow(number, digit, &number)) {
353 #else
354 if (number < maxMult || (number == maxMult && number < maxAdd)) {
355 number = number * Base + digit;
356 } else {
357 #endif
359 while(++current < end) {
360 if (toDigit<K, Base>(*current) >= Base) {
361 break;
362 }
363 }
364 break;
365 }
366 if (++current == end) {
368 break;
369 }
370 }
371 }
372 }
373 T result;
374 if constexpr (std::is_signed_v<T>) {
375 result = negate ? 0 - number : number;
376 if constexpr (CheckOverflow) {
377 if (error != IntConvertResult::Overflow) {
378 if (number > std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
380 }
381 }
382 }
383 } else {
384 result = number;
385 }
386 if (error == IntConvertResult::NotNumber && current > from) {
388 }
389 return {result, error, current - start};
390 }
391public:
392 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
393 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
394 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
395 requires(Base == -1 || (Base < 37 && Base != 1))
396 static std::tuple<T, IntConvertResult, size_t> to_integer(const K* start, size_t len) noexcept {
397 const K *ptr = start, *end = ptr + len;
398 bool negate = false;
399 if constexpr (SkipWs) {
400 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
401 ptr++;
402 }
403 if (ptr != end) {
404 if constexpr (std::is_signed_v<T>) {
405 if constexpr (AllowSign) {
406 // Может быть число, +число или -число
407 if (*ptr == '+') {
408 ptr++;
409 } else if (*ptr == '-') {
410 negate = true;
411 ptr++;
412 }
413 } else {
414 // Может быть число или -число
415 if (*ptr == '-') {
416 negate = true;
417 ptr++;
418 }
419 }
420 } else if constexpr (AllowSign) {
421 // Может быть число или +число
422 if (*ptr == '+') {
423 ptr++;
424 }
425 }
426 }
427 if (ptr != end) {
428 if constexpr (Base == 0 || Base == -1) {
429 if (*ptr == '0') {
430 ptr++;
431 if (ptr != end) {
432 if (*ptr == 'x' || *ptr == 'X') {
433 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
434 }
435 if constexpr (Base == -1) {
436 if (*ptr == 'b' || *ptr == 'B') {
437 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
438 }
439 if (*ptr == 'o' || *ptr == 'O') {
440 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
441 }
442 }
443 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
444 }
445 return {0, IntConvertResult::Success, ptr - start};
446 }
447 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
448 } else
449 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
450 }
451 return {0, IntConvertResult::NotNumber, ptr - start};
452 }
453};
454
455template<typename K>
456class Splitter;
457
458template<typename K, typename Impl, bool Mutable> class buffer_pointers;
459
460template<typename K, typename Impl>
461class buffer_pointers<K, Impl, false> {
462 const Impl& d() const { return *static_cast<const Impl*>(this); }
463public:
468 const K* c_str() const { return d().symbols(); }
473 const K* data() const { return d().symbols(); }
478 const K* begin() const { return d().symbols(); }
483 const K* end() const { return d().symbols() + d().length(); }
484};
485
486template<typename K, typename Impl>
487class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
488 Impl& d() { return *static_cast<Impl*>(this); }
489 using base = buffer_pointers<K, Impl, false>;
490public:
495 const K* data() const { return base::data(); }
500 const K* begin() const { return base::begin(); }
505 const K* end() const { return base::end(); }
510 K* data() { return d().str(); }
515 K* begin() { return d().str(); }
520 K* end() { return d().str() + d().length(); }
521};
522
536template<typename K, typename StrRef, typename Impl, bool Mutable>
537class str_algs : public buffer_pointers<K, Impl, Mutable> {
538 const Impl& d() const noexcept {
539 return *static_cast<const Impl*>(this);
540 }
541 size_t _len() const noexcept {
542 return d().length();
543 }
544 const K* _str() const noexcept {
545 return d().symbols();
546 }
547 bool _is_empty() const noexcept {
548 return d().is_empty();
549 }
550
551public:
552 using symb_type = K;
553 using str_piece = StrRef;
554 using traits = ch_traits<K>;
555 using uni = unicode_traits<K>;
556 using uns_type = std::make_unsigned_t<K>;
557 using my_type = Impl;
558 using base = str_algs<K, StrRef, Impl, Mutable>;
559 // Пустой конструктор
560 str_algs() = default;
561
569 constexpr K* place(K* ptr) const noexcept {
570 size_t myLen = _len();
571 if (myLen) {
572 traits::copy(ptr, _str(), myLen);
573 return ptr + myLen;
574 }
575 return ptr;
576 }
577
583 void copy_to(K* buffer, size_t bufSize) {
584 size_t tlen = std::min(_len(), bufSize - 1);
585 if (tlen)
586 traits::copy(buffer, _str(), tlen);
587 buffer[tlen] = 0;
588 }
589
593 size_t size() const {
594 return _len();
595 }
596
601 constexpr operator str_piece() const noexcept {
602 return str_piece{_str(), _len()};
603 }
604
608 str_piece to_str() const noexcept {
609 return {_str(), _len()};
610 }
611
615 std::string_view to_sv() const noexcept {
616 return {_str(), _len()};
617 }
618
622 std::string to_string() const noexcept {
623 return {_str(), _len()};
624 }
625
641 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
642 size_t myLen = _len(), idxStart = from >= 0 ? from : myLen + from, idxEnd = len > 0 ? idxStart + len : myLen > -len ? myLen + len : 0;
643 if (idxEnd > myLen)
644 idxEnd = myLen;
645 if (idxStart > idxEnd)
646 idxStart = idxEnd;
647 return str_piece{_str() + idxStart, idxEnd - idxStart};
648 }
649
655 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
656 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
657 if (idxEnd > myLen)
658 idxEnd = myLen;
659 if (idxStart > idxEnd)
660 idxStart = idxEnd;
661 return str_piece{_str() + idxStart, idxEnd - idxStart};
662 }
663
671 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
672 return str_piece{_str() + from, to - from};
673 }
674
677 bool operator!() const noexcept {
678 return _is_empty();
679 }
680
686 K at(ptrdiff_t idx) const {
687 return _str()[idx >= 0 ? idx : _len() + idx];
688 }
689 // Сравнение строк
690 constexpr int compare(const K* text, size_t len) const {
691 size_t myLen = _len();
692 int cmp = traits::compare(_str(), text, std::min(myLen, len));
693 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
694 }
700 constexpr int compare(str_piece o) const {
701 return compare(o.symbols(), o.length());
702 }
703
708 constexpr int strcmp(const K* text) const {
709 size_t myLen = _len(), idx = 0;
710 const K* ptr = _str();
711 for (; idx < myLen; idx++) {
712 uns_type s1 = (uns_type)text[idx];
713 if (!s1) {
714 return 1;
715 }
716 uns_type s2 = (uns_type)ptr[idx];
717 if (s1 < s2) {
718 return 1;
719 } else if (s1 > s2) {
720 return -1;
721 }
722 }
723 return text[idx] == 0 ? 0 : -1;
724 }
725
726 constexpr bool equal(const K* text, size_t len) const noexcept {
727 return len == _len() && traits::compare(_str(), text, len) == 0;
728 }
734 constexpr bool equal(str_piece other) const noexcept {
735 return equal(other.symbols(), other.length());
736 }
737
742 constexpr bool operator==(const base& other) const noexcept {
743 return equal(other._str(), other._len());
744 }
745
749 constexpr auto operator<=>(const base& other) const noexcept {
750 return compare(other._str(), other._len()) <=> 0;
751 }
752
756 template<typename T, size_t N = const_lit_for<K, T>::Count>
757 bool operator==(T&& other) const noexcept {
758 return N - 1 == _len() && traits::compare(_str(), other, N - 1) == 0;
759 }
760
765 template<typename T, size_t N = const_lit_for<K, T>::Count>
766 auto operator<=>(T&& other) const noexcept {
767 size_t myLen = _len();
768 int cmp = traits::compare(_str(), other, std::min(myLen, N - 1));
769 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
770 return res <=> 0;
771 }
772
773 // Сравнение ascii строк без учёта регистра
774 int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
775 if (!len)
776 return _is_empty() ? 0 : 1;
777 size_t myLen = _len(), checkLen = std::min(myLen, len);
778 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
779 while (checkLen--) {
780 uns_type s1 = *ptr1++, s2 = *ptr2++;
781 if (s1 == s2)
782 continue;
783 s1 = makeAsciiLower(s1);
784 s2 = makeAsciiLower(s2);
785 if (s1 > s2)
786 return 1;
787 else if (s1 < s2)
788 return -1;
789 }
790 return myLen == len ? 0 : myLen > len ? 1 : -1;
791 }
797 int compare_ia(str_piece text) const noexcept { // NOLINT
798 return compare_ia(text.symbols(), text.length());
799 }
800
806 bool equal_ia(str_piece text) const noexcept { // NOLINT
807 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
808 }
809
814 bool less_ia(str_piece text) const noexcept { // NOLINT
815 return compare_ia(text.symbols(), text.length()) < 0;
816 }
817
818 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
819 if (!len)
820 return _is_empty() ? 0 : 1;
821 return uni::compareiu(_str(), _len(), text, len);
822 }
828 int compare_iu(str_piece text) const noexcept { // NOLINT
829 return compare_iu(text.symbols(), text.length());
830 }
831
837 bool equal_iu(str_piece text) const noexcept { // NOLINT
838 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
839 }
840
845 bool less_iu(str_piece text) const noexcept { // NOLINT
846 return compare_iu(text.symbols(), text.length()) < 0;
847 }
848
849 size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
850 size_t lenText = _len();
851 // Образец, не вмещающийся в строку и пустой образец не находим
852 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
853 return str::npos;
854 lenPattern--;
855 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
856 pattern++;
857 for (const K* fnd = text + offset;; ++fnd) {
858 fnd = traits::find(fnd, last - fnd, first);
859 if (!fnd)
860 return str::npos;
861 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
862 return static_cast<size_t>(fnd - text);
863 }
864 }
871 size_t find(str_piece pattern, size_t offset = 0) const noexcept {
872 return find(pattern.symbols(), pattern.length(), offset);
873 }
874
882 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
883 size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
884 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
885 return fnd;
886 }
887 throw Exc(std::forward<Args>(args)...);
888 }
889
895 size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
896 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
897 return fnd == str::npos ? fnd : fnd + pattern.length();
898 }
899
905 size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
906 auto fnd = find(pattern.symbols(), pattern.length(), offset);
907 return fnd == str::npos ? _len() : fnd;
908 }
909
915 size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
916 auto fnd = find(pattern.symbols(), pattern.length(), offset);
917 return fnd == str::npos ? _len() : fnd + pattern.length();
918 }
919
920 size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
921 if (lenPattern == 1)
922 return find_last(pattern[0], offset);
923 size_t lenText = std::min(_len(), offset);
924 // Образец, не вмещающийся в строку и пустой образец не находим
925 if (!lenPattern || lenPattern > lenText)
926 return str::npos;
927
928 lenPattern--;
929 const K *text = _str() + lenPattern, last = pattern[lenPattern];
930 lenText -= lenPattern;
931 while(lenText) {
932 if (text[--lenText] == last) {
933 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
934 return lenText;
935 }
936 }
937 }
938 return str::npos;
939 }
946 size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
947 return find_last(pattern.symbols(), pattern.length(), offset);
948 }
949
955 size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
956 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
957 return fnd == str::npos ? fnd : fnd + pattern.length();
958 }
959
965 size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
966 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
967 return fnd == str::npos ? _len() : fnd;
968 }
969
975 size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
976 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
977 return fnd == str::npos ? _len() : fnd + pattern.length();
978 }
979
985 bool contains(str_piece pattern, size_t offset = 0) const noexcept {
986 return find(pattern, offset) != str::npos;
987 }
988
994 size_t find(K s, size_t offset = 0) const noexcept {
995 size_t len = _len();
996 if (offset < len) {
997 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
998 if (fnd)
999 return static_cast<size_t>(fnd - str);
1000 }
1001 return str::npos;
1002 }
1003
1009 size_t find_or_all(K s, size_t offset = 0) const noexcept {
1010 size_t len = _len();
1011 if (offset < len) {
1012 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
1013 if (fnd)
1014 return static_cast<size_t>(fnd - str);
1015 }
1016 return len;
1017 }
1018
1019 template<typename Op>
1020 void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
1021 if (!maxCount)
1022 maxCount--;
1023 while (maxCount-- > 0) {
1024 size_t fnd = find(pattern, patternLen, offset);
1025 if (fnd == str::npos)
1026 break;
1027 op(fnd);
1028 offset = fnd + patternLen;
1029 }
1030 }
1038 template<typename Op>
1039 void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
1040 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
1041 }
1042
1043 std::vector<size_t> find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
1044 std::vector<size_t> result;
1045 for_all_finded([&](auto f) { result.push_back(f); }, pattern, patternLen, offset, maxCount);
1046 return result;
1047 }
1055 std::vector<size_t> find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
1056 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
1057 }
1058
1064 size_t find_last(K s, size_t offset = -1) const noexcept {
1065 size_t len = std::min(_len(), offset);
1066 const K *text = _str();
1067 while (len > 0) {
1068 if (text[--len] == s)
1069 return len;
1070 }
1071 return str::npos;
1072 }
1073
1079 size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
1080 return std::string_view{_str(), _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
1081 }
1082
1088 std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
1089 const K* text = _str();
1090 size_t fnd = std::string_view{text, _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
1091 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
1092 }
1093
1099 size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
1100 return std::string_view{_str(), _len()}.find_first_not_of(std::string_view{pattern.str, pattern.len}, offset);
1101 }
1102
1108 size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
1109 return std::string_view{_str(), _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
1110 }
1111
1117 std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
1118 const K* text = _str();
1119 size_t fnd = std::string_view{text, _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
1120 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
1121 }
1122
1128 size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
1129 return std::string_view{_str(), _len()}.find_last_not_of(std::string_view{pattern.str, pattern.len}, offset);
1130 }
1131
1137 my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units
1138 return my_type{d()(from, len)};
1139 }
1140
1146 my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units
1147 return my_type{d().mid(from, len)};
1148 }
1149
1164 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
1165 T as_int() const noexcept {
1166 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
1167 return err == IntConvertResult::Overflow ? 0 : res;
1168 }
1169
1184 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
1185 std::tuple<T, IntConvertResult, size_t> to_int() const noexcept {
1186 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
1187 }
1188
1192 double to_double() const noexcept {
1193 static_assert(sizeof(K) == 1 || sizeof(K) == sizeof(wchar_t), "Only char and wchar available for conversion to double now");
1194 size_t len = _len();
1195 if (len) {
1196 const size_t copyLen = 64;
1197 K buf[copyLen + 1];
1198 const K* ptr = _str();
1199 if (ptr[len] != 0) {
1200 while (len && uns_type(*ptr) <= ' ') {
1201 len--;
1202 ptr++;
1203 }
1204 if (len) {
1205 len = std::min(copyLen, len);
1206 traits::copy(buf, ptr, len);
1207 buf[len] = 0;
1208 ptr = buf;
1209 }
1210 }
1211 if (len) {
1212#ifdef _MSC_VER
1213 static const _locale_t lc = _wcreate_locale(LC_NUMERIC, L"C");
1214 if constexpr (sizeof(K) == 1) {
1215 return _strtod_l(ptr, nullptr, lc);
1216 }
1217 if constexpr (sizeof(K) == sizeof(wchar_t)) {
1218 return _wcstod_l((const wchar_t*)ptr, nullptr, lc);
1219 }
1220#else
1221 if constexpr (sizeof(K) == 1) {
1222 return std::strtod(ptr, nullptr);
1223 } else if constexpr (sizeof(K) == sizeof(wchar_t)) {
1224 return std::wcstod((const wchar_t*)ptr, nullptr);
1225 }
1226#endif
1227 }
1228 }
1229 return 0.0;
1230 }
1231
1237 template<ToIntNumber T>
1238 void as_number(T& t) {
1239 t = as_int<T>();
1240 }
1241
1245 void as_number(double& t) {
1246 t = to_double();
1247 }
1248
1249 template<typename T, typename Op>
1250 T splitf(const K* delimeter, size_t lenDelimeter, const Op& beforeFunc, size_t offset) const {
1251 size_t mylen = _len();
1252 std::conditional_t<std::is_same_v<T, void>, char, T> results;
1253 str_piece me{_str(), mylen};
1254 for (int i = 0;; i++) {
1255 size_t beginOfDelim = find(delimeter, lenDelimeter, offset);
1256 if (beginOfDelim == str::npos) {
1257 str_piece last{me.symbols() + offset, me.length() - offset};
1258 if constexpr (std::is_invocable_v<Op, str_piece&>) {
1259 beforeFunc(last);
1260 }
1261 if constexpr (requires { results.emplace_back(last); }) {
1262 if (last.is_same(me)) {
1263 // Пробуем положить весь объект.
1264 results.emplace_back(d());
1265 } else {
1266 results.emplace_back(last);
1267 }
1268 } else if constexpr (requires { results.push_back(last); }) {
1269 if (last.is_same(me)) {
1270 // Пробуем положить весь объект.
1271 results.push_back(d());
1272 } else {
1273 results.push_back(last);
1274 }
1275 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
1276 if (i < std::size(results)) {
1277 if (last.is_same(me)) {
1278 // Пробуем положить весь объект.
1279 results[i] = d();
1280 } else
1281 results[i] = last;
1282 }
1283 }
1284 break;
1285 }
1286 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
1287 if constexpr (std::is_invocable_v<Op, str_piece&>) {
1288 beforeFunc(piece);
1289 }
1290 if constexpr (requires { results.emplace_back(piece); }) {
1291 results.emplace_back(piece);
1292 } else if constexpr (requires { results.push_back(piece); }) {
1293 results.push_back(piece);
1294 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
1295 if (i < std::size(results)) {
1296 results[i] = piece;
1297 if (i == results.size() - 1) {
1298 break;
1299 }
1300 }
1301 }
1302 offset = beginOfDelim + lenDelimeter;
1303 }
1304 if constexpr (!std::is_same_v<T, void>) {
1305 return results;
1306 }
1307 }
1324 template<typename T, typename Op>
1325 T splitf(str_piece delimeter, const Op& beforeFunc, size_t offset = 0) const {
1326 return splitf<T>(delimeter.symbols(), delimeter.length(), beforeFunc, offset);
1327 }
1328
1335 template<typename T>
1336 T split(str_piece delimeter, size_t offset = 0) const {
1337 return splitf<T>(delimeter.symbols(), delimeter.length(), 0, offset);
1338 }
1339
1345 Splitter<K> splitter(str_piece delimeter) const;
1346
1347 // Начинается ли эта строка с указанной подстроки
1348 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
1349 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
1350 }
1355 constexpr bool starts_with(str_piece prefix) const noexcept {
1356 return starts_with(prefix.symbols(), prefix.length());
1357 }
1358
1359 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
1360 size_t myLen = _len();
1361 if (myLen < len) {
1362 return false;
1363 }
1364 const K* ptr1 = _str();
1365 while (len--) {
1366 K s1 = *ptr1++, s2 = *prefix++;
1367 if (s1 == s2)
1368 continue;
1369 if (makeAsciiLower(s1) != makeAsciiLower(s2))
1370 return false;
1371 }
1372 return true;
1373 }
1378 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
1379 return starts_with_ia(prefix.symbols(), prefix.length());
1380 }
1381 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
1382 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
1383 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
1384 }
1389 bool starts_with_iu(str_piece prefix) const noexcept {
1390 return starts_with_iu(prefix.symbols(), prefix.length());
1391 }
1392
1393 // Является ли эта строка началом указанной строки
1394 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
1395 size_t myLen = _len();
1396 if (myLen > len)
1397 return false;
1398 return !myLen || 0 == traits::compare(text, _str(), myLen);
1399 }
1404 constexpr bool prefix_in(str_piece text) const noexcept {
1405 return prefix_in(text.symbols(), text.length());
1406 }
1407 // Заканчивается ли строка указанной подстрокой
1408 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
1409 size_t myLen = _len();
1410 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
1411 }
1416 constexpr bool ends_with(str_piece suffix) const noexcept {
1417 return ends_with(suffix.symbols(), suffix.length());
1418 }
1419 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
1420 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
1421 size_t myLen = _len();
1422 if (myLen < len) {
1423 return false;
1424 }
1425 const K* ptr1 = _str() + myLen - len;
1426 while (len--) {
1427 K s1 = *ptr1++, s2 = *suffix++;
1428 if (s1 == s2)
1429 continue;
1430 if (makeAsciiLower(s1) != makeAsciiLower(s2))
1431 return false;
1432 }
1433 return true;
1434 }
1439 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
1440 return ends_with_ia(suffix.symbols(), suffix.length());
1441 }
1442 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
1443 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
1444 size_t myLen = _len();
1445 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
1446 }
1451 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
1452 return ends_with_iu(suffix.symbols(), suffix.length());
1453 }
1454
1457 bool is_ascii() const noexcept {
1458 if (_is_empty())
1459 return true;
1460 const int sl = ascii_mask<K>::WIDTH;
1461 const size_t mask = ascii_mask<K>::VALUE;
1462 size_t len = _len();
1463 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
1464 if constexpr (sl > 1) {
1465 const size_t roundMask = sizeof(size_t) - 1;
1466 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
1467 if (*ptr++ > 127)
1468 return false;
1469 len--;
1470 }
1471 while (len >= sl) {
1472 if (*reinterpret_cast<const size_t*>(ptr) & mask)
1473 return false;
1474 ptr += sl;
1475 len -= sl;
1476 }
1477 }
1478 while (len--) {
1479 if (*ptr++ > 127)
1480 return false;
1481 }
1482 return true;
1483 }
1484
1489 template<typename R = my_type>
1491 return R::uppered_only_ascii_from(d());
1492 }
1493
1498 template<typename R = my_type>
1500 return R::lowered_only_ascii_from(d());
1501 }
1502
1507 template<typename R = my_type>
1508 R uppered() const {
1509 return R::uppered_from(d());
1510 }
1511
1516 template<typename R = my_type>
1517 R lowered() const {
1518 return R::lowered_from(d());
1519 }
1520
1529 template<typename R = my_type>
1530 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
1531 return R::replaced_from(d(), pattern, repl, offset, maxCount);
1532 }
1533
1540 template<typename T, size_t N = const_lit_for<K, T>::Count, typename M, size_t L = const_lit_for<K, M>::Count>
1541 expr_replaces<K, N - 1, L - 1> replace_init(T&& pattern, M&& repl) const {
1542 return expr_replaces<K, N - 1, L - 1>{d(), pattern, repl};
1543 }
1544
1545 template<StrType<K> From>
1546 static my_type make_trim_op(const From& from, const auto& opTrim) {
1547 str_piece sfrom = from, newPos = opTrim(sfrom);
1548 return newPos.is_same(sfrom) ? my_type{from} : my_type{newPos};
1549 }
1550 template<TrimSides S, StrType<K> From>
1551 static my_type trim_static(const From& from) {
1552 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
1553 }
1554
1555 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
1556 requires is_const_pattern<N>
1557 static my_type trim_static(const From& from, T&& pattern) {
1558 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
1559 }
1560
1561 template<TrimSides S, bool withSpaces, StrType<K> From>
1562 static my_type trim_static(const From& from, str_piece pattern) {
1563 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
1564 }
1570 template<typename R = str_piece>
1571 R trimmed() const {
1572 return R::template trim_static<TrimSides::TrimAll>(d());
1573 }
1574
1579 template<typename R = str_piece>
1580 R trimmed_left() const {
1581 return R::template trim_static<TrimSides::TrimLeft>(d());
1582 }
1583
1588 template<typename R = str_piece>
1589 R trimmed_right() const {
1590 return R::template trim_static<TrimSides::TrimRight>(d());
1591 }
1592
1598 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1599 requires is_const_pattern<N>
1600 R trimmed(T&& pattern) const {
1601 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
1602 }
1603
1609 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1610 requires is_const_pattern<N>
1611 R trimmed_left(T&& pattern) const {
1612 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
1613 }
1614
1620 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1621 requires is_const_pattern<N>
1622 R trimmed_right(T&& pattern) const {
1623 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
1624 }
1625 // Триминг по символам в литерале и пробелам
1626
1635 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1636 requires is_const_pattern<N>
1637 R trimmed_with_spaces(T&& pattern) const {
1638 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
1639 }
1640
1648 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1649 requires is_const_pattern<N>
1650 R trimmed_left_with_spaces(T&& pattern) const {
1651 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
1652 }
1653
1661 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
1662 requires is_const_pattern<N>
1663 R trimmed_right_with_spaces(T&& pattern) const {
1664 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
1665 }
1666 // Триминг по динамическому источнику
1667
1674 template<typename R = str_piece>
1675 R trimmed(str_piece pattern) const {
1676 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
1677 }
1678
1684 template<typename R = str_piece>
1685 R trimmed_left(str_piece pattern) const {
1686 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
1687 }
1688
1694 template<typename R = str_piece>
1695 R trimmed_right(str_piece pattern) const {
1696 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
1697 }
1698
1706 template<typename R = str_piece>
1707 R trimmed_with_spaces(str_piece pattern) const {
1708 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
1709 }
1710
1718 template<typename R = str_piece>
1719 R trimmed_left_with_spaces(str_piece pattern) const {
1720 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
1721 }
1722
1730 template<typename R = str_piece>
1731 R trimmed_right_with_spaces(str_piece pattern) const {
1732 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
1733 }
1734};
1735
1736#ifdef _MSC_VER
1737#define empty_bases __declspec(empty_bases)
1738#else
1739#define empty_bases
1740#endif
1741
1742/*
1743* Базовая структура с информацией о строке.
1744* Это структура для невладеющих строк.
1745* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
1746* в результате размер класса не увеличивается
1747*/
1748
1755template<typename K>
1756struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
1757 using symb_type = K;
1758 using my_type = simple_str<K>;
1759
1760 const symb_type* str;
1761 size_t len;
1762
1763 simple_str() = default;
1764
1768 template<typename T, size_t N = const_lit_for<K, T>::Count>
1769 constexpr simple_str(T&& v) noexcept : str(v), len(N - 1) {}
1773 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
1778 template<typename S>
1779 requires(std::is_same_v<S, std::string&> || std::is_same_v<S, const std::string&>
1780 || std::is_same_v<S, std::string_view&> || std::is_same_v<S, const std::string_view&>)
1781 constexpr simple_str(S&& s) noexcept : str(s.data()), len(s.length()) {}
1785 constexpr size_t length() const noexcept {
1786 return len;
1787 }
1788
1791 constexpr const symb_type* symbols() const noexcept {
1792 return str;
1793 }
1794
1797 constexpr bool is_empty() const noexcept {
1798 return len == 0;
1799 }
1800
1804 bool is_same(simple_str<K> other) const noexcept {
1805 return str == other.str && len == other.len;
1806 }
1807
1811 bool is_part_of(simple_str<K> other) const noexcept {
1812 return str >= other.str && str + len <= other.str + other.len;
1813 }
1814
1819 K operator[](size_t idx) const {
1820 return str[idx];
1821 }
1822
1827 my_type& remove_prefix(size_t delta) {
1828 str += delta;
1829 len -= delta;
1830 return *this;
1831 }
1832
1837 my_type& remove_suffix(size_t delta) {
1838 len -= delta;
1839 return *this;
1840 }
1841};
1842
1854template<typename K>
1855struct simple_str_nt : simple_str<K> {
1856 using symb_type = K;
1857 using my_type = simple_str_nt<K>;
1858 using base = simple_str<K>;
1859 using base::base;
1860
1861 constexpr static const K empty_string[1] = {0};
1862
1863 simple_str_nt() = default;
1873 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
1874 explicit simple_str_nt(T&& p) noexcept {
1875 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
1876 base::str = base::len ? p : empty_string;
1877 }
1878
1882 template<typename S>
1883 requires(std::is_same_v<S, std::string&> || std::is_same_v<S, const std::string&>
1884 || std::is_same_v<S, std::string_view&> || std::is_same_v<S, const std::string_view&>)
1885 constexpr simple_str_nt(S&& s) noexcept : base(s) {}
1886
1887 static const my_type empty_str;
1892 operator const K*() const noexcept {
1893 return base::str;
1894 }
1895
1900 my_type to_nts(size_t from) {
1901 if (from > base::len) {
1902 from = base::len;
1903 }
1904 return {base::str + from, base::len - from};
1905 }
1906};
1907
1908template<typename K>
1909inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
1910
1911using ssa = simple_str<u8s>;
1912using ssw = simple_str<wchar_t>;
1913using ssu = simple_str<u16s>;
1914using ssuu = simple_str<u32s>;
1915using stra = simple_str_nt<u8s>;
1916using strw = simple_str_nt<wchar_t>;
1917using stru = simple_str_nt<u16s>;
1918using struu = simple_str_nt<u32s>;
1919
1924template<typename K>
1925class Splitter {
1926 simple_str<K> text_;
1927 simple_str<K> delim_;
1928
1929public:
1930 Splitter(simple_str<K> text, simple_str<K> delim) : text_(text), delim_(delim) {}
1934 bool is_done() const {
1935 return text_.length() == str::npos;
1936 }
1937
1942 if (!text_.length()) {
1943 auto ret = text_;
1944 text_.str++;
1945 text_.len--;
1946 return ret;
1947 } else if (text_.length() == str::npos) {
1948 return {nullptr, 0};
1949 }
1950 size_t pos = text_.find(delim_), next = 0;
1951 if (pos == str::npos) {
1952 pos = text_.length();
1953 next = pos + 1;
1954 } else {
1955 next = pos + delim_.length();
1956 }
1957 simple_str<K> result{text_.str, pos};
1958 text_.str += next;
1959 text_.len -= next;
1960 return result;
1961 }
1962};
1963
1964template<typename K, typename StrRef, typename Impl, bool Mutable>
1966 return Splitter<K>{*this, delimeter};
1967}
1968
1969template<typename K, bool withSpaces>
1970struct CheckSpaceTrim {
1971 bool is_trim_spaces(K s) const {
1972 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
1973 }
1974};
1975template<typename K>
1976struct CheckSpaceTrim<K, false> {
1977 bool is_trim_spaces(K) const {
1978 return false;
1979 }
1980};
1981
1982template<typename K>
1983struct CheckSymbolsTrim {
1984 simple_str<K> symbols;
1985 bool is_trim_symbols(K s) const {
1986 return symbols.len != 0 && simple_str<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
1987 }
1988};
1989
1990template<typename K, size_t N>
1991struct CheckConstSymbolsTrim {
1992 const const_lit_to_array<K, N> symbols;
1993
1994 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
1995 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
1996
1997 bool is_trim_symbols(K s) const noexcept {
1998 return symbols.contain(s);
1999 }
2000};
2001
2002template<typename K>
2003struct CheckConstSymbolsTrim<K, 0> {
2004 bool is_trim_symbols(K) const {
2005 return false;
2006 }
2007};
2008
2009template<typename K, size_t N>
2010struct SymbSelector {
2011 using type = CheckConstSymbolsTrim<K, N>;
2012};
2013
2014template<typename K>
2015struct SymbSelector<K, 0> {
2016 using type = CheckSymbolsTrim<K>;
2017};
2018
2019template<typename K>
2020struct SymbSelector<K, static_cast<size_t>(-1)> {
2021 using type = CheckConstSymbolsTrim<K, 0>;
2022};
2023
2024template<TrimSides S, typename K, size_t N, bool withSpaces>
2025struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
2026 bool isTrim(K s) const {
2027 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
2028 }
2029 simple_str<K> operator()(simple_str<K> from) const {
2030 if constexpr ((S & TrimSides::TrimLeft) != 0) {
2031 while (from.len) {
2032 if (isTrim(*from.str)) {
2033 from.str++;
2034 from.len--;
2035 } else
2036 break;
2037 }
2038 }
2039 if constexpr ((S & TrimSides::TrimRight) != 0) {
2040 const K* back = from.str + from.len - 1;
2041 while (from.len) {
2042 if (isTrim(*back)) {
2043 back--;
2044 from.len--;
2045 } else
2046 break;
2047 }
2048 }
2049 return from;
2050 }
2051};
2052
2053template<TrimSides S, typename K>
2054using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
2055
2056using trim_w = SimpleTrim<TrimSides::TrimAll, u16s>;
2057using trim_a = SimpleTrim<TrimSides::TrimAll, u8s>;
2058using triml_w = SimpleTrim<TrimSides::TrimLeft, u16s>;
2059using triml_a = SimpleTrim<TrimSides::TrimLeft, u8s>;
2060using trimr_w = SimpleTrim<TrimSides::TrimRight, u16s>;
2061using trimr_a = SimpleTrim<TrimSides::TrimRight, u8s>;
2062
2063template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
2064 requires is_const_pattern<N>
2065inline auto trimOp(T&& pattern) {
2066 return trim_operator<S, K, N - 1, withSpaces>{pattern};
2067}
2068
2069template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
2070inline auto trimOp(simple_str<K> pattern) {
2071 return trim_operator<S, K, 0, withSpaces>{pattern};
2072}
2073
2074template<typename Src, typename Dest>
2075struct utf_convert_selector;
2076
2077template<>
2078struct utf_convert_selector<u8s, u16s> {
2079 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
2080 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
2081};
2082
2083template<>
2084struct utf_convert_selector<u8s, u32s> {
2085 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
2086 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
2087};
2088
2089template<>
2090struct utf_convert_selector<u8s, wchar_t> {
2091 static size_t need_len(const u8s* src, size_t srcLen) {
2092 return utf_convert_selector<u8s, wchar_type>::need_len(src, srcLen);
2093 }
2094 static size_t convert(const u8s* src, size_t srcLen, wchar_t* dest) {
2095 return utf_convert_selector<u8s, wchar_type>::convert(src, srcLen, to_w(dest));
2096 }
2097};
2098
2099template<>
2100struct utf_convert_selector<u16s, u8s> {
2101 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
2102 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
2103};
2104
2105template<>
2106struct utf_convert_selector<u16s, u32s> {
2107 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
2108 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
2109};
2110
2111template<>
2112struct utf_convert_selector<u16s, u16s> {
2113 // При конвертации char16_t в wchar_t под windows будет вызываться эта реализация
2114 static size_t need_len(const u16s* src, size_t srcLen) {
2115 return srcLen;
2116 }
2117 static size_t convert(const u16s* src, size_t srcLen, u16s* dest) {
2118 ch_traits<u16s>::copy(dest, src, srcLen + 1);
2119 return srcLen;
2120 }
2121};
2122
2123template<>
2124struct utf_convert_selector<u32s, u32s> {
2125 // При конвертации char32_t в wchar_t под linux будет вызываться эта реализация
2126 static size_t need_len(const u32s* src, size_t srcLen) {
2127 return srcLen;
2128 }
2129 static size_t convert(const u32s* src, size_t srcLen, u32s* dest) {
2130 ch_traits<u32s>::copy(dest, src, srcLen + 1);
2131 return srcLen;
2132 }
2133};
2134
2135template<>
2136struct utf_convert_selector<u16s, wchar_t> {
2137 static size_t need_len(const u16s* src, size_t srcLen) {
2138 return utf_convert_selector<u16s, wchar_type>::need_len(src, srcLen);
2139 }
2140 static size_t convert(const u16s* src, size_t srcLen, wchar_t* dest) {
2141 return utf_convert_selector<u16s, wchar_type>::convert(src, srcLen, to_w(dest));
2142 }
2143};
2144
2145template<>
2146struct utf_convert_selector<u32s, u8s> {
2147 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
2148 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
2149};
2150
2151template<>
2152struct utf_convert_selector<u32s, u16s> {
2153 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
2154 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
2155};
2156
2157template<>
2158struct utf_convert_selector<u32s, wchar_t> {
2159 static size_t need_len(const u32s* src, size_t srcLen) {
2160 return utf_convert_selector<u32s, wchar_type>::need_len(src, srcLen);
2161 }
2162 static size_t convert(const u32s* src, size_t srcLen, wchar_t* dest) {
2163 return utf_convert_selector<u32s, wchar_type>::convert(src, srcLen, to_w(dest));
2164 }
2165};
2166
2167template<>
2168struct utf_convert_selector<wchar_t, u8s> {
2169 static size_t need_len(const wchar_t* src, size_t srcLen) {
2170 return utf_convert_selector<wchar_type, u8s>::need_len(to_w(src), srcLen);
2171 }
2172 static size_t convert(const wchar_t* src, size_t srcLen, u8s* dest) {
2173 return utf_convert_selector<wchar_type, u8s>::convert(to_w(src), srcLen, dest);
2174 }
2175};
2176
2177template<>
2178struct utf_convert_selector<wchar_t, u16s> {
2179 static size_t need_len(const wchar_t* src, size_t srcLen) {
2180 return utf_convert_selector<wchar_type, u16s>::need_len(to_w(src), srcLen);
2181 }
2182 static size_t convert(const wchar_t* src, size_t srcLen, u16s* dest) {
2183 return utf_convert_selector<wchar_type, u16s>::convert(to_w(src), srcLen, dest);
2184 }
2185};
2186
2187template<>
2188struct utf_convert_selector<wchar_t, u32s> {
2189 static size_t need_len(const wchar_t* src, size_t srcLen) {
2190 return utf_convert_selector<wchar_type, u32s>::need_len(to_w(src), srcLen);
2191 }
2192 static size_t convert(const wchar_t* src, size_t srcLen, u32s* dest) {
2193 return utf_convert_selector<wchar_type, u32s>::convert(to_w(src), srcLen, dest);
2194 }
2195};
2196
2205template<typename K, typename Impl>
2206class from_utf_convertable {
2207protected:
2208 from_utf_convertable() = default;
2209 using my_type = Impl;
2210 /*
2211 Эти методы должен реализовать класс-наследник.
2212 вызывается только при создании объекта
2213 init(size_t size)
2214 set_size(size_t size)
2215 */
2216public:
2217 template<typename O>
2218 requires(!std::is_same_v<O, K>)
2219 from_utf_convertable(simple_str<O> init) {
2220 using worker = utf_convert_selector<O, K>;
2221 Impl* d = static_cast<Impl*>(this);
2222 size_t len = init.length();
2223 if (!len)
2224 d->create_empty();
2225 else {
2226 size_t need = worker::need_len(init.symbols(), len);
2227 K* str = d->init(need);
2228 str[need] = 0;
2229 worker::convert(init.symbols(), len, str);
2230 }
2231 }
2232 template<typename O, typename I, bool M>
2233 requires(!std::is_same_v<O, K>)
2234 from_utf_convertable(const str_algs<O, simple_str<O>, I, M>& init) : from_utf_convertable(init.to_str()) {}
2235};
2236
2242template<typename From, typename To> requires (!std::is_same_v<From, To>)
2243struct expr_utf {
2244 using symb_type = To;
2245 using worker = utf_convert_selector<From, To>;
2246
2247 simple_str<From> source_;
2248
2249 size_t length() const noexcept {
2250 return worker::need_len(source_.symbols(), source_.length());
2251 }
2252 To* place(To* ptr) const noexcept {
2253 return ptr + worker::convert(source_.symbols(), source_.length(), ptr);
2254 }
2255};
2256
2264template<typename To, typename From> requires (!std::is_same_v<From, To>)
2266 return expr_utf<From, To>{from};
2267}
2268
2272template<typename A, typename K>
2273concept storable_str = requires {
2274 A::is_str_storable == true;
2275 std::is_same_v<typename A::symb_type, K>;
2276};
2277
2281template<typename A, typename K>
2282concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
2283
2287template<typename A, typename K>
2289
2313template<typename K, typename Impl, typename Allocator>
2314class str_storable {
2315public:
2316 using my_type = Impl;
2317 using traits = ch_traits<K>;
2318 using allocator_t = Allocator;
2319
2320protected:
2321 [[_no_unique_address]]
2322 allocator_t allocator_;
2323
2327 allocator_t& allocator() {
2328 return allocator_;
2329 }
2330 const allocator_t& allocator() const {
2331 return allocator_;
2332 }
2333
2334 using uni = unicode_traits<K>;
2335
2336 Impl& d() noexcept {
2337 return *static_cast<Impl*>(this);
2338 }
2339 const Impl& d() const noexcept {
2340 return *static_cast<const Impl*>(this);
2341 }
2342 template<typename... Args>
2343 requires std::is_constructible_v<allocator_t, Args...>
2344 explicit constexpr str_storable(size_t size, Args&&... args) : allocator_(std::forward<Args>(args)...) {
2345 if (size)
2346 d().init(size);
2347 else
2348 d().create_empty();
2349 }
2350
2351 template<StrType<K> From, typename Op1, typename... Args>
2352 requires std::is_constructible_v<allocator_t, Args...>
2353 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
2354 my_type result{std::forward<Args>(args)...};
2355 size_t len = f.length();
2356 if (len) {
2357 const K* source = f.symbols();
2358 K* destination = result.init(len);
2359 for (size_t l = 0; l < len; l++) {
2360 destination[l] = opMakeNeedCase(source[l]);
2361 }
2362 }
2363 return result;
2364 }
2365 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
2366 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
2367 template<typename T, bool Dummy = true>
2368 struct ChangeCase {
2369 template<typename From, typename Op1, typename... Args>
2370 requires std::is_constructible_v<allocator_t, Args...>
2371 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
2372 my_type result{std::forward<Args>(args)...};
2373 size_t len = f.length();
2374 if (len) {
2375 opChangeCase(f.symbols(), len, result.init(len));
2376 }
2377 return result;
2378 }
2379 };
2380 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
2381 template<bool Dummy>
2382 struct ChangeCase<u8s, Dummy> {
2383 template<typename From, typename Op1, typename... Args>
2384 requires std::is_constructible_v<allocator_t, Args...>
2385 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
2386 my_type result{std::forward<Args>(args)...};
2387 ;
2388 size_t len = f.length();
2389 if (len) {
2390 const K* ptr = f.symbols();
2391 K* pWrite = result.init(len);
2392
2393 const u8s* source = ptr;
2394 u8s* dest = pWrite;
2395 size_t newLen = opChangeCase(source, len, dest, len);
2396 if (newLen < len) {
2397 // Строка просто укоротилась
2398 result.set_size(newLen);
2399 } else if (newLen > len) {
2400 // Строка не влезла в буфер.
2401 size_t readed = static_cast<size_t>(source - ptr);
2402 size_t writed = static_cast<size_t>(dest - pWrite);
2403 pWrite = result.set_size(newLen);
2404 dest = pWrite + writed;
2405 opChangeCase(source, len - readed, dest, newLen - writed);
2406 }
2407 pWrite[newLen] = 0;
2408 }
2409 return result;
2410 }
2411 };
2412
2413public:
2414 using s_str = simple_str<K>;
2415 using s_str_nt = simple_str_nt<K>;
2416
2417 inline static constexpr bool is_str_storable = true;
2418
2423 template<typename... Args>
2424 requires std::is_constructible_v<allocator_t, Args...>
2425 constexpr str_storable(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2426 : allocator_(std::forward<Args>(args)...) {
2427 d().create_empty();
2428 }
2429
2435 template<typename... Args>
2436 requires std::is_constructible_v<allocator_t, Args...>
2437 constexpr str_storable(s_str other, Args&&... args) : allocator_(std::forward<Args>(args)...) {
2438 if (other.length()) {
2439 K* ptr = d().init(other.length());
2440 traits::copy(ptr, other.symbols(), other.length());
2441 ptr[other.length()] = 0;
2442 } else
2443 d().create_empty();
2444 }
2445
2451 template<typename... Args>
2452 requires std::is_constructible_v<allocator_t, Args...>
2453 constexpr str_storable(size_t repeat, s_str pattern, Args&&... args) : allocator_(std::forward<Args>(args)...) {
2454 size_t l = pattern.length(), allLen = l * repeat;
2455 if (allLen) {
2456 K* ptr = d().init(allLen);
2457 for (size_t i = 0; i < repeat; i++) {
2458 traits::copy(ptr, pattern.symbols(), l);
2459 ptr += l;
2460 }
2461 *ptr = 0;
2462 } else
2463 d().create_empty();
2464 }
2465
2471 template<typename... Args>
2472 requires std::is_constructible_v<allocator_t, Args...>
2473 str_storable(size_t count, K pad, Args&&... args) : allocator_(std::forward<Args>(args)...) {
2474 if (count) {
2475 K* str = d().init(count);
2476 traits::assign(str, count, pad);
2477 str[count] = 0;
2478 } else
2479 d().create_empty();
2480 }
2481
2489 template<typename... Args>
2490 requires std::is_constructible_v<allocator_t, Args...>
2491 constexpr str_storable(const StrExprForType<K> auto& expr, Args&&... args) : allocator_(std::forward<Args>(args)...) {
2492 size_t len = expr.length();
2493 if (len)
2494 *expr.place(d().init(len)) = 0;
2495 else
2496 d().create_empty();
2497 }
2498
2507 template<StrType<K> From, typename... Args>
2508 requires std::is_constructible_v<allocator_t, Args...>
2509 str_storable(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
2510 : allocator_(std::forward<Args>(args)...) {
2511
2512 auto findes = f.find_all(pattern, offset, maxCount);
2513 if (!findes.size()) {
2514 new (this) my_type{f};
2515 return;
2516 }
2517 size_t srcLen = f.length();
2518 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
2519
2520 if (!newSize) {
2521 new (this) my_type{};
2522 return;
2523 }
2524
2525 K* ptr = d().init(newSize);
2526 const K* src = f.symbols();
2527 size_t from = 0;
2528 for (const auto& s: findes) {
2529 size_t copyLen = s - from;
2530 if (copyLen) {
2531 traits::copy(ptr, src + from, copyLen);
2532 ptr += copyLen;
2533 }
2534 if (repl.len) {
2535 traits::copy(ptr, repl.str, repl.len);
2536 ptr += repl.len;
2537 }
2538 from = s + pattern.len;
2539 }
2540 srcLen -= from;
2541 if (srcLen) {
2542 traits::copy(ptr, src + from, srcLen);
2543 ptr += srcLen;
2544 }
2545 *ptr = 0;
2546 }
2547
2551 operator const K*() const noexcept {
2552 return d().symbols();
2553 }
2554
2559 s_str_nt to_nts(size_t from = 0) const {
2560 size_t len = d().length();
2561 if (from >= len) {
2562 from = len;
2563 }
2564 return {d().symbols() + from, len - from};
2565 }
2566
2570 operator s_str_nt() const {
2571 return {d().symbols(), d().length()};
2572 }
2573
2593 template<typename T, typename... Args>
2594 requires std::is_constructible_v<allocator_t, Args...>
2595 static my_type join(const T& strings, s_str delimeter, bool tail = false, bool skip_empty = false, Args&&... args) {
2596 my_type result(std::forward<Args>(args)...);
2597 if (strings.size()) {
2598 if (strings.size() == 1 && (!delimeter.length() || !tail)) {
2599 result = strings.front();
2600 } else {
2601 size_t commonLen = 0;
2602 for (const auto& t: strings) {
2603 size_t len = t.length();
2604 if (len > 0 || !skip_empty) {
2605 if (commonLen > 0) {
2606 commonLen += delimeter.len;
2607 }
2608 commonLen += len;
2609 }
2610 }
2611 commonLen += (tail && delimeter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimeter.len : 0);
2612 if (commonLen) {
2613 K* ptr = result.init(commonLen);
2614 K* write = ptr;
2615 for (const auto& t: strings) {
2616 size_t copyLen = t.length();
2617 if (delimeter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
2618 ch_traits<K>::copy(write, delimeter.str, delimeter.len);
2619 write += delimeter.len;
2620 }
2621 ch_traits<K>::copy(write, t.symbols(), copyLen);
2622 write += copyLen;
2623 }
2624 if (delimeter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
2625 ch_traits<K>::copy(write, delimeter.str, delimeter.len);
2626 write += delimeter.len;
2627 }
2628 *write = 0;
2629 } else {
2630 result.create_empty();
2631 }
2632 }
2633 }
2634 return result;
2635 }
2636
2641 template<StrType<K> From, typename... Args>
2642 requires std::is_constructible_v<allocator_t, Args...>
2643 static my_type uppered_only_ascii_from(const From& f, Args&&... args) {
2644 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
2645 }
2646
2651 template<StrType<K> From, typename... Args>
2652 requires std::is_constructible_v<allocator_t, Args...>
2653 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
2654 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
2655 }
2656
2663 template<StrType<K> From, typename... Args>
2664 requires std::is_constructible_v<allocator_t, Args...>
2665 static my_type uppered_from(const From& f, Args&&... args) {
2666 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
2667 }
2668
2675 template<StrType<K> From, typename... Args>
2676 requires std::is_constructible_v<allocator_t, Args...>
2677 static my_type lowered_from(const From& f, Args&&... args) {
2678 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
2679 }
2680
2689 template<StrType<K> From, typename... Args>
2690 requires std::is_constructible_v<allocator_t, Args...>
2691 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
2692 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
2693 }
2694};
2695
2699template<typename A>
2700concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
2701 { a.allocate(size) } -> std::same_as<void*>;
2702 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
2703};
2704
2705struct printf_selector {
2706 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
2707 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
2708 if constexpr (std::is_same_v<K, u8s>) {
2709 #ifndef _WIN32
2710 return std::snprintf(buffer, count, format, std::forward<T>(args)...);
2711 #else
2712 // Поддерживает позиционные параметры
2713 return _sprintf_p(buffer, count, format, args...);
2714 #endif
2715 } else {
2716 #ifndef _WIN32
2717 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
2718 #else
2719 // Поддерживает позиционные параметры
2720 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
2721 #endif
2722 }
2723 }
2724 template<typename K> requires (is_one_of_std_char_v<K>)
2725 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
2726 if constexpr (std::is_same_v<K, u8s>) {
2727 #ifndef _WIN32
2728 return std::vsnprintf(buffer, count, format, args);
2729 #else
2730 // Поддерживает позиционные параметры
2731 return _vsprintf_p(buffer, count, format, args);
2732 #endif
2733 } else {
2734 #ifndef _WIN32
2735 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
2736 #else
2737 // Поддерживает позиционные параметры
2738 return _vswprintf_p(buffer, count, format, args);
2739 #endif
2740 }
2741 }
2742};
2743
2744inline size_t grow2(size_t ret, size_t currentCapacity) {
2745 return ret <= currentCapacity ? ret : ret * 2;
2746}
2747
2768template<typename K, typename Impl>
2770public:
2771 using my_type = Impl;
2772
2773private:
2774 Impl& d() {
2775 return *static_cast<Impl*>(this);
2776 }
2777 const Impl& d() const {
2778 return *static_cast<const Impl*>(this);
2779 }
2780 size_t _len() const noexcept {
2781 return d().length();
2782 }
2783 const K* _str() const noexcept {
2784 return d().symbols();
2785 }
2786 using str_piece = simple_str<K>;
2787 using symb_type = K;
2788 using traits = ch_traits<K>;
2789 using uni = unicode_traits<K>;
2790 using uns_type = std::make_unsigned_t<K>;
2791
2792 template<typename Op>
2793 Impl& make_trim_op(const Op& op) {
2794 str_piece me = static_cast<str_piece>(d()), pos = op(me);
2795 if (me.length() != pos.length()) {
2796 if (me.symbols() != pos.symbols())
2797 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
2798 d().set_size(pos.length());
2799 }
2800 return d();
2801 }
2802
2803 template<auto Op>
2804 Impl& commonChangeCase() {
2805 size_t len = _len();
2806 if (len)
2807 Op(_str(), len, str());
2808 return d();
2809 }
2810 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
2811 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
2812 template<typename T, bool Dummy = true>
2813 struct CaseTraits {
2814 static Impl& upper(Impl& obj) {
2815 return obj.template commonChangeCase<unicode_traits<K>::upper>();
2816 }
2817 static Impl& lower(Impl& obj) {
2818 return obj.template commonChangeCase<unicode_traits<K>::lower>();
2819 }
2820 };
2821
2822 template<auto Op>
2823 Impl& utf8CaseChange() {
2824 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
2825 size_t len = _len();
2826 if (len) {
2827 u8s* writePos = str();
2828 const u8s *startData = writePos, *readPos = writePos;
2829 size_t newLen = Op(readPos, len, writePos, len);
2830 if (newLen < len) {
2831 // Строка просто укоротилась
2832 d().set_size(newLen);
2833 } else if (newLen > len) {
2834 // Строка не влезла в буфер.
2835 size_t readed = static_cast<size_t>(readPos - startData);
2836 size_t writed = static_cast<size_t>(writePos - startData);
2837 d().set_size(newLen);
2838 startData = str(); // при изменении размера могло изменится
2839 readPos = startData + readed;
2840 writePos = const_cast<u8s*>(startData) + writed;
2841 Op(readPos, len - readed, writePos, newLen - writed);
2842 }
2843 }
2844 return d();
2845 }
2846 template<bool Dummy>
2847 struct CaseTraits<u8s, Dummy> {
2848 static Impl& upper(Impl& obj) {
2849 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
2850 }
2851 static Impl& lower(Impl& obj) {
2852 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
2853 }
2854 };
2855
2856 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
2857 Impl& makeTrim(T&& pattern) {
2858 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
2859 }
2860
2861 template<TrimSides S, bool withSpaces>
2862 Impl& makeTrim(str_piece pattern) {
2863 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
2864 }
2865
2866public:
2871 K* str() noexcept {
2872 return d().str();
2873 }
2874
2878 explicit operator K*() noexcept {
2879 return str();
2880 }
2881
2885 Impl& trim() {
2886 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
2887 }
2888
2892 Impl& trim_left() {
2893 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
2894 }
2895
2899 Impl& trim_right() {
2900 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
2901 }
2902
2907 template<typename T, size_t N = const_lit_for<K, T>::Count>
2908 requires is_const_pattern<N>
2909 Impl& trim(T&& pattern) {
2910 return makeTrim<TrimSides::TrimAll, false>(pattern);
2911 }
2912
2917 template<typename T, size_t N = const_lit_for<K, T>::Count>
2918 requires is_const_pattern<N>
2919 Impl& trim_left(T&& pattern) {
2920 return makeTrim<TrimSides::TrimLeft, false>(pattern);
2921 }
2922
2927 template<typename T, size_t N = const_lit_for<K, T>::Count>
2928 requires is_const_pattern<N>
2929 Impl& trim_right(T&& pattern) {
2930 return makeTrim<TrimSides::TrimRight, false>(pattern);
2931 }
2932
2937 template<typename T, size_t N = const_lit_for<K, T>::Count>
2938 requires is_const_pattern<N>
2939 Impl& trim_with_spaces(T&& pattern) {
2940 return makeTrim<TrimSides::TrimAll, true>(pattern);
2941 }
2942
2947 template<typename T, size_t N = const_lit_for<K, T>::Count>
2948 requires is_const_pattern<N>
2949 Impl& trim_left_with_spaces(T&& pattern) {
2950 return makeTrim<TrimSides::TrimLeft, true>(pattern);
2951 }
2952
2957 template<typename T, size_t N = const_lit_for<K, T>::Count>
2958 requires is_const_pattern<N>
2959 Impl& trim_right_with_wpaces(T&& pattern) {
2960 return makeTrim<TrimSides::TrimRight, true>(pattern);
2961 }
2962
2967 Impl& trim(str_piece pattern) {
2968 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
2969 }
2970
2975 Impl& trim_left(str_piece pattern) {
2976 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
2977 }
2978
2983 Impl& trim_right(str_piece pattern) {
2984 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
2985 }
2986
2991 Impl& trim_with_spaces(str_piece pattern) {
2992 return makeTrim<TrimSides::TrimAll, true>(pattern);
2993 }
2994
2999 Impl& trim_left_with_spaces(str_piece pattern) {
3000 return makeTrim<TrimSides::TrimLeft, true>(pattern);
3001 }
3002
3007 Impl& trim_right_with_spaces(str_piece pattern) {
3008 return makeTrim<TrimSides::TrimRight, true>(pattern);
3009 }
3010
3015 K* ptr = str();
3016 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
3017 K s = *ptr;
3018 if (isAsciiLower(s))
3019 *ptr = s & ~0x20;
3020 }
3021 return d();
3022 }
3023
3028 K* ptr = str();
3029 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
3030 K s = *ptr;
3031 if (isAsciiUpper(s))
3032 *ptr = s | 0x20;
3033 }
3034 return d();
3035 }
3036
3042 Impl& upper() {
3043 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3044 return CaseTraits<K>::upper(d());
3045 }
3046
3052 Impl& lower() {
3053 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3054 return CaseTraits<K>::lower(d());
3055 }
3056
3057private:
3058 template<typename T>
3059 Impl& changeImpl(size_t from, size_t len, T expr) {
3060 size_t myLen = _len();
3061 if (from > myLen) {
3062 from = myLen;
3063 }
3064 if (from + len > myLen) {
3065 len = myLen - from;
3066 }
3067 K* buffer = str();
3068 size_t otherLen = expr.length();
3069 if (len == otherLen) {
3070 expr.place(buffer + from);
3071 } else {
3072 size_t tailLen = myLen - from - len;
3073 if (len > otherLen) {
3074 expr.place(buffer + from);
3075 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
3076 d().set_size(myLen - (len - otherLen));
3077 } else {
3078 buffer = d().set_size(myLen + otherLen - len);
3079 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
3080 expr.place(buffer + from);
3081 }
3082 }
3083 return d();
3084 }
3085
3086 template<typename T>
3087 Impl& appendImpl(T expr) {
3088 if (size_t len = expr.length(); len) {
3089 size_t size = _len();
3090 expr.place(d().set_size(size + len) + size);
3091 }
3092 return d();
3093 }
3094
3095 template<typename T>
3096 Impl& appendFromImpl(size_t pos, T expr) {
3097 if (pos > _len())
3098 pos = _len();
3099 if (size_t len = expr.length())
3100 expr.place(d().set_size(pos + len) + pos);
3101 else
3102 d().set_size(pos);
3103 return d();
3104 }
3105
3106public:
3107 inline static constexpr bool is_str_mutable = true;
3113 Impl& append(str_piece other) {
3114 return appendImpl<str_piece>(other);
3115 }
3116
3121 template<StrExprForType<K> A>
3122 Impl& append(const A& expr) {
3123 return appendImpl<const A&>(expr);
3124 }
3125
3130 Impl& operator+=(str_piece other) {
3131 return appendImpl<str_piece>(other);
3132 }
3133
3138 template<StrExprForType<K> A>
3139 Impl& operator+=(const A& expr) {
3140 return appendImpl<const A&>(expr);
3141 }
3142
3150 Impl& append_in(size_t pos, str_piece other) {
3151 return appendFromImpl<str_piece>(pos, other);
3152 }
3153
3161 template<StrExprForType<K> A>
3162 Impl& append_in(size_t pos, const A& expr) {
3163 return appendFromImpl<const A&>(pos, expr);
3164 }
3165
3172 Impl& change(size_t from, size_t len, str_piece other) {
3173 return changeImpl<str_piece>(from, len, other);
3174 }
3175
3182 template<StrExprForType<K> A>
3183 Impl& change(size_t from, size_t len, const A& expr) {
3184 return changeImpl<const A&>(from, len, expr);
3185 }
3186
3192 Impl& insert(size_t to, str_piece other) {
3193 return changeImpl<str_piece>(to, 0, other);
3194 }
3195
3201 template<StrExprForType<K> A>
3202 Impl& insert(size_t to, const A& expr) {
3203 return changeImpl<const A&>(to, 0, expr);
3204 }
3205
3211 Impl& remove(size_t from, size_t len) {
3212 return changeImpl<const empty_expr<K>&>(from, len, {});
3213 }
3214
3219 Impl& prepend(str_piece other) {
3220 return changeImpl<str_piece>(0, 0, other);
3221 }
3222
3227 template<StrExprForType<K> A>
3228 Impl& prepend(const A& expr) {
3229 return changeImpl<const A&>(0, 0, expr);
3230 }
3231
3239 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
3240 offset = d().find(pattern, offset);
3241 if (offset == str::npos) {
3242 return d();
3243 }
3244 if (!maxCount)
3245 maxCount--;
3246 size_t replLength = repl.length(), patternLength = pattern.length();
3247
3248 if (patternLength == replLength) {
3249 // Заменяем inplace на подстроку такой же длины
3250 K* ptr = str();
3251 for (size_t i = 0; i < maxCount; i++) {
3252 traits::copy(ptr + offset, repl.symbols(), replLength);
3253 offset = d().find(pattern, offset + replLength);// replLength == patternLength
3254 if (offset == str::npos)
3255 break;
3256 }
3257 } else if (patternLength > replLength) {
3258 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
3259 K* ptr = str();
3260 traits::copy(ptr + offset, repl.symbols(), replLength);
3261 size_t posWrite = offset + replLength;
3262 maxCount--;
3263 offset += patternLength;
3264
3265 for (size_t i = 0; i < maxCount; i++) {
3266 size_t idx = d().find(pattern, offset);
3267 if (idx == str::npos)
3268 break;
3269 size_t lenOfPiece = idx - offset;
3270 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
3271 posWrite += lenOfPiece;
3272 traits::copy(ptr + posWrite, repl.symbols(), replLength);
3273 posWrite += replLength;
3274 offset = idx + patternLength;
3275 }
3276 size_t tailLen = _len() - offset;
3277 traits::move(ptr + posWrite, ptr + offset, tailLen);
3278 d().set_size(posWrite + tailLen);
3279 } else {
3280 struct replace_grow_helper {
3281 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
3282 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
3283 my_type& source;
3284 const str_piece pattern;
3285 const str_piece repl;
3286 size_t maxCount;
3287 const size_t delta;
3288 size_t all_delta{};
3289 K* reserve_for_copy{};
3290 size_t end_of_piece{};
3291 size_t total_length{};
3292
3293 void replace(size_t offset) {
3294 size_t finded[16] = {source.find(pattern, offset)};
3295 if (finded[0] == str::npos) {
3296 return;
3297 }
3298 maxCount--;
3299 offset = finded[0] + pattern.length();
3300 all_delta += delta;
3301 size_t idx = 1;
3302 for (size_t end = std::min(maxCount, std::size(finded)); idx < end; idx++, maxCount--) {
3303 finded[idx] = source.find(pattern, offset);
3304 if (finded[idx] == str::npos) {
3305 break;
3306 }
3307 offset = finded[idx] + pattern.length();
3308 all_delta += delta;
3309 }
3310 bool needMore = maxCount > 0 && idx == std::size(finded) && offset < source.length() - pattern.length();
3311 if (needMore) {
3312 replace(offset); // здесь произведутся замены в оставшемся хвосте
3313 }
3314 // Теперь делаем свои замены
3315 if (!reserve_for_copy) {
3316 // Только начинаем
3317 end_of_piece = source.length();
3318 total_length = end_of_piece + all_delta;
3319 reserve_for_copy = source.alloc_for_copy(total_length);
3320 }
3321 K* dst_start = reserve_for_copy;
3322 const K* src_start = source.symbols();
3323 while(idx-- > 0) {
3324 size_t pos = finded[idx] + pattern.length();
3325 size_t lenOfPiece = end_of_piece - pos;
3326 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
3327 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
3328 all_delta -= delta;
3329 end_of_piece = finded[idx];
3330 }
3331 if (!all_delta && reserve_for_copy != src_start) {
3332 ch_traits<K>::copy(dst_start, src_start, finded[0]);
3333 }
3334 }
3335 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
3336 helper.replace(offset);
3337 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
3338 }
3339 return d();
3340 }
3341
3350 template<StrType<K> From>
3351 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
3352 if (pattern.length() >= repl.length()) {
3353 K* dst = d().reserve_no_preserve(f.length());
3354 const K* src = f.symbols();
3355 size_t delta = 0;
3356 if (maxCount == 0) {
3357 maxCount--;
3358 }
3359 size_t src_length = f.length(), start = 0;
3360 while (maxCount--) {
3361 offset = f.find(pattern, offset);
3362 if (offset == str::npos) {
3363 break;
3364 }
3365 size_t piece_len = offset - start;
3366 if (piece_len) {
3367 ch_traits<K>::copy(dst, src + start, piece_len);
3368 dst += piece_len;
3369 }
3370 if (repl.length()) {
3371 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
3372 dst += repl.length();
3373 }
3374 delta += pattern.length() - repl.length();
3375 offset += pattern.length();
3376 start = offset;
3377 }
3378 if (start < src_length) {
3379 ch_traits<K>::copy(dst, src + start, src_length - start);
3380 }
3381 d().set_size(src_length - delta);
3382 } else {
3383 d() = f;
3384 replace(pattern, repl, offset, maxCount);
3385 }
3386 return d();
3387 }
3388
3401 template<typename Op>
3402 Impl& fill(size_t from, const Op& fillFunction) {
3403 size_t size = _len();
3404 if (from > size)
3405 from = size;
3406 size_t capacity = d().capacity();
3407 K* ptr = str();
3408 capacity -= from;
3409 for (;;) {
3410 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
3411 if (capacity >= needSize) {
3412 d().set_size(from + needSize);
3413 break;
3414 }
3415 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
3416 capacity = d().capacity() - from;
3417 }
3418 return d();
3419 }
3420
3425 template<typename Op>
3426 requires std::is_invocable_v<Op, K*, size_t>
3427 Impl& operator<<(const Op& fillFunction) {
3428 return fill(0, fillFunction);
3429 }
3430
3435 template<typename Op>
3436 requires std::is_invocable_v<Op, K*, size_t>
3437 Impl& operator<<=(const Op& fillFunction) {
3438 return fill(_len(), fillFunction);
3439 }
3440
3445 template<typename Op>
3446 requires std::is_invocable_v<Op, my_type&>
3447 Impl& operator<<(const Op& fillFunction) {
3448 fillFunction(d());
3449 return d();
3450 }
3451
3459 template<typename... T> requires (is_one_of_std_char_v<K>)
3460 Impl& printf_from(size_t from, const K* format, T&&... args) {
3461 size_t size = _len();
3462 if (from > size)
3463 from = size;
3464 size_t capacity = d().capacity();
3465 K* ptr = str();
3466 capacity -= from;
3467
3468 int result = 0;
3469 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
3470 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
3471 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
3472 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
3473 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
3474 if (result > (int)capacity) {
3475 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
3476 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
3477 }
3478 } else {
3479 for (;;) {
3480 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
3481 if (result < 0) {
3482 // Не хватило буфера или ошибка конвертации.
3483 // Попробуем увеличить буфер в два раза
3484 capacity *= 2;
3485 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
3486 } else
3487 break;
3488 }
3489 }
3490 if (result < 0)
3491 d().set_size(static_cast<size_t>(traits::length(_str())));
3492 else
3493 d().set_size(from + result);
3494 return d();
3495 }
3496
3503 template<typename... T> requires (is_one_of_std_char_v<K>)
3504 Impl& printf(const K* format, T&&... args) {
3505 return printf_from(0, format, std::forward<T>(args)...);
3506 }
3507
3514 template<typename... T> requires (is_one_of_std_char_v<K>)
3515 Impl& append_printf(const K* format, T&&... args) {
3516 return printf_from(_len(), format, std::forward<T>(args)...);
3517 }
3518
3519 struct writer {
3520 my_type* store;
3521 K* ptr;
3522 const K* end;
3523 size_t max_write;
3524 size_t writed = 0;
3525 inline static K pad;
3526 K& operator*() const {
3527 return *ptr;
3528 }
3529 writer& operator++() {
3530 if (writed < max_write) {
3531 ++ptr;
3532 if (ptr == end) {
3533 size_t l = ptr - store->begin();
3534 store->set_size(l);
3535 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
3536 end = store->end();
3537 }
3538 } else {
3539 ptr = &pad;
3540 }
3541 return *this;
3542 }
3543 writer operator++(int) {
3544 auto ret = *this;
3545 operator++();
3546 return ret;
3547 }
3548
3549 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
3550 writer() = default;
3551 writer(const writer&) = delete;
3552 writer& operator=(const writer&) noexcept = delete;
3553 writer(writer&&) noexcept = default;
3554 writer& operator=(writer&&) noexcept = default;
3555 using difference_type = int;
3556 };
3565 template<typename... T> requires (is_one_of_std_char_v<K>)
3566 Impl& format_from(size_t from, const FmtString<K, T...>& format, T&&... args) {
3567 size_t size = _len();
3568 if (from > size)
3569 from = size;
3570 size_t capacity = d().capacity();
3571 K* ptr = str();
3572
3573 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
3574 std::forward<decltype(format)>(format), std::forward<T>(args)...);
3575 d().set_size(result.ptr - _str());
3576 return d();
3577 }
3578
3587 template<typename... T> requires (is_one_of_std_char_v<K>)
3588 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
3589 size_t size = _len();
3590 if (from > size)
3591 from = size;
3592 size_t capacity = d().capacity();
3593 K* ptr = str();
3594
3595 if constexpr (std::is_same_v<K, u8s>) {
3596 auto result = std::vformat_to(
3597 writer{d(), ptr + from, ptr + capacity, max_write},
3598 std::basic_string_view<K>{format.symbols(), format.length()},
3599 std::make_format_args(args...));
3600 d().set_size(result.ptr - _str());
3601 } else {
3602 auto result = std::vformat_to(
3603 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
3604 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
3605 std::make_wformat_args(std::forward<T>(args)...));
3606 d().set_size(result.ptr - _str());
3607 }
3608 return d();
3609 }
3610
3617 template<typename... T> requires (is_one_of_std_char_v<K>)
3618 Impl& format(const FmtString<K, T...>& pattern, T&&... args) {
3619 return format_from(0, pattern, std::forward<T>(args)...);
3620 }
3621
3628 template<typename... T> requires (is_one_of_std_char_v<K>)
3629 Impl& append_formatted(const FmtString<K, T...>& format, T&&... args) {
3630 return format_from(_len(), format, std::forward<T>(args)...);
3631 }
3632
3639 template<typename... T> requires (is_one_of_std_char_v<K>)
3640 Impl& vformat(str_piece format, T&&... args) {
3641 return vformat_from(0, -1, format, std::forward<T>(args)...);
3642 }
3643
3650 template<typename... T> requires (is_one_of_std_char_v<K>)
3651 Impl& append_vformatted(str_piece format, T&&... args) {
3652 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
3653 }
3654
3662 template<typename... T> requires (is_one_of_std_char_v<K>)
3663 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
3664 return vformat_from(0, max_write, format, std::forward<T>(args)...);
3665 }
3666
3674 template<typename... T> requires (is_one_of_std_char_v<K>)
3675 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
3676 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
3677 }
3678
3684 template<typename Op, typename... Args>
3685 Impl& with(const Op& fillFunction, Args&&... args) {
3686 fillFunction(d(), std::forward<Args>(args)...);
3687 return d();
3688 }
3689};
3690
3691template<typename K>
3692struct SharedStringData {
3693 std::atomic_size_t ref_; // Счетчик ссылок
3694
3695 SharedStringData() {
3696 ref_ = 1;
3697 }
3698 K* str() const {
3699 return (K*)(this + 1);
3700 }
3701 void incr() {
3702 ref_.fetch_add(1, std::memory_order_relaxed);
3703 }
3704 void decr(Allocatorable auto& allocator) {
3705 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
3706 if (val == 1) {
3707 allocator.deallocate(this);
3708 }
3709 }
3710 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
3711 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
3712 return new (allocator.allocate(size)) SharedStringData();
3713 }
3714 static SharedStringData<K>* from_str(const K* p) {
3715 return (SharedStringData<K>*)p - 1;
3716 }
3717 K* place(K* p, size_t len) {
3718 ch_traits<K>::copy(p, str(), len);
3719 return p + len;
3720 }
3721};
3722
3723// Дефолтный аллокатор для строк, может работать статически
3724class string_common_allocator {
3725public:
3726 void* allocate(size_t bytes) {
3727 return new char[bytes];
3728 }
3729 void deallocate(void* address) noexcept {
3730 delete [] static_cast<char*>(address);
3731 }
3732};
3733
3734string_common_allocator default_string_allocator_selector(...);
3735// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
3736// объявите функцию
3737// ваш_тип_аллокатора default_string_allocator_selector(int);
3738using allocator_string = decltype(default_string_allocator_selector(int(0)));
3739
3740template<typename K, Allocatorable Allocator>
3741class sstring;
3742
3743/*
3744* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
3745* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4 байта.
3746* Укажем ему явно
3747*/
3748
3760template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
3761class empty_bases lstring :
3762 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
3763 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
3764 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
3765 public from_utf_convertable<K, lstring<K, N, forShared, Allocator>> {
3766public:
3767 using symb_type = K;
3768 using my_type = lstring<K, N, forShared, Allocator>;
3769 using allocator_t = Allocator;
3770
3771 enum : size_t {
3772 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
3773 };
3774
3775protected:
3776 enum : size_t {
3777 extra = forShared ? sizeof(SharedStringData<K>) : 0,
3778 };
3779
3780 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
3781 using base_storable = str_storable<K, my_type, Allocator>;
3782 using base_mutable = str_mutable<K, my_type>;
3783 using base_utf = from_utf_convertable<K, my_type>;
3784 using traits = ch_traits<K>;
3785
3786 friend base_storable;
3787 friend base_mutable;
3788 friend base_utf;
3789 friend class sstring<K, Allocator>;
3790
3791 // Данные
3792 K* data_;
3793 size_t size_; // Поле не должно инициализироваться, так как может устанавливаться в базовых конструкторах
3794
3795 union {
3796 size_t capacity_; // Поле не должно инициализироваться, так как может устанавливаться в базовых конструкторах
3797 K local_[LocalCapacity + 1];
3798 };
3799
3800 void create_empty() {
3801 data_ = local_;
3802 size_ = 0;
3803 local_[0] = 0;
3804 }
3805 static size_t calc_capacity(size_t s) {
3806 size_t real_need = (s + 1) * sizeof(K) + extra;
3807 size_t aligned_alloced = (real_need + alignof(std::max_align_t) - 1) / alignof(std::max_align_t) * alignof(std::max_align_t);
3808 return (aligned_alloced - extra) / sizeof(K) - 1;
3809 }
3810
3811 K* init(size_t s) {
3812 size_ = s;
3813 if (size_ > LocalCapacity) {
3814 s = calc_capacity(s);
3815 data_ = alloc_place(s);
3816 capacity_ = s;
3817 } else {
3818 data_ = local_;
3819 }
3820 return str();
3821 }
3822 // Методы для себя
3823 bool is_alloced() const noexcept {
3824 return data_ != local_;
3825 }
3826
3827 void dealloc() {
3828 if (is_alloced()) {
3829 base_storable::allocator().deallocate(to_real_address(data_));
3830 data_ = local_;
3831 }
3832 }
3833
3834 static K* to_real_address(void* ptr) {
3835 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
3836 }
3837 static K* from_real_address(void* ptr) {
3838 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
3839 }
3840
3841 K* alloc_place(size_t newSize) {
3842 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
3843 }
3844 // Вызывается при replace, когда меняют на более длинную замену
3845 K* alloc_for_copy(size_t newSize) {
3846 if (capacity() >= newSize) {
3847 // Замена войдёт в текущий буфер
3848 return data_;
3849 }
3850 return alloc_place(calc_capacity(newSize));
3851 }
3852 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
3853 void set_from_copy(K* ptr, size_t newSize) {
3854 if (ptr != data_) {
3855 // Да, копировали в новый буфер
3856 dealloc();
3857 data_ = ptr;
3858 capacity_ = calc_capacity(newSize);
3859 }
3860 size_ = newSize;
3861 data_[newSize] = 0;
3862 }
3863
3864public:
3865 using base_storable::base_storable;
3866 using base_utf::base_utf;
3867
3868 lstring() = default;
3869
3870 ~lstring() {
3871 dealloc();
3872 }
3873
3878 lstring(const my_type& other) : base_storable(other.allocator()) {
3879 if (other.size_) {
3880 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
3881 }
3882 }
3883
3888 template<typename... Args>
3889 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
3890 lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3891 if (other.size_) {
3892 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
3893 }
3894 }
3895
3901 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
3902 requires std::is_constructible_v<allocator_t, Args...>
3903 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3904 if constexpr (I > 1) {
3905 K* ptr = init(I - 1);
3906 traits::copy(ptr, value, I - 1);
3907 ptr[I - 1] = 0;
3908 } else
3909 create_empty();
3910 }
3911
3915 lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3916 if (other.size_) {
3917 size_ = other.size_;
3918 if (other.is_alloced()) {
3919 data_ = other.data_;
3920 capacity_ = other.capacity_;
3921 } else {
3922 data_ = local_;
3923 traits::copy(local_, other.local_, size_ + 1);
3924 }
3925 other.data_ = other.local_;
3926 other.size_ = 0;
3927 other.local_[0] = 0;
3928 }
3929 }
3930
3935 template<typename Op, typename... Args>
3936 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
3937 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3938 this->operator<<(op);
3939 }
3940
3941 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
3942 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
3943
3949 my_type& operator=(const my_type& other) {
3950 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3951 if (&other != this) {
3952 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
3953 size_ = other.size_;
3954 }
3955 return *this;
3956 }
3957
3962 my_type& operator=(my_type&& other) noexcept {
3963 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
3964 if (&other != this) {
3965 dealloc();
3966 if (other.is_alloced()) {
3967 data_ = other.data_;
3968 capacity_ = other.capacity_;
3969 } else {
3970 traits::copy(data_, other.local_, other.size_ + 1);
3971 }
3972 base_storable::allocator() = std::move(other.allocator());
3973 size_ = other.size_;
3974 other.create_empty();
3975 }
3976 return *this;
3977 }
3978
3979 my_type& assign(const K* other, size_t len) {
3980 if (len) {
3981 bool isIntersect = other >= data_ && other + len <= data_ + size_;
3982 if (isIntersect) {
3983 // Особый случай, нам пытаются присвоить кусок нашей же строки.
3984 // Просто переместим текст в буфере, и установим новый размер
3985 if (other > data_) {
3986 traits::move(data_, other, len);
3987 }
3988 } else {
3989 traits::copy(reserve_no_preserve(len), other, len);
3990 }
3991 }
3992 size_ = len;
3993 data_[size_] = 0;
3994 return *this;
3995 }
4001 my_type& operator=(simple_str<K> other) {
4002 return assign(other.str, other.len);
4003 }
4004
4009 template<typename T, size_t S = const_lit_for<K, T>::Count>
4010 my_type& operator=(T&& other) {
4011 return assign(other, S - 1);
4012 }
4013
4019 my_type& operator=(const StrExprForType<K> auto& expr) {
4020 size_t newLen = expr.length();
4021 if (newLen) {
4022 expr.place(reserve_no_preserve(newLen));
4023 }
4024 size_ = newLen;
4025 data_[size_] = 0;
4026 return *this;
4027 }
4028
4029 size_t length() const noexcept {
4030 return size_;
4031 }
4032
4033 const K* symbols() const noexcept {
4034 return data_;
4035 }
4036
4037 K* str() noexcept {
4038 return data_;
4039 }
4040
4041 bool is_empty() const noexcept {
4042 return size_ == 0;
4043 }
4044
4045 bool empty() const noexcept {
4046 return size_ == 0;
4047 }
4048
4049 size_t capacity() const noexcept {
4050 return is_alloced() ? capacity_ : LocalCapacity;
4051 }
4052
4059 K* reserve_no_preserve(size_t newSize) {
4060 if (newSize > capacity()) {
4061 newSize = calc_capacity(newSize);
4062 K* newData = alloc_place(newSize);
4063 dealloc();
4064 data_ = newData;
4065 capacity_ = newSize;
4066 }
4067 return data_;
4068 }
4069
4076 K* reserve(size_t newSize) {
4077 if (newSize > capacity()) {
4078 newSize = calc_capacity(newSize);
4079 K* newData = alloc_place(newSize);
4080 traits::copy(newData, data_, size_);
4081 dealloc();
4082 data_ = newData;
4083 capacity_ = newSize;
4084 }
4085 return data_;
4086 }
4087
4094 K* set_size(size_t newSize) {
4095 size_t cap = capacity();
4096 if (newSize > cap) {
4097 size_t needPlace = newSize;
4098 if (needPlace < (cap + 1) * 2) {
4099 needPlace = (cap + 1) * 2 - 1;
4100 }
4101 reserve(needPlace);
4102 }
4103 size_ = newSize;
4104 data_[newSize] = 0;
4105 return data_;
4106 }
4107
4110 bool is_local() const noexcept {
4111 return !is_alloced();
4112 }
4113
4118 size_t cap = capacity();
4119 for (size_t i = 0; i < cap; i++) {
4120 if (data_[i] == 0) {
4121 size_ = i;
4122 return;
4123 }
4124 }
4125 size_ = cap;
4126 data_[size_] = 0;
4127 }
4128
4133 size_t need_capacity = calc_capacity(size_);
4134 if (is_alloced() && capacity_ > need_capacity) {
4135 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
4136 traits::copy(newData, data_, size_ + 1);
4137 base_storable::allocator().deallocate(to_real_address(data_));
4138 data_ = newData;
4139
4140 if (size_ > LocalCapacity) {
4141 capacity_ = need_capacity;
4142 }
4143 }
4144 }
4145
4146 void clear() {
4147 set_size(0);
4148 }
4149
4150 void reset() {
4151 dealloc();
4152 local_[0] = 0;
4153 size_ = 0;
4154 }
4155};
4156
4157template<size_t N = 15>
4158using lstringa = lstring<u8s, N>;
4159template<size_t N = 15>
4160using lstringw = lstring<wchar_t, N>;
4161template<size_t N = 15>
4162using lstringu = lstring<u16s, N>;
4163template<size_t N = 15>
4164using lstringuu = lstring<u32s, N>;
4165
4166template<size_t N = 15>
4167using lstringsa = lstring<u8s, N, true>;
4168template<size_t N = 15>
4169using lstringsw = lstring<wchar_t, N, true>;
4170template<size_t N = 15>
4171using lstringsu = lstring<u16s, N, true>;
4172template<size_t N = 15>
4173using lstringsuu = lstring<u32s, N, true>;
4174
4175
4176template<typename T, typename K = typename const_lit<T>::symb_type>
4177auto getLiteralType(T&&) {
4178 return K{};
4179};
4180
4181template<size_t Arch, size_t L>
4182inline constexpr const size_t _local_count = 0;
4183
4184template<>
4185inline constexpr const size_t _local_count<8, 1> = 23;
4186template<>
4187inline constexpr const size_t _local_count<8, 2> = 15;
4188template<>
4189inline constexpr const size_t _local_count<8, 4> = 7;
4190template<>
4191inline constexpr const size_t _local_count<4, 1> = 15;
4192template<>
4193inline constexpr const size_t _local_count<4, 2> = 11;
4194template<>
4195inline constexpr const size_t _local_count<4, 4> = 5;
4196
4197template<typename T>
4198constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
4199
4227template<typename K, Allocatorable Allocator = allocator_string>
4228class empty_bases sstring :
4229 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
4230 public str_storable<K, sstring<K, Allocator>, Allocator>,
4231 public from_utf_convertable<K, sstring<K, Allocator>> {
4232public:
4233 using symb_type = K;
4234 using uns_type = std::make_unsigned_t<K>;
4235 using my_type = sstring<K, Allocator>;
4236 using allocator_t = Allocator;
4237
4238 enum { LocalCount = local_count<K> };
4239
4240protected:
4241 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
4242 using base_storable = str_storable<K, my_type, Allocator>;
4243 using base_utf = from_utf_convertable<K, my_type>;
4244 using traits = ch_traits<K>;
4245 using uni = unicode_traits<K>;
4246
4247 friend base_storable;
4248 friend base_utf;
4249
4250 enum Types { Local, Constant, Shared };
4251
4252 union {
4253 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
4254 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
4255 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
4256 // и в итоге после символов строки получается 0, как и надо!
4257 struct {
4258 K buf_[LocalCount]; // Локальный буфер строки
4259 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
4260 uns_type type_ : 2;
4261 };
4262 struct {
4263 union {
4264 const K* cstr_; // Указатель на конcтантную строку
4265 const K* sstr_; // Указатель на строку, перед которой лежит SharedStringData
4266 };
4267 size_t bigLen_; // Длина не локальной строки.
4268 };
4269 };
4270
4271 void create_empty() {
4272 type_ = Local;
4273 localRemain_ = LocalCount;
4274 buf_[0] = 0;
4275 }
4276 K* init(size_t s) {
4277 if (s > LocalCount) {
4278 type_ = Shared;
4279 localRemain_ = 0;
4280 bigLen_ = s;
4281 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
4282 return (K*)sstr_;
4283 } else {
4284 type_ = Local;
4285 localRemain_ = LocalCount - s;
4286 return buf_;
4287 }
4288 }
4289
4290 K* set_size(size_t newSize) {
4291 // Вызывается при создании строки при необходимости изменить размер.
4292 // Других ссылок на shared buffer нет.
4293 size_t size = length();
4294 if (newSize != size) {
4295 if (type_ == Constant) {
4296 bigLen_ = newSize;
4297 } else {
4298 if (newSize <= LocalCount) {
4299 if (type_ == Shared) {
4300 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
4301 traits::copy(buf_, sstr_, newSize);
4302 str_buf->decr(base_storable::allocator());
4303 }
4304 type_ = Local;
4305 localRemain_ = LocalCount - newSize;
4306 } else {
4307 if (type_ == Shared) {
4308 if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
4309 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
4310 traits::copy(newStr, sstr_, newSize);
4311 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
4312 sstr_ = newStr;
4313 }
4314 } else if (type_ == Local) {
4315 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
4316 if (size)
4317 traits::copy(newStr, buf_, size);
4318 sstr_ = newStr;
4319 type_ = Shared;
4320 localRemain_ = 0;
4321 }
4322 bigLen_ = newSize;
4323 }
4324 }
4325 }
4326 K* str = type_ == Local ? buf_ : (K*)sstr_;
4327 str[newSize] = 0;
4328 return str;
4329 }
4330
4331public:
4332 using base_storable::base_storable;
4333 using base_utf::base_utf;
4334
4335 sstring() = default;
4336
4341 template<typename... Args>
4342 requires(sizeof...(Args) > 0 && std::is_constructible_v<Allocator, Args...>)
4343 sstring(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
4344
4345 static const sstring<K> empty_str;
4348 if (type_ == Shared) {
4349 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
4350 }
4351 }
4352
4356 sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
4357 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
4358 if (type_ == Shared)
4359 SharedStringData<K>::from_str(sstr_)->incr();
4360 }
4361
4365 sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
4366 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
4367 other.create_empty();
4368 }
4369
4376 template<size_t N>
4377 sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
4378 size_t size = src.length();
4379 if (size) {
4380 if (src.is_alloced()) {
4381 // Там динамический буфер, выделенный с запасом для SharedStringData.
4382 K* str = src.str();
4383 if (size > LocalCount) {
4384 // Просто присвоим его себе.
4385 sstr_ = str;
4386 bigLen_ = size;
4387 type_ = Shared;
4388 localRemain_ = 0;
4389 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
4390 } else {
4391 // Скопируем локально
4392 type_ = Local;
4393 localRemain_ = LocalCount - size;
4394 traits::copy(buf_, str, size + 1);
4395 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
4396 src.dealloc();
4397 }
4398 } else {
4399 // Копируем из локального буфера
4400 K* str = init(src.size_);
4401 traits::copy(str, src.symbols(), size + 1);
4402 }
4403 src.create_empty();
4404 } else
4405 create_empty();
4406 }
4407
4414 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
4415 requires std::is_constructible_v<allocator_t, Args...>
4416 sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4417 type_ = Constant;
4418 localRemain_ = 0;
4419 cstr_ = s;
4420 bigLen_ = N - 1;
4421 }
4422
4423 void swap(my_type&& other) noexcept {
4424 char buf[sizeof(buf_) + sizeof(K)];
4425 memcpy(buf, buf_, sizeof(buf));
4426 memcpy(buf_, other.buf_, sizeof(buf));
4427 memcpy(other.buf_, buf, sizeof(buf));
4428
4429 std::swap(base_storable::allocator(), other.allocator());
4430 }
4436 my_type& operator=(my_type other) noexcept {
4437 swap(std::move(other));
4438 return *this;
4439 }
4440
4445 my_type& operator=(simple_str<K> other) {
4446 return operator=(my_type{other, base_storable::allocator()});
4447 }
4448
4453 template<typename T, size_t N = const_lit_for<K, T>::Count>
4454 my_type& operator=(T&& other) {
4455 return operator=(my_type{other, base_storable::allocator()});
4456 }
4457
4462 template<size_t N, bool forShared, typename A>
4464 return operator=(my_type{other.to_str(), base_storable::allocator()});
4465 }
4466
4471 template<size_t N>
4473 return operator=(my_type{std::move(other)});
4474 }
4475
4481 my_type& operator=(const StrExprForType<K> auto& expr) {
4482 return operator=(my_type{expr, base_storable::allocator()});
4483 }
4484
4488 my_type& make_empty() noexcept {
4489 if (type_ == Shared)
4490 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
4491 create_empty();
4492 return *this;
4493 }
4494
4495 const K* symbols() const noexcept {
4496 return type_ == Local ? buf_ : cstr_;
4497 }
4498
4499 size_t length() const noexcept {
4500 return type_ == Local ? LocalCount - localRemain_ : bigLen_;
4501 }
4502
4503 bool is_empty() const noexcept {
4504 return length() == 0;
4505 }
4506
4507 bool empty() const noexcept {
4508 return is_empty();
4509 }
4510
4517 template<typename... T>
4518 static my_type printf(const K* pattern, T&&... args) {
4519 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
4520 }
4521
4527 template<typename... T>
4528 static my_type format(const FmtString<K, T...>& fmtString, T&&... args) {
4529 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
4530 }
4531
4537 template<typename... T>
4538 static my_type vformat(simple_str<K> fmtString, T&&... args) {
4539 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
4540 }
4541};
4542
4543template<typename K, Allocatorable Allocator>
4544inline const sstring<K> sstring<K, Allocator>::empty_str{};
4545
4546template<size_t I>
4547struct digits_selector {
4548 using wider_type = uint16_t;
4549};
4550
4551template<>
4552struct digits_selector<2> {
4553 using wider_type = uint32_t;
4554};
4555
4556template<>
4557struct digits_selector<4> {
4558 using wider_type = uint64_t;
4559};
4560
4561template<typename K, typename T>
4562constexpr size_t fromInt(K* bufEnd, T val) {
4563 const char* twoDigit =
4564 "0001020304050607080910111213141516171819"
4565 "2021222324252627282930313233343536373839"
4566 "4041424344454647484950515253545556575859"
4567 "6061626364656667686970717273747576777879"
4568 "8081828384858687888990919293949596979899";
4569 if (val) {
4570 need_sign<K, std::is_signed_v<T>, T> sign(val);
4571 K* itr = bufEnd;
4572 // Когда у нас минимальное отрицательное число, оно не меняется и остается меньше нуля
4573 if constexpr (std::is_signed_v<T>) {
4574 if (val < 0) {
4575 // Возьмем две последние цифры
4576 const char* ptr = twoDigit - (val % 100) * 2;
4577 *--itr = static_cast<K>(ptr[1]);
4578 *--itr = static_cast<K>(ptr[0]);
4579 val /= 100;
4580 val = -val;
4581 }
4582 }
4583 while (val >= 100) {
4584 const char* ptr = twoDigit + (val % 100) * 2;
4585 *--itr = static_cast<K>(ptr[1]);
4586 *--itr = static_cast<K>(ptr[0]);
4587 val /= 100;
4588 }
4589 if (val < 10) {
4590 *--itr = static_cast<K>('0' + val);
4591 } else {
4592 const char* ptr = twoDigit + val * 2;
4593 *--itr = static_cast<K>(ptr[1]);
4594 *--itr = static_cast<K>(ptr[0]);
4595 }
4596 sign.after(itr);
4597 return size_t(bufEnd - itr);
4598 }
4599 bufEnd[-1] = '0';
4600 return 1;
4601}
4602
4603template<typename K, typename T>
4604struct expr_num {
4605 using symb_type = K;
4606 using my_type = expr_num<K, T>;
4607
4608 enum { bufSize = 24 };
4609 mutable T value;
4610 mutable K buf[bufSize];
4611
4612 expr_num(T t) : value(t) {}
4613 expr_num(expr_num<K, T>&& t) : value(t.value) {}
4614
4615 size_t length() const noexcept {
4616 value = (T)fromInt(buf + bufSize, value);
4617 return (size_t)value;
4618 }
4619 K* place(K* ptr) const noexcept {
4620 ch_traits<K>::copy(ptr, buf + bufSize - (size_t)value, (size_t)value);
4621 return ptr + (size_t)value;
4622 }
4623};
4624
4632template<StrExpr A, FromIntNumber T>
4633inline constexpr auto operator + (const A& a, T s) {
4635}
4636
4644template<StrExpr A, FromIntNumber T>
4645inline constexpr auto operator + (T s, const A& a) {
4647}
4648
4658template<typename K, typename T>
4659inline constexpr auto e_num(T t) {
4660 return expr_num<K, T>{t};
4661}
4662
4663template<typename K>
4664simple_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) {
4665 if constexpr (std::is_same_v<K, u8s>)
4666 return s8;
4667 if constexpr (std::is_same_v<K, uws>)
4668 return sw;
4669 if constexpr (std::is_same_v<K, u16s>)
4670 return s16;
4671 if constexpr (std::is_same_v<K, u32s>)
4672 return s32;
4673}
4674
4675#define uni_string(K, p) select_str<K>(p, L##p, u##p, U##p)
4676
4677template<typename K> requires (is_one_of_std_char_v<K>)
4678struct expr_real {
4679 using symb_type = K;
4680 mutable K buf[40];
4681 mutable size_t l;
4682 double v;
4683 expr_real(double d) : v(d) {}
4684 expr_real(float d) : v(d) {}
4685
4686 size_t length() const noexcept {
4687 printf_selector::snprintf(buf, 40, uni_string(K, "%.16g").str, v);
4688 l = (size_t)ch_traits<K>::length(buf);
4689 return l;
4690 }
4691 K* place(K* ptr) const noexcept {
4692 ch_traits<K>::copy(ptr, buf, l);
4693 return ptr + l;
4694 }
4695};
4696
4704template<StrExpr A, typename R>
4705 requires(is_one_of_std_char_v<typename A::symb_type> && (std::is_same_v<R, double> || std::is_same_v<R, float>))
4706inline constexpr auto operator+(const A& a, R s) {
4708}
4709
4717template<StrExpr A, typename R>
4718 requires(is_one_of_std_char_v<typename A::symb_type> && (std::is_same_v<R, double> || std::is_same_v<R, float>))
4719inline constexpr auto operator+(R s, const A& a) {
4721}
4722
4730template<typename K> requires(is_one_of_std_char_v<K>)
4731inline constexpr auto e_real(double t) {
4732 return expr_real<K>{t};
4733}
4734
4735/*
4736* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
4737* K - тип символов строки
4738* T - тип контейнера строк (vector, list)
4739* I - длина разделителя в символах
4740* tail - добавлять разделитель после последнего элемента контейнера.
4741* Если контейнер пустой, разделитель в любом случае не добавляется
4742* skip_empty - пропускать пустые строки без добавления разделителя
4743*/
4744template<typename K, typename T, size_t I, bool tail, bool skip_empty>
4745struct expr_join {
4746 using symb_type = K;
4747 using my_type = expr_join<K, T, I, tail, skip_empty>;
4748
4749 const T& s;
4750 const K* delim;
4751
4752 constexpr size_t length() const noexcept {
4753 size_t l = 0;
4754 for (const auto& t: s) {
4755 size_t len = t.length();
4756 if (len > 0 || !skip_empty) {
4757 if (I > 0 && l > 0) {
4758 l += I;
4759 }
4760 l += len;
4761 }
4762 }
4763 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
4764 }
4765 constexpr K* place(K* ptr) const noexcept {
4766 if (s.empty()) {
4767 return ptr;
4768 }
4769 K* write = ptr;
4770 for (const auto& t: s) {
4771 size_t copyLen = t.length();
4772 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
4773 ch_traits<K>::copy(write, delim, I);
4774 write += I;
4775 }
4776 ch_traits<K>::copy(write, t.symbols(), copyLen);
4777 write += copyLen;
4778 }
4779 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
4780 ch_traits<K>::copy(write, delim, I);
4781 write += I;
4782 }
4783 return write;
4784 }
4785};
4786
4795template<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>
4796inline constexpr auto e_join(const T& s, L&& d) {
4797 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
4798}
4799
4800template<typename K, size_t N, size_t L>
4801struct expr_replaces {
4802 using symb_type = K;
4803 using my_type = expr_replaces<K, N, L>;
4804 simple_str<K> what;
4805 const K* pattern;
4806 const K* repl;
4807 mutable size_t first_, last_;
4808
4809 constexpr expr_replaces(simple_str<K> w, const K* p, const K* r) : what(w), pattern(p), repl(r) {}
4810
4811 constexpr size_t length() const {
4812 size_t l = what.length();
4813 if constexpr (N == L) {
4814 return l;
4815 }
4816 first_ = what.find(pattern, N, 0);
4817 if (first_ != str::npos) {
4818 last_ = first_ + N;
4819 for (;;) {
4820 l += L - N;
4821 size_t next = what.find(pattern, N, last_);
4822 if (next == str::npos) {
4823 break;
4824 }
4825 last_ = next + N;
4826 }
4827 }
4828 return l;
4829 }
4830 constexpr K* place(K* ptr) const noexcept {
4831 if constexpr (N == L) {
4832 const K* from = what.symbols();
4833 for (size_t start = 0; start < what.length();) {
4834 size_t next = what.find(pattern, N, start);
4835 if (next == str::npos) {
4836 next = what.length();
4837 }
4838 size_t delta = next - start;
4839 ch_traits<K>::copy(ptr, from + start, delta);
4840 ptr += delta;
4841 ch_traits<K>::copy(ptr, repl, L);
4842 ptr += L;
4843 start = next + N;
4844 }
4845 return ptr;
4846 }
4847 if (first_ == str::npos) {
4848 return what.place(ptr);
4849 }
4850 const K* from = what.symbols();
4851 for (size_t start = 0, offset = first_; ;) {
4852 ch_traits<K>::copy(ptr, from + start, offset - start);
4853 ptr += offset - start;
4854 ch_traits<K>::copy(ptr, repl, L);
4855 ptr += L;
4856 start = offset + N;
4857 if (start >= last_) {
4858 size_t tail = what.length() - last_;
4859 ch_traits<K>::copy(ptr, from + last_, tail);
4860 ptr += tail;
4861 break;
4862 } else {
4863 offset = what.find(pattern, N, start);
4864 }
4865 }
4866 return ptr;
4867 }
4868};
4869
4878template<typename K, typename T, size_t N = const_lit_for<K, T>::Count, typename X, size_t L = const_lit_for<K, X>::Count>
4879 requires(N > 1)
4880inline constexpr auto e_repl(simple_str<K> w, T&& p, X&& r) {
4881 return expr_replaces<K, N - 1, L - 1>{w, p, r};
4882}
4883
4895template<typename K>
4897 using symb_type = K;
4898 using my_type = expr_replaced<K>;
4899 simple_str<K> what;
4900 const simple_str<K> pattern;
4901 const simple_str<K> repl;
4902 mutable size_t first_, last_;
4909 constexpr expr_replaced(simple_str<K> w, simple_str<K> p, simple_str<K> r) : what(w), pattern(p), repl(r) {}
4910
4911 constexpr size_t length() const {
4912 size_t l = what.length();
4913 if (pattern.length() == repl.length()) {
4914 return l;
4915 }
4916 first_ = what.find(pattern);
4917 if (first_ != str::npos) {
4918 last_ = first_ + pattern.length();
4919 for (;;) {
4920 l += repl.length() - pattern.length();
4921 size_t next = what.find(pattern, last_);
4922 if (next == str::npos) {
4923 break;
4924 }
4925 last_ = next + pattern.length();
4926 }
4927 }
4928 return l;
4929 }
4930 constexpr K* place(K* ptr) const noexcept {
4931 if (repl.length() == pattern.length()) {
4932 const K* from = what.symbols();
4933 for (size_t start = 0; start < what.length();) {
4934 size_t next = what.find(pattern, start);
4935 if (next == str::npos) {
4936 next = what.length();
4937 }
4938 size_t delta = next - start;
4939 ch_traits<K>::copy(ptr, from + start, delta);
4940 ptr += delta;
4941 ch_traits<K>::copy(ptr, repl.symbols(), repl.length());
4942 ptr += repl.length();
4943 start = next + pattern.length();
4944 }
4945 return ptr;
4946 }
4947 if (first_ == str::npos) {
4948 return what.place(ptr);
4949 }
4950 const K* from = what.symbols();
4951 for (size_t start = 0, offset = first_; ;) {
4952 ch_traits<K>::copy(ptr, from + start, offset - start);
4953 ptr += offset - start;
4954 ch_traits<K>::copy(ptr, repl.symbols(), repl.length());
4955 ptr += repl.length();
4956 start = offset + pattern.length();
4957 if (start >= last_) {
4958 size_t tail = what.length() - last_;
4959 ch_traits<K>::copy(ptr, from + last_, tail);
4960 ptr += tail;
4961 break;
4962 } else {
4963 offset = what.find(pattern, start);
4964 }
4965 }
4966 return ptr;
4967 }
4968};
4969
4970template<bool UseVectorForReplace>
4971struct replace_search_result_store {
4972 size_t count_{};
4973 std::pair<size_t, size_t> replaces_[16];
4974};
4975
4976template<>
4977struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
4978
5000template<typename K, bool UseVectorForReplace = false>
5002 using symb_type = K;
5003 inline static const int BIT_SEARCH_TRESHHOLD = 4;
5004
5005 const simple_str<K> source_;
5006 const std::vector<std::pair<K, simple_str<K>>>& replaces_;
5007
5008 lstring<K, 32> pattern_;
5009
5010 mutable replace_search_result_store<UseVectorForReplace> search_results_;
5011
5012 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
5032 constexpr expr_replace_symbols(simple_str<K> source, const std::vector<std::pair<K, simple_str<K>>>& repl )
5033 : source_(source), replaces_(repl)
5034 {
5035 size_t pattern_len = replaces_.size();
5036 K* pattern = pattern_.set_size(pattern_len);
5037
5038 for (size_t idx = 0; idx < replaces_.size(); idx++) {
5039 *pattern++ = replaces_[idx].first;
5040 }
5041
5042 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
5043 for (size_t idx = 0; idx < pattern_len; idx++) {
5044 uu8s s = static_cast<uu8s>(pattern_[idx]);
5045 if constexpr (sizeof(K) == 1) {
5046 bit_mask_[s >> 3] |= (1 << (s & 7));
5047 } else {
5048 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
5049 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
5050 } else {
5051 bit_mask_[s >> 3] |= (1 << (s & 7));
5052 }
5053 }
5054 }
5055 }
5056 }
5057
5058 size_t length() const {
5059 size_t l = source_.length();
5060 auto [fnd, num] = find_first_of(source_.str, source_.len);
5061 if (fnd == str::npos) {
5062 return l;
5063 }
5064 l += replaces_[num].second.len - 1;
5065 if constexpr (UseVectorForReplace) {
5066 search_results_.reserve((l >> 4) + 8);
5067 search_results_.emplace_back(fnd, num);
5068 for (size_t start = fnd + 1;;) {
5069 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5070 if (fnd == str::npos) {
5071 break;
5072 }
5073 search_results_.emplace_back(fnd, idx);
5074 start = fnd + 1;
5075 l += replaces_[idx].second.len - 1;
5076 }
5077 } else {
5078 const size_t max_store = std::size(search_results_.replaces_);
5079 search_results_.replaces_[0] = {fnd, num};
5080 search_results_.count_++;
5081 for (size_t start = fnd + 1;;) {
5082 auto [found, idx] = find_first_of(source_.str, source_.len, start);
5083 if (found == str::npos) {
5084 break;
5085 }
5086 if (search_results_.count_ < max_store) {
5087 search_results_.replaces_[search_results_.count_] = {found, idx};
5088 }
5089 l += replaces_[idx].second.len - 1;
5090 search_results_.count_++;
5091 start = found + 1;
5092 }
5093 }
5094 return l;
5095 }
5096 K* place(K* ptr) const noexcept {
5097 size_t start = 0;
5098 const K* text = source_.str;
5099 if constexpr (UseVectorForReplace) {
5100 for (const auto& [pos, num] : search_results_) {
5101 size_t delta = pos - start;
5102 ch_traits<K>::copy(ptr, text + start, delta);
5103 ptr += delta;
5104 ptr = replaces_[num].second.place(ptr);
5105 start = pos + 1;
5106 }
5107 } else {
5108 const size_t max_store = std::size(search_results_.replaces_);
5109 size_t founded = search_results_.count_;
5110 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
5111 const auto [pos, num] = search_results_.replaces_[idx];
5112 size_t delta = pos - start;
5113 ch_traits<K>::copy(ptr, text + start, delta);
5114 ptr += delta;
5115 ptr = replaces_[num].second.place(ptr);
5116 start = pos + 1;
5117 }
5118 if (founded > max_store) {
5119 founded -= max_store;
5120 while (founded--) {
5121 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5122 size_t delta = fnd - start;
5123 ch_traits<K>::copy(ptr, text + start, delta);
5124 ptr += delta;
5125 ptr = replaces_[idx].second.place(ptr);
5126 start = fnd + 1;
5127 }
5128 }
5129 }
5130 size_t tail = source_.len - start;
5131 ch_traits<K>::copy(ptr, text + start, tail);
5132 return ptr + tail;
5133 }
5134
5135protected:
5136 size_t index_of(K s) const {
5137 return pattern_.find(s);
5138 }
5139
5140 bool is_in_mask(uu8s s) const {
5141 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
5142 }
5143 bool is_in_mask2(uu8s s) const {
5144 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
5145 }
5146
5147 bool is_in_pattern(K s, size_t& idx) const {
5148 if constexpr (sizeof(K) == 1) {
5149 if (is_in_mask(s)) {
5150 idx = index_of(s);
5151 return true;
5152 }
5153 } else {
5154 if (std::make_unsigned_t<const K>(s) > 255) {
5155 if (is_in_mask2(s)) {
5156 return (idx = index_of(s)) != -1;
5157 }
5158 } else {
5159 if (is_in_mask(s)) {
5160 idx = index_of(s);
5161 return true;
5162 }
5163 }
5164 }
5165 return false;
5166 }
5167
5168 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
5169 size_t pl = pattern_.length();
5170 if (pl >= BIT_SEARCH_TRESHHOLD) {
5171 size_t idx;
5172 while (offset < len) {
5173 if (is_in_pattern(text[offset], idx)) {
5174 return {offset, idx};
5175 }
5176 offset++;
5177 }
5178 } else {
5179 while (offset < len) {
5180 if (size_t idx = index_of(text[offset]); idx != -1) {
5181 return {offset, idx};
5182 }
5183 offset++;
5184 }
5185 }
5186 return {-1, -1};
5187 }
5188};
5189
5190// Строковое выражение для замены символов
5191template<typename K, size_t N, bool UseVectorForReplace>
5192struct expr_replace_const_symbols {
5193 using symb_type = K;
5194 inline static const int BIT_SEARCH_TRESHHOLD = 4;
5195 const K pattern_[N];
5196 const simple_str<K> source_;
5197 const simple_str<K> replaces_[N];
5198
5199 mutable replace_search_result_store<UseVectorForReplace> search_results_;
5200
5201 [[_no_unique_address]]
5202 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
5203
5204 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
5205 constexpr expr_replace_const_symbols(simple_str<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
5206
5207 size_t length() const {
5208 size_t l = source_.length();
5209 auto [fnd, num] = find_first_of(source_.str, source_.len);
5210 if (fnd == str::npos) {
5211 return l;
5212 }
5213 l += replaces_[num].len - 1;
5214 if constexpr (UseVectorForReplace) {
5215 search_results_.reserve((l >> 4) + 8);
5216 search_results_.emplace_back(fnd, num);
5217 for (size_t start = fnd + 1;;) {
5218 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5219 if (fnd == str::npos) {
5220 break;
5221 }
5222 search_results_.emplace_back(fnd, idx);
5223 start = fnd + 1;
5224 l += replaces_[idx].len - 1;
5225 }
5226 } else {
5227 const size_t max_store = std::size(search_results_.replaces_);
5228 search_results_.replaces_[0] = {fnd, num};
5229 search_results_.count_++;
5230 for (size_t start = fnd + 1;;) {
5231 auto [found, idx] = find_first_of(source_.str, source_.len, start);
5232 if (found == str::npos) {
5233 break;
5234 }
5235 if (search_results_.count_ < max_store) {
5236 search_results_.replaces_[search_results_.count_] = {found, idx};
5237 }
5238 l += replaces_[idx].len - 1;
5239 search_results_.count_++;
5240 start = found + 1;
5241 }
5242 }
5243 return l;
5244 }
5245 K* place(K* ptr) const noexcept {
5246 size_t start = 0;
5247 const K* text = source_.str;
5248 if constexpr (UseVectorForReplace) {
5249 for (const auto& [pos, num] : search_results_) {
5250 size_t delta = pos - start;
5251 ch_traits<K>::copy(ptr, text + start, delta);
5252 ptr += delta;
5253 ptr = replaces_[num].place(ptr);
5254 start = pos + 1;
5255 }
5256 } else {
5257 const size_t max_store = std::size(search_results_.replaces_);
5258 size_t founded = search_results_.count_;
5259 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
5260 const auto [pos, num] = search_results_.replaces_[idx];
5261 size_t delta = pos - start;
5262 ch_traits<K>::copy(ptr, text + start, delta);
5263 ptr += delta;
5264 ptr = replaces_[num].place(ptr);
5265 start = pos + 1;
5266 }
5267 if (founded > max_store) {
5268 founded -= max_store;
5269 while (founded--) {
5270 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
5271 size_t delta = fnd - start;
5272 ch_traits<K>::copy(ptr, text + start, delta);
5273 ptr += delta;
5274 ptr = replaces_[idx].place(ptr);
5275 start = fnd + 1;
5276 }
5277 }
5278 }
5279 size_t tail = source_.len - start;
5280 ch_traits<K>::copy(ptr, text + start, tail);
5281 return ptr + tail;
5282 }
5283
5284protected:
5285 template<typename ... Repl>
5286 constexpr expr_replace_const_symbols(int, simple_str<K> source, K s, simple_str<K> r, Repl&&... repl) :
5287 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
5288
5289 template<typename ... Repl> requires (sizeof...(Repl) == N)
5290 constexpr expr_replace_const_symbols(int, simple_str<K> source, Repl&&... repl) :
5291 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
5292 {
5293 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
5294 for (size_t idx = 0; idx < N; idx++) {
5295 uu8s s = static_cast<uu8s>(pattern_[idx]);
5296 if constexpr (sizeof(K) == 1) {
5297 bit_mask_[s >> 3] |= 1 << (s & 7);
5298 } else {
5299 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
5300 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
5301 } else {
5302 bit_mask_[s >> 3] |= 1 << (s & 7);
5303 }
5304 }
5305 }
5306 }
5307 }
5308
5309 template<size_t Idx>
5310 size_t index_of(K s) const {
5311 if constexpr (Idx < N) {
5312 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
5313 }
5314 return -1;
5315 }
5316 bool is_in_mask(uu8s s) const {
5317 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
5318 }
5319 bool is_in_mask2(uu8s s) const {
5320 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
5321 }
5322
5323 bool is_in_pattern(K s, size_t& idx) const {
5324 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
5325 if constexpr (sizeof(K) == 1) {
5326 if (is_in_mask(s)) {
5327 idx = index_of<0>(s);
5328 return true;
5329 }
5330 } else {
5331 if (std::make_unsigned_t<const K>(s) > 255) {
5332 if (is_in_mask2(s)) {
5333 return (idx = index_of<0>(s)) != -1;
5334 }
5335 } else {
5336 if (is_in_mask(s)) {
5337 idx = index_of<0>(s);
5338 return true;
5339 }
5340 }
5341 }
5342 }
5343 return false;
5344 }
5345 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
5346 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
5347 size_t idx;
5348 while (offset < len) {
5349 if (is_in_pattern(text[offset], idx)) {
5350 return {offset, idx};
5351 }
5352 offset++;
5353 }
5354 } else {
5355 while (offset < len) {
5356 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
5357 return {offset, idx};
5358 }
5359 offset++;
5360 }
5361 }
5362 return {-1, -1};
5363 }
5364};
5365
5391template<bool UseVector = false, typename K, typename ... Repl>
5392requires (sizeof...(Repl) % 2 == 0)
5393auto e_repl_const_symbols(simple_str<K> src, Repl&& ... other) {
5394 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(src, std::forward<Repl>(other)...);
5395}
5396
5397template<typename K>
5398struct StoreType {
5399 simple_str<K> str;
5400 size_t hash;
5401 char node[sizeof(sstring<K>)];
5402
5403 const simple_str_nt<K>& to_nt() const noexcept {
5404 return static_cast<const simple_str_nt<K>&>(str);
5405 }
5406 const sstring<K>& to_str() const noexcept {
5407 return *reinterpret_cast<const sstring<K>*>(node);
5408 }
5409};
5410
5411using HashKeyA = StoreType<u8s>;
5412using HashKeyW = StoreType<u16s>;
5413using HashKeyU = StoreType<u32s>;
5414
5415template<bool Wide>
5416struct fnv_const {
5417 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
5418 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
5419};
5420
5421template<>
5422struct fnv_const<false> {
5423 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
5424 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
5425};
5426
5427using fnv = fnv_const<sizeof(size_t) == 8>;
5428
5429template<typename K>
5430inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
5431 size_t h = fnv::basis;
5432 for (size_t i = 0; i < l; i++) {
5433 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
5434 }
5435 return h;
5436};
5437
5438template<typename K>
5439inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
5440 size_t h = fnv::basis;
5441 for (size_t i = 0; i < l; i++) {
5442 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
5443 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
5444 }
5445 return h;
5446};
5447
5448template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
5449inline constexpr size_t fnv_hash(T&& value) {
5450 size_t h = fnv::basis;
5451 for (size_t i = 0; i < N - 1; i++) {
5452 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
5453 }
5454 return h;
5455};
5456
5457template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
5458inline constexpr size_t fnv_hash_ia(T&& value) {
5459 size_t h = fnv::basis;
5460 for (size_t i = 0; i < N - 1; i++) {
5461 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
5462 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
5463 }
5464 return h;
5465};
5466
5467template<typename K>
5468inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
5469 return fnv_hash(ptr, l);
5470};
5471
5472template<typename K>
5473inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
5474 return fnv_hash_ia(ptr, l);
5475};
5476
5477static_assert(std::is_trivially_copyable_v<StoreType<u8s>>, "Store type must be trivially copyable");
5478
5479template<typename K>
5480struct streql;
5481template<typename K>
5482struct strhash;
5483
5532template<typename K, typename T, typename H = strhash<K>, typename E = streql<K>>
5533class hashStrMap : public std::unordered_map<StoreType<K>, T, H, E> {
5534protected:
5535 using InStore = StoreType<K>;
5536
5537public:
5538 using my_type = hashStrMap<K, T, H, E>;
5539 using hash_t = std::unordered_map<InStore, T, H, E>;
5540 using hasher = H;
5541
5542 hashStrMap() = default;
5543 hashStrMap(const my_type& other) : hash_t(other) {
5544 for (const auto& [k, v] : *this) {
5545 InStore& stored = const_cast<InStore&>(k);
5546 sstring<K> tmp = *(sstring<K>*)stored.node;
5547 new (stored.node) sstring<K>(std::move(tmp));
5548 stored.str.str = stored.to_str().symbols();
5549 }
5550 }
5551 ~hashStrMap() {
5552 for (auto& k: *this)
5553 ((sstring<K>*)k.first.node)->~sstring();
5554 }
5555
5556 hashStrMap(my_type&& o) = default;
5557
5558 my_type& operator=(const my_type& other) {
5559 hash_t::operator=(other);
5560 for (const auto& [k, v] : *this) {
5561 InStore& stored = const_cast<InStore&>(k);
5562 sstring<K> tmp = *(sstring<K>*)stored.node;
5563 new (stored.node) sstring<K>(std::move(tmp));
5564 stored.str.str = stored.to_str().symbols();
5565 }
5566 return *this;
5567 };
5568 my_type& operator=(my_type&&) = default;
5569
5570 hashStrMap(std::initializer_list<std::pair<const StoreType<K>, T>>&& init) {
5571 for (const auto& e: init)
5572 emplace(e.first, e.second);
5573 }
5574
5575 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
5576
5577 hashStrMap(init_str&& init) {
5578 for (const auto& e: init)
5579 emplace(e.first, e.second);
5580 }
5581
5582 // При входе хэш должен быть уже посчитан
5583 template<typename... ValArgs>
5584 auto try_emplace(const StoreType<K>& key, ValArgs&&... args) {
5585 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
5586 if (it.second) {
5587 InStore& stored = const_cast<InStore&>(it.first->first);
5588 new (stored.node) sstring<K>(key.str);
5589 stored.str.str = stored.to_str().symbols();
5590 }
5591 return it;
5592 }
5593
5594 static StoreType<K> toStoreType(simple_str<K> key) {
5595 return {key, H{}(key)};
5596 }
5597
5598 template<typename Key, typename... ValArgs>
5599 requires(std::is_convertible_v<Key, simple_str<K>>)
5600 auto try_emplace(Key&& key, ValArgs&&... args) {
5601 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
5602 if (it.second) {
5603 InStore& stored = const_cast<InStore&>(it.first->first);
5604 new (stored.node) sstring<K>(std::forward<Key>(key));
5605 stored.str.str = stored.to_str().symbols();
5606 }
5607 return it;
5608 }
5609
5610 template<typename... ValArgs>
5611 auto emplace(const StoreType<K>& key, ValArgs&&... args) {
5612 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
5613 if (!it.second) {
5614 it.first->second = T(std::forward<ValArgs>(args)...);
5615 }
5616 return it;
5617 }
5618
5619 template<typename Key, typename... ValArgs>
5620 requires(std::is_convertible_v<Key, simple_str<K>>)
5621 auto emplace(Key&& key, ValArgs&&... args) {
5622 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
5623 if (!it.second) {
5624 it.first->second = T(std::forward<ValArgs>(args)...);
5625 }
5626 return it;
5627 }
5628
5629 auto& operator[](const StoreType<K>& key) {
5630 return try_emplace(key).first->second;
5631 }
5632
5633 template<typename Key>
5634 requires(std::is_convertible_v<Key, simple_str<K>>)
5635 auto& operator[](Key&& key) {
5636 return try_emplace(std::forward<Key>(key)).first->second;
5637 }
5638
5639 auto at(const StoreType<K>& key) {
5640 return hash_t::at(key);
5641 }
5642 auto at(const StoreType<K>& key) const {
5643 return hash_t::at(key);
5644 }
5645
5646 auto at(simple_str<K> key) {
5647 return hash_t::at(toStoreType(key));
5648 }
5649 auto at(simple_str<K> key) const {
5650 return hash_t::at(toStoreType(key));
5651 }
5652
5653 auto find(const StoreType<K>& key) const {
5654 return hash_t::find(key);
5655 }
5656
5657 auto find(simple_str<K> key) const {
5658 return find(toStoreType(key));
5659 }
5660
5661 auto find(const StoreType<K>& key) {
5662 return hash_t::find(key);
5663 }
5664
5665 auto find(simple_str<K> key) {
5666 return find(toStoreType(key));
5667 }
5668
5669 auto erase(typename hash_t::const_iterator it) {
5670 if (it != hash_t::end()) {
5671 ((sstring<K>*)it->first.node)->~sstring();
5672 }
5673 return hash_t::erase(it);
5674 }
5675
5676 auto erase(const StoreType<K>& key) {
5677 auto it = hash_t::find(key);
5678 if (it != hash_t::end()) {
5679 ((sstring<K>*)it->first.node)->~sstring();
5680 hash_t::erase(it);
5681 return 1;
5682 }
5683 return 0;
5684 }
5685
5686 auto erase(simple_str<K> key) {
5687 return erase(toStoreType(key));
5688 }
5689
5690 bool lookup(const K* txt, T& val) const {
5691 auto it = find(e_s(txt));
5692 if (it != hash_t::end()) {
5693 val = it->second;
5694 return true;
5695 }
5696 return false;
5697 }
5698
5699 bool lookup(simple_str<K> txt, T& val) const {
5700 auto it = find(txt);
5701 if (it != hash_t::end()) {
5702 val = it->second;
5703 return true;
5704 }
5705 return false;
5706 }
5707
5708 void clear() {
5709 for (auto& k: *this)
5710 ((sstring<K>*)k.first.node)->~sstring();
5711 hash_t::clear();
5712 }
5713 bool contains(const StoreType<K>& key) const {
5714 return hash_t::find(key) != this->end();
5715 }
5716
5717 bool contains(simple_str<K> key) const {
5718 return find(toStoreType(key)) != this->end();
5719 }
5720};
5721
5722template<typename K>
5723struct streql {
5724 bool operator()(const StoreType<K>& _Left, const StoreType<K>& _Right) const {
5725 return _Left.hash == _Right.hash && _Left.str == _Right.str;
5726 }
5727};
5728
5729template<typename K>
5730struct strhash { // hash functor for basic_string
5731 size_t operator()(simple_str<K> _Keyval) const {
5732 return fnv_hash(_Keyval.symbols(), _Keyval.length());
5733 }
5734 size_t operator()(const StoreType<K>& _Keyval) const {
5735 return _Keyval.hash;
5736 }
5737};
5738
5739template<typename K>
5740struct streqlia {
5741 bool operator()(const StoreType<K>& _Left, const StoreType<K>& _Right) const {
5742 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
5743 }
5744};
5745
5746template<typename K>
5747struct strhashia {
5748 size_t operator()(simple_str<K> _Keyval) const {
5749 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
5750 }
5751 size_t operator()(const StoreType<K>& _Keyval) const {
5752 return _Keyval.hash;
5753 }
5754};
5755
5756template<typename K>
5757struct streqliu {
5758 bool operator()(const StoreType<K>& _Left, const StoreType<K>& _Right) const {
5759 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
5760 }
5761};
5762
5763template<typename K>
5764struct strhashiu {
5765 size_t operator()(simple_str<K> _Keyval) const {
5766 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
5767 }
5768 size_t operator()(const StoreType<K>& _Keyval) const {
5769 return _Keyval.hash;
5770 }
5771};
5772
5781template<typename K>
5782class chunked_string_builder {
5783 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
5784 std::vector<chunk_t> chunks; // блоки и длина данных в них
5785 K* write{}; // Текущая позиция записи
5786 size_t len{}; // Общая длина
5787 size_t remain{}; // Сколько осталось места в текущем блоке
5788 size_t align{1024};
5789
5790public:
5791 using my_type = chunked_string_builder<K>;
5792 using symb_type = K;
5793 chunked_string_builder() = default;
5794 chunked_string_builder(size_t a) : align(a){};
5795 chunked_string_builder(const my_type&) = delete;
5796 chunked_string_builder(my_type&& other) noexcept
5797 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
5798 other.len = other.remain = 0;
5799 other.write = nullptr;
5800 }
5801 my_type& operator=(my_type other) noexcept {
5802 chunks.swap(other.chunks);
5803 write = other.write;
5804 len = other.len;
5805 remain = other.remain;
5806 align = other.align;
5807 other.len = other.remain = 0;
5808 other.write = nullptr;
5809 return *this;
5810 }
5811
5814 if (data.len) {
5815 len += data.len;
5816 if (data.len <= remain) {
5817 // Добавляемые данные влезают в выделенный блок, просто скопируем их
5818 ch_traits<K>::copy(write, data.str, data.len);
5819 write += data.len; // Сдвинем позицию записи
5820 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных
5821 remain -= data.len; // Уменьшим остаток места в блоке
5822 } else {
5823 // Не влезают
5824 if (remain) {
5825 // Сначала запишем сколько влезет
5826 ch_traits<K>::copy(write, data.str, remain);
5827 data.len -= remain;
5828 data.str += remain;
5829 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных
5830 }
5831 // Выделим новый блок и впишем в него данные
5832 size_t blockSize = (data.len + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
5833 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
5834 write = chunks.back().first.get();
5835 ch_traits<K>::copy(write, data.str, data.len);
5836 write += data.len;
5837 remain = blockSize - data.len;
5838 }
5839 }
5840 return *this;
5841 }
5842
5843 my_type& operator<<(const StrExprForType<K> auto& expr) {
5844 size_t l = expr.length();
5845 if (l) {
5846 if (l < remain) {
5847 write = expr.place(write);
5848 chunks.back().second += l;
5849 len += l;
5850 remain -= l;
5851 } else if (!remain) {
5852 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
5853 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
5854 write = expr.place(chunks.back().first.get());
5855 len += l;
5856 remain = blockSize - l;
5857 } else {
5858 auto store = std::make_unique<K[]>(l);
5859 expr.place(store.get());
5860 return operator<<({store.get(), l});
5861 }
5862 }
5863 return *this;
5864 }
5865
5866 template<typename T>
5867 my_type& operator<<(T data)
5868 requires std::is_same_v<T, K>
5869 {
5870 return operator<<(expr_char<K>(data));
5871 }
5872
5873 constexpr size_t length() const noexcept {
5874 return len;
5875 }
5876
5877 void reset() {
5878 if (chunks.empty()) {
5879 return;
5880 }
5881 if (chunks.size() > 1) {
5882 remain = 0;
5883 chunks.resize(1);
5884 }
5885 remain += chunks[0].second;
5886 chunks[0].second = 0;
5887 len = 0;
5888 write = chunks[0].first.get();
5889 }
5890
5891 constexpr K* place(K* p) const noexcept {
5892 for (const auto& block: chunks) {
5893 ch_traits<K>::copy(p, block.first.get(), block.second);
5894 p += block.second;
5895 }
5896 return p;
5897 }
5903 template<typename Op>
5904 void out(const Op& o) const {
5905 for (const auto& block: chunks)
5906 o(block.first.get(), block.second);
5907 }
5908
5911 bool is_continuous() const {
5912 if (chunks.size()) {
5913 const K* ptr = chunks.front().first.get();
5914 for (const auto& chunk: chunks) {
5915 if (chunk.first.get() != ptr)
5916 return false;
5917 ptr += chunk.second;
5918 }
5919 }
5920 return true;
5921 }
5922
5926 const K* begin() const {
5927 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
5928 }
5929
5932 void clear() {
5933 chunks.clear();
5934 write = nullptr;
5935 len = 0;
5936 remain = 0;
5937 }
5938
5943 typename decltype(chunks)::const_iterator it, end;
5944 size_t writedFromCurrentChunk;
5948 bool is_end() {
5949 return it == end;
5950 }
5951
5957 size_t store(K* buffer, size_t size) {
5958 size_t writed = 0;
5959 while (size && !is_end()) {
5960 size_t remain = it->second - writedFromCurrentChunk;
5961 size_t write = std::min(size, remain);
5962 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
5963 writed += write;
5964 remain -= write;
5965 size -= write;
5966 if (!remain) {
5967 ++it;
5968 writedFromCurrentChunk = 0;
5969 } else
5970 writedFromCurrentChunk += write;
5971 }
5972 return writed;
5973 }
5974 };
5975
5981 return {chunks.begin(), chunks.end(), 0};
5982 }
5983
5987 const auto& data() const {
5988 return chunks;
5989 }
5990};
5991
5992using stringa = sstring<u8s>;
5993using stringw = sstring<wchar_t>;
5994using stringu = sstring<u16s>;
5995using stringuu = sstring<u32s>;
5996
6000template<typename T>
6005template<typename T>
6010template<typename T>
6012
6016template<typename T>
6018
6022template<typename T>
6024
6028template<typename T>
6030
6034template<typename T>
6036template<typename T>
6044template<typename T>
6046
6050template<typename T>
6055template<typename T>
6060template<typename T>
6062
6063inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
6064
6065inline namespace literals {
6066
6073consteval simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
6074 return simple_str_nt<u8s>{ptr, l};
6075}
6076
6083consteval simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
6084 return simple_str_nt<uws>{ptr, l};
6085}
6086
6093consteval simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
6094 return simple_str_nt<u16s>{ptr, l};
6095}
6096
6103consteval simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
6104 return simple_str_nt<u32s>{ptr, l};
6105}
6106
6113consteval StoreType<u8s> operator""_h(const u8s* ptr, size_t l) {
6114 return StoreType<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
6115}
6116
6123consteval StoreType<u8s> operator""_ia(const u8s* ptr, size_t l) {
6124 return StoreType<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
6125}
6126
6133inline StoreType<u8s> operator""_iu(const u8s* ptr, size_t l) {
6134 return StoreType<u8s>{{ptr, l}, strhashiu<u8s>{}(simple_str<u8s>{ptr, l})};
6135}
6136
6143consteval StoreType<u16s> operator""_h(const u16s* ptr, size_t l) {
6144 return StoreType<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
6145}
6146
6153consteval StoreType<u16s> operator""_ia(const u16s* ptr, size_t l) {
6154 return StoreType<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
6155}
6156
6163inline StoreType<u16s> operator""_iu(const u16s* ptr, size_t l) {
6164 return StoreType<u16s>{{ptr, l}, strhashiu<u16s>{}(simple_str<u16s>{ptr, l})};
6165}
6166
6173consteval StoreType<u32s> operator""_h(const u32s* ptr, size_t l) {
6174 return StoreType<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
6175}
6176
6183consteval StoreType<u32s> operator""_ia(const u32s* ptr, size_t l) {
6184 return StoreType<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
6185}
6186
6193inline StoreType<u32s> operator""_iu(const u32s* ptr, size_t l) {
6194 return StoreType<u32s>{{ptr, l}, strhashiu<u32s>{}(simple_str<u32s>{ptr, l})};
6195}
6196
6197} // namespace literals
6198
6205inline std::ostream& operator<<(std::ostream& stream, ssa text) {
6206 return stream << std::string_view{text.symbols(), text.length()};
6207}
6208
6215inline std::wostream& operator<<(std::wostream& stream, ssw text) {
6216 return stream << std::wstring_view{text.symbols(), text.length()};
6217}
6218
6225inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
6226 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
6227}
6228
6235inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
6236 return stream << std::string_view{text.symbols(), text.length()};
6237}
6238
6245inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
6246 return stream << std::wstring_view{text.symbols(), text.length()};
6247}
6248
6255inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
6256 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
6257}
6258
6265template<size_t N, bool S, simstr::Allocatorable A>
6266inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
6267 return stream << std::string_view{text.symbols(), text.length()};
6268}
6269
6276template<size_t N, bool S, simstr::Allocatorable A>
6277inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
6278 return stream << std::wstring_view{text.symbols(), text.length()};
6279}
6280
6287template<size_t N, bool S, simstr::Allocatorable A>
6288inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
6289 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
6290}
6291
6292} // namespace simstr
6293
6297template<typename K>
6298struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
6299 // Define format() by calling the base class implementation with the wrapped value
6300 template<typename FormatContext>
6301 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
6302 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
6303 }
6304};
6305
6309template<typename K>
6310struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
6311 // Define format() by calling the base class implementation with the wrapped value
6312 template<typename FormatContext>
6313 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
6314 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
6315 }
6316};
6317
6321template<typename K>
6322struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
6323 // Define format() by calling the base class implementation with the wrapped value
6324 template<typename FormatContext>
6325 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
6326 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
6327 }
6328};
6329
6333template<typename K, size_t N, bool S, typename A>
6334struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
6335 // Define format() by calling the base class implementation with the wrapped value
6336 template<typename FormatContext>
6337 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
6338 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
6339 }
6340};
Класс для последовательного получения подстрок по заданному разделителю
Определения sstring.h:1925
simple_str< K > next()
Получить следующую подстроку
Определения sstring.h:1941
bool is_done() const
Узнать, не закончились ли подстроки
Определения sstring.h:1934
my_type & operator<<(simple_str< K > data)
Добавление порции данных
Определения sstring.h:5813
portion_store get_portion() const
Получить portion_store, черезк который можно последовательно извлекать данные во внешний буфер
Определения sstring.h:5980
constexpr size_t length() const noexcept
Длина сохранённого текста
Определения sstring.h:5873
my_type & operator<<(T data)
Добавление символа
Определения sstring.h:5867
void reset()
Сбрасывает содержимое, но при этом не удаляет первый буфер, чтобы потом избежать аллокации
Определения sstring.h:5877
void clear()
Очистить объект, освободив все выделенные буфера.
Определения sstring.h:5932
my_type & operator<<(const StrExprForType< K > auto &expr)
Добавление строкового выражения
Определения sstring.h:5843
bool is_continuous() const
Проверяет, расположен ли весь текст одним непрерывным куском в памяти
Определения sstring.h:5911
void out(const Op &o) const
Применяет функтор к каждому сохранённому буферу
Определения sstring.h:5904
const auto & data() const
Получить внутренние буфера с данными
Определения sstring.h:5987
const K * begin() const
Получить указатель на начало первого буфера. Имеет смысл применять только если is_continuous true.
Определения sstring.h:5926
Контейнер для более эффективного поиска по строковым ключам.
Определения sstring.h:5533
Класс мутабельной, владеющей строки. Содержит внутренний буфер для строк заданного размера.
Определения sstring.h:3765
lstring(const my_type &other, Args &&... args)
Копирование из другой строки такого же типа, но с другим аллокатором
Определения sstring.h:3890
lstring(const my_type &other)
Копирование из другой строки такого же типа
Определения sstring.h:3878
constexpr lstring(T &&value, Args &&... args)
Конструктор из строкового литерала
Определения sstring.h:3903
K * reserve_no_preserve(size_t newSize)
Определения sstring.h:4059
my_type & operator=(T &&other)
Оператор присваивания строкового литерала
Определения sstring.h:4010
@ LocalCapacity
Определения sstring.h:3772
my_type & operator=(my_type &&other) noexcept
Оператор присваивания перемещением из строки такого же типа
Определения sstring.h:3962
void clear()
Делает строку пустой, не меняя буфер строки.
Определения sstring.h:4146
bool is_empty() const noexcept
Пустая ли строка
Определения sstring.h:4041
void shrink_to_fit()
Уменьшает размер внешнего буфера до минимально возможного для хранения строки. Если строка уместится ...
Определения sstring.h:4132
size_t length() const noexcept
Длина строки
Определения sstring.h:4029
size_t capacity() const noexcept
Текущая ёмкость буфера строки
Определения sstring.h:4049
K * reserve(size_t newSize)
Выделить буфер, достаточный для размещения newSize символов плюс завершающий ноль.
Определения sstring.h:4076
bool empty() const noexcept
Пустая ли строка, для совместимости с std::string.
Определения sstring.h:4045
my_type & operator=(const StrExprForType< K > auto &expr)
Оператор присаивания строкового выражения
Определения sstring.h:4019
K * str() noexcept
Указатель на буфер строки
Определения sstring.h:4037
my_type & operator=(const my_type &other)
Оператор присваивания копией из строки такого же типа
Определения sstring.h:3949
lstring(const Op &op, Args &&... args)
Конструктор заполнения с помощью функтора (см. str_mutable::fill)
Определения sstring.h:3937
void reset()
Делает строку пустой и освобождает внешний буфер, если он был
Определения sstring.h:4150
const K * symbols() const noexcept
Указатель на константные символы
Определения sstring.h:4033
lstring(my_type &&other) noexcept
Конструктор перемещения из строки такого же типа
Определения sstring.h:3915
K * set_size(size_t newSize)
Устанавливает размер текущей строки, при необходимости выделяя место.
Определения sstring.h:4094
my_type & operator=(simple_str< K > other)
Оператор присваивания из simple_str.
Определения sstring.h:4001
bool is_local() const noexcept
Узнать, локальный или внешний буфер используется для символов
Определения sstring.h:4110
void define_size()
Определить длину строки. Ищет символ 0 в буфере строки до его ёмкости, после чего устаналивает длину ...
Определения sstring.h:4117
Класс иммутабельной владеющей строки
Определения sstring.h:4231
sstring(lstring< K, N, true, Allocator > &&src)
Конструктор перемещения из lstring с совместимым с sstring внешним буфером
Определения sstring.h:4377
my_type & make_empty() noexcept
Сделать строку пустой
Определения sstring.h:4488
sstring(Args &&... args)
Конструктор пустой строки
Определения sstring.h:4343
bool empty() const noexcept
for std::string compatibility
Определения sstring.h:4507
my_type & operator=(my_type other) noexcept
Оператор присвоения другой строки того же типа
Определения sstring.h:4436
my_type & operator=(simple_str< K > other)
Оператор присвоения другой строки другого типа
Определения sstring.h:4445
sstring(const my_type &other) noexcept
Конструктор копирования строки
Определения sstring.h:4356
~sstring()
Деструктор строки
Определения sstring.h:4347
static my_type format(const FmtString< K, T... > &fmtString, T &&... args)
Получить строку, отформатированную с помощью std::format
Определения sstring.h:4528
static my_type printf(const K *pattern, T &&... args)
Получить строку, отформатированную с помощью std::sprintf
Определения sstring.h:4518
my_type & operator=(lstring< K, N, true, Allocator > &&other)
Оператор присвоения перемещаемой строки типа lstring с совместимым буфером
Определения sstring.h:4472
my_type & operator=(const lstring< K, N, forShared, A > &other)
Оператор присвоения другой строки типа lstring.
Определения sstring.h:4463
bool is_empty() const noexcept
Пустая ли строка
Определения sstring.h:4503
my_type & operator=(const StrExprForType< K > auto &expr)
Оператор присвоения строкового выражения
Определения sstring.h:4481
static my_type vformat(simple_str< K > fmtString, T &&... args)
Получить строку, отформатированную с помощью std::vformat
Определения sstring.h:4538
my_type & operator=(T &&other)
Оператор присвоения строкового литерала
Определения sstring.h:4454
const K * symbols() const noexcept
Указатель на символы строки
Определения sstring.h:4495
sstring(T &&s, Args &&... args)
Инициализация из строкового литерала
Определения sstring.h:4416
size_t length() const noexcept
Длина строки
Определения sstring.h:4499
sstring(my_type &&other) noexcept
Конструктор перемещения
Определения sstring.h:4365
Класс с базовыми константными строковыми алгоритмами.
Определения sstring.h:537
bool is_ascii() const noexcept
Содержит ли строка только ASCII символы
Определения sstring.h:1457
T as_int() const noexcept
Преобразовать строку в число заданного типа
Определения sstring.h:1165
constexpr bool starts_with(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки
Определения sstring.h:1355
size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Найти конец последнего вхождения подстроки в этой строке.
Определения sstring.h:955
auto operator<=>(T &&other) const noexcept
Оператор сравнения строки и строкового литерала
Определения sstring.h:766
R lowered_only_ascii() const
Получить копию строки в нижнем регистре ASCII символов
Определения sstring.h:1499
R trimmed() const
Получить строку с удалением пробельных символов слева и справа
Определения sstring.h:1571
str_piece to_str() const noexcept
Преобразовать себя в "кусок строки", включающий всю строку
Определения sstring.h:608
size_t find_last(K s, size_t offset=-1) const noexcept
Найти последнее вхождения символа в этой строке
Определения sstring.h:1064
size_t find_or_all(K s, size_t offset=0) const noexcept
Найти символ в этой строке или конец строки.
Определения sstring.h:1009
R trimmed_left(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, слева
Определения sstring.h:1611
size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа из заданного набора символов
Определения sstring.h:1108
size_t find(str_piece pattern, size_t offset=0) const noexcept
Найти начало первого вхождения подстроки в этой строке.
Определения sstring.h:871
bool starts_with_iu(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки без учёта регистра Unicode символов первой плоскости (<0xFF...
Определения sstring.h:1389
std::tuple< T, IntConvertResult, size_t > to_int() const noexcept
Преобразовать строку в число заданного типа
Определения sstring.h:1185
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Получить копию строки с заменёнными вхождениями подстрок
Определения sstring.h:1530
int compare_ia(str_piece text) const noexcept
Сравнение строк посимвольно без учёта регистра ASCII символов
Определения sstring.h:797
expr_replaces< K, N - 1, L - 1 > replace_init(T &&pattern, M &&repl) const
Получить строковое выражение, которое выдает строку с заменёнными подстроками, заданными строковыми л...
Определения sstring.h:1541
R uppered() const
Получить копию строки в верхнем регистре Unicode символов первой плоскости (<0xFFFF)
Определения sstring.h:1508
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Получить часть строки как "кусок строки".
Определения sstring.h:655
std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа из заданного набора символов
Определения sstring.h:1088
size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Найти конец последнего вхождения подстроки в этой строке или конец строки.
Определения sstring.h:975
T splitf(str_piece delimeter, const Op &beforeFunc, size_t offset=0) const
Разделить строку на части по заданному разделителю, с возможным применением функтора к каждой подстро...
Определения sstring.h:1325
void as_number(double &t)
Преобразовать строку в double.
Определения sstring.h:1245
R trimmed_right_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения sstring.h:1731
constexpr bool prefix_in(str_piece text) const noexcept
Является ли эта строка началом другой строки
Определения sstring.h:1404
int compare_iu(str_piece text) const noexcept
Сравнение строк посимвольно без учёта регистра Unicode символов первой плоскости (<0xFFFF)
Определения sstring.h:828
size_t find(K s, size_t offset=0) const noexcept
Найти символ в этой строке.
Определения sstring.h:994
size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Найти начало последнего вхождения подстроки в этой строке.
Определения sstring.h:946
bool less_iu(str_piece text) const noexcept
Меньше ли строка другой строки посимвольно без учёта регистра Unicode символов первой плоскости (<0xF...
Определения sstring.h:845
std::string_view to_sv() const noexcept
Конвертировать в std::string_view.
Определения sstring.h:615
R trimmed(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, слева и справа
Определения sstring.h:1675
R trimmed_left(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, слева
Определения sstring.h:1685
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой без учёта регистра Unicode символов первой плоскости (<0...
Определения sstring.h:1451
double to_double() const noexcept
Преобразовать строку в double.
Определения sstring.h:1192
bool contains(str_piece pattern, size_t offset=0) const noexcept
Содержит ли строка указанную подстроку.
Определения sstring.h:985
R uppered_only_ascii() const
Получить копию строки в верхнем регистре ASCII символов
Определения sstring.h:1490
Splitter< K > splitter(str_piece delimeter) const
Получить объект Splitter по заданному разделителю, который позволяет последовательно получать подстро...
Определения sstring.h:1965
my_type str_mid(size_t from, size_t len=-1) const
Получить часть строки объектом того же типа, к которому применён метод, аналогично mid.
Определения sstring.h:1146
R trimmed_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения sstring.h:1637
std::vector< size_t > find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Найти все вхождения подстроки в этой строке
Определения sstring.h:1055
R trimmed_right() const
Получить строку с удалением пробельных символов справа
Определения sstring.h:1589
R trimmed_right_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения sstring.h:1663
constexpr bool ends_with(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой
Определения sstring.h:1416
R trimmed_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения sstring.h:1707
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Получить часть строки как "simple_str".
Определения sstring.h:641
bool operator==(T &&other) const noexcept
Оператор сравнения строки и строкового литерала на равенство
Определения sstring.h:757
constexpr auto operator<=>(const base &other) const noexcept
Оператор сравнения строк
Определения sstring.h:749
R lowered() const
Получить копию строки в нижнем регистре Unicode символов первой плоскости (<0xFFFF)
Определения sstring.h:1517
R trimmed_right(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, справа
Определения sstring.h:1622
size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Найти начало первого вхождения подстроки в этой строке или конец строки.
Определения sstring.h:905
bool equal_ia(str_piece text) const noexcept
Равна ли строка другой строке посимвольно без учёта регистра ASCII символов
Определения sstring.h:806
constexpr K * place(K *ptr) const noexcept
Копировать строку в указанный буфер.
Определения sstring.h:569
std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа из заданного набора символов
Определения sstring.h:1117
R trimmed_left_with_spaces(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, а также пробельных символов,...
Определения sstring.h:1719
void copy_to(K *buffer, size_t bufSize)
Копировать строку в указанный буфер.
Определения sstring.h:583
R trimmed_right(str_piece pattern) const
Получить строку с удалением символов, заданных другой строкой, справа
Определения sstring.h:1695
R trimmed_left() const
Получить строку с удалением пробельных символов слева
Определения sstring.h:1580
size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Найти конец первого вхождения подстроки в этой строке или конец строки.
Определения sstring.h:915
constexpr int compare(str_piece o) const
Сравнение строк посимвольно
Определения sstring.h:700
K at(ptrdiff_t idx) const
Получить символ на заданной позиции
Определения sstring.h:686
std::string to_string() const noexcept
Конвертировать в std::string.
Определения sstring.h:622
constexpr int strcmp(const K *text) const
Сравнение с C-строкой посимвольно
Определения sstring.h:708
size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Найти начало последнего вхождения подстроки в этой строке или конец строки.
Определения sstring.h:965
R trimmed_left_with_spaces(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, а также пробельных символов,...
Определения sstring.h:1650
void as_number(T &t)
Преобразовать строку в целое число
Определения sstring.h:1238
void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Вызвать функтор для всех найденных вхождений подстроки в этой строке
Определения sstring.h:1039
size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const noexcept
Найти начало первого вхождения подстроки в этой строке или выкинуть исключение.
Определения sstring.h:883
bool operator!() const noexcept
Проверка на пустоту
Определения sstring.h:677
size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Найти последнее вхождение символа не из заданного набора символов
Определения sstring.h:1128
size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа не из заданного набора символов
Определения sstring.h:1099
bool equal_iu(str_piece text) const noexcept
Равна ли строка другой строке посимвольно без учёта регистра Unicode символов первой плоскости (<0xFF...
Определения sstring.h:837
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Заканчивается ли строка указанной подстрокой без учёта регистра ASCII символов
Определения sstring.h:1439
my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Получить подстроку. Работает аналогично operator(), только результат выдает того же типа,...
Определения sstring.h:1137
T split(str_piece delimeter, size_t offset=0) const
Разделить строку на подстроки по заданному разделителю
Определения sstring.h:1336
size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Найти конец вхождения подстроки в этой строке.
Определения sstring.h:895
constexpr bool operator==(const base &other) const noexcept
Оператор сравнение строк на равенство
Определения sstring.h:742
size_t size() const
Размер строки в символах.
Определения sstring.h:593
R trimmed(T &&pattern) const
Получить строку с удалением символов, заданных строковым литералом, слева и справа
Определения sstring.h:1600
constexpr bool equal(str_piece other) const noexcept
Сравнение строк на равенство
Определения sstring.h:734
size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Найти первое вхождение символа из заданного набора символов
Определения sstring.h:1079
bool less_ia(str_piece text) const noexcept
Меньше ли строка другой строки посимвольно без учёта регистра ASCII символов
Определения sstring.h:814
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Получить подстроку simple_str с позиции от from до позиции to (не включая её)
Определения sstring.h:671
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Начинается ли строка с заданной подстроки без учёта регистра ASCII символов
Определения sstring.h:1378
Базовый класс работы с изменяемыми строками
Определения sstring.h:2769
Impl & insert(size_t to, const A &expr)
Вставить строковое выражение в указанную позицию
Определения sstring.h:3202
Impl & operator<<=(const Op &fillFunction)
Заполняет строку методом fill после конца строки
Определения sstring.h:3437
Impl & append(const A &expr)
Добавить строковое выражение в конец строки
Определения sstring.h:3122
Impl & operator<<(const Op &fillFunction)
Вызывает переданный функтор, передав ссылку на себя
Определения sstring.h:3447
Impl & trim(str_piece pattern)
Удалить символы, входящие в переданную строку, в начале и в конце строки.
Определения sstring.h:2967
Impl & upper_only_ascii()
Преобразовать в верхний регистр ASCII символы
Определения sstring.h:3014
Impl & lower_only_ascii()
Преобразовать в нижний регистр ASCII символы
Определения sstring.h:3027
Impl & trim_left()
Удалить пробельные символы в начале строки
Определения sstring.h:2892
Impl & append_printf(const K *format, T &&... args)
Добавляет отформатированный с помощью sprintf вывод в конец строки
Определения sstring.h:3515
Impl & trim_right_with_wpaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в конце строки.
Определения sstring.h:2959
Impl & trim_left(str_piece pattern)
Удалить символы, входящие в переданную строку, в начале строки.
Определения sstring.h:2975
Impl & trim_with_spaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале и в конце строки.
Определения sstring.h:2939
Impl & prepend(str_piece other)
Добавить другую строку в начало строки
Определения sstring.h:3219
Impl & printf(const K *format, T &&... args)
Форматирует строку помощью sprintf.
Определения sstring.h:3504
Impl & with(const Op &fillFunction, Args &&... args)
Вызов функтора со строкой и переданными аргументами
Определения sstring.h:3685
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод в конец строки, записывая не более указанног...
Определения sstring.h:3675
Impl & operator<<(const Op &fillFunction)
Заполняет строку методом fill с нулевой позиции
Определения sstring.h:3427
Impl & vformat(str_piece format, T &&... args)
Форматирует строку с помощью std::vformat.
Определения sstring.h:3640
Impl & change(size_t from, size_t len, const A &expr)
Заменить кусок строки на строковое выражение
Определения sstring.h:3183
Impl & fill(size_t from, const Op &fillFunction)
Заполнение буфера строки с помощью функтора
Определения sstring.h:3402
Impl & change(size_t from, size_t len, str_piece other)
Заменить кусок строки на другую строку
Определения sstring.h:3172
Impl & remove(size_t from, size_t len)
Удалить часть строку
Определения sstring.h:3211
Impl & trim_left_with_spaces(T &&pattern)
Удалить символы, входящие в строковый литерал, а также пробельные символы, в начале строки.
Определения sstring.h:2949
Impl & trim_left(T &&pattern)
Удалить символы, входящие в строковый литерал, в начале строки.
Определения sstring.h:2919
Impl & trim_right(T &&pattern)
Удалить символы, входящие в строковый литерал, в конце строки.
Определения sstring.h:2929
Impl & trim_right_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в конце строки.
Определения sstring.h:3007
Impl & append_in(size_t pos, str_piece other)
Добавить другую строку, начиная с заданной позиции
Определения sstring.h:3150
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод, начиная с указанной позиции
Определения sstring.h:3588
Impl & trim_left_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в начале строки.
Определения sstring.h:2999
Impl & insert(size_t to, str_piece other)
Вставить строку в указанную позицию
Определения sstring.h:3192
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Форматирует строку с помощью std::vformat не более указанного размера
Определения sstring.h:3663
my_type & format(const FmtString< K, T... > &pattern, T &&... args)
Определения sstring.h:3618
Impl & printf_from(size_t from, const K *format, T &&... args)
Добавляет отформатированный с помощью sprintf вывод, начиная с указанной позиции
Определения sstring.h:3460
Impl & operator+=(str_piece other)
Добавить другую строку в конец строки
Определения sstring.h:3130
Impl & trim_right()
Удалить пробельные символы в конце строки
Определения sstring.h:2899
Impl & append(str_piece other)
Добавить другую строку в конец строки
Определения sstring.h:3113
Impl & trim_right(str_piece pattern)
Удалить символы, входящие в переданную строку, в конце строки.
Определения sstring.h:2983
Impl & trim_with_spaces(str_piece pattern)
Удалить символы, входящие в переданную строку, а также пробельные символы, в начале и в конце строки.
Определения sstring.h:2991
K * str() noexcept
Получить указатель на буфер строки
Определения sstring.h:2871
my_type & format_from(size_t from, const FmtString< K, T... > &format, T &&... args)
Определения sstring.h:3566
Impl & prepend(const A &expr)
Добавить строковое выражение в начало строки
Определения sstring.h:3228
Impl & trim()
Удалить пробельные символы в начале и в конце строки
Определения sstring.h:2885
Impl & lower()
Преобразовать в нижний регистр Unicode символы первой плоскости (<0xFFFF).
Определения sstring.h:3052
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Заменить вхождения подстроки на другую строку
Определения sstring.h:3239
Impl & upper()
Преобразовать в верхний регистр Unicode символы первой плоскости (<0xFFFF).
Определения sstring.h:3042
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Скопировать строку-источник, заменив вхождения подстрок на другую строку
Определения sstring.h:3351
Impl & append_vformatted(str_piece format, T &&... args)
Добавляет отформатированный с помощью std::vformat вывод в конец строки
Определения sstring.h:3651
Impl & append_formatted(const FmtString< K, T... > &format, T &&... args)
Добавляет отформатированный с помощью std::format вывод в конец строки
Определения sstring.h:3629
Impl & append_in(size_t pos, const A &expr)
Добавить строковое выражение, начиная с заданной позиции
Определения sstring.h:3162
Impl & trim(T &&pattern)
Удалить символы, входящие в строковый литерал, в начале и в конце строки.
Определения sstring.h:2909
Impl & operator+=(const A &expr)
Добавить строковое выражение в конец строки
Определения sstring.h:3139
constexpr str_storable(s_str other, Args &&... args)
Конструктор из другого строкового объекта
Определения sstring.h:2437
constexpr str_storable(size_t repeat, s_str pattern, Args &&... args)
Конструктор повторения строки
Определения sstring.h:2453
allocator_t & allocator()
Получить аллокатор
Определения sstring.h:2327
static my_type uppered_from(const From &f, Args &&... args)
Создать копию переданной строки в верхнем регистре символов Unicode первой плоскости (<0xFFFF)
Определения sstring.h:2665
constexpr str_storable(const StrExprForType< K > auto &expr, Args &&... args)
Конструктор из строкового выражения
Определения sstring.h:2491
static my_type lowered_from(const From &f, Args &&... args)
Создать копию переданной строки в нижнем регистре символов Unicode первой плоскости (<0xFFFF)
Определения sstring.h:2677
s_str_nt to_nts(size_t from=0) const
Получить simple_str_nt, начиная с заданного символа
Определения sstring.h:2559
static my_type uppered_only_ascii_from(const From &f, Args &&... args)
Создать строку, копию переданной в верхнем регистре символов ASCII.
Определения sstring.h:2643
constexpr str_storable(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Создать пустой объект
Определения sstring.h:2425
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Создать копию переданной строки в нижнем регистре символов ASCII.
Определения sstring.h:2653
static my_type join(const T &strings, s_str delimeter, bool tail=false, bool skip_empty=false, Args &&... args)
Конкатенация строк из контейнера в одну строку
Определения sstring.h:2595
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Создать копию переданной строки с заменой подстрок
Определения sstring.h:2691
str_storable(size_t count, K pad, Args &&... args)
Конструктор повторения символа
Определения sstring.h:2473
str_storable(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Конструктор из строкового источника с заменой
Определения sstring.h:2509
Концепт типа, управляющего памятью
Определения sstring.h:2700
Концепт строкового выражения заданного типа символов
Определения strexpr.h:242
Концепт типа, который не может модифицировать хранимую строку
Определения sstring.h:2288
Концепт типа, который может модифицировать хранимую строку
Определения sstring.h:2282
Концепт типа, который может сохранить строку
Определения sstring.h:2273
constexpr auto e_repl(simple_str< K > w, T &&p, X &&r)
Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки
Определения sstring.h:4880
constexpr auto e_real(double t)
Преобразование double числа в строковое выражение
Определения sstring.h:4731
constexpr auto e_num(T t)
Преобразование целого числа в строковое выражение
Определения sstring.h:4659
constexpr auto operator+(const A &a, T s)
Оператор конкатенации для строкового выражения и целого числа.
Определения sstring.h:4633
auto e_repl_const_symbols(simple_str< K > src, Repl &&... other)
Возвращает строковое выражение, генерирующее строку, в которой заданные символы заменены на заданные ...
Определения sstring.h:5393
constexpr auto e_join(const T &s, L &&d)
Получить строковое выражение, конкатенирующее строки в контейнере в одну строку с заданным разделител...
Определения sstring.h:4796
Пространство имён для объектов библиотеки
Определения sstring.cpp:10
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Тип хеш-словаря для char16_t строк, регистрозависимый поиск
Определения sstring.h:6035
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Тип хеш-словаря для char строк, регистронезависимый поиск для ASCII символов
Определения sstring.h:6006
IntConvertResult
Перечисление с возможными результатами преобразования строки в целое число
Определения sstring.h:236
@ Overflow
Переполнение, число не помещается в заданный тип
Определения sstring.h:239
@ Success
Успешно
Определения sstring.h:237
@ NotNumber
Вообще не число
Определения sstring.h:240
@ BadSymbolAtTail
Число закончилось не числовым символом
Определения sstring.h:238
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Тип хеш-словаря для char32_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:6061
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Тип хеш-словаря для char32_t строк, регистронезависимый поиск для ASCII символов
Определения sstring.h:6056
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:6029
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Тип хеш-словаря для wchar_t строк, регистронезависимый поиск для ASCII символов
Определения sstring.h:6023
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Тип хеш-словаря для char строк, регистрозависимый поиск
Определения sstring.h:6001
auto e_utf(simple_str< From > from)
Возвращает строковое выражение, преобразующую строку из одного типа символов в другой тип,...
Определения sstring.h:2265
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Тип хеш-словаря для char16_t строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:6045
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Тип хеш-словаря для char16_t строк, регистронезависимый поиск для ASCII символов
Определения sstring.h:6040
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Тип хеш-словаря для wchar_t строк, регистрозависимый поиск
Определения sstring.h:6017
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Тип хеш-словаря для char строк, регистронезависимый поиск для Unicode символов до 0xFFFF.
Определения sstring.h:6011
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Тип хеш-словаря для char32_t строк, регистрозависимый поиск
Определения sstring.h:6051
Объект, позволяющий последовательно копировать содержимое в буфер заданного размера
Определения sstring.h:5942
bool is_end()
Проверить, что данные ещё не кончились
Определения sstring.h:5948
size_t store(K *buffer, size_t size)
Сохранить очередную порцию данных в буфер
Определения sstring.h:5957
constexpr expr_replace_symbols(simple_str< K > source, const std::vector< std::pair< K, simple_str< K > > > &repl)
Конструктор выражения
Определения sstring.h:5032
constexpr expr_replaced(simple_str< K > w, simple_str< K > p, simple_str< K > r)
Конструктор
Определения sstring.h:4909
Строковое выражение для конвертации строк в разные виды UTF.
Определения sstring.h:2243
Класс, заявляющий, что ссылается на нуль-терминированную строку.
Определения sstring.h:1855
constexpr simple_str_nt(S &&s) noexcept
Конструктор, позволяющий инициализировать объектами std::string, и std::string_view при условии,...
Определения sstring.h:1885
my_type to_nts(size_t from)
Получить нуль-терминированную строку, сдвинув начало на заданное количество символов
Определения sstring.h:1900
simple_str_nt(T &&p) noexcept
Явный конструктор из С-строки.
Определения sstring.h:1874
Простейший класс иммутабельной не владеющей строки.
Определения sstring.h:1756
constexpr simple_str(T &&v) noexcept
Конструктор из строкового литерала.
Определения sstring.h:1769
bool is_part_of(simple_str< K > other) const noexcept
Проверить, не является ли строка частью другой строки
Определения sstring.h:1811
my_type & remove_prefix(size_t delta)
Сдвигает начало строки на заданное количество символов
Определения sstring.h:1827
constexpr size_t length() const noexcept
Получить длину строки
Определения sstring.h:1785
constexpr const symb_type * symbols() const noexcept
Получить указатель на константный буфер с символами строки
Определения sstring.h:1791
constexpr simple_str(const K *p, size_t l) noexcept
Конструктор из указателя и длины
Определения sstring.h:1773
constexpr bool is_empty() const noexcept
Проверить, не пуста ли строка
Определения sstring.h:1797
constexpr simple_str(S &&s) noexcept
Конструктор, позволяющий инициализировать объектами std::string, и std::string_view при условии,...
Определения sstring.h:1781
K operator[](size_t idx) const
Получить символ из указанной позиции. Проверка границ не выполняется.
Определения sstring.h:1819
my_type & remove_suffix(size_t delta)
Укорачивает строку на заданное количество символов
Определения sstring.h:1837
bool is_same(simple_str< K > other) const noexcept
Проверить, не указывают ли два объекта на одну строку
Определения sstring.h:1804
Конкатенация ссылки на строковое выражение и значения строкового выражения
Определения strexpr.h:311