simstr 1.3.1
Yet another strings library
 
Loading...
Searching...
No Matches
sstring.h
1/*
2 * (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
3 * ver. 1.3.1
4 * Классы для работы со строками
5* (c) Project "SimStr", Aleksandr Orefkov orefkov@gmail.com
6* ver. 1.3.1
7* Classes for working with strings
8 */
9
20#pragma once
21#include <optional>
22#ifndef __has_declspec_attribute
23#define __has_declspec_attribute(x) 0
24#endif
25
26#ifdef SIMSTR_IN_SHARED
27 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
28 #ifdef SIMSTR_EXPORT
29 #define SIMSTR_API __declspec(dllexport)
30 #else
31 #define SIMSTR_API __declspec(dllimport)
32 #endif
33 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
34 #define SIMSTR_API __attribute__((visibility("default")))
35 #else
36 #define SIMSTR_API
37 #endif
38#else
39 #define SIMSTR_API
40#endif
41const bool isWindowsOs = // NOLINT
42#ifdef _WIN32
43 true
44#else
45 false
46#endif
47 ;
48const bool isx64 = sizeof(void*) == 8; // NOLINT
49
50#ifdef _MSC_VER
51#define _no_unique_address msvc::no_unique_address
52#define decl_empty_bases __declspec(empty_bases)
53#else
54#define _no_unique_address no_unique_address
55#define decl_empty_bases
56#endif
57
58#if defined __has_builtin
59# if __has_builtin (__builtin_mul_overflow) && __has_builtin (__builtin_add_overflow)
60# define HAS_BUILTIN_OVERFLOW
61# endif
62#endif
63
64#include "strexpr.h"
65
66#include <cstddef>
67#include <string>
68#include <type_traits>
69#include <vector>
70#include <format>
71#include <unordered_map>
72#include <tuple>
73#include <limits>
74#include <cstdint>
75#include <atomic>
76#include <memory>
77#include <string.h>
78#include <iostream>
79#include <cmath>
80#include <charconv>
81
82#ifdef _WIN32
83#include <stdio.h>
84#endif
85
86#ifdef _MSC_VER
87// warning C4201 : nonstandard extension used : nameless struct / union
88#pragma warning(disable : 4201)
89#endif
90
91namespace simstr {
92
93template<typename T>
94struct unicode_traits {}; // NOLINT
95
96template<>
97struct unicode_traits<u8s> {
98 // Эти операции с utf-8 могут изменить длину строки
99 // Поэтому их специализации отличаются
100 // В функцию помимо текста и адреса буфера для записи передается размер буфера
101 // Возвращает длину получающейся строки.
102 // Если получающеюся строка не влезает в отведенный буфер, указатели устанавливаются на последние
103 // обработанные символы, для повторного возобновления работы,
104 // а для оставшихся символов считается нужный размер буфера.
105 // These utf-8 operations can change the length of the string
106 // Therefore their specializations are different
107 // In addition to the text and address of the buffer for writing, the buffer size is passed to the function
108 // Returns the length of the resulting string.
109 // If the resulting string does not fit into the allocated buffer, pointers are set to the last
110 // processed characters to resume work again,
111 // and for the remaining characters the required buffer size is calculated.
112 static SIMSTR_API size_t upper(const u8s*& src, size_t lenStr, u8s*& dest, size_t lenBuf);
113 static SIMSTR_API size_t lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuf);
114
115 static SIMSTR_API int compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2);
116
117 static SIMSTR_API size_t hashia(const u8s* src, size_t l);
118 static SIMSTR_API size_t hashiu(const u8s* src, size_t l);
119};
120
121template<>
122struct unicode_traits<u16s> {
123 static SIMSTR_API void upper(const u16s* src, size_t len, u16s* dest);
124 static SIMSTR_API void lower(const u16s* src, size_t len, u16s* dest);
125
126 static SIMSTR_API int compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2);
127 static SIMSTR_API size_t hashia(const u16s* src, size_t l);
128 static SIMSTR_API size_t hashiu(const u16s* src, size_t l);
129};
130
131template<>
132struct unicode_traits<u32s> {
133 static SIMSTR_API void upper(const u32s* src, size_t len, u32s* dest);
134 static SIMSTR_API void lower(const u32s* src, size_t len, u32s* dest);
135
136 static SIMSTR_API int compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2);
137 static SIMSTR_API size_t hashia(const u32s* src, size_t s);
138 static SIMSTR_API size_t hashiu(const u32s* src, size_t s);
139};
140
141template<>
142struct unicode_traits<wchar_t> {
143 static void upper(const wchar_t* src, size_t len, wchar_t* dest) {
144 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
145 }
146 static void lower(const wchar_t* src, size_t len, wchar_t* dest) {
147 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
148 }
149
150 static int compareiu(const wchar_t* text1, size_t len1, const wchar_t* text2, size_t len2) {
151 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
152 }
153 static size_t hashia(const wchar_t* src, size_t s) {
154 return unicode_traits<wchar_type>::hashia(to_w(src), s);
155 }
156 static size_t hashiu(const wchar_t* src, size_t s) {
157 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
158 }
159};
160
161namespace str {
162constexpr const size_t npos = static_cast<size_t>(-1); //NOLINT
163} // namespace str
164
165template<typename K>
166struct ch_traits : std::char_traits<K>{};
167
168template<size_t N>
169concept is_const_pattern = N > 1 && N <= 17;
170
171template<typename K, size_t I>
172struct _ascii_mask { // NOLINT
173 constexpr static const size_t value = size_t(K(~0x7F)) << ((I - 1) * sizeof(K) * 8) | _ascii_mask<K, I - 1>::value;
174};
175
176template<typename K>
177struct _ascii_mask<K, 0> {
178 constexpr static const size_t value = 0;
179};
180
181template<typename K>
182struct ascii_mask { // NOLINT
183 using uns = std::make_unsigned_t<K>;
184 constexpr static const size_t WIDTH = sizeof(size_t) / sizeof(uns);
185 constexpr static const size_t VALUE = _ascii_mask<uns, WIDTH>::value;
186};
187
188template<typename K>
189constexpr inline bool isAsciiUpper(K k) {
190 return k >= 'A' && k <= 'Z';
191}
192
193template<typename K>
194constexpr inline bool isAsciiLower(K k) {
195 return k >= 'a' && k <= 'z';
196}
197
198template<typename K>
199constexpr inline K makeAsciiLower(K k) {
200 return isAsciiUpper(k) ? k | 0x20 : k;
201}
202
203template<typename K>
204constexpr inline K makeAsciiUpper(K k) {
205 return isAsciiLower(k) ? k & ~0x20 : k;
206}
207
208enum TrimSides { TrimLeft = 1, TrimRight = 2, TrimAll = 3 };
209template<TrimSides S, typename K, size_t N, bool withSpaces = false>
210struct trim_operator;
211
212template<typename K, size_t N, size_t L>
213struct expr_replaces;
214
215template<typename T>
216concept FromIntNumber =
217 is_one_of_type<std::remove_cv_t<T>, unsigned char, int, short, long, long long, unsigned, unsigned short, unsigned long, unsigned long long>::value;
218
219template<typename T>
220concept ToIntNumber = FromIntNumber<T> || is_one_of_type<T, int8_t>::value;
221
222#if defined(_MSC_VER) && _MSC_VER <= 1933
223template<typename K, typename... Args>
224using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
225#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
226template<typename K, typename... Args>
227using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
228#else
229template<typename K, typename... Args>
230using FmtString = std::basic_string_view<K>;
231#endif
232
233template<typename K, bool I, typename T>
234struct need_sign { // NOLINT
235 bool sign;
236 need_sign(T& t) : sign(t < 0) {
237 if (sign && t != std::numeric_limits<T>::min())
238 t = -t;
239 }
240 void after(K*& ptr) {
241 if (sign)
242 *--ptr = '-';
243 }
244};
245
246template<typename K, typename T>
247struct need_sign<K, false, T> {
248 need_sign(T&) {}
249 void after(K*&) {}
250};
251
262
263template<bool CanNegate, bool CheckOverflow, typename T>
264struct result_type_selector { // NOLINT
265 using type = T;
266};
267
268template<typename T>
269struct result_type_selector<true, false, T> {
270 using type = std::make_unsigned_t<T>;
271};
272
273template<unsigned Base>
274constexpr unsigned digit_width() {
275 if (Base <=2) {
276 return 1;
277 }
278 if (Base <= 4) {
279 return 2;
280 }
281 if (Base <= 8) {
282 return 3;
283 }
284 if (Base <= 16) {
285 return 4;
286 }
287 if (Base <= 32) {
288 return 5;
289 }
290 return 6;
291}
292
293template<typename T, unsigned Base>
294constexpr unsigned max_overflow_digits = (sizeof(T) * CHAR_BIT) / digit_width<Base>();
295
296template<typename T>
297struct convert_result {
298 T value;
300 size_t read;
301};
302
303struct int_convert { // NOLINT
304 inline static const uint8_t NUMBERS[] = {
305 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
306 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3,
307 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
308 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16,
309 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 255,
310 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
311 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
312 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
313 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
314 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
315
316 template<typename K, unsigned Base>
317 static constexpr std::make_unsigned_t<K> toDigit(K s) {
318 auto us = static_cast<std::make_unsigned_t<K>>(s);
319 if constexpr (Base <= 10) {
320 return us - '0';
321 } else {
322 if constexpr (sizeof(K) == 1) {
323 return NUMBERS[us];
324 } else {
325 return us < 256 ? NUMBERS[us] : us;
326 }
327 }
328 }
329
330 template<typename K, ToIntNumber T, unsigned Base, bool CheckOverflow>
331 requires(Base != 0)
332 static constexpr convert_result<T> parse(const K* start, const K* current, const K* end, bool negate) {
333 using u_type = std::make_unsigned_t<T>;
334 #ifndef HAS_BUILTIN_OVERFLOW
335 u_type maxMult = 0, maxAdd = 0;
336 if constexpr (CheckOverflow) {
337 maxMult = std::numeric_limits<u_type>::max() / Base;
338 maxAdd = std::numeric_limits<u_type>::max() % Base;
339 }
340 #endif
341 u_type number = 0;
342 unsigned maxDigits = max_overflow_digits<u_type, Base>;
344 const K* from = current;
345
346 bool no_need_check_o_f = !CheckOverflow || end - current <= maxDigits;
347
348 if (no_need_check_o_f) {
349 for (;;) {
350 const u_type digit = toDigit<K, Base>(*current);
351 if (digit >= Base) {
352 break;
353 }
354 number = number * Base + digit;
355 if (++current == end) {
357 break;
358 }
359 }
360 } else {
361 for (;maxDigits; maxDigits--) {
362 const u_type digit = toDigit<K, Base>(*current);
363 if (digit >= Base) {
364 break;
365 }
366 number = number * Base + digit;
367 ++current;
368 }
369 if (!maxDigits) {
370 // Прошли все цифры, дальше надо с проверкой на overflow
371 // All numbers have passed, then we need to check for overflow
372 for (;;) {
373 const u_type digit = toDigit<K, Base>(*current);
374 if (digit >= Base) {
375 break;
376 }
377 #ifdef HAS_BUILTIN_OVERFLOW
378 if (__builtin_mul_overflow(number, Base, &number) ||
379 __builtin_add_overflow(number, digit, &number)) {
380 #else
381 if (number < maxMult || (number == maxMult && number < maxAdd)) {
382 number = number * Base + digit;
383 } else {
384 #endif
386 while(++current < end) {
387 if (toDigit<K, Base>(*current) >= Base) {
388 break;
389 }
390 }
391 break;
392 }
393 if (++current == end) {
395 break;
396 }
397 }
398 }
399 }
400 T result;
401 if constexpr (std::is_signed_v<T>) {
402 result = negate ? 0 - number : number;
403 if constexpr (CheckOverflow) {
404 if (error != IntConvertResult::Overflow) {
405 if (number > std::numeric_limits<T>::max() + (negate ? 1 : 0)) {
407 }
408 }
409 }
410 } else {
411 result = number;
412 }
413 if (error == IntConvertResult::NotNumber && current > from) {
415 }
416 return {result, error, size_t(current - start)};
417 }
418public:
419 // Если Base = 0 - то пытается определить основание по префиксу 0[xX] как 16, 0 как 8, иначе 10
420 // Если Base = -1 - то пытается определить основание по префиксу 0[xX] как 16, 0[bB] как 2, 0[oO] или 0 как 8, иначе 10
421 // If Base = 0, then it tries to determine the base by the prefix 0[xX] as 16, 0 as 8, otherwise 10
422 // If Base = -1 - then tries to determine the base by the prefix 0[xX] as 16, 0[bB] as 2, 0[oO] or 0 as 8, otherwise 10
423 template<typename K, ToIntNumber T, unsigned Base = 0, bool CheckOverflow = true, bool SkipWs = true, bool AllowSign = true>
424 requires(Base == -1 || (Base < 37 && Base != 1))
425 static constexpr convert_result<T> to_integer(const K* start, size_t len) noexcept {
426 const K *ptr = start, *end = ptr + len;
427 bool negate = false;
428 if constexpr (SkipWs) {
429 while (ptr < end && std::make_unsigned_t<K>(*ptr) <= ' ')
430 ptr++;
431 }
432 if (ptr != end) {
433 if constexpr (std::is_signed_v<T>) {
434 if constexpr (AllowSign) {
435 // Может быть число, +число или -число
436 // Can be a number, +number or -number
437 if (*ptr == '+') {
438 ptr++;
439 } else if (*ptr == '-') {
440 negate = true;
441 ptr++;
442 }
443 } else {
444 // Может быть число или -число
445 // Can be a number or -number
446 if (*ptr == '-') {
447 negate = true;
448 ptr++;
449 }
450 }
451 } else if constexpr (AllowSign) {
452 // Может быть число или +число
453 // Can be a number or +number
454 if (*ptr == '+') {
455 ptr++;
456 }
457 }
458 }
459 if (ptr != end) {
460 if constexpr (Base == 0 || Base == -1) {
461 if (*ptr == '0') {
462 ptr++;
463 if (ptr != end) {
464 if (*ptr == 'x' || *ptr == 'X') {
465 return parse<K, T, 16, CheckOverflow>(start, ++ptr, end, negate);
466 }
467 if constexpr (Base == -1) {
468 if (*ptr == 'b' || *ptr == 'B') {
469 return parse<K, T, 2, CheckOverflow>(start, ++ptr, end, negate);
470 }
471 if (*ptr == 'o' || *ptr == 'O') {
472 return parse<K, T, 8, CheckOverflow>(start, ++ptr, end, negate);
473 }
474 }
475 return parse<K, T, 8, CheckOverflow>(start, --ptr, end, negate);
476 }
477 return {0, IntConvertResult::Success, size_t(ptr - start)};
478 }
479 return parse<K, T, 10, CheckOverflow>(start, ptr, end, negate);
480 } else
481 return parse<K, T, Base, CheckOverflow>(start, ptr, end, negate);
482 }
483 return {0, IntConvertResult::NotNumber, size_t(ptr - start)};
484 }
485};
486
487template<typename K>
488SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end);
489
490template<typename K>
491class Splitter;
492
493template<typename K, typename Impl>
494class null_terminated {
495public:
502 constexpr const K* c_str() const { return static_cast<const Impl*>(this)->symbols(); }
503};
504
505template<typename K, typename Impl, bool Mutable> class buffer_pointers;
506
515template<typename K, typename Impl>
516class buffer_pointers<K, Impl, false> {
517 constexpr const Impl& d() const { return *static_cast<const Impl*>(this); }
518public:
525 constexpr const K* data() const { return d().symbols(); }
532 constexpr const K* begin() const { return d().symbols(); }
539 constexpr const K* end() const { return d().symbols() + d().length(); }
546 constexpr const K* cbegin() const { return d().symbols(); }
553 constexpr const K* cend() const { return d().symbols() + d().length(); }
554};
555
556template<typename K, typename Impl>
557class buffer_pointers<K, Impl, true> : public buffer_pointers<K, Impl, false> {
558 constexpr Impl& d() { return *static_cast<Impl*>(this); }
559 using base = buffer_pointers<K, Impl, false>;
560public:
567 constexpr const K* data() const { return base::data(); }
574 constexpr const K* begin() const { return base::begin(); }
581 constexpr const K* end() const { return base::end(); }
588 constexpr const K* cbegin() const { return base::cbegin(); }
595 constexpr const K* cend() const { return base::cend(); }
602 constexpr K* data() { return d().str(); }
609 constexpr K* begin() { return d().str(); }
616 constexpr K* end() { return d().str() + d().length(); }
617};
618
643template<typename K, typename StrRef, typename Impl, bool Mutable>
644class str_algs : public buffer_pointers<K, Impl, Mutable> {
645 constexpr const Impl& d() const noexcept {
646 return *static_cast<const Impl*>(this);
647 }
648 constexpr size_t _len() const noexcept {
649 return d().length();
650 }
651 constexpr const K* _str() const noexcept {
652 return d().symbols();
653 }
654 constexpr bool _is_empty() const noexcept {
655 return d().is_empty();
656 }
657
658public:
659 using symb_type = K;
660 using str_piece = StrRef;
661 using traits = ch_traits<K>;
662 using uni = unicode_traits<K>;
663 using uns_type = std::make_unsigned_t<K>;
664 using my_type = Impl;
665 using base = str_algs<K, StrRef, Impl, Mutable>;
666 str_algs() = default;
667
680 constexpr K* place(K* ptr) const noexcept {
681 size_t myLen = _len();
682 if (myLen) {
683 traits::copy(ptr, _str(), myLen);
684 return ptr + myLen;
685 }
686 return ptr;
687 }
688
698 void copy_to(K* buffer, size_t bufSize) {
699 size_t tlen = std::min(_len(), bufSize - 1);
700 if (tlen)
701 traits::copy(buffer, _str(), tlen);
702 buffer[tlen] = 0;
703 }
704
710 constexpr size_t size() const {
711 return _len();
712 }
713
720 constexpr operator str_piece() const noexcept {
721 return str_piece{_str(), _len()};
722 }
723
729 constexpr str_piece to_str() const noexcept {
730 return {_str(), _len()};
731 }
732
738 template<typename=int> requires is_one_of_std_char_v<K>
739 constexpr std::basic_string_view<K> to_sv() const noexcept {
740 return {_str(), _len()};
741 }
742
748 template<typename=int> requires is_one_of_std_char_v<K>
749 constexpr std::basic_string<K> to_string() const noexcept {
750 return {_str(), _len()};
751 }
752
775 constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len = 0) const noexcept {
776 size_t myLen = _len(), idxStart = from >= 0 ? from : myLen > -from ? myLen + from : 0,
777 idxEnd = len > 0 ? idxStart + len : myLen > -len ? myLen + len : 0;
778 if (idxEnd > myLen)
779 idxEnd = myLen;
780 if (idxStart > idxEnd)
781 idxStart = idxEnd;
782 return str_piece{_str() + idxStart, idxEnd - idxStart};
783 }
784
794 constexpr str_piece mid(size_t from, size_t len = -1) const noexcept {
795 size_t myLen = _len(), idxStart = from, idxEnd = from > std::numeric_limits<size_t>::max() - len ? myLen : from + len;
796 if (idxEnd > myLen)
797 idxEnd = myLen;
798 if (idxStart > idxEnd)
799 idxStart = idxEnd;
800 return str_piece{_str() + idxStart, idxEnd - idxStart};
801 }
802
816 constexpr str_piece from_to(size_t from, size_t to) const noexcept {
817 return str_piece{_str() + from, to - from};
818 }
819
823 constexpr bool operator!() const noexcept {
824 return _is_empty();
825 }
826
836 constexpr K at(ptrdiff_t idx) const {
837 return _str()[idx >= 0 ? idx : _len() + idx];
838 }
839 // Сравнение строк
840 // String comparison
841 constexpr int compare(const K* text, size_t len) const {
842 size_t myLen = _len();
843 int cmp = traits::compare(_str(), text, std::min(myLen, len));
844 return cmp == 0 ? (myLen > len ? 1 : myLen == len ? 0 : -1) : cmp;
845 }
854 constexpr int compare(str_piece o) const {
855 return compare(o.symbols(), o.length());
856 }
857
865 constexpr int strcmp(const K* text) const {
866 size_t myLen = _len(), idx = 0;
867 const K* ptr = _str();
868 for (; idx < myLen; idx++) {
869 uns_type s1 = (uns_type)text[idx];
870 if (!s1) {
871 return 1;
872 }
873 uns_type s2 = (uns_type)ptr[idx];
874 if (s1 < s2) {
875 return 1;
876 } else if (s1 > s2) {
877 return -1;
878 }
879 }
880 return text[idx] == 0 ? 0 : -1;
881 }
882
883 constexpr bool equal(const K* text, size_t len) const noexcept {
884 return len == _len() && traits::compare(_str(), text, len) == 0;
885 }
894 constexpr bool equal(str_piece other) const noexcept {
895 return equal(other.symbols(), other.length());
896 }
897
905 constexpr bool operator==(const base& other) const noexcept {
906 return equal(other._str(), other._len());
907 }
908
914 constexpr auto operator<=>(const base& other) const noexcept {
915 return compare(other._str(), other._len()) <=> 0;
916 }
917
923 template<typename T, size_t N = const_lit_for<K, T>::Count>
924 constexpr bool operator==(T&& other) const noexcept {
925 return N - 1 == _len() && traits::compare(_str(), other, N - 1) == 0;
926 }
927
933 template<typename T, size_t N = const_lit_for<K, T>::Count>
934 constexpr auto operator<=>(T&& other) const noexcept {
935 size_t myLen = _len();
936 int cmp = traits::compare(_str(), other, std::min(myLen, N - 1));
937 int res = cmp == 0 ? (myLen > N - 1 ? 1 : myLen == N - 1 ? 0 : -1) : cmp;
938 return res <=> 0;
939 }
940
941 // Сравнение ascii строк без учёта регистра
942 // Compare ascii strings without taking into account case
943 constexpr int compare_ia(const K* text, size_t len) const noexcept { // NOLINT
944 if (!len)
945 return _is_empty() ? 0 : 1;
946 size_t myLen = _len(), checkLen = std::min(myLen, len);
947 const uns_type *ptr1 = reinterpret_cast<const uns_type*>(_str()), *ptr2 = reinterpret_cast<const uns_type*>(text);
948 while (checkLen--) {
949 uns_type s1 = *ptr1++, s2 = *ptr2++;
950 if (s1 == s2)
951 continue;
952 s1 = makeAsciiLower(s1);
953 s2 = makeAsciiLower(s2);
954 if (s1 > s2)
955 return 1;
956 else if (s1 < s2)
957 return -1;
958 }
959 return myLen == len ? 0 : myLen > len ? 1 : -1;
960 }
969 constexpr int compare_ia(str_piece text) const noexcept { // NOLINT
970 return compare_ia(text.symbols(), text.length());
971 }
972
981 constexpr bool equal_ia(str_piece text) const noexcept { // NOLINT
982 return text.length() == _len() && compare_ia(text.symbols(), text.length()) == 0;
983 }
984
992 constexpr bool less_ia(str_piece text) const noexcept { // NOLINT
993 return compare_ia(text.symbols(), text.length()) < 0;
994 }
995
996 int compare_iu(const K* text, size_t len) const noexcept { // NOLINT
997 if (!len)
998 return _is_empty() ? 0 : 1;
999 return uni::compareiu(_str(), _len(), text, len);
1000 }
1009 int compare_iu(str_piece text) const noexcept { // NOLINT
1010 return compare_iu(text.symbols(), text.length());
1011 }
1012
1021 bool equal_iu(str_piece text) const noexcept { // NOLINT
1022 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
1023 }
1024
1032 bool less_iu(str_piece text) const noexcept { // NOLINT
1033 return compare_iu(text.symbols(), text.length()) < 0;
1034 }
1035
1036 constexpr size_t find(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
1037 size_t lenText = _len();
1038 // Образец, не вмещающийся в строку и пустой образец не находим
1039 // We don't look for an empty line or a line longer than the text.
1040 if (!lenPattern || offset >= lenText || offset + lenPattern > lenText)
1041 return str::npos;
1042 lenPattern--;
1043 const K *text = _str(), *last = text + lenText - lenPattern, first = pattern[0];
1044 pattern++;
1045 for (const K* fnd = text + offset;; ++fnd) {
1046 fnd = traits::find(fnd, last - fnd, first);
1047 if (!fnd)
1048 return str::npos;
1049 if (traits::compare(fnd + 1, pattern, lenPattern) == 0)
1050 return static_cast<size_t>(fnd - text);
1051 }
1052 }
1063 constexpr size_t find(str_piece pattern, size_t offset = 0) const noexcept {
1064 return find(pattern.symbols(), pattern.length(), offset);
1065 }
1066
1082 template<typename Exc, typename ... Args> requires std::is_constructible_v<Exc, Args...>
1083 constexpr size_t find_or_throw(str_piece pattern, size_t offset = 0, Args&& ... args) const noexcept {
1084 if (auto fnd = find(pattern.symbols(), pattern.length(), offset); fnd != str::npos) {
1085 return fnd;
1086 }
1087 throw Exc(std::forward<Args>(args)...);
1088 }
1089
1099 constexpr size_t find_end(str_piece pattern, size_t offset = 0) const noexcept {
1100 size_t fnd = find(pattern.symbols(), pattern.length(), offset);
1101 return fnd == str::npos ? fnd : fnd + pattern.length();
1102 }
1103
1113 constexpr size_t find_or_all(str_piece pattern, size_t offset = 0) const noexcept {
1114 auto fnd = find(pattern.symbols(), pattern.length(), offset);
1115 return fnd == str::npos ? _len() : fnd;
1116 }
1117
1127 constexpr size_t find_end_or_all(str_piece pattern, size_t offset = 0) const noexcept {
1128 auto fnd = find(pattern.symbols(), pattern.length(), offset);
1129 return fnd == str::npos ? _len() : fnd + pattern.length();
1130 }
1131
1132 constexpr size_t find_last(const K* pattern, size_t lenPattern, size_t offset) const noexcept {
1133 if (lenPattern == 1)
1134 return find_last(pattern[0], offset);
1135 size_t lenText = std::min(_len(), offset);
1136 // Образец, не вмещающийся в строку и пустой образец не находим
1137 // We don't look for an empty line or a line longer than the text.
1138 if (!lenPattern || lenPattern > lenText)
1139 return str::npos;
1140
1141 lenPattern--;
1142 const K *text = _str() + lenPattern, last = pattern[lenPattern];
1143 lenText -= lenPattern;
1144 while(lenText) {
1145 if (text[--lenText] == last) {
1146 if (traits::compare(text + lenText - lenPattern, pattern, lenPattern) == 0) {
1147 return lenText;
1148 }
1149 }
1150 }
1151 return str::npos;
1152 }
1163 constexpr size_t find_last(str_piece pattern, size_t offset = -1) const noexcept {
1164 return find_last(pattern.symbols(), pattern.length(), offset);
1165 }
1166
1176 constexpr size_t find_end_of_last(str_piece pattern, size_t offset = -1) const noexcept {
1177 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
1178 return fnd == str::npos ? fnd : fnd + pattern.length();
1179 }
1180
1190 constexpr size_t find_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
1191 auto fnd = find_last(pattern.symbols(), pattern.length(), offset);
1192 return fnd == str::npos ? _len() : fnd;
1193 }
1194
1204 constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset = -1) const noexcept {
1205 size_t fnd = find_last(pattern.symbols(), pattern.length(), offset);
1206 return fnd == str::npos ? _len() : fnd + pattern.length();
1207 }
1208
1218 constexpr bool contains(str_piece pattern, size_t offset = 0) const noexcept {
1219 return find(pattern, offset) != str::npos;
1220 }
1221
1231 constexpr size_t find(K s, size_t offset = 0) const noexcept {
1232 size_t len = _len();
1233 if (offset < len) {
1234 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
1235 if (fnd)
1236 return static_cast<size_t>(fnd - str);
1237 }
1238 return str::npos;
1239 }
1240
1250 constexpr size_t find_or_all(K s, size_t offset = 0) const noexcept {
1251 size_t len = _len();
1252 if (offset < len) {
1253 const K *str = _str(), *fnd = traits::find(str + offset, len - offset, s);
1254 if (fnd)
1255 return static_cast<size_t>(fnd - str);
1256 }
1257 return len;
1258 }
1259
1260 template<typename Op>
1261 constexpr void for_all_finded(const Op& op, const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
1262 if (!maxCount)
1263 maxCount--;
1264 while (maxCount-- > 0) {
1265 size_t fnd = find(pattern, patternLen, offset);
1266 if (fnd == str::npos)
1267 break;
1268 op(fnd);
1269 offset = fnd + patternLen;
1270 }
1271 }
1284 template<typename Op>
1285 constexpr void for_all_finded(const Op& op, str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
1286 for_all_finded(op, pattern.symbols(), pattern.length(), offset, maxCount);
1287 }
1288
1289 std::vector<size_t> find_all(const K* pattern, size_t patternLen, size_t offset, size_t maxCount) const {
1290 std::vector<size_t> result;
1291 for_all_finded([&](auto f) { result.push_back(f); }, pattern, patternLen, offset, maxCount);
1292 return result;
1293 }
1306 constexpr std::vector<size_t> find_all(str_piece pattern, size_t offset = 0, size_t maxCount = 0) const {
1307 return find_all(pattern.symbols(), pattern.length(), offset, maxCount);
1308 }
1309
1319 constexpr size_t find_last(K s, size_t offset = -1) const noexcept {
1320 size_t len = std::min(_len(), offset);
1321 const K *text = _str();
1322 while (len > 0) {
1323 if (text[--len] == s)
1324 return len;
1325 }
1326 return str::npos;
1327 }
1328
1338 constexpr size_t find_first_of(str_piece pattern, size_t offset = 0) const noexcept {
1339 return std::string_view{_str(), _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
1340 }
1341
1351 constexpr std::pair<size_t, size_t> find_first_of_idx(str_piece pattern, size_t offset = 0) const noexcept {
1352 const K* text = _str();
1353 size_t fnd = std::string_view{text, _len()}.find_first_of(std::string_view{pattern.str, pattern.len}, offset);
1354 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
1355 }
1356
1366 constexpr size_t find_first_not_of(str_piece pattern, size_t offset = 0) const noexcept {
1367 return std::string_view{_str(), _len()}.find_first_not_of(std::string_view{pattern.str, pattern.len}, offset);
1368 }
1369
1379 constexpr size_t find_last_of(str_piece pattern, size_t offset = str::npos) const noexcept {
1380 return std::string_view{_str(), _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
1381 }
1382
1392 constexpr std::pair<size_t, size_t> find_last_of_idx(str_piece pattern, size_t offset = str::npos) const noexcept {
1393 const K* text = _str();
1394 size_t fnd = std::string_view{text, _len()}.find_last_of(std::string_view{pattern.str, pattern.len}, offset);
1395 return {fnd, fnd == std::string::npos ? fnd : pattern.find(text[fnd]) };
1396 }
1397
1407 constexpr size_t find_last_not_of(str_piece pattern, size_t offset = str::npos) const noexcept {
1408 return std::string_view{_str(), _len()}.find_last_not_of(std::string_view{pattern.str, pattern.len}, offset);
1409 }
1410
1420 constexpr my_type substr(ptrdiff_t from, ptrdiff_t len = 0) const { // индексация в code units | indexing in code units
1421 return my_type{d()(from, len)};
1422 }
1423
1433 constexpr my_type str_mid(size_t from, size_t len = -1) const { // индексация в code units | indexing in code units
1434 return my_type{d().mid(from, len)};
1435 }
1436
1464 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
1465 constexpr T as_int() const noexcept {
1466 auto [res, err, _] = int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
1467 return err == IntConvertResult::Overflow ? 0 : res;
1468 }
1469
1497 template<ToIntNumber T, bool CheckOverflow = true, unsigned Base = 0, bool SkipWs = true, bool AllowSign = true>
1498 constexpr convert_result<T> to_int() const noexcept {
1499 return int_convert::to_integer<K, T, Base, CheckOverflow, SkipWs, AllowSign>(_str(), _len());
1500 }
1501
1507 template<bool SkipWS = true, bool AllowPlus = true>
1508 std::optional<double> to_double() const noexcept {
1509 size_t len = _len();
1510 const K* ptr = _str();
1511 if constexpr (SkipWS) {
1512 while (len && uns_type(*ptr) <= ' ') {
1513 len--;
1514 ptr++;
1515 }
1516 }
1517 if constexpr (AllowPlus) {
1518 if (len && *ptr == K('+')) {
1519 ptr++;
1520 len--;
1521 }
1522 }
1523 if (!len) {
1524 return {};
1525 }
1526 #ifdef __linux__
1527 if constexpr(sizeof(K) == 1) {
1528 double d{};
1529 if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
1530 return d;
1531 }
1532 return {};
1533 }
1534 #endif
1535 return impl_to_double(ptr, ptr + len);
1536 }
1537
1543 template<bool SkipWS = true> requires (sizeof(K) == 1)
1544 std::optional<double> to_double_hex() const noexcept {
1545 size_t len = _len();
1546 const K* ptr = _str();
1547 if constexpr (SkipWS) {
1548 while (len && uns_type(*ptr) <= ' ') {
1549 len--;
1550 ptr++;
1551 }
1552 }
1553 if (len) {
1554 double d{};
1555 if (std::from_chars(ptr, ptr + len, d, std::chars_format::hex).ec == std::errc{}) {
1556 return d;
1557 }
1558 }
1559 return {};
1560 }
1561
1569 template<ToIntNumber T>
1570 constexpr void as_number(T& t) const {
1571 t = as_int<T>();
1572 }
1573
1579 void as_number(double& t) const {
1580 auto res = to_double();
1581 t = res ? *res : std::nan("0");
1582 }
1583
1584 template<typename T, typename Op>
1585 constexpr T splitf(const K* delimeter, size_t lenDelimeter, const Op& beforeFunc, size_t offset) const {
1586 size_t mylen = _len();
1587 std::conditional_t<std::is_same_v<T, void>, char, T> results;
1588 str_piece me{_str(), mylen};
1589 for (int i = 0;; i++) {
1590 size_t beginOfDelim = find(delimeter, lenDelimeter, offset);
1591 if (beginOfDelim == str::npos) {
1592 str_piece last{me.symbols() + offset, me.length() - offset};
1593 if constexpr (std::is_invocable_v<Op, str_piece&>) {
1594 beforeFunc(last);
1595 }
1596 if constexpr (requires { results.emplace_back(last); }) {
1597 if (last.is_same(me)) {
1598 // Пробуем положить весь объект.
1599 // Try to put the entire object.
1600 results.emplace_back(d());
1601 } else {
1602 results.emplace_back(last);
1603 }
1604 } else if constexpr (requires { results.push_back(last); }) {
1605 if (last.is_same(me)) {
1606 // Пробуем положить весь объект.
1607 // Try to put the entire object.
1608 results.push_back(d());
1609 } else {
1610 results.push_back(last);
1611 }
1612 } else if constexpr (requires {results[i] = last;} && requires{std::size(results);}) {
1613 if (i < std::size(results)) {
1614 if (last.is_same(me)) {
1615 // Пробуем положить весь объект.
1616 // Try to put the entire object.
1617 results[i] = d();
1618 } else
1619 results[i] = last;
1620 }
1621 }
1622 break;
1623 }
1624 str_piece piece{me.symbols() + offset, beginOfDelim - offset};
1625 if constexpr (std::is_invocable_v<Op, str_piece&>) {
1626 beforeFunc(piece);
1627 }
1628 if constexpr (requires { results.emplace_back(piece); }) {
1629 results.emplace_back(piece);
1630 } else if constexpr (requires { results.push_back(piece); }) {
1631 results.push_back(piece);
1632 } else if constexpr (requires { results[i] = piece; } && requires{std::size(results);}) {
1633 if (i < std::size(results)) {
1634 results[i] = piece;
1635 if (i == results.size() - 1) {
1636 break;
1637 }
1638 }
1639 }
1640 offset = beginOfDelim + lenDelimeter;
1641 }
1642 if constexpr (!std::is_same_v<T, void>) {
1643 return results;
1644 }
1645 }
1676 template<typename T, typename Op>
1677 constexpr T splitf(str_piece delimeter, const Op& beforeFunc, size_t offset = 0) const {
1678 return splitf<T>(delimeter.symbols(), delimeter.length(), beforeFunc, offset);
1679 }
1680
1692 template<typename T>
1693 constexpr T split(str_piece delimeter, size_t offset = 0) const {
1694 return splitf<T>(delimeter.symbols(), delimeter.length(), 0, offset);
1695 }
1696
1706 constexpr Splitter<K> splitter(str_piece delimeter) const;
1707
1708 // Начинается ли эта строка с указанной подстроки
1709 // Does this string start with the specified substring
1710 constexpr bool starts_with(const K* prefix, size_t l) const noexcept {
1711 return _len() >= l && 0 == traits::compare(_str(), prefix, l);
1712 }
1719 constexpr bool starts_with(str_piece prefix) const noexcept {
1720 return starts_with(prefix.symbols(), prefix.length());
1721 }
1722
1723 constexpr bool starts_with_ia(const K* prefix, size_t len) const noexcept {
1724 size_t myLen = _len();
1725 if (myLen < len) {
1726 return false;
1727 }
1728 const K* ptr1 = _str();
1729 while (len--) {
1730 K s1 = *ptr1++, s2 = *prefix++;
1731 if (s1 == s2)
1732 continue;
1733 if (makeAsciiLower(s1) != makeAsciiLower(s2))
1734 return false;
1735 }
1736 return true;
1737 }
1744 constexpr bool starts_with_ia(str_piece prefix) const noexcept {
1745 return starts_with_ia(prefix.symbols(), prefix.length());
1746 }
1747 // Начинается ли эта строка с указанной подстроки без учета unicode регистра
1748 // Does this string begin with the specified substring, insensitive to unicode case
1749 bool starts_with_iu(const K* prefix, size_t len) const noexcept {
1750 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
1751 }
1758 bool starts_with_iu(str_piece prefix) const noexcept {
1759 return starts_with_iu(prefix.symbols(), prefix.length());
1760 }
1761
1762 // Является ли эта строка началом указанной строки
1763 // Is this string the beginning of the specified string
1764 constexpr bool prefix_in(const K* text, size_t len) const noexcept {
1765 size_t myLen = _len();
1766 if (myLen > len)
1767 return false;
1768 return !myLen || 0 == traits::compare(text, _str(), myLen);
1769 }
1776 constexpr bool prefix_in(str_piece text) const noexcept {
1777 return prefix_in(text.symbols(), text.length());
1778 }
1779 // Заканчивается ли строка указанной подстрокой
1780 // Does the string end with the specified substring
1781 constexpr bool ends_with(const K* suffix, size_t len) const noexcept {
1782 size_t myLen = _len();
1783 return len <= myLen && traits::compare(_str() + myLen - len, suffix, len) == 0;
1784 }
1791 constexpr bool ends_with(str_piece suffix) const noexcept {
1792 return ends_with(suffix.symbols(), suffix.length());
1793 }
1794 // Заканчивается ли строка указанной подстрокой без учета регистра ASCII
1795 // Whether the string ends with the specified substring, case insensitive ASCII
1796 constexpr bool ends_with_ia(const K* suffix, size_t len) const noexcept {
1797 size_t myLen = _len();
1798 if (myLen < len) {
1799 return false;
1800 }
1801 const K* ptr1 = _str() + myLen - len;
1802 while (len--) {
1803 K s1 = *ptr1++, s2 = *suffix++;
1804 if (s1 == s2)
1805 continue;
1806 if (makeAsciiLower(s1) != makeAsciiLower(s2))
1807 return false;
1808 }
1809 return true;
1810 }
1817 constexpr bool ends_with_ia(str_piece suffix) const noexcept {
1818 return ends_with_ia(suffix.symbols(), suffix.length());
1819 }
1820 // Заканчивается ли строка указанной подстрокой без учета регистра UNICODE
1821 // Whether the string ends with the specified substring, case insensitive UNICODE
1822 constexpr bool ends_with_iu(const K* suffix, size_t len) const noexcept {
1823 size_t myLen = _len();
1824 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
1825 }
1832 constexpr bool ends_with_iu(str_piece suffix) const noexcept {
1833 return ends_with_iu(suffix.symbols(), suffix.length());
1834 }
1835
1839 constexpr bool is_ascii() const noexcept {
1840 if (_is_empty())
1841 return true;
1842 const int sl = ascii_mask<K>::WIDTH;
1843 const size_t mask = ascii_mask<K>::VALUE;
1844 size_t len = _len();
1845 const uns_type* ptr = reinterpret_cast<const uns_type*>(_str());
1846 if constexpr (sl > 1) {
1847 const size_t roundMask = sizeof(size_t) - 1;
1848 while (len >= sl && (reinterpret_cast<size_t>(ptr) & roundMask) != 0) {
1849 if (*ptr++ > 127)
1850 return false;
1851 len--;
1852 }
1853 while (len >= sl) {
1854 if (*reinterpret_cast<const size_t*>(ptr) & mask)
1855 return false;
1856 ptr += sl;
1857 len -= sl;
1858 }
1859 }
1860 while (len--) {
1861 if (*ptr++ > 127)
1862 return false;
1863 }
1864 return true;
1865 }
1866
1874 template<typename R = my_type>
1876 return R::upperred_only_ascii_from(d());
1877 }
1878
1886 template<typename R = my_type>
1888 return R::lowered_only_ascii_from(d());
1889 }
1890
1898 template<typename R = my_type>
1899 R upperred() const {
1900 return R::upperred_from(d());
1901 }
1902
1910 template<typename R = my_type>
1911 R lowered() const {
1912 return R::lowered_from(d());
1913 }
1914
1930 template<typename R = my_type>
1931 R replaced(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) const {
1932 return R::replaced_from(d(), pattern, repl, offset, maxCount);
1933 }
1934
1945 template<typename T, size_t N = const_lit_for<K, T>::Count, typename M, size_t L = const_lit_for<K, M>::Count>
1946 constexpr expr_replaces<K, N - 1, L - 1> replace_init(T&& pattern, M&& repl) const {
1947 return expr_replaces<K, N - 1, L - 1>{d(), pattern, repl};
1948 }
1949
1950 template<StrType<K> From>
1951 constexpr static my_type make_trim_op(const From& from, const auto& opTrim) {
1952 str_piece sfrom = from, newPos = opTrim(sfrom);
1953 return newPos.is_same(sfrom) ? my_type{from} : my_type{newPos};
1954 }
1955 template<TrimSides S, StrType<K> From>
1956 constexpr static my_type trim_static(const From& from) {
1957 return make_trim_op(from, trim_operator<S, K, static_cast<size_t>(-1), true>{});
1958 }
1959
1960 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count, StrType<K> From>
1961 requires is_const_pattern<N>
1962 constexpr static my_type trim_static(const From& from, T&& pattern) {
1963 return make_trim_op(from, trim_operator<S, K, N - 1, withSpaces>{pattern});
1964 }
1965
1966 template<TrimSides S, bool withSpaces, StrType<K> From>
1967 constexpr static my_type trim_static(const From& from, str_piece pattern) {
1968 return make_trim_op(from, trim_operator<S, K, 0, withSpaces>{{pattern}});
1969 }
1978 template<typename R = str_piece>
1979 constexpr R trimmed() const {
1980 return R::template trim_static<TrimSides::TrimAll>(d());
1981 }
1982
1990 template<typename R = str_piece>
1991 R trimmed_left() const {
1992 return R::template trim_static<TrimSides::TrimLeft>(d());
1993 }
1994
2002 template<typename R = str_piece>
2003 R trimmed_right() const {
2004 return R::template trim_static<TrimSides::TrimRight>(d());
2005 }
2006
2016 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2017 requires is_const_pattern<N>
2018 R trimmed(T&& pattern) const {
2019 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
2020 }
2021
2031 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2032 requires is_const_pattern<N>
2033 R trimmed_left(T&& pattern) const {
2034 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
2035 }
2036
2046 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2047 requires is_const_pattern<N>
2048 R trimmed_right(T&& pattern) const {
2049 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
2050 }
2051 // Триминг по символам в литерале и пробелам
2052 // Trimming by characters in literal and spaces
2053
2068 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2069 requires is_const_pattern<N>
2070 R trimmed_with_spaces(T&& pattern) const {
2071 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
2072 }
2073
2087 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2088 requires is_const_pattern<N>
2089 R trimmed_left_with_spaces(T&& pattern) const {
2090 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
2091 }
2092
2106 template<typename R = str_piece, typename T, size_t N = const_lit_for<K, T>::Count>
2107 requires is_const_pattern<N>
2108 R trimmed_right_with_spaces(T&& pattern) const {
2109 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
2110 }
2111 // Триминг по динамическому источнику
2112 // Trimming by dynamic source
2113
2124 template<typename R = str_piece>
2125 R trimmed(str_piece pattern) const {
2126 return R::template trim_static<TrimSides::TrimAll, false>(d(), pattern);
2127 }
2128
2138 template<typename R = str_piece>
2139 R trimmed_left(str_piece pattern) const {
2140 return R::template trim_static<TrimSides::TrimLeft, false>(d(), pattern);
2141 }
2142
2152 template<typename R = str_piece>
2153 R trimmed_right(str_piece pattern) const {
2154 return R::template trim_static<TrimSides::TrimRight, false>(d(), pattern);
2155 }
2156
2170 template<typename R = str_piece>
2171 R trimmed_with_spaces(str_piece pattern) const {
2172 return R::template trim_static<TrimSides::TrimAll, true>(d(), pattern);
2173 }
2174
2188 template<typename R = str_piece>
2189 R trimmed_left_with_spaces(str_piece pattern) const {
2190 return R::template trim_static<TrimSides::TrimLeft, true>(d(), pattern);
2191 }
2192
2206 template<typename R = str_piece>
2207 R trimmed_right_with_spaces(str_piece pattern) const {
2208 return R::template trim_static<TrimSides::TrimRight, true>(d(), pattern);
2209 }
2210};
2211
2212/*
2213* Базовая структура с информацией о строке.
2214* Это структура для не владеющих строк.
2215* Так как здесь только один базовый класс, MSVC компилятор автоматом применяет empty base optimization,
2216* в результате размер класса не увеличивается
2217* Basic structure with string information.
2218* This is the structure for non-owning strings.
2219* Since there is only one base class, the MSVC compiler automatically applies empty base optimization,
2220* as a result the class size does not increase
2221*/
2222
2233template<typename K>
2234struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
2235 using symb_type = K;
2236 using my_type = simple_str<K>;
2237
2238 const symb_type* str;
2239 size_t len;
2240
2241 simple_str() = default;
2242
2247 template<typename T, size_t N = const_lit_for<K, T>::Count>
2248 constexpr simple_str(T&& v) noexcept : str(v), len(N - 1) {}
2253 constexpr simple_str(const K* p, size_t l) noexcept : str(p), len(l) {}
2260 template<typename S>
2261 requires(std::is_same_v<S, std::basic_string<K>&> || std::is_same_v<S, const std::basic_string<K>&>
2262 || std::is_same_v<S, std::basic_string_view<K>&> || std::is_same_v<S, const std::basic_string_view<K>&>)
2263 constexpr simple_str(S&& s) noexcept : str(s.data()), len(s.length()) {}
2268 constexpr size_t length() const noexcept {
2269 return len;
2270 }
2271
2275 constexpr const symb_type* symbols() const noexcept {
2276 return str;
2277 }
2278
2282 constexpr bool is_empty() const noexcept {
2283 return len == 0;
2284 }
2285
2291 constexpr bool is_same(simple_str<K> other) const noexcept {
2292 return str == other.str && len == other.len;
2293 }
2294
2300 constexpr bool is_part_of(simple_str<K> other) const noexcept {
2301 return str >= other.str && str + len <= other.str + other.len;
2302 }
2303
2311 constexpr K operator[](size_t idx) const {
2312 return str[idx];
2313 }
2314
2322 constexpr my_type& remove_prefix(size_t delta) {
2323 str += delta;
2324 len -= delta;
2325 return *this;
2326 }
2327
2335 constexpr my_type& remove_suffix(size_t delta) {
2336 len -= delta;
2337 return *this;
2338 }
2339};
2340
2361template<typename K>
2362struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
2363 using symb_type = K;
2364 using my_type = simple_str_nt<K>;
2365 using base = simple_str<K>;
2366 using base::base;
2367
2368 constexpr static const K empty_string[1] = {0};
2369
2370 simple_str_nt() = default;
2387 template<typename T> requires std::is_same_v<std::remove_const_t<std::remove_pointer_t<std::remove_cvref_t<T>>>, K>
2388 constexpr explicit simple_str_nt(T&& p) noexcept {
2389 base::len = p ? static_cast<size_t>(base::traits::length(p)) : 0;
2390 base::str = base::len ? p : empty_string;
2391 }
2392
2398 template<typename S>
2399 requires(std::is_same_v<S, std::string&> || std::is_same_v<S, const std::string&>
2400 || std::is_same_v<S, std::string_view&> || std::is_same_v<S, const std::string_view&>)
2401 constexpr simple_str_nt(S&& s) noexcept : base(s) {}
2402
2403 static const my_type empty_str;
2410 constexpr operator const K*() const noexcept {
2411 return base::str;
2412 }
2413
2421 constexpr my_type to_nts(size_t from) {
2422 if (from > base::len) {
2423 from = base::len;
2424 }
2425 return {base::str + from, base::len - from};
2426 }
2427};
2428
2429template<typename K>
2430inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
2431
2432using ssa = simple_str<u8s>;
2433using ssw = simple_str<wchar_t>;
2434using ssu = simple_str<u16s>;
2435using ssuu = simple_str<u32s>;
2436using stra = simple_str_nt<u8s>;
2437using strw = simple_str_nt<wchar_t>;
2438using stru = simple_str_nt<u16s>;
2439using struu = simple_str_nt<u32s>;
2440
2447template<typename K>
2448class Splitter {
2449 simple_str<K> text_;
2450 simple_str<K> delim_;
2451
2452public:
2453 constexpr Splitter(simple_str<K> text, simple_str<K> delim) : text_(text), delim_(delim) {}
2458 constexpr bool is_done() const {
2459 return text_.length() == str::npos;
2460 }
2461
2467 constexpr simple_str<K> next() {
2468 if (!text_.length()) {
2469 auto ret = text_;
2470 text_.str++;
2471 text_.len--;
2472 return ret;
2473 } else if (text_.length() == str::npos) {
2474 return {nullptr, 0};
2475 }
2476 size_t pos = text_.find(delim_), next = 0;
2477 if (pos == str::npos) {
2478 pos = text_.length();
2479 next = pos + 1;
2480 } else {
2481 next = pos + delim_.length();
2482 }
2483 simple_str<K> result{text_.str, pos};
2484 text_.str += next;
2485 text_.len -= next;
2486 return result;
2487 }
2488};
2489
2490template<typename K, typename StrRef, typename Impl, bool Mutable>
2492 return Splitter<K>{*this, delimeter};
2493}
2494
2495template<typename K, bool withSpaces>
2496struct CheckSpaceTrim {
2497 constexpr bool is_trim_spaces(K s) const {
2498 return s == ' ' || (s >= 9 && s <= 13); // || isspace(s);
2499 }
2500};
2501template<typename K>
2502struct CheckSpaceTrim<K, false> {
2503 constexpr bool is_trim_spaces(K) const {
2504 return false;
2505 }
2506};
2507
2508template<typename K>
2509struct CheckSymbolsTrim {
2510 simple_str<K> symbols;
2511 constexpr bool is_trim_symbols(K s) const {
2512 return symbols.len != 0 && simple_str<K>::traits::find(symbols.str, symbols.len, s) != nullptr;
2513 }
2514};
2515
2516template<typename K, size_t N>
2517struct CheckConstSymbolsTrim {
2518 const const_lit_to_array<K, N> symbols;
2519
2520 template<typename T, size_t M = const_lit_for<K, T>::Count> requires (M == N + 1)
2521 constexpr CheckConstSymbolsTrim(T&& s) : symbols(std::forward<T>(s)) {}
2522
2523 constexpr bool is_trim_symbols(K s) const noexcept {
2524 return symbols.contain(s);
2525 }
2526};
2527
2528template<typename K>
2529struct CheckConstSymbolsTrim<K, 0> {
2530 constexpr bool is_trim_symbols(K) const {
2531 return false;
2532 }
2533};
2534
2535template<typename K, size_t N>
2536struct SymbSelector {
2537 using type = CheckConstSymbolsTrim<K, N>;
2538};
2539
2540template<typename K>
2541struct SymbSelector<K, 0> {
2542 using type = CheckSymbolsTrim<K>;
2543};
2544
2545template<typename K>
2546struct SymbSelector<K, static_cast<size_t>(-1)> {
2547 using type = CheckConstSymbolsTrim<K, 0>;
2548};
2549
2550template<TrimSides S, typename K, size_t N, bool withSpaces>
2551struct trim_operator : SymbSelector<K, N>::type, CheckSpaceTrim<K, withSpaces> {
2552 constexpr bool isTrim(K s) const {
2553 return CheckSpaceTrim<K, withSpaces>::is_trim_spaces(s) || SymbSelector<K, N>::type::is_trim_symbols(s);
2554 }
2555 constexpr simple_str<K> operator()(simple_str<K> from) const {
2556 if constexpr ((S & TrimSides::TrimLeft) != 0) {
2557 while (from.len) {
2558 if (isTrim(*from.str)) {
2559 from.str++;
2560 from.len--;
2561 } else
2562 break;
2563 }
2564 }
2565 if constexpr ((S & TrimSides::TrimRight) != 0) {
2566 const K* back = from.str + from.len - 1;
2567 while (from.len) {
2568 if (isTrim(*back)) {
2569 back--;
2570 from.len--;
2571 } else
2572 break;
2573 }
2574 }
2575 return from;
2576 }
2577};
2578
2579template<TrimSides S, typename K>
2580using SimpleTrim = trim_operator<S, K, size_t(-1), true>;
2581
2582using trim_w = SimpleTrim<TrimSides::TrimAll, u16s>;
2583using trim_a = SimpleTrim<TrimSides::TrimAll, u8s>;
2584using triml_w = SimpleTrim<TrimSides::TrimLeft, u16s>;
2585using triml_a = SimpleTrim<TrimSides::TrimLeft, u8s>;
2586using trimr_w = SimpleTrim<TrimSides::TrimRight, u16s>;
2587using trimr_a = SimpleTrim<TrimSides::TrimRight, u8s>;
2588
2589template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K, typename T, size_t N = const_lit_for<K, T>::Count>
2590 requires is_const_pattern<N>
2591constexpr inline auto trimOp(T&& pattern) {
2592 return trim_operator<S, K, N - 1, withSpaces>{pattern};
2593}
2594
2595template<TrimSides S = TrimSides::TrimAll, bool withSpaces = false, typename K>
2596constexpr inline auto trimOp(simple_str<K> pattern) {
2597 return trim_operator<S, K, 0, withSpaces>{pattern};
2598}
2599
2600template<typename Src, typename Dest>
2601struct utf_convert_selector;
2602
2603template<>
2604struct utf_convert_selector<u8s, u16s> {
2605 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
2606 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u16s* dest);
2607};
2608
2609template<>
2610struct utf_convert_selector<u8s, u32s> {
2611 static SIMSTR_API size_t need_len(const u8s* src, size_t srcLen);
2612 static SIMSTR_API size_t convert(const u8s* src, size_t srcLen, u32s* dest);
2613};
2614
2615template<>
2616struct utf_convert_selector<u8s, wchar_t> {
2617 static size_t need_len(const u8s* src, size_t srcLen) {
2618 return utf_convert_selector<u8s, wchar_type>::need_len(src, srcLen);
2619 }
2620 static size_t convert(const u8s* src, size_t srcLen, wchar_t* dest) {
2621 return utf_convert_selector<u8s, wchar_type>::convert(src, srcLen, to_w(dest));
2622 }
2623};
2624
2625template<>
2626struct utf_convert_selector<u16s, u8s> {
2627 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
2628 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u8s* dest);
2629};
2630
2631template<>
2632struct utf_convert_selector<u16s, u32s> {
2633 static SIMSTR_API size_t need_len(const u16s* src, size_t srcLen);
2634 static SIMSTR_API size_t convert(const u16s* src, size_t srcLen, u32s* dest);
2635};
2636
2637template<>
2638struct utf_convert_selector<u16s, u16s> {
2639 // При конвертации char16_t в wchar_t под windows будет вызываться эта реализация
2640 // When converting char16_t to wchar_t under windows this implementation will be called
2641 static size_t need_len(const u16s* src, size_t srcLen) {
2642 return srcLen;
2643 }
2644 static size_t convert(const u16s* src, size_t srcLen, u16s* dest) {
2645 ch_traits<u16s>::copy(dest, src, srcLen + 1);
2646 return srcLen;
2647 }
2648};
2649
2650template<>
2651struct utf_convert_selector<u32s, u32s> {
2652 // При конвертации char32_t в wchar_t под linux будет вызываться эта реализация
2653 // When converting char32_t to wchar_t under Linux, this implementation will be called
2654 static size_t need_len(const u32s* src, size_t srcLen) {
2655 return srcLen;
2656 }
2657 static size_t convert(const u32s* src, size_t srcLen, u32s* dest) {
2658 ch_traits<u32s>::copy(dest, src, srcLen + 1);
2659 return srcLen;
2660 }
2661};
2662
2663template<>
2664struct utf_convert_selector<u16s, wchar_t> {
2665 static size_t need_len(const u16s* src, size_t srcLen) {
2666 return utf_convert_selector<u16s, wchar_type>::need_len(src, srcLen);
2667 }
2668 static size_t convert(const u16s* src, size_t srcLen, wchar_t* dest) {
2669 return utf_convert_selector<u16s, wchar_type>::convert(src, srcLen, to_w(dest));
2670 }
2671};
2672
2673template<>
2674struct utf_convert_selector<u32s, u8s> {
2675 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
2676 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u8s* dest);
2677};
2678
2679template<>
2680struct utf_convert_selector<u32s, u16s> {
2681 static SIMSTR_API size_t need_len(const u32s* src, size_t srcLen);
2682 static SIMSTR_API size_t convert(const u32s* src, size_t srcLen, u16s* dest);
2683};
2684
2685template<>
2686struct utf_convert_selector<u32s, wchar_t> {
2687 static size_t need_len(const u32s* src, size_t srcLen) {
2688 return utf_convert_selector<u32s, wchar_type>::need_len(src, srcLen);
2689 }
2690 static size_t convert(const u32s* src, size_t srcLen, wchar_t* dest) {
2691 return utf_convert_selector<u32s, wchar_type>::convert(src, srcLen, to_w(dest));
2692 }
2693};
2694
2695template<>
2696struct utf_convert_selector<wchar_t, u8s> {
2697 static size_t need_len(const wchar_t* src, size_t srcLen) {
2698 return utf_convert_selector<wchar_type, u8s>::need_len(to_w(src), srcLen);
2699 }
2700 static size_t convert(const wchar_t* src, size_t srcLen, u8s* dest) {
2701 return utf_convert_selector<wchar_type, u8s>::convert(to_w(src), srcLen, dest);
2702 }
2703};
2704
2705template<>
2706struct utf_convert_selector<wchar_t, u16s> {
2707 static size_t need_len(const wchar_t* src, size_t srcLen) {
2708 return utf_convert_selector<wchar_type, u16s>::need_len(to_w(src), srcLen);
2709 }
2710 static size_t convert(const wchar_t* src, size_t srcLen, u16s* dest) {
2711 return utf_convert_selector<wchar_type, u16s>::convert(to_w(src), srcLen, dest);
2712 }
2713};
2714
2715template<>
2716struct utf_convert_selector<wchar_t, u32s> {
2717 static size_t need_len(const wchar_t* src, size_t srcLen) {
2718 return utf_convert_selector<wchar_type, u32s>::need_len(to_w(src), srcLen);
2719 }
2720 static size_t convert(const wchar_t* src, size_t srcLen, u32s* dest) {
2721 return utf_convert_selector<wchar_type, u32s>::convert(to_w(src), srcLen, dest);
2722 }
2723};
2724
2739template<typename K, typename Impl>
2740class from_utf_convertible {
2741protected:
2742 from_utf_convertible() = default;
2743 using my_type = Impl;
2744 /*
2745 Эти методы должен реализовать класс-наследник.
2746 вызывается только при создании объекта
2747 init(size_t size)
2748 set_size(size_t size)
2749 */
2750public:
2751 template<typename O>
2752 requires(!std::is_same_v<O, K>)
2753 from_utf_convertible(simple_str<O> init) {
2754 using worker = utf_convert_selector<O, K>;
2755 Impl* d = static_cast<Impl*>(this);
2756 size_t len = init.length();
2757 if (!len)
2758 d->create_empty();
2759 else {
2760 size_t need = worker::need_len(init.symbols(), len);
2761 K* str = d->init(need);
2762 str[need] = 0;
2763 worker::convert(init.symbols(), len, str);
2764 }
2765 }
2766 template<typename O, typename I, bool M>
2767 requires(!std::is_same_v<O, K>)
2768 from_utf_convertible(const str_algs<O, simple_str<O>, I, M>& init) : from_utf_convertible(init.to_str()) {}
2769};
2770
2779template<typename From, typename To> requires (!std::is_same_v<From, To>)
2780struct expr_utf {
2781 using symb_type = To;
2782 using worker = utf_convert_selector<From, To>;
2783
2784 simple_str<From> source_;
2785
2786 size_t length() const noexcept {
2787 return worker::need_len(source_.symbols(), source_.length());
2788 }
2789 To* place(To* ptr) const noexcept {
2790 return ptr + worker::convert(source_.symbols(), source_.length(), ptr);
2791 }
2792};
2793
2806template<typename To, typename From> requires (!std::is_same_v<From, To>)
2808 return expr_utf<From, To>{from};
2809}
2810
2815template<typename A, typename K>
2816concept storable_str = requires {
2817 A::is_str_storable == true;
2818 std::is_same_v<typename A::symb_type, K>;
2819};
2820
2825template<typename A, typename K>
2826concept mutable_str = storable_str<A, K> && requires { A::is_str_mutable == true; };
2827
2832template<typename A, typename K>
2834
2877template<typename K, typename Impl, typename Allocator>
2878class str_storable : protected Allocator {
2879public:
2880 using my_type = Impl;
2881 using traits = ch_traits<K>;
2882 using allocator_t = Allocator;
2883 using s_str = simple_str<K>;
2884 using s_str_nt = simple_str_nt<K>;
2885
2886protected:
2891 constexpr allocator_t& allocator() {
2892 return *static_cast<Allocator*>(this);
2893 }
2894 constexpr const allocator_t& allocator() const {
2895 return *static_cast<const Allocator*>(this);
2896 }
2897
2898 using uni = unicode_traits<K>;
2899
2900 constexpr Impl& d() noexcept {
2901 return *static_cast<Impl*>(this);
2902 }
2903 constexpr const Impl& d() const noexcept {
2904 return *static_cast<const Impl*>(this);
2905 }
2906
2913 template<typename... Args>
2914 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
2915
2922 constexpr void init_from_str_other(s_str other) {
2923 if (other.length()) {
2924 K* ptr = d().init(other.length());
2925 traits::copy(ptr, other.symbols(), other.length());
2926 ptr[other.length()] = 0;
2927 } else
2928 d().create_empty();
2929 }
2930
2938 constexpr void init_str_repeat(size_t repeat, s_str pattern) {
2939 size_t l = pattern.length(), allLen = l * repeat;
2940 if (allLen) {
2941 K* ptr = d().init(allLen);
2942 for (size_t i = 0; i < repeat; i++) {
2943 traits::copy(ptr, pattern.symbols(), l);
2944 ptr += l;
2945 }
2946 *ptr = 0;
2947 } else
2948 d().create_empty();
2949 }
2950
2958 constexpr void init_symb_repeat(size_t count, K pad) {
2959 if (count) {
2960 K* str = d().init(count);
2961 traits::assign(str, count, pad);
2962 str[count] = 0;
2963 } else
2964 d().create_empty();
2965 }
2966
2978 constexpr void init_str_expr(const StrExprForType<K> auto& expr) {
2979 size_t len = expr.length();
2980 if (len)
2981 *expr.place(d().init(len)) = 0;
2982 else
2983 d().create_empty();
2984 }
2985
2999 template<StrType<K> From>
3000 void init_replaced(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0) {
3001 auto findes = f.find_all(pattern, offset, maxCount);
3002 if (!findes.size()) {
3003 new (this) my_type{f};
3004 return;
3005 }
3006 size_t srcLen = f.length();
3007 size_t newSize = srcLen + static_cast<ptrdiff_t>(repl.len - pattern.len) * findes.size();
3008
3009 if (!newSize) {
3010 new (this) my_type{};
3011 return;
3012 }
3013
3014 K* ptr = d().init(newSize);
3015 const K* src = f.symbols();
3016 size_t from = 0;
3017 for (const auto& s: findes) {
3018 size_t copyLen = s - from;
3019 if (copyLen) {
3020 traits::copy(ptr, src + from, copyLen);
3021 ptr += copyLen;
3022 }
3023 if (repl.len) {
3024 traits::copy(ptr, repl.str, repl.len);
3025 ptr += repl.len;
3026 }
3027 from = s + pattern.len;
3028 }
3029 srcLen -= from;
3030 if (srcLen) {
3031 traits::copy(ptr, src + from, srcLen);
3032 ptr += srcLen;
3033 }
3034 *ptr = 0;
3035 }
3036
3037 template<StrType<K> From, typename Op1, typename... Args>
3038 requires std::is_constructible_v<allocator_t, Args...>
3039 static my_type changeCaseAscii(const From& f, const Op1& opMakeNeedCase, Args&&... args) {
3040 my_type result{std::forward<Args>(args)...};
3041 size_t len = f.length();
3042 if (len) {
3043 const K* source = f.symbols();
3044 K* destination = result.init(len);
3045 for (size_t l = 0; l < len; l++) {
3046 destination[l] = opMakeNeedCase(source[l]);
3047 }
3048 }
3049 return result;
3050 }
3051 // GCC до сих пор не даёт делать полную специализацию вложенного шаблонного класса внутри внешнего класса, только частичную.
3052 // Поэтому добавим фиктивный параметр шаблона, чтобы сделать специализацию для u8s прямо в классе.
3053 // GCC still does not allow full specialization of a nested template class inside an outer class, only partial.
3054 // So let's add a dummy template parameter to make the specialization for u8s right in the class.
3055 template<typename T, bool Dummy = true>
3056 struct ChangeCase {
3057 template<typename From, typename Op1, typename... Args>
3058 requires std::is_constructible_v<allocator_t, Args...>
3059 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
3060 my_type result{std::forward<Args>(args)...};
3061 size_t len = f.length();
3062 if (len) {
3063 opChangeCase(f.symbols(), len, result.init(len));
3064 }
3065 return result;
3066 }
3067 };
3068 // Для utf8 сделаем отдельную спецификацию, так как при смене регистра может изменится длина строки
3069 // For utf8 we will make a separate specification, since changing the register may change the length of the string
3070 template<bool Dummy>
3071 struct ChangeCase<u8s, Dummy> {
3072 template<typename From, typename Op1, typename... Args>
3073 requires std::is_constructible_v<allocator_t, Args...>
3074 static my_type changeCase(const From& f, const Op1& opChangeCase, Args&&... args) {
3075 my_type result{std::forward<Args>(args)...};
3076 ;
3077 size_t len = f.length();
3078 if (len) {
3079 const K* ptr = f.symbols();
3080 K* pWrite = result.init(len);
3081
3082 const u8s* source = ptr;
3083 u8s* dest = pWrite;
3084 size_t newLen = opChangeCase(source, len, dest, len);
3085 if (newLen < len) {
3086 // Строка просто укоротилась
3087 // The string was simply shortened
3088 result.set_size(newLen);
3089 } else if (newLen > len) {
3090 // Строка не влезла в буфер.
3091 // The line did not fit into the buffer.
3092 size_t readed = static_cast<size_t>(source - ptr);
3093 size_t writed = static_cast<size_t>(dest - pWrite);
3094 pWrite = result.set_size(newLen);
3095 dest = pWrite + writed;
3096 opChangeCase(source, len - readed, dest, newLen - writed);
3097 }
3098 pWrite[newLen] = 0;
3099 }
3100 return result;
3101 }
3102 };
3103
3104public:
3105
3106 inline static constexpr bool is_str_storable = true;
3113 constexpr operator const K*() const noexcept {
3114 return d().symbols();
3115 }
3116
3124 constexpr s_str_nt to_nts(size_t from = 0) const {
3125 size_t len = d().length();
3126 if (from >= len) {
3127 from = len;
3128 }
3129 return {d().symbols() + from, len - from};
3130 }
3131
3137 constexpr operator s_str_nt() const {
3138 return {d().symbols(), d().length()};
3139 }
3140
3178 template<typename T, typename... Args>
3179 requires std::is_constructible_v<allocator_t, Args...>
3180 static my_type join(const T& strings, s_str delimeter, bool tail = false, bool skip_empty = false, Args&&... args) {
3181 my_type result(std::forward<Args>(args)...);
3182 if (strings.size()) {
3183 if (strings.size() == 1 && (!delimeter.length() || !tail)) {
3184 result = strings.front();
3185 } else {
3186 size_t commonLen = 0;
3187 for (const auto& t: strings) {
3188 size_t len = t.length();
3189 if (len > 0 || !skip_empty) {
3190 if (commonLen > 0) {
3191 commonLen += delimeter.len;
3192 }
3193 commonLen += len;
3194 }
3195 }
3196 commonLen += (tail && delimeter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimeter.len : 0);
3197 if (commonLen) {
3198 K* ptr = result.init(commonLen);
3199 K* write = ptr;
3200 for (const auto& t: strings) {
3201 size_t copyLen = t.length();
3202 if (delimeter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
3203 ch_traits<K>::copy(write, delimeter.str, delimeter.len);
3204 write += delimeter.len;
3205 }
3206 ch_traits<K>::copy(write, t.symbols(), copyLen);
3207 write += copyLen;
3208 }
3209 if (delimeter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
3210 ch_traits<K>::copy(write, delimeter.str, delimeter.len);
3211 write += delimeter.len;
3212 }
3213 *write = 0;
3214 } else {
3215 result.create_empty();
3216 }
3217 }
3218 }
3219 return result;
3220 }
3221
3229 template<StrType<K> From, typename... Args>
3230 requires std::is_constructible_v<allocator_t, Args...>
3231 static my_type upperred_only_ascii_from(const From& f, Args&&... args) {
3232 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
3233 }
3234
3242 template<StrType<K> From, typename... Args>
3243 requires std::is_constructible_v<allocator_t, Args...>
3244 static my_type lowered_only_ascii_from(const From& f, Args&&... args) {
3245 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
3246 }
3247
3259 template<StrType<K> From, typename... Args>
3260 requires std::is_constructible_v<allocator_t, Args...>
3261 static my_type upperred_from(const From& f, Args&&... args) {
3262 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
3263 }
3264
3276 template<StrType<K> From, typename... Args>
3277 requires std::is_constructible_v<allocator_t, Args...>
3278 static my_type lowered_from(const From& f, Args&&... args) {
3279 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
3280 }
3281
3297 template<StrType<K> From, typename... Args>
3298 requires std::is_constructible_v<allocator_t, Args...>
3299 static my_type replaced_from(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args) {
3300 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
3301 }
3302};
3303
3308template<typename A>
3309concept Allocatorable = requires(A& a, size_t size, void* void_ptr) {
3310 { a.allocate(size) } -> std::same_as<void*>;
3311 { a.deallocate(void_ptr) } noexcept -> std::same_as<void>;
3312};
3313
3314struct printf_selector {
3315 template<typename K, typename... T> requires (is_one_of_std_char_v<K>)
3316 static int snprintf(K* buffer, size_t count, const K* format, T&&... args) {
3317 if constexpr (std::is_same_v<K, u8s>) {
3318 #ifndef _WIN32
3319 return std::snprintf(buffer, count, format, std::forward<T>(args)...);
3320 #else
3321 // Поддерживает позиционные параметры
3322 // Supports positional parameters
3323 return _sprintf_p(buffer, count, format, args...);
3324 #endif
3325 } else {
3326 #ifndef _WIN32
3327 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
3328 #else
3329 // Поддерживает позиционные параметры
3330 // Supports positional parameters
3331 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
3332 #endif
3333 }
3334 }
3335 template<typename K> requires (is_one_of_std_char_v<K>)
3336 static int vsnprintf(K* buffer, size_t count, const K* format, va_list args) {
3337 if constexpr (std::is_same_v<K, u8s>) {
3338 #ifndef _WIN32
3339 return std::vsnprintf(buffer, count, format, args);
3340 #else
3341 // Поддерживает позиционные параметры
3342 // Supports positional parameters
3343 return _vsprintf_p(buffer, count, format, args);
3344 #endif
3345 } else {
3346 #ifndef _WIN32
3347 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
3348 #else
3349 // Поддерживает позиционные параметры
3350 // Supports positional parameters
3351 return _vswprintf_p(buffer, count, format, args);
3352 #endif
3353 }
3354 }
3355};
3356
3357inline size_t grow2(size_t ret, size_t currentCapacity) {
3358 return ret <= currentCapacity ? ret : ret * 2;
3359}
3360
3399template<typename K, typename Impl>
3401public:
3402 using my_type = Impl;
3403
3404private:
3405 Impl& d() {
3406 return *static_cast<Impl*>(this);
3407 }
3408 const Impl& d() const {
3409 return *static_cast<const Impl*>(this);
3410 }
3411 size_t _len() const noexcept {
3412 return d().length();
3413 }
3414 const K* _str() const noexcept {
3415 return d().symbols();
3416 }
3417 using str_piece = simple_str<K>;
3418 using symb_type = K;
3419 using traits = ch_traits<K>;
3420 using uni = unicode_traits<K>;
3421 using uns_type = std::make_unsigned_t<K>;
3422
3423 template<typename Op>
3424 Impl& make_trim_op(const Op& op) {
3425 str_piece me = static_cast<str_piece>(d()), pos = op(me);
3426 if (me.length() != pos.length()) {
3427 if (me.symbols() != pos.symbols())
3428 traits::move(const_cast<K*>(me.symbols()), pos.symbols(), pos.length());
3429 d().set_size(pos.length());
3430 }
3431 return d();
3432 }
3433
3434 template<auto Op>
3435 Impl& commonChangeCase() {
3436 size_t len = _len();
3437 if (len)
3438 Op(_str(), len, str());
3439 return d();
3440 }
3441 // GCC до сих пор не позволяет делать внутри класса полную специализацию вложенного класса,
3442 // только частичную. Поэтому добавим неиспользуемый параметр шаблона.
3443 // GCC still does not allow full specialization of a nested class within a class,
3444 // only partial. Resources additive unused parameter template.
3445 template<typename T, bool Dummy = true>
3446 struct CaseTraits {
3447 static Impl& upper(Impl& obj) {
3448 return obj.template commonChangeCase<unicode_traits<K>::upper>();
3449 }
3450 static Impl& lower(Impl& obj) {
3451 return obj.template commonChangeCase<unicode_traits<K>::lower>();
3452 }
3453 };
3454
3455 template<auto Op>
3456 Impl& utf8CaseChange() {
3457 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3458 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
3459 size_t len = _len();
3460 if (len) {
3461 u8s* writePos = str();
3462 const u8s *startData = writePos, *readPos = writePos;
3463 size_t newLen = Op(readPos, len, writePos, len);
3464 if (newLen < len) {
3465 // Строка просто укоротилась
3466 // The string was simply shortened
3467 d().set_size(newLen);
3468 } else if (newLen > len) {
3469 // Строка не влезла в буфер.
3470 // The line did not fit into the buffer.
3471 size_t readed = static_cast<size_t>(readPos - startData);
3472 size_t writed = static_cast<size_t>(writePos - startData);
3473 d().set_size(newLen);
3474 startData = str(); // при изменении размера могло изменится | may change when resizing
3475 readPos = startData + readed;
3476 writePos = const_cast<u8s*>(startData) + writed;
3477 Op(readPos, len - readed, writePos, newLen - writed);
3478 }
3479 }
3480 return d();
3481 }
3482 template<bool Dummy>
3483 struct CaseTraits<u8s, Dummy> {
3484 static Impl& upper(Impl& obj) {
3485 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
3486 }
3487 static Impl& lower(Impl& obj) {
3488 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
3489 }
3490 };
3491
3492 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
3493 Impl& makeTrim(T&& pattern) {
3494 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
3495 }
3496
3497 template<TrimSides S, bool withSpaces>
3498 Impl& makeTrim(str_piece pattern) {
3499 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
3500 }
3501
3502public:
3509 K* str() noexcept {
3510 return d().str();
3511 }
3512
3518 explicit operator K*() noexcept {
3519 return str();
3520 }
3521
3527 Impl& trim() {
3528 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
3529 }
3530
3536 Impl& trim_left() {
3537 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
3538 }
3539
3545 Impl& trim_right() {
3546 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
3547 }
3548
3556 template<typename T, size_t N = const_lit_for<K, T>::Count>
3557 requires is_const_pattern<N>
3558 Impl& trim(T&& pattern) {
3559 return makeTrim<TrimSides::TrimAll, false>(pattern);
3560 }
3561
3569 template<typename T, size_t N = const_lit_for<K, T>::Count>
3570 requires is_const_pattern<N>
3571 Impl& trim_left(T&& pattern) {
3572 return makeTrim<TrimSides::TrimLeft, false>(pattern);
3573 }
3574
3582 template<typename T, size_t N = const_lit_for<K, T>::Count>
3583 requires is_const_pattern<N>
3584 Impl& trim_right(T&& pattern) {
3585 return makeTrim<TrimSides::TrimRight, false>(pattern);
3586 }
3587
3595 template<typename T, size_t N = const_lit_for<K, T>::Count>
3596 requires is_const_pattern<N>
3597 Impl& trim_with_spaces(T&& pattern) {
3598 return makeTrim<TrimSides::TrimAll, true>(pattern);
3599 }
3600
3608 template<typename T, size_t N = const_lit_for<K, T>::Count>
3609 requires is_const_pattern<N>
3610 Impl& trim_left_with_spaces(T&& pattern) {
3611 return makeTrim<TrimSides::TrimLeft, true>(pattern);
3612 }
3613
3621 template<typename T, size_t N = const_lit_for<K, T>::Count>
3622 requires is_const_pattern<N>
3623 Impl& trim_right_with_wpaces(T&& pattern) {
3624 return makeTrim<TrimSides::TrimRight, true>(pattern);
3625 }
3626
3634 Impl& trim(str_piece pattern) {
3635 return pattern.length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
3636 }
3637
3645 Impl& trim_left(str_piece pattern) {
3646 return pattern.length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
3647 }
3648
3656 Impl& trim_right(str_piece pattern) {
3657 return pattern.length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
3658 }
3659
3667 Impl& trim_with_spaces(str_piece pattern) {
3668 return makeTrim<TrimSides::TrimAll, true>(pattern);
3669 }
3670
3678 Impl& trim_left_with_spaces(str_piece pattern) {
3679 return makeTrim<TrimSides::TrimLeft, true>(pattern);
3680 }
3681
3689 Impl& trim_right_with_spaces(str_piece pattern) {
3690 return makeTrim<TrimSides::TrimRight, true>(pattern);
3691 }
3692
3699 K* ptr = str();
3700 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
3701 K s = *ptr;
3702 if (isAsciiLower(s))
3703 *ptr = s & ~0x20;
3704 }
3705 return d();
3706 }
3707
3714 K* ptr = str();
3715 for (size_t i = 0, l = _len(); i < l; i++, ptr++) {
3716 K s = *ptr;
3717 if (isAsciiUpper(s))
3718 *ptr = s | 0x20;
3719 }
3720 return d();
3721 }
3722
3732 Impl& upper() {
3733 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3734 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
3735 return CaseTraits<K>::upper(d());
3736 }
3737
3747 Impl& lower() {
3748 // Для utf-8 такая операция может изменить длину строки, поэтому для них делаем разные специализации
3749 // For utf-8, such an operation can change the length of the string, so we make different specializations for them
3750 return CaseTraits<K>::lower(d());
3751 }
3752
3753private:
3754 template<typename T>
3755 Impl& changeImpl(size_t from, size_t len, T expr) {
3756 size_t myLen = _len();
3757 if (from > myLen) {
3758 from = myLen;
3759 }
3760 if (from + len > myLen) {
3761 len = myLen - from;
3762 }
3763 K* buffer = str();
3764 size_t otherLen = expr.length();
3765 if (len == otherLen) {
3766 expr.place(buffer + from);
3767 } else {
3768 size_t tailLen = myLen - from - len;
3769 if (len > otherLen) {
3770 expr.place(buffer + from);
3771 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
3772 d().set_size(myLen - (len - otherLen));
3773 } else {
3774 buffer = d().set_size(myLen + otherLen - len);
3775 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
3776 expr.place(buffer + from);
3777 }
3778 }
3779 return d();
3780 }
3781
3782 template<typename T>
3783 Impl& appendImpl(T expr) {
3784 if (size_t len = expr.length(); len) {
3785 size_t size = _len();
3786 expr.place(d().set_size(size + len) + size);
3787 }
3788 return d();
3789 }
3790
3791 template<typename T>
3792 Impl& appendFromImpl(size_t pos, T expr) {
3793 if (pos > _len())
3794 pos = _len();
3795 if (size_t len = expr.length())
3796 expr.place(d().set_size(pos + len) + pos);
3797 else
3798 d().set_size(pos);
3799 return d();
3800 }
3801
3802public:
3803 inline static constexpr bool is_str_mutable = true;
3812 Impl& append(str_piece other) {
3813 return appendImpl<str_piece>(other);
3814 }
3815
3823 template<StrExprForType<K> A>
3824 Impl& append(const A& expr) {
3825 return appendImpl<const A&>(expr);
3826 }
3827
3835 Impl& operator+=(str_piece other) {
3836 return appendImpl<str_piece>(other);
3837 }
3838
3846 template<StrExprForType<K> A>
3847 Impl& operator+=(const A& expr) {
3848 return appendImpl<const A&>(expr);
3849 }
3850
3864 Impl& append_in(size_t pos, str_piece other) {
3865 return appendFromImpl<str_piece>(pos, other);
3866 }
3867
3881 template<StrExprForType<K> A>
3882 Impl& append_in(size_t pos, const A& expr) {
3883 return appendFromImpl<const A&>(pos, expr);
3884 }
3885
3897 Impl& change(size_t from, size_t len, str_piece other) {
3898 return changeImpl<str_piece>(from, len, other);
3899 }
3900
3912 template<StrExprForType<K> A>
3913 Impl& change(size_t from, size_t len, const A& expr) {
3914 return changeImpl<const A&>(from, len, expr);
3915 }
3916
3926 Impl& insert(size_t to, str_piece other) {
3927 return changeImpl<str_piece>(to, 0, other);
3928 }
3929
3939 template<StrExprForType<K> A>
3940 Impl& insert(size_t to, const A& expr) {
3941 return changeImpl<const A&>(to, 0, expr);
3942 }
3943
3953 Impl& remove(size_t from, size_t len) {
3954 return changeImpl<const empty_expr<K>&>(from, len, {});
3955 }
3956
3964 Impl& prepend(str_piece other) {
3965 return changeImpl<str_piece>(0, 0, other);
3966 }
3967
3975 template<StrExprForType<K> A>
3976 Impl& prepend(const A& expr) {
3977 return changeImpl<const A&>(0, 0, expr);
3978 }
3979
3993 Impl& replace(str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
3994 offset = d().find(pattern, offset);
3995 if (offset == str::npos) {
3996 return d();
3997 }
3998 if (!maxCount)
3999 maxCount--;
4000 size_t replLength = repl.length(), patternLength = pattern.length();
4001
4002 if (patternLength == replLength) {
4003 // Заменяем inplace на подстроку такой же длины
4004 // Replace inplace with a substring of the same length
4005 K* ptr = str();
4006 for (size_t i = 0; i < maxCount; i++) {
4007 traits::copy(ptr + offset, repl.symbols(), replLength);
4008 offset = d().find(pattern, offset + replLength);// replLength == patternLength
4009 if (offset == str::npos)
4010 break;
4011 }
4012 } else if (patternLength > replLength) {
4013 // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо
4014 // Replace with a shorter piece, the length of the text will decrease, go from left to right
4015 K* ptr = str();
4016 traits::copy(ptr + offset, repl.symbols(), replLength);
4017 size_t posWrite = offset + replLength;
4018 maxCount--;
4019 offset += patternLength;
4020
4021 for (size_t i = 0; i < maxCount; i++) {
4022 size_t idx = d().find(pattern, offset);
4023 if (idx == str::npos)
4024 break;
4025 size_t lenOfPiece = idx - offset;
4026 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
4027 posWrite += lenOfPiece;
4028 traits::copy(ptr + posWrite, repl.symbols(), replLength);
4029 posWrite += replLength;
4030 offset = idx + patternLength;
4031 }
4032 size_t tailLen = _len() - offset;
4033 traits::move(ptr + posWrite, ptr + offset, tailLen);
4034 d().set_size(posWrite + tailLen);
4035 } else {
4036 struct replace_grow_helper {
4037 replace_grow_helper(my_type& src, str_piece p, str_piece r, size_t mc, size_t d)
4038 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
4039 my_type& source;
4040 const str_piece pattern;
4041 const str_piece repl;
4042 size_t maxCount;
4043 const size_t delta;
4044 size_t all_delta{};
4045 K* reserve_for_copy{};
4046 size_t end_of_piece{};
4047 size_t total_length{};
4048
4049 void replace(size_t offset) {
4050 size_t finded[16] = {source.find(pattern, offset)};
4051 if (finded[0] == str::npos) {
4052 return;
4053 }
4054 maxCount--;
4055 offset = finded[0] + pattern.length();
4056 all_delta += delta;
4057 size_t idx = 1;
4058 for (size_t end = std::min(maxCount, std::size(finded)); idx < end; idx++, maxCount--) {
4059 finded[idx] = source.find(pattern, offset);
4060 if (finded[idx] == str::npos) {
4061 break;
4062 }
4063 offset = finded[idx] + pattern.length();
4064 all_delta += delta;
4065 }
4066 bool needMore = maxCount > 0 && idx == std::size(finded) && offset < source.length() - pattern.length();
4067 if (needMore) {
4068 replace(offset); // здесь произведутся замены в оставшемся хвосте | replacements will be made here in the remaining tail
4069 }
4070 // Теперь делаем свои замены
4071 // Now we make our replacements
4072 if (!reserve_for_copy) {
4073 // Только начинаем
4074 // Just getting started
4075 end_of_piece = source.length();
4076 total_length = end_of_piece + all_delta;
4077 reserve_for_copy = source.alloc_for_copy(total_length);
4078 }
4079 K* dst_start = reserve_for_copy;
4080 const K* src_start = source.symbols();
4081 while(idx-- > 0) {
4082 size_t pos = finded[idx] + pattern.length();
4083 size_t lenOfPiece = end_of_piece - pos;
4084 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
4085 ch_traits<K>::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length());
4086 all_delta -= delta;
4087 end_of_piece = finded[idx];
4088 }
4089 if (!all_delta && reserve_for_copy != src_start) {
4090 ch_traits<K>::copy(dst_start, src_start, finded[0]);
4091 }
4092 }
4093 } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length());
4094 helper.replace(offset);
4095 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
4096 }
4097 return d();
4098 }
4099
4115 template<StrType<K> From>
4116 Impl& replace_from(const From& f, str_piece pattern, str_piece repl, size_t offset = 0, size_t maxCount = 0) {
4117 if (pattern.length() >= repl.length()) {
4118 K* dst = d().reserve_no_preserve(f.length());
4119 const K* src = f.symbols();
4120 size_t delta = 0;
4121 if (maxCount == 0) {
4122 maxCount--;
4123 }
4124 size_t src_length = f.length(), start = 0;
4125 while (maxCount--) {
4126 offset = f.find(pattern, offset);
4127 if (offset == str::npos) {
4128 break;
4129 }
4130 size_t piece_len = offset - start;
4131 if (piece_len) {
4132 ch_traits<K>::copy(dst, src + start, piece_len);
4133 dst += piece_len;
4134 }
4135 if (repl.length()) {
4136 ch_traits<K>::copy(dst, repl.symbols(), repl.length());
4137 dst += repl.length();
4138 }
4139 delta += pattern.length() - repl.length();
4140 offset += pattern.length();
4141 start = offset;
4142 }
4143 if (start < src_length) {
4144 ch_traits<K>::copy(dst, src + start, src_length - start);
4145 }
4146 d().set_size(src_length - delta);
4147 } else {
4148 d() = f;
4149 replace(pattern, repl, offset, maxCount);
4150 }
4151 return d();
4152 }
4153
4177 template<typename Op>
4178 Impl& fill(size_t from, const Op& fillFunction) {
4179 size_t size = _len();
4180 if (from > size)
4181 from = size;
4182 size_t capacity = d().capacity();
4183 K* ptr = str();
4184 capacity -= from;
4185 for (;;) {
4186 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
4187 if (capacity >= needSize) {
4188 d().set_size(from + needSize);
4189 break;
4190 }
4191 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
4192 capacity = d().capacity() - from;
4193 }
4194 return d();
4195 }
4196
4204 template<typename Op>
4205 requires std::is_invocable_v<Op, K*, size_t>
4206 Impl& operator<<(const Op& fillFunction) {
4207 return fill(0, fillFunction);
4208 }
4209
4217 template<typename Op>
4218 requires std::is_invocable_v<Op, K*, size_t>
4219 Impl& operator<<=(const Op& fillFunction) {
4220 return fill(_len(), fillFunction);
4221 }
4222
4230 template<typename Op>
4231 requires std::is_invocable_v<Op, my_type&>
4232 Impl& operator<<(const Op& fillFunction) {
4233 fillFunction(d());
4234 return d();
4235 }
4236
4250 template<typename... T> requires (is_one_of_std_char_v<K>)
4251 Impl& printf_from(size_t from, const K* format, T&&... args) {
4252 size_t size = _len();
4253 if (from > size)
4254 from = size;
4255 size_t capacity = d().capacity();
4256 K* ptr = str();
4257 capacity -= from;
4258
4259 int result = 0;
4260 // Тут грязный хак для u8s и wide_char. u8s версия snprintf сразу возвращает размер нужного буфера, если он мал
4261 // а swprintf - возвращает -1. Под windows оба варианта xxx_p - тоже возвращают -1.
4262 // Поэтому для них надо тупо увеличивать буфер наугад, пока не подойдет
4263 // Here's a dirty hack for u8s and wide_char. u8s version of snprintf immediately returns the size of the required buffer if it is small
4264 // and swprintf returns -1. Under Windows, both options xxx_p also return -1.
4265 // Therefore, for them you need to stupidly increase the buffer at random until it fits
4266 if constexpr (sizeof(K) == 1 && !isWindowsOs) {
4267 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
4268 if (result > (int)capacity) {
4269 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
4270 result = printf_selector::snprintf(ptr + from, result + 1, format, std::forward<T>(args)...);
4271 }
4272 } else {
4273 for (;;) {
4274 result = printf_selector::snprintf(ptr + from, capacity + 1, format, std::forward<T>(args)...);
4275 if (result < 0) {
4276 // Не хватило буфера или ошибка конвертации.
4277 // Попробуем увеличить буфер в два раза
4278 // Not enough buffer or conversion error.
4279 // Let's try to double the buffer
4280 capacity *= 2;
4281 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
4282 } else
4283 break;
4284 }
4285 }
4286 if (result < 0)
4287 d().set_size(static_cast<size_t>(traits::length(_str())));
4288 else
4289 d().set_size(from + result);
4290 return d();
4291 }
4292
4304 template<typename... T> requires (is_one_of_std_char_v<K>)
4305 Impl& printf(const K* format, T&&... args) {
4306 return printf_from(0, format, std::forward<T>(args)...);
4307 }
4308
4320 template<typename... T> requires (is_one_of_std_char_v<K>)
4321 Impl& append_printf(const K* format, T&&... args) {
4322 return printf_from(_len(), format, std::forward<T>(args)...);
4323 }
4324
4325 struct writer {
4326 my_type* store;
4327 K* ptr;
4328 const K* end;
4329 size_t max_write;
4330 size_t writed = 0;
4331 inline static K pad;
4332 K& operator*() const {
4333 return *ptr;
4334 }
4335 writer& operator++() {
4336 if (writed < max_write) {
4337 ++ptr;
4338 if (ptr == end) {
4339 size_t l = ptr - store->begin();
4340 store->set_size(l);
4341 ptr = store->set_size(l + std::min(l / 2, size_t(8192))) + l;
4342 end = store->end();
4343 }
4344 } else {
4345 ptr = &pad;
4346 }
4347 return *this;
4348 }
4349 writer operator++(int) {
4350 auto ret = *this;
4351 operator++();
4352 return ret;
4353 }
4354
4355 writer(my_type& s, K* p, K* e, size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
4356 writer() = default;
4357 writer(const writer&) = delete;
4358 writer& operator=(const writer&) noexcept = delete;
4359 writer(writer&&) noexcept = default;
4360 writer& operator=(writer&&) noexcept = default;
4361 using difference_type = int;
4362 };
4377 template<typename... T> requires (is_one_of_std_char_v<K>)
4378 Impl& format_from(size_t from, const FmtString<K, T...>& format, T&&... args) {
4379 size_t size = _len();
4380 if (from > size)
4381 from = size;
4382 size_t capacity = d().capacity();
4383 K* ptr = str();
4384
4385 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
4386 std::forward<decltype(format)>(format), std::forward<T>(args)...);
4387 d().set_size(result.ptr - _str());
4388 return d();
4389 }
4390
4406 template<typename... T> requires (is_one_of_std_char_v<K>)
4407 Impl& vformat_from(size_t from, size_t max_write, str_piece format, T&&... args) {
4408 size_t size = _len();
4409 if (from > size)
4410 from = size;
4411 size_t capacity = d().capacity();
4412 K* ptr = str();
4413
4414 if constexpr (std::is_same_v<K, u8s>) {
4415 auto result = std::vformat_to(
4416 writer{d(), ptr + from, ptr + capacity, max_write},
4417 std::basic_string_view<K>{format.symbols(), format.length()},
4418 std::make_format_args(args...));
4419 d().set_size(result.ptr - _str());
4420 } else {
4421 auto result = std::vformat_to(
4422 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
4423 std::basic_string_view<wchar_t>{to_one_of_std_char(format.symbols()), format.length()},
4424 std::make_wformat_args(std::forward<T>(args)...));
4425 d().set_size(result.ptr - _str());
4426 }
4427 return d();
4428 }
4429
4441 template<typename... T> requires (is_one_of_std_char_v<K>)
4442 Impl& format(const FmtString<K, T...>& pattern, T&&... args) {
4443 return format_from(0, pattern, std::forward<T>(args)...);
4444 }
4445
4457 template<typename... T> requires (is_one_of_std_char_v<K>)
4458 Impl& append_formatted(const FmtString<K, T...>& format, T&&... args) {
4459 return format_from(_len(), format, std::forward<T>(args)...);
4460 }
4461
4473 template<typename... T> requires (is_one_of_std_char_v<K>)
4474 Impl& vformat(str_piece format, T&&... args) {
4475 return vformat_from(0, -1, format, std::forward<T>(args)...);
4476 }
4477
4489 template<typename... T> requires (is_one_of_std_char_v<K>)
4490 Impl& append_vformatted(str_piece format, T&&... args) {
4491 return vformat_from(_len(), -1, format, std::forward<T>(args)...);
4492 }
4493
4507 template<typename... T> requires (is_one_of_std_char_v<K>)
4508 Impl& vformat_n(size_t max_write, str_piece format, T&&... args) {
4509 return vformat_from(0, max_write, format, std::forward<T>(args)...);
4510 }
4511
4525 template<typename... T> requires (is_one_of_std_char_v<K>)
4526 Impl& append_vformatted_n(size_t max_write, str_piece format, T&&... args) {
4527 return vformat_from(_len(), max_write, format, std::forward<T>(args)...);
4528 }
4529
4539 template<typename Op, typename... Args>
4540 Impl& with(const Op& fillFunction, Args&&... args) {
4541 fillFunction(d(), std::forward<Args>(args)...);
4542 return d();
4543 }
4544};
4545
4546template<typename K>
4547struct SharedStringData {
4548 std::atomic_size_t ref_; // Счетчик ссылок | Reference count
4549
4550 SharedStringData() {
4551 ref_ = 1;
4552 }
4553 K* str() const {
4554 return (K*)(this + 1);
4555 }
4556 void incr() {
4557 ref_.fetch_add(1, std::memory_order_relaxed);
4558 }
4559 void decr(Allocatorable auto& allocator) {
4560 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
4561 if (val == 1) {
4562 allocator.deallocate(this);
4563 }
4564 }
4565 static SharedStringData<K>* create(size_t l, Allocatorable auto& allocator) {
4566 size_t size = sizeof(SharedStringData<K>) + (l + 1) * sizeof(K);
4567 return new (allocator.allocate(size)) SharedStringData();
4568 }
4569 static SharedStringData<K>* from_str(const K* p) {
4570 return (SharedStringData<K>*)p - 1;
4571 }
4572 K* place(K* p, size_t len) {
4573 ch_traits<K>::copy(p, str(), len);
4574 return p + len;
4575 }
4576};
4577
4578// Дефолтный аллокатор для строк, может работать статически
4579// Default allocator for strings, can work statically
4580class string_common_allocator {
4581public:
4582 void* allocate(size_t bytes) {
4583 return new char[bytes];
4584 }
4585 void deallocate(void* address) noexcept {
4586 delete [] static_cast<char*>(address);
4587 }
4588};
4589
4590string_common_allocator default_string_allocator_selector(...);
4591// Если вы хотите задать свой дефолтный аллокатор для строк, перед включение sstring.h
4592// объявите функцию
4593// ваш_тип_аллокатора default_string_allocator_selector(int);
4594// If you want to set your default allocator for strings, before including sstring.h
4595// declare a function
4596// your_allocator_type default_string_allocator_selector(int);
4597using allocator_string = decltype(default_string_allocator_selector(int(0)));
4598
4599template<typename K, Allocatorable Allocator>
4600class sstring;
4601
4602/*
4603* Так как у класса несколько базовых классов, MSVC не применяет автоматом empty base optimization,
4604* и без явного указания - вставит в начало класса пустые байты, сдвинув поле size на 4-8 байта.
4605* Укажем ему явно.
4606* Since a class has several base classes, MSVC does not automatically apply empty base optimization,
4607* and without explicit indication - will insert empty bytes at the beginning of the class, shifting the size field by 4-8 bytes.
4608* Let's tell him explicitly.
4609*/
4610
4631template<typename K, size_t N, bool forShared = false, Allocatorable Allocator = allocator_string>
4632class decl_empty_bases lstring :
4633 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
4634 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
4635 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
4636 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
4637 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
4638public:
4639 using symb_type = K;
4641 using allocator_t = Allocator;
4642
4643 enum : size_t {
4645 LocalCapacity = N | (sizeof(void*) / sizeof(K) - 1),
4646 };
4647
4648protected:
4649 enum : size_t {
4650 extra = forShared ? sizeof(SharedStringData<K>) : 0,
4651 };
4652
4653 using base_algs = str_algs<K, simple_str<K>, my_type, true>;
4654 using base_storable = str_storable<K, my_type, Allocator>;
4655 using base_mutable = str_mutable<K, my_type>;
4656 using base_utf = from_utf_convertible<K, my_type>;
4657 using traits = ch_traits<K>;
4658 using s_str = base_storable::s_str;
4659
4660 friend base_storable;
4661 friend base_mutable;
4662 friend base_utf;
4663 friend class sstring<K, Allocator>;
4664
4665 K* data_;
4666 // Поле не должно инициализироваться, так как может устанавливаться в базовых конструкторах
4667 // The field should not be initialized, as it can be set in base constructors
4668 size_t size_;
4669
4670 union {
4671 size_t capacity_;
4672 K local_[LocalCapacity + 1];
4673 };
4674
4675 constexpr void create_empty() {
4676 data_ = local_;
4677 size_ = 0;
4678 local_[0] = 0;
4679 }
4680 constexpr static size_t calc_capacity(size_t s) {
4681 size_t real_need = (s + 1) * sizeof(K) + extra;
4682 size_t aligned_alloced = (real_need + alignof(std::max_align_t) - 1) / alignof(std::max_align_t) * alignof(std::max_align_t);
4683 return (aligned_alloced - extra) / sizeof(K) - 1;
4684 }
4685
4686 constexpr K* init(size_t s) {
4687 size_ = s;
4688 if (size_ > LocalCapacity) {
4689 s = calc_capacity(s);
4690 data_ = alloc_place(s);
4691 capacity_ = s;
4692 } else {
4693 data_ = local_;
4694 }
4695 return str();
4696 }
4697 // Методы для себя | Methods for yourself
4698 constexpr bool is_alloced() const noexcept {
4699 return data_ != local_;
4700 }
4701
4702 constexpr void dealloc() {
4703 if (is_alloced()) {
4704 base_storable::allocator().deallocate(to_real_address(data_));
4705 data_ = local_;
4706 }
4707 }
4708
4709 constexpr static K* to_real_address(void* ptr) {
4710 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) - extra);
4711 }
4712 constexpr static K* from_real_address(void* ptr) {
4713 return reinterpret_cast<K*>(reinterpret_cast<u8s*>(ptr) + extra);
4714 }
4715
4716 constexpr K* alloc_place(size_t newSize) {
4717 return from_real_address(base_storable::allocator().allocate((newSize + 1) * sizeof(K) + extra));
4718 }
4719 // Вызывается при replace, когда меняют на более длинную замену
4720 // Called on replace when changing to a longer replacement
4721 constexpr K* alloc_for_copy(size_t newSize) {
4722 if (capacity() >= newSize) {
4723 // Замена войдёт в текущий буфер
4724 // Replacement will go into the current buffer
4725 return data_;
4726 }
4727 return alloc_place(calc_capacity(newSize));
4728 }
4729 // Вызывается после replace, когда меняли на более длинную замену, могли скопировать в новый буфер
4730 // Called after replace, when they changed to a longer replacement, they could have copied it to a new buffer
4731 constexpr void set_from_copy(K* ptr, size_t newSize) {
4732 if (ptr != data_) {
4733 // Да, копировали в новый буфер
4734 // Yes, copied to a new buffer
4735 dealloc();
4736 data_ = ptr;
4737 capacity_ = calc_capacity(newSize);
4738 }
4739 size_ = newSize;
4740 data_[newSize] = 0;
4741 }
4742
4743public:
4744 using base_utf::base_utf;
4745
4752 template<typename... Args>
4753 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
4754 constexpr lstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
4755 : base_storable(std::forward<Args>(args)...) {
4756 create_empty();
4757 }
4758
4767 template<typename... Args>
4768 requires std::is_constructible_v<allocator_t, Args...>
4769 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4771 }
4772
4782 template<typename... Args>
4783 requires std::is_constructible_v<allocator_t, Args...>
4784 constexpr lstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4785 base_storable::init_str_repeat(repeat, pattern);
4786 }
4787
4797 template<typename... Args>
4798 requires std::is_constructible_v<allocator_t, Args...>
4799 constexpr lstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4801 }
4802
4816 template<typename... Args>
4817 requires std::is_constructible_v<allocator_t, Args...>
4818 constexpr lstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4820 }
4821
4837 template<StrType<K> From, typename... Args>
4838 requires std::is_constructible_v<allocator_t, Args...>
4839 constexpr lstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
4840 : base_storable(std::forward<Args>(args)...) {
4841 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
4842 }
4843
4844 constexpr lstring() {
4845 create_empty();
4846 }
4847
4848 constexpr ~lstring() {
4849 dealloc();
4850 }
4851
4858 constexpr lstring(const my_type& other) : base_storable(other.allocator()) {
4859 if (other.size_) {
4860 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
4861 }
4862 }
4863
4871 template<typename... Args>
4872 requires(sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
4873 constexpr lstring(const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4874 if (other.size_) {
4875 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
4876 }
4877 }
4878
4886 template<typename T, size_t I = const_lit_for<K, T>::Count, typename... Args>
4887 requires std::is_constructible_v<allocator_t, Args...>
4888 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4889 if constexpr (I > 1) {
4890 K* ptr = init(I - 1);
4891 traits::copy(ptr, value, I - 1);
4892 ptr[I - 1] = 0;
4893 } else
4894 create_empty();
4895 }
4896
4902 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
4903 if (other.size_) {
4904 size_ = other.size_;
4905 if (other.is_alloced()) {
4906 data_ = other.data_;
4907 capacity_ = other.capacity_;
4908 } else {
4909 data_ = local_;
4910 traits::copy(local_, other.local_, size_ + 1);
4911 }
4912 other.data_ = other.local_;
4913 other.size_ = 0;
4914 other.local_[0] = 0;
4915 }
4916 }
4917
4925 template<typename Op, typename... Args>
4926 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
4927 lstring(const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
4928 create_empty();
4929 this->operator<<(op);
4930 }
4931
4932 // copy and swap для присваиваний здесь не очень применимо, так как для строк с большим локальным буфером лишняя копия даже перемещением будет дорого стоить
4933 // Поэтому реализуем копирующее и перемещающее присваивание отдельно
4934 // copy and swap for assignments is not very applicable here, since for strings with a large local buffer, an extra copy, even by moving, will be expensive
4935 // Therefore, we implement the copy and move assignment separately
4936
4945 my_type& operator=(const my_type& other) {
4946 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
4947 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
4948 if (&other != this) {
4949 traits::copy(reserve_no_preserve(other.size_), other.data_, other.size_ + 1);
4950 size_ = other.size_;
4951 }
4952 return *this;
4953 }
4954
4962 my_type& operator=(my_type&& other) noexcept {
4963 // Так как между этими объектами не может быть косвенной зависимости, достаточно проверить только на равенство
4964 // Since there cannot be an indirect dependency between these objects, it is enough to check only for equality
4965 if (&other != this) {
4966 dealloc();
4967 if (other.is_alloced()) {
4968 data_ = other.data_;
4969 capacity_ = other.capacity_;
4970 } else {
4971 traits::copy(data_, other.local_, other.size_ + 1);
4972 }
4973 base_storable::allocator() = std::move(other.allocator());
4974 size_ = other.size_;
4975 other.create_empty();
4976 }
4977 return *this;
4978 }
4979
4980 my_type& assign(const K* other, size_t len) {
4981 if (len) {
4982 bool isIntersect = other >= data_ && other + len <= data_ + size_;
4983 if (isIntersect) {
4984 // Особый случай, нам пытаются присвоить кусок нашей же строки.
4985 // Просто переместим текст в буфере, и установим новый размер
4986 // A special case, they are trying to assign us a piece of our own string.
4987 // Just move the text in the buffer and set a new size
4988 if (other > data_) {
4989 traits::move(data_, other, len);
4990 }
4991 } else {
4992 traits::copy(reserve_no_preserve(len), other, len);
4993 }
4994 }
4995 size_ = len;
4996 data_[size_] = 0;
4997 return *this;
4998 }
5007 my_type& operator=(simple_str<K> other) {
5008 return assign(other.str, other.len);
5009 }
5010
5018 template<typename T, size_t S = const_lit_for<K, T>::Count>
5019 my_type& operator=(T&& other) {
5020 return assign(other, S - 1);
5021 }
5022
5032 my_type& operator=(const StrExprForType<K> auto& expr) {
5033 size_t newLen = expr.length();
5034 if (newLen) {
5035 expr.place(reserve_no_preserve(newLen));
5036 }
5037 size_ = newLen;
5038 data_[size_] = 0;
5039 return *this;
5040 }
5041
5042 constexpr size_t length() const noexcept {
5043 return size_;
5044 }
5045
5046 constexpr const K* symbols() const noexcept {
5047 return data_;
5048 }
5049
5050 constexpr K* str() noexcept {
5051 return data_;
5052 }
5053
5054 constexpr bool is_empty() const noexcept {
5055 return size_ == 0;
5056 }
5057
5058 constexpr bool empty() const noexcept {
5059 return size_ == 0;
5060 }
5061
5062 constexpr size_t capacity() const noexcept {
5063 return is_alloced() ? capacity_ : LocalCapacity;
5064 }
5065
5077 constexpr K* reserve_no_preserve(size_t newSize) {
5078 if (newSize > capacity()) {
5079 newSize = calc_capacity(newSize);
5080 K* newData = alloc_place(newSize);
5081 dealloc();
5082 data_ = newData;
5083 capacity_ = newSize;
5084 }
5085 return data_;
5086 }
5087
5099 constexpr K* reserve(size_t newSize) {
5100 if (newSize > capacity()) {
5101 newSize = calc_capacity(newSize);
5102 K* newData = alloc_place(newSize);
5103 traits::copy(newData, data_, size_);
5104 dealloc();
5105 data_ = newData;
5106 capacity_ = newSize;
5107 }
5108 return data_;
5109 }
5110
5122 constexpr K* set_size(size_t newSize) {
5123 size_t cap = capacity();
5124 if (newSize > cap) {
5125 size_t needPlace = newSize;
5126 if (needPlace < (cap + 1) * 2) {
5127 needPlace = (cap + 1) * 2 - 1;
5128 }
5129 reserve(needPlace);
5130 }
5131 size_ = newSize;
5132 data_[newSize] = 0;
5133 return data_;
5134 }
5135
5139 constexpr bool is_local() const noexcept {
5140 return !is_alloced();
5141 }
5142
5148 constexpr void define_size() {
5149 size_t cap = capacity();
5150 for (size_t i = 0; i < cap; i++) {
5151 if (data_[i] == 0) {
5152 size_ = i;
5153 return;
5154 }
5155 }
5156 size_ = cap;
5157 data_[size_] = 0;
5158 }
5159
5165 constexpr void shrink_to_fit() {
5166 size_t need_capacity = calc_capacity(size_);
5167 if (is_alloced() && capacity_ > need_capacity) {
5168 K* newData = size_ <= LocalCapacity ? local_ : alloc_place(need_capacity);
5169 traits::copy(newData, data_, size_ + 1);
5170 base_storable::allocator().deallocate(to_real_address(data_));
5171 data_ = newData;
5172
5173 if (size_ > LocalCapacity) {
5174 capacity_ = need_capacity;
5175 }
5176 }
5177 }
5178
5179 constexpr void clear() {
5180 set_size(0);
5181 }
5182
5183 constexpr void reset() {
5184 dealloc();
5185 local_[0] = 0;
5186 size_ = 0;
5187 }
5188};
5189
5190template<size_t N = 15>
5191using lstringa = lstring<u8s, N>;
5192template<size_t N = 15>
5193using lstringw = lstring<wchar_t, N>;
5194template<size_t N = 15>
5195using lstringu = lstring<u16s, N>;
5196template<size_t N = 15>
5197using lstringuu = lstring<u32s, N>;
5198
5199template<size_t N = 15>
5200using lstringsa = lstring<u8s, N, true>;
5201template<size_t N = 15>
5202using lstringsw = lstring<wchar_t, N, true>;
5203template<size_t N = 15>
5204using lstringsu = lstring<u16s, N, true>;
5205template<size_t N = 15>
5206using lstringsuu = lstring<u32s, N, true>;
5207
5208
5209template<typename T, typename K = typename const_lit<T>::symb_type>
5210auto getLiteralType(T&&) {
5211 return K{};
5212};
5213
5214template<size_t Arch, size_t L>
5215inline constexpr const size_t _local_count = 0;
5216
5217template<>
5218inline constexpr const size_t _local_count<8, 1> = 23;
5219template<>
5220inline constexpr const size_t _local_count<8, 2> = 15;
5221template<>
5222inline constexpr const size_t _local_count<8, 4> = 7;
5223template<>
5224inline constexpr const size_t _local_count<4, 1> = 15;
5225template<>
5226inline constexpr const size_t _local_count<4, 2> = 11;
5227template<>
5228inline constexpr const size_t _local_count<4, 4> = 5;
5229
5230template<typename T>
5231constexpr const size_t local_count = _local_count<sizeof(size_t), sizeof(T)>;
5232
5285template<typename K, Allocatorable Allocator = allocator_string>
5286class decl_empty_bases sstring :
5287 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
5288 public str_storable<K, sstring<K, Allocator>, Allocator>,
5289 public null_terminated<K, sstring<K, Allocator>>,
5290 public from_utf_convertible<K, sstring<K, Allocator>> {
5291public:
5292 using symb_type = K;
5293 using uns_type = std::make_unsigned_t<K>;
5294 using my_type = sstring<K, Allocator>;
5295 using allocator_t = Allocator;
5296
5297 enum { LocalCount = local_count<K> };
5298
5299protected:
5300 using base_algs = str_algs<K, simple_str<K>, my_type, false>;
5301 using base_storable = str_storable<K, my_type, Allocator>;
5302 using base_utf = from_utf_convertible<K, my_type>;
5303 using traits = ch_traits<K>;
5304 using uni = unicode_traits<K>;
5305 using s_str = base_storable::s_str;
5306
5307 friend base_storable;
5308 friend base_utf;
5309
5310 enum Types { Local, Constant, Shared };
5311
5312 union {
5313 // Когда у нас короткая строка, она лежит в самом объекте, а в localRemain
5314 // пишется, сколько символов ещё можно вписать. Когда строка занимает всё
5315 // возможное место, то localRemain становится 0, type в этом случае тоже 0,
5316 // и в итоге после символов строки получается 0, как и надо!
5317 // When we have a short string, it lies in the object itself, and in localRemain
5318 // writes how many more characters can be entered. When a line takes up everything
5319 // possible location, then localRemain becomes 0, type in this case is also 0,
5320 // and as a result, after the characters of the line we get 0, as it should!
5321 struct {
5322 K buf_[LocalCount]; // Локальный буфер строки | Local line buffer
5323 uns_type localRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
5324 uns_type type_ : 2;
5325 };
5326 struct {
5327 union {
5328 // Указатель на конcтантную строку | Pointer to a constant string
5329 const K* cstr_;
5330 // Указатель на строку, перед которой лежит SharedStringData
5331 // Pointer to the string preceded by SharedStringData
5332 const K* sstr_;
5333 };
5334 size_t bigLen_; // Длина не локальной строки | Non-local string length
5335 uns_type pad_[LocalCount - (sizeof(const K*) + sizeof(size_t)) / sizeof(K)];
5336 uns_type blocalRemain_ : sizeof(uns_type) * CHAR_BIT - 2;
5337 uns_type btype_ : 2;
5338 };
5339 };
5340
5341 constexpr void create_empty() {
5342 type_ = Local;
5343 localRemain_ = LocalCount;
5344 buf_[0] = 0;
5345 }
5346 K* init(size_t s) {
5347 if (s > LocalCount) {
5348 type_ = Shared;
5349 localRemain_ = 0;
5350 bigLen_ = s;
5351 sstr_ = SharedStringData<K>::create(s, base_storable::allocator())->str();
5352 return (K*)sstr_;
5353 } else {
5354 type_ = Local;
5355 localRemain_ = LocalCount - s;
5356 return buf_;
5357 }
5358 }
5359
5360 K* set_size(size_t newSize) {
5361 // Вызывается при создании строки при необходимости изменить размер.
5362 // Других ссылок на shared buffer нет.
5363 // Called when a string is created and needs to be resized.
5364 // There are no other references to the shared buffer.
5365 size_t size = length();
5366 if (newSize != size) {
5367 if (type_ == Constant) {
5368 bigLen_ = newSize;
5369 } else {
5370 if (newSize <= LocalCount) {
5371 if (type_ == Shared) {
5372 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
5373 traits::copy(buf_, sstr_, newSize);
5374 str_buf->decr(base_storable::allocator());
5375 }
5376 type_ = Local;
5377 localRemain_ = LocalCount - newSize;
5378 } else {
5379 if (type_ == Shared) {
5380 if (newSize > size || (newSize > 64 && newSize <= size * 3 / 4)) {
5381 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
5382 traits::copy(newStr, sstr_, newSize);
5383 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
5384 sstr_ = newStr;
5385 }
5386 } else if (type_ == Local) {
5387 K* newStr = SharedStringData<K>::create(newSize, base_storable::allocator())->str();
5388 if (size)
5389 traits::copy(newStr, buf_, size);
5390 sstr_ = newStr;
5391 type_ = Shared;
5392 localRemain_ = 0;
5393 }
5394 bigLen_ = newSize;
5395 }
5396 }
5397 }
5398 K* str = type_ == Local ? buf_ : (K*)sstr_;
5399 str[newSize] = 0;
5400 return str;
5401 }
5402
5403public:
5404 using base_utf::base_utf;
5405
5406 sstring() {
5407 create_empty();
5408 }
5409
5416 template<typename... Args>
5417 requires (std::is_constructible_v<allocator_t, Args...> && sizeof...(Args) > 0)
5418 sstring(Args&&... args) noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
5419 : base_storable(std::forward<Args>(args)...) {
5420 create_empty();
5421 }
5422
5431 template<typename... Args>
5432 requires std::is_constructible_v<allocator_t, Args...>
5433 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...), buf_{0} {
5435 }
5436
5446 template<typename... Args>
5447 requires std::is_constructible_v<allocator_t, Args...>
5448 sstring(size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
5449 base_storable::init_str_repeat(repeat, pattern);
5450 }
5451
5461 template<typename... Args>
5462 requires std::is_constructible_v<allocator_t, Args...>
5463 sstring(size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
5465 }
5466
5480 template<typename... Args>
5481 requires std::is_constructible_v<allocator_t, Args...>
5482 constexpr sstring(const StrExprForType<K> auto& expr, Args&&... args) : base_storable(std::forward<Args>(args)...) {
5484 }
5485
5501 template<StrType<K> From, typename... Args>
5502 requires std::is_constructible_v<allocator_t, Args...>
5503 sstring(const From& f, s_str pattern, s_str repl, size_t offset = 0, size_t maxCount = 0, Args&&... args)
5504 : base_storable(std::forward<Args>(args)...) {
5505 base_storable::init_replaced(f, pattern, repl, offset, maxCount);
5506 }
5507
5508 static const sstring<K> empty_str;
5510 constexpr ~sstring() {
5511 if (btype_ == Shared) {
5512 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
5513 }
5514 }
5515
5521 constexpr sstring(const my_type& other) noexcept : base_storable(other.allocator()) {
5522 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
5523 if (type_ == Shared)
5524 SharedStringData<K>::from_str(sstr_)->incr();
5525 }
5526
5532 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
5533 memcpy(buf_, other.buf_, sizeof(buf_) + sizeof(K));
5534 other.create_empty();
5535 }
5536
5547 template<size_t N>
5548 constexpr sstring(lstring<K, N, true, Allocator>&& src) : base_storable(std::move(src.allocator())) {
5549 size_t size = src.length();
5550 if (size) {
5551 if (src.is_alloced()) {
5552 // Там динамический буфер, выделенный с запасом для SharedStringData.
5553 // There is a dynamic buffer allocated with a reserve for SharedStringData.
5554 K* str = src.str();
5555 if (size > LocalCount) {
5556 // Просто присвоим его себе.
5557 // Let's just assign it to ourselves.
5558 sstr_ = str;
5559 bigLen_ = size;
5560 type_ = Shared;
5561 localRemain_ = 0;
5562 new (SharedStringData<K>::from_str(str)) SharedStringData<K>();
5563 } else {
5564 // Скопируем локально
5565 // Copy locally
5566 type_ = Local;
5567 localRemain_ = LocalCount - size;
5568 traits::copy(buf_, str, size + 1);
5569 // Освободим тот буфер, у локальной строки буфер не разделяется с другими
5570 // Let's free that buffer; a local string's buffer is not shared with others
5571 src.dealloc();
5572 }
5573 } else {
5574 // Копируем из локального буфера
5575 // Copy from local buffer
5576 K* str = init(src.size_);
5577 traits::copy(str, src.symbols(), size + 1);
5578 }
5579 src.create_empty();
5580 } else
5581 create_empty();
5582 }
5583
5594 template<typename T, size_t N = const_lit_for<K, T>::Count, typename... Args>
5595 requires std::is_constructible_v<allocator_t, Args...>
5596 constexpr sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...)
5597 , btype_(Constant)
5598 , blocalRemain_(0)
5599 , cstr_(s)
5600 , bigLen_(N - 1)
5601 , pad_{}
5602 {
5603 }
5604
5605 constexpr void swap(my_type&& other) noexcept {
5606 char buf[sizeof(buf_) + sizeof(K)];
5607 memcpy(buf, buf_, sizeof(buf));
5608 memcpy(buf_, other.buf_, sizeof(buf));
5609 memcpy(other.buf_, buf, sizeof(buf));
5610
5611 std::swap(base_storable::allocator(), other.allocator());
5612 }
5621 constexpr my_type& operator=(my_type other) noexcept {
5622 swap(std::move(other));
5623 return *this;
5624 }
5625
5633 constexpr my_type& operator=(simple_str<K> other) {
5634 return operator=(my_type{other, base_storable::allocator()});
5635 }
5636
5644 template<typename T, size_t N = const_lit_for<K, T>::Count>
5645 constexpr my_type& operator=(T&& other) {
5646 return operator=(my_type{other, base_storable::allocator()});
5647 }
5648
5656 template<size_t N, bool forShared, typename A>
5657 constexpr my_type& operator=(const lstring<K, N, forShared, A>& other) {
5658 return operator=(my_type{other.to_str(), base_storable::allocator()});
5659 }
5660
5668 template<size_t N>
5669 constexpr my_type& operator=(lstring<K, N, true, Allocator>&& other) {
5670 return operator=(my_type{std::move(other)});
5671 }
5672
5682 constexpr my_type& operator=(const StrExprForType<K> auto& expr) {
5683 return operator=(my_type{expr, base_storable::allocator()});
5684 }
5685
5691 constexpr my_type& make_empty() noexcept {
5692 if (type_ == Shared)
5693 SharedStringData<K>::from_str(sstr_)->decr(base_storable::allocator());
5694 create_empty();
5695 return *this;
5696 }
5697
5698 constexpr const K* symbols() const noexcept {
5699 return btype_ == Local ? buf_ : cstr_;
5700 }
5701
5702 constexpr size_t length() const noexcept {
5703 return btype_ == Local ? LocalCount - blocalRemain_ : bigLen_;
5704 }
5705
5706 constexpr bool is_empty() const noexcept {
5707 return length() == 0;
5708 }
5709
5710 constexpr bool empty() const noexcept {
5711 return is_empty();
5712 }
5713
5725 template<typename... T>
5726 static my_type printf(const K* pattern, T&&... args) {
5727 return my_type{lstring<K, 256, true>{}.printf(pattern, std::forward<T>(args)...)};
5728 }
5729
5739 template<typename... T>
5740 static my_type format(const FmtString<K, T...>& fmtString, T&&... args) {
5741 return my_type{lstring<K, 256, true, Allocator>{}.format(fmtString, std::forward<T>(args)...)};
5742 }
5743
5753 template<typename... T>
5754 static my_type vformat(simple_str<K> fmtString, T&&... args) {
5755 return my_type{lstring<K, 256, true, Allocator>{}.vformat(fmtString, std::forward<T>(args)...)};
5756 }
5757};
5758
5759template<typename K, Allocatorable Allocator>
5760inline const sstring<K> sstring<K, Allocator>::empty_str{};
5761
5762template<size_t I>
5763struct digits_selector {
5764 using wider_type = uint16_t;
5765};
5766
5767template<>
5768struct digits_selector<2> {
5769 using wider_type = uint32_t;
5770};
5771
5772template<>
5773struct digits_selector<4> {
5774 using wider_type = uint64_t;
5775};
5776
5777template<typename K, typename T>
5778constexpr size_t fromInt(K* bufEnd, T val) {
5779 const char* twoDigit =
5780 "0001020304050607080910111213141516171819"
5781 "2021222324252627282930313233343536373839"
5782 "4041424344454647484950515253545556575859"
5783 "6061626364656667686970717273747576777879"
5784 "8081828384858687888990919293949596979899";
5785 if (val) {
5786 need_sign<K, std::is_signed_v<T>, T> sign(val);
5787 K* itr = bufEnd;
5788 // Когда у нас минимальное отрицательное число, оно не меняется и остается меньше нуля
5789 // When we have a minimum negative number, it does not change and remains less than zero
5790 if constexpr (std::is_signed_v<T>) {
5791 if (val < 0) {
5792 // Возьмем две последние цифры
5793 // Take the last two digits
5794 const char* ptr = twoDigit - (val % 100) * 2;
5795 *--itr = static_cast<K>(ptr[1]);
5796 *--itr = static_cast<K>(ptr[0]);
5797 val /= 100;
5798 val = -val;
5799 }
5800 }
5801 while (val >= 100) {
5802 const char* ptr = twoDigit + (val % 100) * 2;
5803 *--itr = static_cast<K>(ptr[1]);
5804 *--itr = static_cast<K>(ptr[0]);
5805 val /= 100;
5806 }
5807 if (val < 10) {
5808 *--itr = static_cast<K>('0' + val);
5809 } else {
5810 const char* ptr = twoDigit + val * 2;
5811 *--itr = static_cast<K>(ptr[1]);
5812 *--itr = static_cast<K>(ptr[0]);
5813 }
5814 sign.after(itr);
5815 return size_t(bufEnd - itr);
5816 }
5817 bufEnd[-1] = '0';
5818 return 1;
5819}
5820
5821template<typename K, typename T>
5822struct expr_num {
5823 using symb_type = K;
5824 using my_type = expr_num<K, T>;
5825
5826 enum { bufSize = 24 };
5827 mutable T value;
5828 mutable K buf[bufSize];
5829
5830 expr_num(T t) : value(t) {}
5831 expr_num(expr_num<K, T>&& t) : value(t.value) {}
5832
5833 size_t length() const noexcept {
5834 value = (T)fromInt(buf + bufSize, value);
5835 return (size_t)value;
5836 }
5837 K* place(K* ptr) const noexcept {
5838 ch_traits<K>::copy(ptr, buf + bufSize - (size_t)value, (size_t)value);
5839 return ptr + (size_t)value;
5840 }
5841};
5842
5854template<StrExpr A, FromIntNumber T>
5855constexpr strexprjoin_c<A, expr_num<typename A::symb_type, T>> operator + (const A& a, T s) {
5856 return {a, s};
5857}
5858
5870template<StrExpr A, FromIntNumber T>
5871constexpr strexprjoin_c<A, expr_num<typename A::symb_type, T>, false> operator + (T s, const A& a) {
5872 return {a, s};
5873}
5874
5890template<typename K, typename T>
5891constexpr expr_num<K, T> e_num(T t) {
5892 return {t};
5893}
5894
5895template<typename K>
5896consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
5897 if constexpr (std::is_same_v<K, u8s>)
5898 return s8;
5899 if constexpr (std::is_same_v<K, uws>)
5900 return sw;
5901 if constexpr (std::is_same_v<K, u16s>)
5902 return s16;
5903 if constexpr (std::is_same_v<K, u32s>)
5904 return s32;
5905}
5906
5907#define uni_string(K, p) select_str<K>(p, L##p, u##p, U##p)
5908
5909template<typename K>
5910struct expr_real {
5911 using symb_type = K;
5912 mutable std::conditional_t<is_one_of_std_char_v<K>, K, u8s> buf[40];
5913 mutable size_t l;
5914 double v;
5915 expr_real(double d) : v(d) {}
5916 expr_real(float d) : v(d) {}
5917
5918 size_t length() const noexcept {
5919 if constexpr (is_one_of_std_char_v<K>) {
5920 printf_selector::snprintf(buf, 40, uni_string(K, "%.16g").str, v);
5921 l = (size_t)ch_traits<K>::length(buf);
5922 } else {
5923 l = std::snprintf(buf, sizeof(buf), "%.16g", v);
5924 }
5925 return l;
5926 }
5927 K* place(K* ptr) const noexcept {
5928 if constexpr (is_one_of_std_char_v<K>) {
5929 ch_traits<K>::copy(ptr, buf, l);
5930 } else {
5931 for (size_t i = 0; i < l; i++) {
5932 ptr[i] = buf[i];
5933 }
5934 }
5935 return ptr + l;
5936 }
5937};
5938
5950template<StrExpr A, typename R>
5951 requires(std::is_same_v<R, double> || std::is_same_v<R, float>)
5952inline constexpr auto operator+(const A& a, R s) {
5954}
5955
5967template<StrExpr A, typename R>
5968 requires(is_one_of_std_char_v<typename A::symb_type> && (std::is_same_v<R, double> || std::is_same_v<R, float>))
5969inline constexpr auto operator+(R s, const A& a) {
5971}
5972
5984template<typename K> requires(is_one_of_std_char_v<K>)
5985inline constexpr auto e_real(double t) {
5986 return expr_real<K>{t};
5987}
5988
5989/*
5990* Для создания строковых конкатенаций с векторами и списками, сджойненными константным разделителем
5991* K - тип символов строки
5992* T - тип контейнера строк (vector, list)
5993* I - длина разделителя в символах
5994* tail - добавлять разделитель после последнего элемента контейнера.
5995* Если контейнер пустой, разделитель в любом случае не добавляется
5996* skip_empty - пропускать пустые строки без добавления разделителя
5997* To create string concatenations with vectors and lists joined by a constant delimiter
5998* K is the symbols
5999* T - type of string container (vector, list)
6000* I - length of separator in characters
6001* tail - add a separator after the last element of the container.
6002* If the container is empty, the separator is not added anyway
6003* skip_empty - skip empty lines without adding a separator
6004*/
6005template<typename K, typename T, size_t I, bool tail, bool skip_empty>
6006struct expr_join {
6007 using symb_type = K;
6008 using my_type = expr_join<K, T, I, tail, skip_empty>;
6009
6010 const T& s;
6011 const K* delim;
6012
6013 constexpr size_t length() const noexcept {
6014 size_t l = 0;
6015 for (const auto& t: s) {
6016 size_t len = t.length();
6017 if (len > 0 || !skip_empty) {
6018 if (I > 0 && l > 0) {
6019 l += I;
6020 }
6021 l += len;
6022 }
6023 }
6024 return l + (tail && I > 0 && (l > 0 || (!skip_empty && s.size() > 0))? I : 0);
6025 }
6026 constexpr K* place(K* ptr) const noexcept {
6027 if (s.empty()) {
6028 return ptr;
6029 }
6030 K* write = ptr;
6031 for (const auto& t: s) {
6032 size_t copyLen = t.length();
6033 if (I > 0 && write != ptr && (copyLen || !skip_empty)) {
6034 ch_traits<K>::copy(write, delim, I);
6035 write += I;
6036 }
6037 ch_traits<K>::copy(write, t.symbols(), copyLen);
6038 write += copyLen;
6039 }
6040 if (I > 0 && tail && (write != ptr || (!skip_empty && s.size() > 0))) {
6041 ch_traits<K>::copy(write, delim, I);
6042 write += I;
6043 }
6044 return write;
6045 }
6046};
6047
6061template<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>
6062inline constexpr auto e_join(const T& s, L&& d) {
6063 return expr_join<K, T, I - 1, tail, skip_empty>{s, d};
6064}
6065
6066template<typename K, size_t N, size_t L>
6067struct expr_replaces {
6068 using symb_type = K;
6069 using my_type = expr_replaces<K, N, L>;
6070 simple_str<K> what;
6071 const K* pattern;
6072 const K* repl;
6073 mutable size_t first_, last_;
6074
6075 constexpr expr_replaces(simple_str<K> w, const K* p, const K* r) : what(w), pattern(p), repl(r) {}
6076
6077 constexpr size_t length() const {
6078 size_t l = what.length();
6079 if constexpr (N == L) {
6080 return l;
6081 }
6082 first_ = what.find(pattern, N, 0);
6083 if (first_ != str::npos) {
6084 last_ = first_ + N;
6085 for (;;) {
6086 l += L - N;
6087 size_t next = what.find(pattern, N, last_);
6088 if (next == str::npos) {
6089 break;
6090 }
6091 last_ = next + N;
6092 }
6093 }
6094 return l;
6095 }
6096 constexpr K* place(K* ptr) const noexcept {
6097 if constexpr (N == L) {
6098 const K* from = what.symbols();
6099 for (size_t start = 0; start < what.length();) {
6100 size_t next = what.find(pattern, N, start);
6101 if (next == str::npos) {
6102 next = what.length();
6103 }
6104 size_t delta = next - start;
6105 ch_traits<K>::copy(ptr, from + start, delta);
6106 ptr += delta;
6107 ch_traits<K>::copy(ptr, repl, L);
6108 ptr += L;
6109 start = next + N;
6110 }
6111 return ptr;
6112 }
6113 if (first_ == str::npos) {
6114 return what.place(ptr);
6115 }
6116 const K* from = what.symbols();
6117 for (size_t start = 0, offset = first_; ;) {
6118 ch_traits<K>::copy(ptr, from + start, offset - start);
6119 ptr += offset - start;
6120 ch_traits<K>::copy(ptr, repl, L);
6121 ptr += L;
6122 start = offset + N;
6123 if (start >= last_) {
6124 size_t tail = what.length() - last_;
6125 ch_traits<K>::copy(ptr, from + last_, tail);
6126 ptr += tail;
6127 break;
6128 } else {
6129 offset = what.find(pattern, N, start);
6130 }
6131 }
6132 return ptr;
6133 }
6134};
6135
6149template<typename K, typename T, size_t N = const_lit_for<K, T>::Count, typename X, size_t L = const_lit_for<K, X>::Count>
6150 requires(N > 1)
6151inline constexpr auto e_repl(simple_str<K> w, T&& p, X&& r) {
6152 return expr_replaces<K, N - 1, L - 1>{w, p, r};
6153}
6154
6172template<typename K>
6174 using symb_type = K;
6175 using my_type = expr_replaced<K>;
6176 simple_str<K> what;
6177 const simple_str<K> pattern;
6178 const simple_str<K> repl;
6179 mutable size_t first_, last_;
6190 constexpr expr_replaced(simple_str<K> w, simple_str<K> p, simple_str<K> r) : what(w), pattern(p), repl(r) {}
6191
6192 constexpr size_t length() const {
6193 size_t l = what.length();
6194 if (pattern.length() == repl.length()) {
6195 return l;
6196 }
6197 first_ = what.find(pattern);
6198 if (first_ != str::npos) {
6199 last_ = first_ + pattern.length();
6200 for (;;) {
6201 l += repl.length() - pattern.length();
6202 size_t next = what.find(pattern, last_);
6203 if (next == str::npos) {
6204 break;
6205 }
6206 last_ = next + pattern.length();
6207 }
6208 }
6209 return l;
6210 }
6211 constexpr K* place(K* ptr) const noexcept {
6212 if (repl.length() == pattern.length()) {
6213 const K* from = what.symbols();
6214 for (size_t start = 0; start < what.length();) {
6215 size_t next = what.find(pattern, start);
6216 if (next == str::npos) {
6217 next = what.length();
6218 }
6219 size_t delta = next - start;
6220 ch_traits<K>::copy(ptr, from + start, delta);
6221 ptr += delta;
6222 ch_traits<K>::copy(ptr, repl.symbols(), repl.length());
6223 ptr += repl.length();
6224 start = next + pattern.length();
6225 }
6226 return ptr;
6227 }
6228 if (first_ == str::npos) {
6229 return what.place(ptr);
6230 }
6231 const K* from = what.symbols();
6232 for (size_t start = 0, offset = first_; ;) {
6233 ch_traits<K>::copy(ptr, from + start, offset - start);
6234 ptr += offset - start;
6235 ch_traits<K>::copy(ptr, repl.symbols(), repl.length());
6236 ptr += repl.length();
6237 start = offset + pattern.length();
6238 if (start >= last_) {
6239 size_t tail = what.length() - last_;
6240 ch_traits<K>::copy(ptr, from + last_, tail);
6241 ptr += tail;
6242 break;
6243 } else {
6244 offset = what.find(pattern, start);
6245 }
6246 }
6247 return ptr;
6248 }
6249};
6250
6251template<bool UseVectorForReplace>
6252struct replace_search_result_store {
6253 size_t count_{};
6254 std::pair<size_t, size_t> replaces_[16];
6255};
6256
6257template<>
6258struct replace_search_result_store<true> : std::vector<std::pair<size_t, size_t>> {};
6259
6299template<typename K, bool UseVectorForReplace = false>
6301 using symb_type = K;
6302 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6303
6304 const simple_str<K> source_;
6305 const std::vector<std::pair<K, simple_str<K>>>& replaces_;
6306
6307 lstring<K, 32> pattern_;
6308
6309 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6310
6311 uu8s bit_mask_[sizeof(K) == 1 ? 32 : 64]{};
6339 constexpr expr_replace_symbols(simple_str<K> source, const std::vector<std::pair<K, simple_str<K>>>& repl )
6340 : source_(source), replaces_(repl)
6341 {
6342 size_t pattern_len = replaces_.size();
6343 K* pattern = pattern_.set_size(pattern_len);
6344
6345 for (size_t idx = 0; idx < replaces_.size(); idx++) {
6346 *pattern++ = replaces_[idx].first;
6347 }
6348
6349 if (pattern_len >= BIT_SEARCH_TRESHHOLD) {
6350 for (size_t idx = 0; idx < pattern_len; idx++) {
6351 uu8s s = static_cast<uu8s>(pattern_[idx]);
6352 if constexpr (sizeof(K) == 1) {
6353 bit_mask_[s >> 3] |= (1 << (s & 7));
6354 } else {
6355 if (std::make_unsigned_t<K>(pattern_[idx]) > 255) {
6356 bit_mask_[32 + (s >> 3)] |= (1 << (s & 7));
6357 } else {
6358 bit_mask_[s >> 3] |= (1 << (s & 7));
6359 }
6360 }
6361 }
6362 }
6363 }
6364
6365 size_t length() const {
6366 size_t l = source_.length();
6367 auto [fnd, num] = find_first_of(source_.str, source_.len);
6368 if (fnd == str::npos) {
6369 return l;
6370 }
6371 l += replaces_[num].second.len - 1;
6372 if constexpr (UseVectorForReplace) {
6373 search_results_.reserve((l >> 4) + 8);
6374 search_results_.emplace_back(fnd, num);
6375 for (size_t start = fnd + 1;;) {
6376 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6377 if (fnd == str::npos) {
6378 break;
6379 }
6380 search_results_.emplace_back(fnd, idx);
6381 start = fnd + 1;
6382 l += replaces_[idx].second.len - 1;
6383 }
6384 } else {
6385 const size_t max_store = std::size(search_results_.replaces_);
6386 search_results_.replaces_[0] = {fnd, num};
6387 search_results_.count_++;
6388 for (size_t start = fnd + 1;;) {
6389 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6390 if (found == str::npos) {
6391 break;
6392 }
6393 if (search_results_.count_ < max_store) {
6394 search_results_.replaces_[search_results_.count_] = {found, idx};
6395 }
6396 l += replaces_[idx].second.len - 1;
6397 search_results_.count_++;
6398 start = found + 1;
6399 }
6400 }
6401 return l;
6402 }
6403 K* place(K* ptr) const noexcept {
6404 size_t start = 0;
6405 const K* text = source_.str;
6406 if constexpr (UseVectorForReplace) {
6407 for (const auto& [pos, num] : search_results_) {
6408 size_t delta = pos - start;
6409 ch_traits<K>::copy(ptr, text + start, delta);
6410 ptr += delta;
6411 ptr = replaces_[num].second.place(ptr);
6412 start = pos + 1;
6413 }
6414 } else {
6415 const size_t max_store = std::size(search_results_.replaces_);
6416 size_t founded = search_results_.count_;
6417 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6418 const auto [pos, num] = search_results_.replaces_[idx];
6419 size_t delta = pos - start;
6420 ch_traits<K>::copy(ptr, text + start, delta);
6421 ptr += delta;
6422 ptr = replaces_[num].second.place(ptr);
6423 start = pos + 1;
6424 }
6425 if (founded > max_store) {
6426 founded -= max_store;
6427 while (founded--) {
6428 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6429 size_t delta = fnd - start;
6430 ch_traits<K>::copy(ptr, text + start, delta);
6431 ptr += delta;
6432 ptr = replaces_[idx].second.place(ptr);
6433 start = fnd + 1;
6434 }
6435 }
6436 }
6437 size_t tail = source_.len - start;
6438 ch_traits<K>::copy(ptr, text + start, tail);
6439 return ptr + tail;
6440 }
6441
6442protected:
6443 size_t index_of(K s) const {
6444 return pattern_.find(s);
6445 }
6446
6447 bool is_in_mask(uu8s s) const {
6448 return (bit_mask_[s >> 3] & (1 << (s & 7))) != 0;
6449 }
6450 bool is_in_mask2(uu8s s) const {
6451 return (bit_mask_[32 + (s >> 3)] & (1 << (s & 7))) != 0;
6452 }
6453
6454 bool is_in_pattern(K s, size_t& idx) const {
6455 if constexpr (sizeof(K) == 1) {
6456 if (is_in_mask(s)) {
6457 idx = index_of(s);
6458 return true;
6459 }
6460 } else {
6461 if (std::make_unsigned_t<const K>(s) > 255) {
6462 if (is_in_mask2(s)) {
6463 return (idx = index_of(s)) != -1;
6464 }
6465 } else {
6466 if (is_in_mask(s)) {
6467 idx = index_of(s);
6468 return true;
6469 }
6470 }
6471 }
6472 return false;
6473 }
6474
6475 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6476 size_t pl = pattern_.length();
6477 if (pl >= BIT_SEARCH_TRESHHOLD) {
6478 size_t idx;
6479 while (offset < len) {
6480 if (is_in_pattern(text[offset], idx)) {
6481 return {offset, idx};
6482 }
6483 offset++;
6484 }
6485 } else {
6486 while (offset < len) {
6487 if (size_t idx = index_of(text[offset]); idx != -1) {
6488 return {offset, idx};
6489 }
6490 offset++;
6491 }
6492 }
6493 return {-1, -1};
6494 }
6495};
6496
6497// Строковое выражение для замены символов
6498// String expression to replace characters
6499template<typename K, size_t N, bool UseVectorForReplace>
6500struct expr_replace_const_symbols {
6501 using symb_type = K;
6502 inline static const int BIT_SEARCH_TRESHHOLD = 4;
6503 const K pattern_[N];
6504 const simple_str<K> source_;
6505 const simple_str<K> replaces_[N];
6506
6507 mutable replace_search_result_store<UseVectorForReplace> search_results_;
6508
6509 [[_no_unique_address]]
6510 uu8s bit_mask_[N >= BIT_SEARCH_TRESHHOLD ? (sizeof(K) == 1 ? 32 : 64) : 0]{};
6511
6512 template<typename ... Repl> requires (sizeof...(Repl) == N * 2)
6513 constexpr expr_replace_const_symbols(simple_str<K> source, Repl&& ... repl) : expr_replace_const_symbols(0, source, std::forward<Repl>(repl)...) {}
6514
6515 size_t length() const {
6516 size_t l = source_.length();
6517 auto [fnd, num] = find_first_of(source_.str, source_.len);
6518 if (fnd == str::npos) {
6519 return l;
6520 }
6521 l += replaces_[num].len - 1;
6522 if constexpr (UseVectorForReplace) {
6523 search_results_.reserve((l >> 4) + 8);
6524 search_results_.emplace_back(fnd, num);
6525 for (size_t start = fnd + 1;;) {
6526 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6527 if (fnd == str::npos) {
6528 break;
6529 }
6530 search_results_.emplace_back(fnd, idx);
6531 start = fnd + 1;
6532 l += replaces_[idx].len - 1;
6533 }
6534 } else {
6535 const size_t max_store = std::size(search_results_.replaces_);
6536 search_results_.replaces_[0] = {fnd, num};
6537 search_results_.count_++;
6538 for (size_t start = fnd + 1;;) {
6539 auto [found, idx] = find_first_of(source_.str, source_.len, start);
6540 if (found == str::npos) {
6541 break;
6542 }
6543 if (search_results_.count_ < max_store) {
6544 search_results_.replaces_[search_results_.count_] = {found, idx};
6545 }
6546 l += replaces_[idx].len - 1;
6547 search_results_.count_++;
6548 start = found + 1;
6549 }
6550 }
6551 return l;
6552 }
6553 K* place(K* ptr) const noexcept {
6554 size_t start = 0;
6555 const K* text = source_.str;
6556 if constexpr (UseVectorForReplace) {
6557 for (const auto& [pos, num] : search_results_) {
6558 size_t delta = pos - start;
6559 ch_traits<K>::copy(ptr, text + start, delta);
6560 ptr += delta;
6561 ptr = replaces_[num].place(ptr);
6562 start = pos + 1;
6563 }
6564 } else {
6565 const size_t max_store = std::size(search_results_.replaces_);
6566 size_t founded = search_results_.count_;
6567 for (size_t idx = 0, stop = std::min(founded, max_store); idx < stop; idx++) {
6568 const auto [pos, num] = search_results_.replaces_[idx];
6569 size_t delta = pos - start;
6570 ch_traits<K>::copy(ptr, text + start, delta);
6571 ptr += delta;
6572 ptr = replaces_[num].place(ptr);
6573 start = pos + 1;
6574 }
6575 if (founded > max_store) {
6576 founded -= max_store;
6577 while (founded--) {
6578 auto [fnd, idx] = find_first_of(source_.str, source_.len, start);
6579 size_t delta = fnd - start;
6580 ch_traits<K>::copy(ptr, text + start, delta);
6581 ptr += delta;
6582 ptr = replaces_[idx].place(ptr);
6583 start = fnd + 1;
6584 }
6585 }
6586 }
6587 size_t tail = source_.len - start;
6588 ch_traits<K>::copy(ptr, text + start, tail);
6589 return ptr + tail;
6590 }
6591
6592protected:
6593 template<typename ... Repl>
6594 constexpr expr_replace_const_symbols(int, simple_str<K> source, K s, simple_str<K> r, Repl&&... repl) :
6595 expr_replace_const_symbols(0, source, std::forward<Repl>(repl)..., std::make_pair(s, r)){}
6596
6597 template<typename ... Repl> requires (sizeof...(Repl) == N)
6598 constexpr expr_replace_const_symbols(int, simple_str<K> source, Repl&&... repl) :
6599 source_(source), pattern_ {repl.first...}, replaces_{repl.second...}
6600 {
6601 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6602 for (size_t idx = 0; idx < N; idx++) {
6603 uu8s s = static_cast<uu8s>(pattern_[idx]);
6604 if constexpr (sizeof(K) == 1) {
6605 bit_mask_[s >> 3] |= 1 << (s & 7);
6606 } else {
6607 if (std::make_unsigned_t<const K>(pattern_[idx]) > 255) {
6608 bit_mask_[32 + (s >> 3)] |= 1 << (s & 7);
6609 } else {
6610 bit_mask_[s >> 3] |= 1 << (s & 7);
6611 }
6612 }
6613 }
6614 }
6615 }
6616
6617 template<size_t Idx>
6618 size_t index_of(K s) const {
6619 if constexpr (Idx < N) {
6620 return pattern_[Idx] == s ? Idx : index_of<Idx + 1>(s);
6621 }
6622 return -1;
6623 }
6624 bool is_in_mask(uu8s s) const {
6625 return (bit_mask_[s >> 3] & (1 <<(s & 7))) != 0;
6626 }
6627 bool is_in_mask2(uu8s s) const {
6628 return (bit_mask_[32 + (s >> 3)] & (1 <<(s & 7))) != 0;
6629 }
6630
6631 bool is_in_pattern(K s, size_t& idx) const {
6632 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6633 if constexpr (sizeof(K) == 1) {
6634 if (is_in_mask(s)) {
6635 idx = index_of<0>(s);
6636 return true;
6637 }
6638 } else {
6639 if (std::make_unsigned_t<const K>(s) > 255) {
6640 if (is_in_mask2(s)) {
6641 return (idx = index_of<0>(s)) != -1;
6642 }
6643 } else {
6644 if (is_in_mask(s)) {
6645 idx = index_of<0>(s);
6646 return true;
6647 }
6648 }
6649 }
6650 }
6651 return false;
6652 }
6653 std::pair<size_t, size_t> find_first_of(const K* text, size_t len, size_t offset = 0) const {
6654 if constexpr (N >= BIT_SEARCH_TRESHHOLD) {
6655 size_t idx;
6656 while (offset < len) {
6657 if (is_in_pattern(text[offset], idx)) {
6658 return {offset, idx};
6659 }
6660 offset++;
6661 }
6662 } else {
6663 while (offset < len) {
6664 if (size_t idx = index_of<0>(text[offset]); idx != -1) {
6665 return {offset, idx};
6666 }
6667 offset++;
6668 }
6669 }
6670 return {-1, -1};
6671 }
6672};
6673
6713template<bool UseVector = false, typename K, typename ... Repl>
6714requires (sizeof...(Repl) % 2 == 0)
6715auto e_repl_const_symbols(simple_str<K> src, Repl&& ... other) {
6716 return expr_replace_const_symbols<K, sizeof...(Repl) / 2, UseVector>(src, std::forward<Repl>(other)...);
6717}
6718
6719template<typename K, typename H>
6720struct StoreType {
6721 simple_str<K> str;
6722 size_t hash;
6723 char node[sizeof(sstring<K>)];
6724
6725 const simple_str_nt<K>& to_nt() const noexcept {
6726 return static_cast<const simple_str_nt<K>&>(str);
6727 }
6728 const sstring<K>& to_str() const noexcept {
6729 return *reinterpret_cast<const sstring<K>*>(node);
6730 }
6731};
6732
6733template<bool Wide>
6734struct fnv_const {
6735 static inline constexpr size_t basis = static_cast<size_t>(14695981039346656037ULL);
6736 static inline constexpr size_t prime = static_cast<size_t>(1099511628211ULL);
6737};
6738
6739template<>
6740struct fnv_const<false> {
6741 static inline constexpr size_t basis = static_cast<size_t>(2166136261U);
6742 static inline constexpr size_t prime = static_cast<size_t>(16777619U);
6743};
6744
6745using fnv = fnv_const<sizeof(size_t) == 8>;
6746
6747template<typename K>
6748inline constexpr size_t fnv_hash(const K* ptr, size_t l) {
6749 size_t h = fnv::basis;
6750 for (size_t i = 0; i < l; i++) {
6751 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
6752 }
6753 return h;
6754};
6755
6756template<typename K>
6757inline constexpr size_t fnv_hash_ia(const K* ptr, size_t l) {
6758 size_t h = fnv::basis;
6759 for (size_t i = 0; i < l; i++) {
6760 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
6761 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
6762 }
6763 return h;
6764};
6765
6766template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
6767inline constexpr size_t fnv_hash(T&& value) {
6768 size_t h = fnv::basis;
6769 for (size_t i = 0; i < N - 1; i++) {
6770 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
6771 }
6772 return h;
6773};
6774
6775template<typename T, typename K = typename const_lit<T>::symb_type, size_t N = const_lit<T>::Count>
6776inline constexpr size_t fnv_hash_ia(T&& value) {
6777 size_t h = fnv::basis;
6778 for (size_t i = 0; i < N - 1; i++) {
6779 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
6780 h = (h ^ (s >= 'A' && s <= 'Z' ? s | 0x20 : s)) * fnv::prime;
6781 }
6782 return h;
6783};
6784
6785template<typename K>
6786inline consteval size_t fnv_hash_compile(const K* ptr, size_t l) {
6787 return fnv_hash(ptr, l);
6788};
6789
6790template<typename K>
6791inline consteval size_t fnv_hash_ia_compile(const K* ptr, size_t l) {
6792 return fnv_hash_ia(ptr, l);
6793};
6794
6795static_assert(std::is_trivially_copyable_v<StoreType<u8s, int>>, "Store type must be trivially copyable");
6796
6797template<typename K>
6798struct streql;
6799template<typename K>
6800struct strhash;
6801
6896template<typename K, typename T, typename H = strhash<K>, typename E = streql<K>>
6897class hashStrMap : public std::unordered_map<StoreType<K, H>, T, H, E> {
6898protected:
6899 using InStore = StoreType<K, H>;
6900
6901public:
6902 using my_type = hashStrMap<K, T, H, E>;
6903 using hash_t = std::unordered_map<InStore, T, H, E>;
6904 using hasher = H;
6905
6906 hashStrMap() = default;
6907 hashStrMap(const my_type& other) : hash_t(other) {
6908 for (const auto& [k, v] : *this) {
6909 InStore& stored = const_cast<InStore&>(k);
6910 sstring<K> tmp = *(sstring<K>*)stored.node;
6911 new (stored.node) sstring<K>(std::move(tmp));
6912 stored.str.str = stored.to_str().symbols();
6913 }
6914 }
6915 ~hashStrMap() {
6916 for (auto& k: *this)
6917 ((sstring<K>*)k.first.node)->~sstring();
6918 }
6919
6920 hashStrMap(my_type&& o) = default;
6921
6922 my_type& operator=(const my_type& other) {
6923 hash_t::operator=(other);
6924 for (const auto& [k, v] : *this) {
6925 InStore& stored = const_cast<InStore&>(k);
6926 sstring<K> tmp = *(sstring<K>*)stored.node;
6927 new (stored.node) sstring<K>(std::move(tmp));
6928 stored.str.str = stored.to_str().symbols();
6929 }
6930 return *this;
6931 };
6932 my_type& operator=(my_type&&) = default;
6933
6934 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
6935 for (const auto& e: init)
6936 emplace(e.first, e.second);
6937 }
6938
6939 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
6940
6941 hashStrMap(init_str&& init) {
6942 for (const auto& e: init)
6943 emplace(e.first, e.second);
6944 }
6945
6946 // При входе хэш должен быть уже посчитан
6947 // When entering, the hash must already be calculated
6948 template<typename... ValArgs>
6949 auto try_emplace(const InStore& key, ValArgs&&... args) {
6950 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
6951 if (it.second) {
6952 InStore& stored = const_cast<InStore&>(it.first->first);
6953 new (stored.node) sstring<K>(key.str);
6954 stored.str.str = stored.to_str().symbols();
6955 }
6956 return it;
6957 }
6958
6959 static InStore toStoreType(simple_str<K> key) {
6960 return {key, H{}(key)};
6961 }
6962
6963 template<typename Key, typename... ValArgs>
6964 requires(std::is_convertible_v<Key, simple_str<K>>)
6965 auto try_emplace(Key&& key, ValArgs&&... args) {
6966 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
6967 if (it.second) {
6968 InStore& stored = const_cast<InStore&>(it.first->first);
6969 new (stored.node) sstring<K>(std::forward<Key>(key));
6970 stored.str.str = stored.to_str().symbols();
6971 }
6972 return it;
6973 }
6974
6975 template<typename... ValArgs>
6976 auto emplace(const InStore& key, ValArgs&&... args) {
6977 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
6978 if (!it.second) {
6979 it.first->second = T(std::forward<ValArgs>(args)...);
6980 }
6981 return it;
6982 }
6983
6984 template<typename Key, typename... ValArgs>
6985 requires(std::is_convertible_v<Key, simple_str<K>>)
6986 auto emplace(Key&& key, ValArgs&&... args) {
6987 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
6988 if (!it.second) {
6989 it.first->second = T(std::forward<ValArgs>(args)...);
6990 }
6991 return it;
6992 }
6993
6994 auto& operator[](const InStore& key) {
6995 return try_emplace(key).first->second;
6996 }
6997
6998 template<typename Key>
6999 requires(std::is_convertible_v<Key, simple_str<K>>)
7000 auto& operator[](Key&& key) {
7001 return try_emplace(std::forward<Key>(key)).first->second;
7002 }
7003
7004 decltype(auto) at(const InStore& key) {
7005 return hash_t::at(key);
7006 }
7007 decltype(auto) at(const InStore& key) const {
7008 return hash_t::at(key);
7009 }
7010
7011 decltype(auto) at(simple_str<K> key) {
7012 return hash_t::at(toStoreType(key));
7013 }
7014 decltype(auto) at(simple_str<K> key) const {
7015 return hash_t::at(toStoreType(key));
7016 }
7017
7018 auto find(const InStore& key) const {
7019 return hash_t::find(key);
7020 }
7021
7022 auto find(simple_str<K> key) const {
7023 return find(toStoreType(key));
7024 }
7025
7026 auto find(const InStore& key) {
7027 return hash_t::find(key);
7028 }
7029
7030 auto find(simple_str<K> key) {
7031 return find(toStoreType(key));
7032 }
7033
7034 auto erase(typename hash_t::const_iterator it) {
7035 if (it != hash_t::end()) {
7036 ((sstring<K>*)it->first.node)->~sstring();
7037 }
7038 return hash_t::erase(it);
7039 }
7040
7041 auto erase(const InStore& key) {
7042 auto it = hash_t::find(key);
7043 if (it != hash_t::end()) {
7044 ((sstring<K>*)it->first.node)->~sstring();
7045 hash_t::erase(it);
7046 return 1;
7047 }
7048 return 0;
7049 }
7050
7051 auto erase(simple_str<K> key) {
7052 return erase(toStoreType(key));
7053 }
7054
7055 bool lookup(simple_str<K> txt, T& val) const {
7056 auto it = find(txt);
7057 if (it != hash_t::end()) {
7058 val = it->second;
7059 return true;
7060 }
7061 return false;
7062 }
7063
7064 void clear() {
7065 for (auto& k: *this)
7066 ((sstring<K>*)k.first.node)->~sstring();
7067 hash_t::clear();
7068 }
7069 bool contains(const InStore& key) const {
7070 return hash_t::find(key) != this->end();
7071 }
7072
7073 bool contains(simple_str<K> key) const {
7074 return find(toStoreType(key)) != this->end();
7075 }
7076};
7077
7078template<typename K>
7079struct streql {
7080 template<typename H>
7081 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
7082 return _Left.hash == _Right.hash && _Left.str == _Right.str;
7083 }
7084};
7085
7086template<typename K>
7087struct strhash { // hash functor for basic_string
7088 size_t operator()(simple_str<K> _Keyval) const {
7089 return fnv_hash(_Keyval.symbols(), _Keyval.length());
7090 }
7091 size_t operator()(const StoreType<K, strhash<K>>& _Keyval) const {
7092 return _Keyval.hash;
7093 }
7094};
7095
7096template<typename K>
7097struct streqlia {
7098 template<typename H>
7099 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
7100 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
7101 }
7102};
7103
7104template<typename K>
7105struct strhashia {
7106 size_t operator()(simple_str<K> _Keyval) const {
7107 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
7108 }
7109 size_t operator()(const StoreType<K, strhashia<K>>& _Keyval) const {
7110 return _Keyval.hash;
7111 }
7112};
7113
7114template<typename K>
7115struct streqliu {
7116 template<typename H>
7117 bool operator()(const StoreType<K, H>& _Left, const StoreType<K, H>& _Right) const {
7118 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
7119 }
7120};
7121
7122template<typename K>
7123struct strhashiu {
7124 size_t operator()(simple_str<K> _Keyval) const {
7125 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
7126 }
7127 size_t operator()(const StoreType<K, strhashiu<K>>& _Keyval) const {
7128 return _Keyval.hash;
7129 }
7130};
7131
7146template<typename K>
7147class chunked_string_builder {
7148 using chunk_t = std::pair<std::unique_ptr<K[]>, size_t>;
7149 std::vector<chunk_t> chunks; // блоки и длина данных в них | blocks and data length in them
7150 K* write{}; // Текущая позиция записи | Current write position
7151 size_t len{}; // Общая длина | Total length
7152 size_t remain{}; // Сколько осталось места в текущем блоке | How much space is left in the current block
7153 size_t align{1024};
7154
7155public:
7156 using my_type = chunked_string_builder<K>;
7157 using symb_type = K;
7158 chunked_string_builder() = default;
7159 chunked_string_builder(size_t a) : align(a){};
7160 chunked_string_builder(const my_type&) = delete;
7161 chunked_string_builder(my_type&& other) noexcept
7162 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
7163 other.len = other.remain = 0;
7164 other.write = nullptr;
7165 }
7166 my_type& operator=(my_type other) noexcept {
7167 chunks.swap(other.chunks);
7168 write = other.write;
7169 len = other.len;
7170 remain = other.remain;
7171 align = other.align;
7172 other.len = other.remain = 0;
7173 other.write = nullptr;
7174 return *this;
7175 }
7176
7179 if (data.len) {
7180 len += data.len;
7181 if (data.len <= remain) {
7182 // Добавляемые данные влезают в выделенный блок, просто скопируем их
7183 // The added data fits into the selected block, just copy it
7184 ch_traits<K>::copy(write, data.str, data.len);
7185 write += data.len; // Сдвинем позицию записи | Let's move the recording position
7186 chunks.back().second += data.len; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
7187 remain -= data.len; // Уменьшим остаток места в блоке | Reduce the remaining space in the block
7188 } else {
7189 // Не влезают | They don't fit
7190 if (remain) {
7191 // Сначала запишем сколько влезет
7192 // First, write down as much as we can
7193 ch_traits<K>::copy(write, data.str, remain);
7194 data.len -= remain;
7195 data.str += remain;
7196 chunks.back().second += remain; // Увеличим длину хранимых в блоке данных | Let's increase the length of the data stored in the block
7197 }
7198 // Выделим новый блок и впишем в него данные
7199 // Рассчитаем размер блока, кратного заданному выравниванию
7200 // Select a new block and write data into it
7201 // Calculate the block size that is a multiple of the given alignment
7202 size_t blockSize = (data.len + align - 1) / align * align;
7203 chunks.emplace_back(std::make_unique<K[]>(blockSize), data.len);
7204 write = chunks.back().first.get();
7205 ch_traits<K>::copy(write, data.str, data.len);
7206 write += data.len;
7207 remain = blockSize - data.len;
7208 }
7209 }
7210 return *this;
7211 }
7212
7213 my_type& operator<<(const StrExprForType<K> auto& expr) {
7214 size_t l = expr.length();
7215 if (l) {
7216 if (l < remain) {
7217 write = expr.place(write);
7218 chunks.back().second += l;
7219 len += l;
7220 remain -= l;
7221 } else if (!remain) {
7222 size_t blockSize = (l + align - 1) / align * align; // Рассчитаем размер блока, кратного заданному выравниванию
7223 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
7224 write = expr.place(chunks.back().first.get());
7225 len += l;
7226 remain = blockSize - l;
7227 } else {
7228 auto store = std::make_unique<K[]>(l);
7229 expr.place(store.get());
7230 return operator<<({store.get(), l});
7231 }
7232 }
7233 return *this;
7234 }
7235
7236 template<typename T>
7237 my_type& operator<<(T data)
7238 requires std::is_same_v<T, K>
7239 {
7240 return operator<<(expr_char<K>(data));
7241 }
7242
7243 constexpr size_t length() const noexcept {
7244 return len;
7245 }
7246
7247 void reset() {
7248 if (chunks.empty()) {
7249 return;
7250 }
7251 if (chunks.size() > 1) {
7252 remain = 0;
7253 chunks.resize(1);
7254 }
7255 remain += chunks[0].second;
7256 chunks[0].second = 0;
7257 len = 0;
7258 write = chunks[0].first.get();
7259 }
7260
7261 constexpr K* place(K* p) const noexcept {
7262 for (const auto& block: chunks) {
7263 ch_traits<K>::copy(p, block.first.get(), block.second);
7264 p += block.second;
7265 }
7266 return p;
7267 }
7276 template<typename Op>
7277 void out(const Op& o) const {
7278 for (const auto& block: chunks)
7279 o(block.first.get(), block.second);
7280 }
7281
7285 bool is_continuous() const {
7286 if (chunks.size()) {
7287 const K* ptr = chunks.front().first.get();
7288 for (const auto& chunk: chunks) {
7289 if (chunk.first.get() != ptr)
7290 return false;
7291 ptr += chunk.second;
7292 }
7293 }
7294 return true;
7295 }
7296
7302 const K* begin() const {
7303 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
7304 }
7305
7309 void clear() {
7310 chunks.clear();
7311 write = nullptr;
7312 len = 0;
7313 remain = 0;
7314 }
7315
7320 typename decltype(chunks)::const_iterator it, end;
7321 size_t writedFromCurrentChunk;
7326 bool is_end() {
7327 return it == end;
7328 }
7329
7339 size_t store(K* buffer, size_t size) {
7340 size_t writed = 0;
7341 while (size && !is_end()) {
7342 size_t remain = it->second - writedFromCurrentChunk;
7343 size_t write = std::min(size, remain);
7344 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
7345 writed += write;
7346 remain -= write;
7347 size -= write;
7348 if (!remain) {
7349 ++it;
7350 writedFromCurrentChunk = 0;
7351 } else
7352 writedFromCurrentChunk += write;
7353 }
7354 return writed;
7355 }
7356 };
7357
7364 return {chunks.begin(), chunks.end(), 0};
7365 }
7366
7372 const auto& data() const {
7373 return chunks;
7374 }
7375};
7376
7377using stringa = sstring<u8s>;
7378using stringw = sstring<wchar_t>;
7379using stringu = sstring<u16s>;
7380using stringuu = sstring<u32s>;
7381static_assert(sizeof(stringa) == (sizeof(void*) == 8 ? 24 : 16), "Bad size of sstring");
7382
7387template<typename T>
7393template<typename T>
7399template<typename T>
7401
7406template<typename T>
7408
7413template<typename T>
7415
7420template<typename T>
7422
7427template<typename T>
7429template<typename T>
7439template<typename T>
7441
7446template<typename T>
7452template<typename T>
7458template<typename T>
7460
7461inline constexpr simple_str_nt<u8s> utf8_bom{"\xEF\xBB\xBF", 3}; // NOLINT
7462
7463inline namespace literals {
7464
7465#ifdef _MSC_VER
7466/* MSVC иногда не может сделать "text"_ss consteval, выдает ошибку C7595.
7467Находил подобное https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
7468Пишут, что баг исправлен, но видимо не до конца.
7469Без этого в тестах в двух местах не понимает "text"_ss, хотя в других местах - нормально работает*/
7470/* MSVC sometimes fails to do "text"_ss consteval and gives error C7595.
7471Found something like this https://developercommunity.visualstudio.com/t/User-defined-literals-not-constant-expre/10108165
7472They write that the bug has been fixed, but apparently not completely.
7473Without this, in tests in two places it does not understand “text”_ss, although in other places it works fine */
7474#define SS_CONSTEVAL constexpr
7475#else
7476#define SS_CONSTEVAL consteval
7477#endif
7478
7489SS_CONSTEVAL simple_str_nt<u8s> operator""_ss(const u8s* ptr, size_t l) {
7490 return simple_str_nt<u8s>{ptr, l};
7491}
7492
7502SS_CONSTEVAL simple_str_nt<uws> operator""_ss(const uws* ptr, size_t l) {
7503 return simple_str_nt<uws>{ptr, l};
7504}
7505
7515SS_CONSTEVAL simple_str_nt<u16s> operator""_ss(const u16s* ptr, size_t l) {
7516 return simple_str_nt<u16s>{ptr, l};
7517}
7518
7529SS_CONSTEVAL simple_str_nt<u32s> operator""_ss(const u32s* ptr, size_t l) {
7530 return simple_str_nt<u32s>{ptr, l};
7531}
7532
7533template<typename K> using HashKey = StoreType<K, strhash<K>>;
7534template<typename K> using HashKeyIA = StoreType<K, strhashia<K>>;
7535template<typename K> using HashKeyIU = StoreType<K, strhashiu<K>>;
7536
7547consteval HashKey<u8s> operator""_h(const u8s* ptr, size_t l) {
7548 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
7549}
7550
7561consteval HashKeyIA<u8s> operator""_ia(const u8s* ptr, size_t l) {
7562 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7563}
7564
7575inline HashKeyIU<u8s> operator""_iu(const u8s* ptr, size_t l) {
7576 return HashKeyIU<u8s>{{ptr, l}, strhashiu<u8s>{}(simple_str<u8s>{ptr, l})};
7577}
7578
7589consteval HashKey<u16s> operator""_h(const u16s* ptr, size_t l) {
7590 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
7591}
7592
7603consteval HashKeyIA<u16s> operator""_ia(const u16s* ptr, size_t l) {
7604 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7605}
7606
7617inline HashKeyIU<u16s> operator""_iu(const u16s* ptr, size_t l) {
7618 return HashKeyIU<u16s>{{ptr, l}, strhashiu<u16s>{}(simple_str<u16s>{ptr, l})};
7619}
7620
7631consteval HashKey<u32s> operator""_h(const u32s* ptr, size_t l) {
7632 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
7633}
7634
7645consteval HashKeyIA<u32s> operator""_ia(const u32s* ptr, size_t l) {
7646 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7647}
7648
7659inline HashKeyIU<u32s> operator""_iu(const u32s* ptr, size_t l) {
7660 return HashKeyIU<u32s>{{ptr, l}, strhashiu<u32s>{}(simple_str<u32s>{ptr, l})};
7661}
7662
7673consteval HashKey<uws> operator""_h(const uws* ptr, size_t l) {
7674 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
7675}
7676
7687consteval HashKeyIA<uws> operator""_ia(const uws* ptr, size_t l) {
7688 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
7689}
7690
7701inline HashKeyIU<uws> operator""_iu(const uws* ptr, size_t l) {
7702 return HashKeyIU<uws>{{ptr, l}, strhashiu<uws>{}(simple_str<uws>{ptr, l})};
7703}
7704} // namespace literals
7705
7716inline std::ostream& operator<<(std::ostream& stream, ssa text) {
7717 return stream << std::string_view{text.symbols(), text.length()};
7718}
7719
7730inline std::wostream& operator<<(std::wostream& stream, ssw text) {
7731 return stream << std::wstring_view{text.symbols(), text.length()};
7732}
7733
7744inline std::wostream& operator<<(std::wostream& stream, simple_str<wchar_type> text) {
7745 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
7746}
7747
7758inline std::ostream& operator<<(std::ostream& stream, const stringa& text) {
7759 return stream << std::string_view{text.symbols(), text.length()};
7760}
7761
7772inline std::wostream& operator<<(std::wostream& stream, const stringw& text) {
7773 return stream << std::wstring_view{text.symbols(), text.length()};
7774}
7775
7786inline std::wostream& operator<<(std::wostream& stream, const sstring<wchar_type>& text) {
7787 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
7788}
7789
7800template<size_t N, bool S, simstr::Allocatorable A>
7801inline std::ostream& operator<<(std::ostream& stream, const lstring<u8s, N, S, A>& text) {
7802 return stream << std::string_view{text.symbols(), text.length()};
7803}
7804
7815template<size_t N, bool S, simstr::Allocatorable A>
7816inline std::wostream& operator<<(std::wostream& stream, const lstring<uws, N, S, A>& text) {
7817 return stream << std::wstring_view{text.symbols(), text.length()};
7818}
7819
7830template<size_t N, bool S, simstr::Allocatorable A>
7831inline std::wostream& operator<<(std::wostream& stream, const lstring<wchar_type, N, S, A>& text) {
7832 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
7833}
7834
7835} // namespace simstr
7836
7841template<typename K>
7842struct std::formatter<simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
7843 // Define format() by calling the base class implementation with the wrapped value
7844 template<typename FormatContext>
7845 auto format(simstr::simple_str<K> t, FormatContext& fc) const {
7846 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
7847 }
7848};
7849
7854template<typename K>
7855struct std::formatter<simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
7856 // Define format() by calling the base class implementation with the wrapped value
7857 template<typename FormatContext>
7858 auto format(simstr::simple_str_nt<K> t, FormatContext& fc) const {
7859 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
7860 }
7861};
7862
7867template<typename K>
7868struct std::formatter<simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
7869 // Define format() by calling the base class implementation with the wrapped value
7870 template<typename FormatContext>
7871 auto format(const simstr::sstring<K>& t, FormatContext& fc) const {
7872 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
7873 }
7874};
7875
7880template<typename K, size_t N, bool S, typename A>
7881struct std::formatter<simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
7882 // Define format() by calling the base class implementation with the wrapped value
7883 template<typename FormatContext>
7884 auto format(const simstr::lstring<K, N, S, A>& t, FormatContext& fc) const {
7885 return std::formatter<std::basic_string_view<K>, K>::format({t.symbols(), t.length()}, fc);
7886 }
7887};
Class for sequentially obtaining substrings by a given delimiter.
Definition sstring.h:2448
constexpr bool is_done() const
Find out if substrings are running out.
Definition sstring.h:2458
constexpr simple_str< K > next()
Get the next substring.
Definition sstring.h:2467
my_type & operator<<(simple_str< K > data)
Adding a piece of data.
Definition sstring.h:7178
portion_store get_portion() const
Get a portion_store through which data can be sequentially retrieved into an external buffer.
Definition sstring.h:7363
constexpr size_t length() const noexcept
Length of the saved text.
Definition sstring.h:7243
my_type & operator<<(T data)
Adding a symbol.
Definition sstring.h:7237
void reset()
Resets the contents, but does not delete the first buffer in order to avoid allocation later.
Definition sstring.h:7247
void clear()
Clear the object, freeing all allocated buffers.
Definition sstring.h:7309
my_type & operator<<(const StrExprForType< K > auto &expr)
Adding a string expression.
Definition sstring.h:7213
bool is_continuous() const
Checks whether all text is located in one contiguous chunk in memory.
Definition sstring.h:7285
void out(const Op &o) const
Applies a functor to each stored buffer.
Definition sstring.h:7277
const auto & data() const
Get internal data buffers.
Definition sstring.h:7372
const K * begin() const
Get a pointer to the beginning of the first buffer. It makes sense to apply only if is_continuous tru...
Definition sstring.h:7302
Container for more efficient searching by string keys.
Definition sstring.h:6897
The mutable, owning string class. Contains an internal buffer for text of a given size.
Definition sstring.h:4637
constexpr void define_size()
Determine the length of the string. Searches for the character 0 in the string buffer to its capacity...
Definition sstring.h:5148
constexpr lstring(T &&value, Args &&... args)
String literal constructor.
Definition sstring.h:4888
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:5019
constexpr void reset()
Makes the string empty and frees the external buffer, if there was one.
Definition sstring.h:5183
@ LocalCapacity
Definition sstring.h:4645
constexpr bool is_local() const noexcept
Find out whether a local or external buffer is used for characters.
Definition sstring.h:5139
my_type & operator=(my_type &&other) noexcept
Assignment operator by moving from a string of the same type.
Definition sstring.h:4962
constexpr size_t length() const noexcept
String length.
Definition sstring.h:5042
constexpr lstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:4769
constexpr lstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:4839
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:4784
constexpr lstring(const my_type &other)
Copy from another string of the same type.
Definition sstring.h:4858
constexpr lstring(const my_type &other, Args &&... args)
Copy from another string of the same type, but with a different allocator.
Definition sstring.h:4873
my_type & operator=(const StrExprForType< K > auto &expr)
String expression appending operator.
Definition sstring.h:5032
constexpr K * reserve_no_preserve(size_t newSize)
Definition sstring.h:5077
my_type & operator=(const my_type &other)
Copy assignment operator from a string of the same type.
Definition sstring.h:4945
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:5054
lstring(const Op &op, Args &&... args)
A fill constructor using a functor (see str_mutable::fill).
Definition sstring.h:4927
constexpr K * set_size(size_t newSize)
Sets the size of the current string, allocating space if necessary.
Definition sstring.h:5122
constexpr lstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:4799
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:4818
constexpr const K * symbols() const noexcept
Pointer to constant characters.
Definition sstring.h:5046
constexpr void clear()
Makes a string empty without changing the string buffer.
Definition sstring.h:5179
my_type & operator=(simple_str< K > other)
Assignment operator from simple_str.
Definition sstring.h:5007
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:5058
constexpr void shrink_to_fit()
Reduces the size of the external buffer to the smallest possible size to hold the string....
Definition sstring.h:5165
constexpr lstring(my_type &&other) noexcept
Constructor for moving from a string of the same type.
Definition sstring.h:4902
constexpr K * reserve(size_t newSize)
Allocate a buffer large enough to hold newSize characters plus a terminating null.
Definition sstring.h:5099
constexpr K * str() noexcept
Pointer to a string buffer.
Definition sstring.h:5050
constexpr size_t capacity() const noexcept
Current row buffer capacity.
Definition sstring.h:5062
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Create an empty object.
Definition sstring.h:4754
Immutable owning string class.
Definition sstring.h:5290
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Assignment operator to another string of type lstring.
Definition sstring.h:5657
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:5710
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:5706
sstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:5433
sstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:5463
constexpr const K * symbols() const noexcept
Pointer to characters in the string.
Definition sstring.h:5698
constexpr sstring(const my_type &other) noexcept
String copy constructor.
Definition sstring.h:5521
constexpr my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:5645
sstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:5448
static my_type format(const FmtString< K, T... > &fmtString, T &&... args)
Get a string formatted with std::format.
Definition sstring.h:5740
constexpr my_type & operator=(my_type other) noexcept
Assignment operator to another string of the same type.
Definition sstring.h:5621
static my_type printf(const K *pattern, T &&... args)
Get a string formatted with std::sprintf.
Definition sstring.h:5726
constexpr sstring(T &&s, Args &&... args)
Initialize from a string literal.
Definition sstring.h:5596
constexpr sstring(lstring< K, N, true, Allocator > &&src)
A move constructor from lstring with an sstring-compatible external buffer.
Definition sstring.h:5548
constexpr size_t length() const noexcept
Line length.
Definition sstring.h:5702
constexpr sstring(my_type &&other) noexcept
Move constructor.
Definition sstring.h:5532
static my_type vformat(simple_str< K > fmtString, T &&... args)
Get a string formatted with std::vformat.
Definition sstring.h:5754
constexpr my_type & operator=(simple_str< K > other)
Assignment operator to another string of a different type.
Definition sstring.h:5633
constexpr my_type & make_empty() noexcept
Make the string empty.
Definition sstring.h:5691
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
String expression assignment operator.
Definition sstring.h:5682
constexpr my_type & operator=(lstring< K, N, true, Allocator > &&other)
Assignment operator to a movable string of type lstring with a compatible buffer.
Definition sstring.h:5669
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:5482
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Constructor for the empty string.
Definition sstring.h:5418
constexpr ~sstring()
String destructor.
Definition sstring.h:5510
sstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:5503
A class with basic constant string algorithms.
Definition sstring.h:644
constexpr size_t size() const
The size of the string in characters.
Definition sstring.h:710
constexpr bool operator!() const noexcept
Check for emptiness.
Definition sstring.h:823
constexpr bool starts_with(str_piece prefix) const noexcept
Whether the string begins with the given substring.
Definition sstring.h:1719
R lowered_only_ascii() const
Get a copy of the string in lowercase ASCII characters.
Definition sstring.h:1887
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition sstring.h:729
R trimmed_left(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left.
Definition sstring.h:2033
constexpr std::basic_string_view< K > to_sv() const noexcept
Convert to std::string_view.
Definition sstring.h:739
bool starts_with_iu(str_piece prefix) const noexcept
Whether the string starts with the given substring, case-insensitive Unicode characters of the first ...
Definition sstring.h:1758
R replaced(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0) const
Get a copy of the string with occurrences of substrings replaced.
Definition sstring.h:1931
constexpr bool equal_ia(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of ASCII characters.
Definition sstring.h:981
constexpr str_piece mid(size_t from, size_t len=-1) const noexcept
Get part of a string as "string chunk".
Definition sstring.h:794
R upperred_only_ascii() const
Get a copy of the string in uppercase ASCII characters.
Definition sstring.h:1875
constexpr size_t find_or_throw(str_piece pattern, size_t offset=0, Args &&... args) const noexcept
Find the beginning of the first occurrence of a substring in this string or throw an exception.
Definition sstring.h:1083
constexpr T split(str_piece delimeter, size_t offset=0) const
Split a string into substrings using a given delimiter.
Definition sstring.h:1693
constexpr convert_result< T > to_int() const noexcept
Convert a string to a number of the given type.
Definition sstring.h:1498
constexpr size_t find_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string or the end of the string.
Definition sstring.h:1190
R trimmed_right_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition sstring.h:2207
constexpr T as_int() const noexcept
Convert a string to a number of the given type.
Definition sstring.h:1465
constexpr size_t find_end_of_last(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string.
Definition sstring.h:1176
constexpr bool operator==(T &&other) const noexcept
Operator for comparing a string and a string literal for equality.
Definition sstring.h:924
constexpr bool prefix_in(str_piece text) const noexcept
Whether this string is the beginning of another string.
Definition sstring.h:1776
constexpr size_t find(K s, size_t offset=0) const noexcept
Find a character in this string.
Definition sstring.h:1231
int compare_iu(str_piece text) const noexcept
Compare strings character by character without taking into account the case of Unicode characters of ...
Definition sstring.h:1009
constexpr bool is_ascii() const noexcept
Whether the string contains only ASCII characters.
Definition sstring.h:1839
bool less_iu(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:1032
R trimmed(str_piece pattern) const
Get a string with characters specified by another string removed, left and right.
Definition sstring.h:2125
R trimmed_left(str_piece pattern) const
Get a string with characters specified by another string removed from the left.
Definition sstring.h:2139
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Whether the string ends with the specified substring, case-insensitive Unicode characters of the firs...
Definition sstring.h:1832
constexpr size_t find_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string or the end of the string.
Definition sstring.h:1113
constexpr size_t find_end_of_last_or_all(str_piece pattern, size_t offset=-1) const noexcept
Find the end of the last occurrence of a substring in this string, or the end of a string.
Definition sstring.h:1204
constexpr my_type str_mid(size_t from, size_t len=-1) const
Get part of a string with an object of the same type to which the method is applied,...
Definition sstring.h:1433
R trimmed_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition sstring.h:2070
std::optional< double > to_double_hex() const noexcept
Convert string in hex form to double.
Definition sstring.h:1544
R upperred() const
Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1899
R trimmed_right() const
Get a string with whitespace removed on the right.
Definition sstring.h:2003
constexpr bool contains(str_piece pattern, size_t offset=0) const noexcept
Whether the string contains the specified substring.
Definition sstring.h:1218
constexpr std::vector< size_t > find_all(str_piece pattern, size_t offset=0, size_t maxCount=0) const
Find all occurrences of a substring in this string.
Definition sstring.h:1306
R trimmed_right_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition sstring.h:2108
constexpr bool ends_with(str_piece suffix) const noexcept
Whether the string ends with the specified substring.
Definition sstring.h:1791
R trimmed_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition sstring.h:2171
constexpr size_t find_or_all(K s, size_t offset=0) const noexcept
Find a character in this string or the end of a string.
Definition sstring.h:1250
constexpr R trimmed() const
Get a string with whitespace removed on the left and right.
Definition sstring.h:1979
constexpr size_t find(str_piece pattern, size_t offset=0) const noexcept
Find the beginning of the first occurrence of a substring in this string.
Definition sstring.h:1063
constexpr str_piece operator()(ptrdiff_t from, ptrdiff_t len=0) const noexcept
Get part of a string as "simple_str".
Definition sstring.h:775
constexpr auto operator<=>(const base &other) const noexcept
String comparison operator.
Definition sstring.h:914
R lowered() const
Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1911
R trimmed_right(T &&pattern) const
Get a string with the characters specified by the string literal removed from the right.
Definition sstring.h:2048
constexpr size_t find_last(str_piece pattern, size_t offset=-1) const noexcept
Find the beginning of the last occurrence of a substring in this string.
Definition sstring.h:1163
constexpr std::pair< size_t, size_t > find_first_of_idx(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition sstring.h:1351
constexpr K * place(K *ptr) const noexcept
Copy the string to the specified buffer.
Definition sstring.h:680
std::optional< double > to_double() const noexcept
Convert string to double.
Definition sstring.h:1508
R trimmed_left_with_spaces(str_piece pattern) const
Get a string, removing characters specified by another string, as well as whitespace characters,...
Definition sstring.h:2189
void copy_to(K *buffer, size_t bufSize)
Copy the string to the specified buffer.
Definition sstring.h:698
R trimmed_right(str_piece pattern) const
Get a string with characters specified by another string removed to the right.
Definition sstring.h:2153
R trimmed_left() const
Get a string with whitespace removed on the left.
Definition sstring.h:1991
constexpr int compare(str_piece o) const
Compare strings character by character.
Definition sstring.h:854
constexpr void for_all_finded(const Op &op, str_piece pattern, size_t offset=0, size_t maxCount=0) const
Call a functor on all found occurrences of a substring in this string.
Definition sstring.h:1285
constexpr int strcmp(const K *text) const
Compare with C-string character by character.
Definition sstring.h:865
R trimmed_left_with_spaces(T &&pattern) const
Get a string with the characters specified by the string literal removed, as well as whitespace chara...
Definition sstring.h:2089
constexpr my_type substr(ptrdiff_t from, ptrdiff_t len=0) const
Get a substring. Works similarly to operator(), only the result is the same type as the method applie...
Definition sstring.h:1420
constexpr size_t find_first_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character from a given character set.
Definition sstring.h:1338
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition sstring.h:1570
constexpr Splitter< K > splitter(str_piece delimeter) const
Retrieve a Splitter object by the given splitter, which allows sequential get substrings using the ne...
Definition sstring.h:2491
constexpr auto operator<=>(T &&other) const noexcept
Comparison operator between a string and a string literal.
Definition sstring.h:934
constexpr size_t find_end(str_piece pattern, size_t offset=0) const noexcept
Find the end of the occurrence of a substring in this string.
Definition sstring.h:1099
constexpr size_t find_last_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition sstring.h:1379
bool equal_iu(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of the Unicode chara...
Definition sstring.h:1021
constexpr size_t find_last_not_of(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character not from the given character set.
Definition sstring.h:1407
constexpr bool ends_with_ia(str_piece suffix) const noexcept
Whether the string ends with the specified substring in a case-insensitive ASCII character.
Definition sstring.h:1817
constexpr K at(ptrdiff_t idx) const
Get the character at the given position.
Definition sstring.h:836
constexpr size_t find_last(K s, size_t offset=-1) const noexcept
Find the last occurrence of a character in this string.
Definition sstring.h:1319
constexpr bool operator==(const base &other) const noexcept
Operator comparing strings for equality.
Definition sstring.h:905
constexpr int compare_ia(str_piece text) const noexcept
Compare strings character by character and not case sensitive ASCII characters.
Definition sstring.h:969
constexpr size_t find_end_or_all(str_piece pattern, size_t offset=0) const noexcept
Find the end of the first occurrence of a substring in this string, or the end of a string.
Definition sstring.h:1127
constexpr T splitf(str_piece delimeter, const Op &beforeFunc, size_t offset=0) const
Split a string into parts at a given delimiter, possibly applying a functor to each substring.
Definition sstring.h:1677
R trimmed(T &&pattern) const
Get a string with the characters specified by the string literal removed from the left and right.
Definition sstring.h:2018
constexpr bool equal(str_piece other) const noexcept
String comparison for equality.
Definition sstring.h:894
constexpr std::pair< size_t, size_t > find_last_of_idx(str_piece pattern, size_t offset=str::npos) const noexcept
Find the last occurrence of a character from a given character set.
Definition sstring.h:1392
constexpr expr_replaces< K, N - 1, L - 1 > replace_init(T &&pattern, M &&repl) const
Get a string expression that produces a string with replaced substrings given by string literals.
Definition sstring.h:1946
constexpr std::basic_string< K > to_string() const noexcept
Convert to std::string.
Definition sstring.h:749
constexpr bool less_ia(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:992
constexpr str_piece from_to(size_t from, size_t to) const noexcept
Get the substring simple_str from position from to position to (not including it).
Definition sstring.h:816
void as_number(double &t) const
Convert string to double.
Definition sstring.h:1579
constexpr size_t find_first_not_of(str_piece pattern, size_t offset=0) const noexcept
Find the first occurrence of a character not from the given character set.
Definition sstring.h:1366
constexpr bool starts_with_ia(str_piece prefix) const noexcept
Whether the string begins with the given substring in a case-insensitive ASCII character.
Definition sstring.h:1744
Base class for working with mutable strings.
Definition sstring.h:3400
Impl & insert(size_t to, const A &expr)
Insert a string expression at the specified position.
Definition sstring.h:3940
Impl & operator<<=(const Op &fillFunction)
Fills a string with the fill method after the end of the string.
Definition sstring.h:4219
Impl & append(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:3824
Impl & operator<<(const Op &fillFunction)
Calls the passed functor, passing a reference to itself.
Definition sstring.h:4232
Impl & trim(str_piece pattern)
Remove characters included in the passed string at the beginning and end of the line.
Definition sstring.h:3634
Impl & upper_only_ascii()
Convert ASCII characters to uppercase.
Definition sstring.h:3698
Impl & lower_only_ascii()
Convert ASCII characters to lowercase.
Definition sstring.h:3713
Impl & trim_left()
Remove whitespace at the beginning of a line.
Definition sstring.h:3536
Impl & append_printf(const K *format, T &&... args)
Appends sprintf formatted output to the end of the line.
Definition sstring.h:4321
Impl & trim_right_with_wpaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the end of a string.
Definition sstring.h:3623
Impl & trim_left(str_piece pattern)
Remove characters included in the passed string at the beginning of the line.
Definition sstring.h:3645
Impl & trim_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning and end of th...
Definition sstring.h:3597
Impl & prepend(str_piece other)
Add another line to the beginning of the line.
Definition sstring.h:3964
Impl & printf(const K *format, T &&... args)
Formats a string using sprintf.
Definition sstring.h:4305
Impl & with(const Op &fillFunction, Args &&... args)
Call a functor with a string and passed arguments.
Definition sstring.h:4540
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line, writing no more than the specified numb...
Definition sstring.h:4526
Impl & operator<<(const Op &fillFunction)
Fills a string with the fill method from position zero.
Definition sstring.h:4206
Impl & vformat(str_piece format, T &&... args)
Formats a string using std::vformat.
Definition sstring.h:4474
Impl & change(size_t from, size_t len, const A &expr)
Replace a piece of string with a string expression.
Definition sstring.h:3913
Impl & fill(size_t from, const Op &fillFunction)
Fill a string buffer using a functor.
Definition sstring.h:4178
Impl & change(size_t from, size_t len, str_piece other)
Replace a piece of string with another string.
Definition sstring.h:3897
Impl & remove(size_t from, size_t len)
Remove part of a line.
Definition sstring.h:3953
Impl & trim_left_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning of a line.
Definition sstring.h:3610
Impl & trim_left(T &&pattern)
Remove characters included in a string literal at the beginning of the line.
Definition sstring.h:3571
Impl & trim_right(T &&pattern)
Remove characters included in a string literal at the end of the line.
Definition sstring.h:3584
Impl & trim_right_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace at the end of the string.
Definition sstring.h:3689
Impl & append_in(size_t pos, str_piece other)
Add another line starting at the given position.
Definition sstring.h:3864
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Appends std::vformat formatted output starting at the specified position.
Definition sstring.h:4407
Impl & trim_left_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace, at the beginning of the strin...
Definition sstring.h:3678
Impl & insert(size_t to, str_piece other)
Insert a line at the specified position.
Definition sstring.h:3926
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Formats a string using std::vformat up to the specified size.
Definition sstring.h:4508
my_type & format(const FmtString< K, T... > &pattern, T &&... args)
Definition sstring.h:4442
Impl & printf_from(size_t from, const K *format, T &&... args)
Appends sprintf formatted output starting at the specified position.
Definition sstring.h:4251
Impl & operator+=(str_piece other)
Add another line to the end of the line.
Definition sstring.h:3835
Impl & trim_right()
Remove whitespace from the end of a line.
Definition sstring.h:3545
Impl & append(str_piece other)
Add another line to the end of the line.
Definition sstring.h:3812
Impl & trim_right(str_piece pattern)
Remove characters included in the passed string from the end of the string.
Definition sstring.h:3656
Impl & trim_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace characters,...
Definition sstring.h:3667
K * str() noexcept
Get a pointer to the string buffer.
Definition sstring.h:3509
my_type & format_from(size_t from, const FmtString< K, T... > &format, T &&... args)
Definition sstring.h:4378
Impl & prepend(const A &expr)
Add a string expression to the beginning of a line.
Definition sstring.h:3976
Impl & trim()
Remove whitespace from the beginning and end of a line.
Definition sstring.h:3527
Impl & lower()
Convert first plane characters (<0xFFFF) to lowercase Unicode.
Definition sstring.h:3747
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Replace occurrences of a substring with another string.
Definition sstring.h:3993
Impl & upper()
Convert first plane characters (<0xFFFF) to uppercase Unicode.
Definition sstring.h:3732
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Copy the source string, replacing occurrences of substrings with another string.
Definition sstring.h:4116
Impl & append_vformatted(str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line.
Definition sstring.h:4490
Impl & append_formatted(const FmtString< K, T... > &format, T &&... args)
Appends std::format-formatted output to the end of the line.
Definition sstring.h:4458
Impl & append_in(size_t pos, const A &expr)
Add a string expression starting at the given position.
Definition sstring.h:3882
Impl & trim(T &&pattern)
Remove characters included in a string literal at the beginning and end of the line.
Definition sstring.h:3558
Impl & operator+=(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:3847
constexpr str_storable(Args &&... args)
Create an empty object.
Definition sstring.h:2914
void init_replaced(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0)
Initialization from string source with replacement.
Definition sstring.h:3000
constexpr void init_str_expr(const StrExprForType< K > auto &expr)
Initialization from a string expression.
Definition sstring.h:2978
static my_type upperred_from(const From &f, Args &&... args)
Create a copy of the passed string in uppercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:3261
constexpr void init_from_str_other(s_str other)
Initialization from another string object.
Definition sstring.h:2922
constexpr allocator_t & allocator()
Get the allocator.
Definition sstring.h:2891
constexpr void init_symb_repeat(size_t count, K pad)
Character repetition initialization.
Definition sstring.h:2958
static my_type upperred_only_ascii_from(const From &f, Args &&... args)
Create a string copy of the passed in uppercase ASCII characters.
Definition sstring.h:3231
static my_type lowered_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:3278
constexpr void init_str_repeat(size_t repeat, s_str pattern)
String repetition initialization.
Definition sstring.h:2938
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase ASCII characters.
Definition sstring.h:3244
static my_type join(const T &strings, s_str delimeter, bool tail=false, bool skip_empty=false, Args &&... args)
Concatenate strings from the container into one string.
Definition sstring.h:3180
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Create a copy of the passed string with substrings replaced.
Definition sstring.h:3299
constexpr s_str_nt to_nts(size_t from=0) const
Get simple_str_nt starting at the given character.
Definition sstring.h:3124
Concept of a memory management type.
Definition sstring.h:3309
The concept of a string expression of a given character type.
Definition strexpr.h:339
A type concept that cannot modify a stored string.
Definition sstring.h:2833
A type concept that can modify a stored string.
Definition sstring.h:2826
A type concept that can store a string.
Definition sstring.h:2816
constexpr auto e_repl(simple_str< K > w, T &&p, X &&r)
Get a string expression that generates a string with all occurrences of a given substring replaced.
Definition sstring.h:6151
constexpr expr_num< K, T > e_num(T t)
Convert an integer to a string expression.
Definition sstring.h:5891
constexpr auto e_real(double t)
Convert a double number to a string expression.
Definition sstring.h:5985
auto e_repl_const_symbols(simple_str< K > src, Repl &&... other)
Returns a string expression that generates a string containing the given characters replaced with giv...
Definition sstring.h:6715
constexpr auto e_join(const T &s, L &&d)
Get a string expression concatenating the strings in the container into a single string with the give...
Definition sstring.h:6062
Library namespace.
Definition sstring.cpp:10
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Hash dictionary type for char16_t strings, case sensitive search.
Definition sstring.h:7428
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7394
IntConvertResult
Enumeration with possible results of converting a string to an integer.
Definition sstring.h:256
@ Overflow
Переполнение, число не помещается в заданный тип
Definition sstring.h:259
@ Success
Успешно
Definition sstring.h:257
@ NotNumber
Вообще не число
Definition sstring.h:260
@ BadSymbolAtTail
Число закончилось не числовым символом
Definition sstring.h:258
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:7459
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7453
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:7421
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7414
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Type of hash dictionary for char strings, case sensitive search.
Definition sstring.h:7388
auto e_utf(simple_str< From > from)
Returns a string expression that converts a string of one character type to another type,...
Definition sstring.h:2807
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:7440
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:7434
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Hash dictionary type for wchar_t strings, case sensitive search.
Definition sstring.h:7407
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
Definition sstring.h:7400
std::ostream & operator<<(std::ostream &stream, ssa text)
Stream output operator simple_str.
Definition sstring.h:7716
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Hash dictionary type for char32_t strings, case sensitive search.
Definition sstring.h:7447
An object that allows you to sequentially copy content into a buffer of a given size.
Definition sstring.h:7319
bool is_end()
Check that the data has not yet run out.
Definition sstring.h:7326
size_t store(K *buffer, size_t size)
Save the next portion of data to the buffer.
Definition sstring.h:7339
constexpr expr_replace_symbols(simple_str< K > source, const std::vector< std::pair< K, simple_str< K > > > &repl)
Expression constructor.
Definition sstring.h:6339
constexpr expr_replaced(simple_str< K > w, simple_str< K > p, simple_str< K > r)
Constructor.
Definition sstring.h:6190
String expression to convert strings to different UTF types.
Definition sstring.h:2780
A class that claims to refer to a null-terminated string.
Definition sstring.h:2362
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition sstring.h:2421
constexpr simple_str_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition sstring.h:2388
constexpr simple_str_nt(S &&s) noexcept
Constructor that allows you to initialize std::string and std::string_view objects provided that they...
Definition sstring.h:2401
The simplest immutable non-owning string class.
Definition sstring.h:2234
constexpr simple_str(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:2248
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition sstring.h:2311
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition sstring.h:2335
constexpr simple_str(S &&s) noexcept
Constructor that allows you to initialize std::string and std::string_view objects provided that they...
Definition sstring.h:2263
constexpr size_t length() const noexcept
Get the length of the string.
Definition sstring.h:2268
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition sstring.h:2275
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a line by the specified number of characters.
Definition sstring.h:2322
constexpr simple_str(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:2253
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition sstring.h:2282
constexpr bool is_same(simple_str< K > other) const noexcept
Check if two objects point to the same string.
Definition sstring.h:2291
constexpr bool is_part_of(simple_str< K > other) const noexcept
Check if a string is part of another string.
Definition sstring.h:2300
Concatenation of a reference to a string expression and the value of the string expression.
Definition strexpr.h:440