22#ifndef __has_declspec_attribute
23#define __has_declspec_attribute(x) 0
25const bool isWindowsOs =
33#ifdef SIMSTR_IN_SHARED
34 #if defined(_MSC_VER) || (defined(__clang__) && __has_declspec_attribute(dllexport))
36 #define SIMSTR_API __declspec(dllexport)
38 #define SIMSTR_API __declspec(dllimport)
40 #elif (defined(__GNUC__) || defined(__GNUG__)) && defined(SIMSTR_EXPORT)
41 #define SIMSTR_API __attribute__((visibility("default")))
54#include <unordered_map>
67#pragma warning(disable : 4201)
73struct unicode_traits {};
76struct unicode_traits<u8s> {
91 static SIMSTR_API
size_t upper(
const u8s*& src,
size_t lenStr, u8s*& dest,
size_t lenBuf);
92 static SIMSTR_API
size_t lower(
const u8s*& src,
size_t len, u8s*& dest,
size_t lenBuf);
94 static SIMSTR_API
int compareiu(
const u8s* text1,
size_t len1,
const u8s* text2,
size_t len2);
96 static SIMSTR_API
size_t hashia(
const u8s* src,
size_t l);
97 static SIMSTR_API
size_t hashiu(
const u8s* src,
size_t l);
101struct unicode_traits<u16s> {
102 static SIMSTR_API
void upper(
const u16s* src,
size_t len, u16s* dest);
103 static SIMSTR_API
void lower(
const u16s* src,
size_t len, u16s* dest);
105 static SIMSTR_API
int compareiu(
const u16s* text1,
size_t len1,
const u16s* text2,
size_t len2);
106 static SIMSTR_API
size_t hashia(
const u16s* src,
size_t l);
107 static SIMSTR_API
size_t hashiu(
const u16s* src,
size_t l);
111struct unicode_traits<u32s> {
112 static SIMSTR_API
void upper(
const u32s* src,
size_t len, u32s* dest);
113 static SIMSTR_API
void lower(
const u32s* src,
size_t len, u32s* dest);
115 static SIMSTR_API
int compareiu(
const u32s* text1,
size_t len1,
const u32s* text2,
size_t len2);
116 static SIMSTR_API
size_t hashia(
const u32s* src,
size_t s);
117 static SIMSTR_API
size_t hashiu(
const u32s* src,
size_t s);
121struct unicode_traits<wchar_t> {
122 static void upper(
const wchar_t* src,
size_t len,
wchar_t* dest) {
123 unicode_traits<wchar_type>::upper(to_w(src), len, to_w(dest));
125 static void lower(
const wchar_t* src,
size_t len,
wchar_t* dest) {
126 unicode_traits<wchar_type>::lower(to_w(src), len, to_w(dest));
129 static int compareiu(
const wchar_t* text1,
size_t len1,
const wchar_t* text2,
size_t len2) {
130 return unicode_traits<wchar_type>::compareiu(to_w(text1), len1, to_w(text2), len2);
132 static size_t hashia(
const wchar_t* src,
size_t s) {
133 return unicode_traits<wchar_type>::hashia(to_w(src), s);
135 static size_t hashiu(
const wchar_t* src,
size_t s) {
136 return unicode_traits<wchar_type>::hashiu(to_w(src), s);
141#if defined(_MSC_VER) && _MSC_VER <= 1933
142template<
typename K,
typename... Args>
143using FmtString = std::_Basic_format_string<K, std::type_identity_t<Args>...>;
144#elif __clang_major__ >= 15 || _MSC_VER > 1933 || __GNUC__ >= 13
145template<
typename K,
typename... Args>
146using FmtString = std::basic_format_string<K, std::type_identity_t<Args>...>;
148template<
typename K,
typename... Args>
149using FmtString = std::basic_string_view<K>;
153SIMSTR_API std::optional<double> impl_to_double(
const K* start,
const K* end);
167template<
typename K,
typename StrRef,
typename Impl,
bool Mutable>
168class str_algs :
public str_src_algs<K, StrRef, Impl, Mutable> {
169 constexpr const Impl& d()
const noexcept {
170 return *
static_cast<const Impl*
>(
this);
172 constexpr size_t _len()
const noexcept {
175 constexpr const K* _str()
const noexcept {
176 return d().symbols();
178 constexpr bool _is_empty()
const noexcept {
179 return d().is_empty();
184 using str_piece = StrRef;
185 using traits = ch_traits<K>;
186 using uni = unicode_traits<K>;
187 using uns_type = std::make_unsigned_t<K>;
188 using my_type = Impl;
189 using base = str_src_algs<K, StrRef, Impl, Mutable>;
190 str_algs() =
default;
192 int compare_iu(
const K* text,
size_t len)
const noexcept {
194 return _is_empty() ? 0 : 1;
195 return uni::compareiu(_str(), _len(), text, len);
206 return compare_iu(text.symbols(), text.length());
217 return text.length() == _len() && compare_iu(text.symbols(), text.length()) == 0;
228 return compare_iu(text.symbols(), text.length()) < 0;
232 bool starts_with_iu(
const K* prefix,
size_t len)
const noexcept {
233 return _len() >= len && 0 == uni::compareiu(_str(), len, prefix, len);
242 return starts_with_iu(prefix.symbols(), prefix.length());
246 constexpr bool ends_with_iu(
const K* suffix,
size_t len)
const noexcept {
247 size_t myLen = _len();
248 return myLen >= len && 0 == uni::compareiu(_str() + myLen - len, len, suffix, len);
257 return ends_with_iu(suffix.symbols(), suffix.length());
267 template<
typename R = my_type>
269 return R::upperred_from(d());
279 template<
typename R = my_type>
281 return R::lowered_from(d());
289 template<
bool SkipWS = true,
bool AllowPlus = true>
292 const K* ptr = _str();
293 if constexpr (SkipWS) {
294 while (len && uns_type(*ptr) <=
' ') {
299 if constexpr (AllowPlus) {
300 if (len && *ptr == K(
'+')) {
309 if constexpr(
sizeof(K) == 1) {
311 if (std::from_chars(ptr, ptr + len, d).ec == std::errc{}) {
317 if constexpr (
sizeof(K) == 1) {
318 return impl_to_double((
const char*)ptr, (
const char*)ptr + len);
319 }
else if constexpr (
sizeof(K) == 2) {
320 return impl_to_double((
const char16_t*)ptr, (
const char16_t*)ptr + len);
322 return impl_to_double((
const char32_t*)ptr, (
const char32_t*)ptr + len);
333 t = res ? *res : std::nan(
"0");
347 template<ToIntNumber T>
375struct simple_str : str_algs<K, simple_str<K>, simple_str<K>, false> {
377 using my_type = simple_str<K>;
379 const symb_type* str;
382 constexpr simple_str() =
default;
384 constexpr simple_str(
str_src<K> src) : str(src.str), len(src.len){}
390 template<typename T, size_t N = const_lit_for<K, T>::Count>
396 constexpr simple_str(
const K* p,
size_t l) noexcept : str(p), len(l) {}
402 constexpr simple_str(
const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : str(s.data()), len(s.length()) {}
407 constexpr simple_str(
const std::basic_string_view<K, std::char_traits<K>>& s) noexcept : str(s.data()), len(s.length()) {}
412 constexpr size_t length() const noexcept {
419 constexpr const symb_type*
symbols() const noexcept {
435 constexpr bool is_same(simple_str<K> other)
const noexcept {
436 return str == other.str && len == other.len;
444 constexpr bool is_part_of(simple_str<K> other)
const noexcept {
445 return str >= other.str && str + len <= other.str + other.len;
486struct simple_str_selector {
487 using type = simple_str<K>;
511struct simple_str_nt : simple_str<K>, null_terminated<K, simple_str_nt<K>> {
513 using my_type = simple_str_nt<K>;
514 using base = simple_str<K>;
516 constexpr static const K empty_string[1] = {0};
518 simple_str_nt() =
default;
535 template<
typename T>
requires is_one_of_type<std::remove_cvref_t<T>,
const K*, K*>::value
538 base::str = base::len ? p : empty_string;
544 template<typename T, size_t N = const_lit_for<K, T>::Count>
553 template<StrType<K> T>
556 base::len = t.length();
563 constexpr simple_str_nt(
const std::basic_string<K, std::char_traits<K>, A>& s) noexcept : base(s) {}
565 static const my_type empty_str;
575 if (from > base::len) {
578 return {base::str + from, base::len - from};
583inline const simple_str_nt<K> simple_str_nt<K>::empty_str{simple_str_nt<K>::empty_string, 0};
599template<
typename Src,
typename Dest>
600struct utf_convert_selector;
603struct utf_convert_selector<u8s, u16s> {
604 static SIMSTR_API
size_t need_len(
const u8s* src,
size_t srcLen);
605 static SIMSTR_API
size_t convert(
const u8s* src,
size_t srcLen, u16s* dest);
609struct utf_convert_selector<u8s, u32s> {
610 static SIMSTR_API
size_t need_len(
const u8s* src,
size_t srcLen);
611 static SIMSTR_API
size_t convert(
const u8s* src,
size_t srcLen, u32s* dest);
615struct utf_convert_selector<u8s, wchar_t> {
616 static size_t need_len(
const u8s* src,
size_t srcLen) {
617 return utf_convert_selector<u8s, wchar_type>::need_len(src, srcLen);
619 static size_t convert(
const u8s* src,
size_t srcLen,
wchar_t* dest) {
620 return utf_convert_selector<u8s, wchar_type>::convert(src, srcLen, to_w(dest));
625struct utf_convert_selector<u16s, u8s> {
626 static SIMSTR_API
size_t need_len(
const u16s* src,
size_t srcLen);
627 static SIMSTR_API
size_t convert(
const u16s* src,
size_t srcLen, u8s* dest);
631struct utf_convert_selector<u16s, u32s> {
632 static SIMSTR_API
size_t need_len(
const u16s* src,
size_t srcLen);
633 static SIMSTR_API
size_t convert(
const u16s* src,
size_t srcLen, u32s* dest);
637struct utf_convert_selector<u16s, u16s> {
640 static size_t need_len(
const u16s* src,
size_t srcLen) {
643 static size_t convert(
const u16s* src,
size_t srcLen, u16s* dest) {
644 ch_traits<u16s>::copy(dest, src, srcLen + 1);
650struct utf_convert_selector<u32s, u32s> {
653 static size_t need_len(
const u32s* src,
size_t srcLen) {
656 static size_t convert(
const u32s* src,
size_t srcLen, u32s* dest) {
657 ch_traits<u32s>::copy(dest, src, srcLen + 1);
663struct utf_convert_selector<u16s, wchar_t> {
664 static size_t need_len(
const u16s* src,
size_t srcLen) {
665 return utf_convert_selector<u16s, wchar_type>::need_len(src, srcLen);
667 static size_t convert(
const u16s* src,
size_t srcLen,
wchar_t* dest) {
668 return utf_convert_selector<u16s, wchar_type>::convert(src, srcLen, to_w(dest));
673struct utf_convert_selector<u32s, u8s> {
674 static SIMSTR_API
size_t need_len(
const u32s* src,
size_t srcLen);
675 static SIMSTR_API
size_t convert(
const u32s* src,
size_t srcLen, u8s* dest);
679struct utf_convert_selector<u32s, u16s> {
680 static SIMSTR_API
size_t need_len(
const u32s* src,
size_t srcLen);
681 static SIMSTR_API
size_t convert(
const u32s* src,
size_t srcLen, u16s* dest);
685struct utf_convert_selector<u32s, wchar_t> {
686 static size_t need_len(
const u32s* src,
size_t srcLen) {
687 return utf_convert_selector<u32s, wchar_type>::need_len(src, srcLen);
689 static size_t convert(
const u32s* src,
size_t srcLen,
wchar_t* dest) {
690 return utf_convert_selector<u32s, wchar_type>::convert(src, srcLen, to_w(dest));
695struct utf_convert_selector<wchar_t, u8s> {
696 static size_t need_len(
const wchar_t* src,
size_t srcLen) {
697 return utf_convert_selector<wchar_type, u8s>::need_len(to_w(src), srcLen);
699 static size_t convert(
const wchar_t* src,
size_t srcLen, u8s* dest) {
700 return utf_convert_selector<wchar_type, u8s>::convert(to_w(src), srcLen, dest);
705struct utf_convert_selector<wchar_t, u16s> {
706 static size_t need_len(
const wchar_t* src,
size_t srcLen) {
707 return utf_convert_selector<wchar_type, u16s>::need_len(to_w(src), srcLen);
709 static size_t convert(
const wchar_t* src,
size_t srcLen, u16s* dest) {
710 return utf_convert_selector<wchar_type, u16s>::convert(to_w(src), srcLen, dest);
715struct utf_convert_selector<wchar_t, u32s> {
716 static size_t need_len(
const wchar_t* src,
size_t srcLen) {
717 return utf_convert_selector<wchar_type, u32s>::need_len(to_w(src), srcLen);
719 static size_t convert(
const wchar_t* src,
size_t srcLen, u32s* dest) {
720 return utf_convert_selector<wchar_type, u32s>::convert(to_w(src), srcLen, dest);
725struct utf_convert_selector<u8s, ubs> {
726 static size_t need_len(
const u8s* src,
size_t srcLen) {
729 static size_t convert(
const u8s* src,
size_t srcLen, ubs* dest) {
730 ch_traits<u8s>::copy((u8s*)dest, src, srcLen);
736struct utf_convert_selector<ubs, u8s> {
737 static size_t need_len(
const ubs* src,
size_t srcLen) {
740 static size_t convert(
const ubs* src,
size_t srcLen, u8s* dest) {
741 ch_traits<u8s>::copy(dest, (
const u8s*)src, srcLen);
760template<
typename K,
typename Impl>
761class from_utf_convertible {
763 from_utf_convertible() =
default;
764 using my_type = Impl;
773 requires(!std::is_same_v<O, K>)
775 using worker = utf_convert_selector<O, K>;
776 Impl* d =
static_cast<Impl*
>(
this);
777 size_t len = init.
length();
781 size_t need = worker::need_len(init.
symbols(), len);
782 K*
str = d->init(need);
787 template<
typename O,
typename I,
bool M>
788 requires(!std::is_same_v<O, K>)
800template<
typename From,
typename To>
requires (!std::is_same_v<From, To>)
802 using symb_type = To;
803 using worker = utf_convert_selector<From, To>;
809 size_t length()
const noexcept {
810 return worker::need_len(source_.symbols(), source_.length());
812 To* place(To* ptr)
const noexcept {
813 return ptr + worker::convert(source_.symbols(), source_.length(), ptr);
829template<
typename To,
typename From>
requires (!std::is_same_v<From, To>)
838template<
typename A,
typename K>
840 A::is_str_storable ==
true;
841 std::is_same_v<typename A::symb_type, K>;
848template<
typename A,
typename K>
855template<
typename A,
typename K>
900template<
typename K,
typename Impl,
typename Allocator>
903 using my_type = Impl;
904 using traits = ch_traits<K>;
905 using allocator_t = Allocator;
915 return *
static_cast<Allocator*
>(
this);
917 constexpr const allocator_t&
allocator()
const {
918 return *
static_cast<const Allocator*
>(
this);
921 using uni = unicode_traits<K>;
923 constexpr Impl& d() noexcept {
924 return *
static_cast<Impl*
>(
this);
926 constexpr const Impl& d() const noexcept {
927 return *
static_cast<const Impl*
>(
this);
936 template<
typename... Args>
937 explicit constexpr str_storable(Args&&... args) : Allocator(std::forward<Args>(args)...) {}
947 K* ptr = d().init(other.
length());
962 size_t l = pattern.
length(), allLen = l * repeat;
964 K* ptr = d().init(allLen);
965 for (
size_t i = 0; i < repeat; i++) {
966 traits::copy(ptr, pattern.
symbols(), l);
983 K*
str = d().init(count);
984 traits::assign(
str, count, pad);
1001 template<StrExprForType<K> A>
1003 size_t len = expr.length();
1005 *expr.place((
typename A::symb_type*)d().init(len)) = 0;
1023 template<StrType<K> From>
1024 void init_replaced(
const From& f, s_str pattern, s_str repl,
size_t offset = 0,
size_t maxCount = 0) {
1025 auto findes = f.find_all(pattern, offset, maxCount);
1026 if (!findes.size()) {
1027 new (
this) my_type{f};
1030 size_t srcLen = f.length();
1031 size_t newSize = srcLen +
static_cast<ptrdiff_t
>(repl.len - pattern.len) * findes.size();
1034 new (
this) my_type{};
1038 K* ptr = d().init(newSize);
1039 const K* src = f.symbols();
1041 for (
const auto& s: findes) {
1042 size_t copyLen = s - from;
1044 traits::copy(ptr, src + from, copyLen);
1048 traits::copy(ptr, repl.str, repl.len);
1051 from = s + pattern.len;
1055 traits::copy(ptr, src + from, srcLen);
1061 template<StrType<K> From,
typename Op1,
typename... Args>
1062 requires std::is_constructible_v<allocator_t, Args...>
1063 static my_type changeCaseAscii(
const From& f,
const Op1& opMakeNeedCase, Args&&... args) {
1064 my_type result{std::forward<Args>(args)...};
1065 size_t len = f.length();
1067 const K* source = f.symbols();
1068 K* destination = result.init(len);
1069 for (
size_t l = 0; l < len; l++) {
1070 destination[l] = opMakeNeedCase(source[l]);
1079 template<
typename T,
bool Dummy = true>
1081 template<
typename From,
typename Op1,
typename... Args>
1082 requires std::is_constructible_v<allocator_t, Args...>
1083 static my_type changeCase(
const From& f,
const Op1& opChangeCase, Args&&... args) {
1084 my_type result{std::forward<Args>(args)...};
1085 size_t len = f.length();
1087 opChangeCase(f.symbols(), len, result.init(len));
1094 template<
bool Dummy>
1095 struct ChangeCase<u8s, Dummy> {
1096 template<
typename From,
typename Op1,
typename... Args>
1097 requires std::is_constructible_v<allocator_t, Args...>
1098 static my_type changeCase(
const From& f,
const Op1& opChangeCase, Args&&... args) {
1099 my_type result{std::forward<Args>(args)...};
1101 size_t len = f.length();
1103 const K* ptr = f.symbols();
1104 K* pWrite = result.init(len);
1106 const u8s* source = ptr;
1108 size_t newLen = opChangeCase(source, len, dest, len);
1112 result.set_size(newLen);
1113 }
else if (newLen > len) {
1116 size_t readed =
static_cast<size_t>(source - ptr);
1117 size_t writed =
static_cast<size_t>(dest - pWrite);
1118 pWrite = result.set_size(newLen);
1119 dest = pWrite + writed;
1120 opChangeCase(source, len - readed, dest, newLen - writed);
1130 inline static constexpr bool is_str_storable =
true;
1137 constexpr operator const K*()
const noexcept {
1138 return d().symbols();
1148 constexpr s_str_nt
to_nts(
size_t from = 0)
const {
1149 size_t len = d().
length();
1153 return {d().symbols() + from, len - from};
1161 constexpr operator s_str_nt()
const {
1162 return {d().symbols(), d().length()};
1202 template<
typename T,
typename... Args>
1203 requires std::is_constructible_v<allocator_t, Args...>
1204 static my_type
join(
const T& strings, s_str delimiter,
bool tail =
false,
bool skip_empty =
false, Args&&... args) {
1205 my_type result(std::forward<Args>(args)...);
1206 if (strings.size()) {
1207 if (strings.size() == 1 && (!delimiter.
length() || !tail)) {
1208 result = strings.front();
1210 size_t commonLen = 0;
1211 for (
const auto& t: strings) {
1212 size_t len = t.length();
1213 if (len > 0 || !skip_empty) {
1214 if (commonLen > 0) {
1215 commonLen += delimiter.len;
1220 commonLen += (tail && delimiter.len > 0 && (commonLen > 0 || (!skip_empty && strings.size() > 0))? delimiter.len : 0);
1222 K* ptr = result.init(commonLen);
1224 for (
const auto& t: strings) {
1225 size_t copyLen = t.length();
1226 if (delimiter.len > 0 && write != ptr && (copyLen || !skip_empty)) {
1227 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1228 write += delimiter.len;
1230 ch_traits<K>::copy(write, t.symbols(), copyLen);
1233 if (delimiter.len > 0 && tail && (write != ptr || (!skip_empty && strings.size() > 0))) {
1234 ch_traits<K>::copy(write, delimiter.str, delimiter.len);
1235 write += delimiter.len;
1239 result.create_empty();
1253 template<StrType<K> From,
typename... Args>
1254 requires std::is_constructible_v<allocator_t, Args...>
1256 return changeCaseAscii(f, makeAsciiUpper<K>, std::forward<Args>(args)...);
1266 template<StrType<K> From,
typename... Args>
1267 requires std::is_constructible_v<allocator_t, Args...>
1269 return changeCaseAscii(f, makeAsciiLower<K>, std::forward<Args>(args)...);
1283 template<StrType<K> From,
typename... Args>
1284 requires std::is_constructible_v<allocator_t, Args...>
1286 return ChangeCase<K>::changeCase(f, uni::upper, std::forward<Args>(args)...);
1300 template<StrType<K> From,
typename... Args>
1301 requires std::is_constructible_v<allocator_t, Args...>
1303 return ChangeCase<K>::changeCase(f, uni::lower, std::forward<Args>(args)...);
1321 template<StrType<K> From,
typename... Args>
1322 requires std::is_constructible_v<allocator_t, Args...>
1323 static my_type
replaced_from(
const From& f, s_str pattern, s_str repl,
size_t offset = 0,
size_t maxCount = 0, Args&&... args) {
1324 return my_type{f, pattern, repl, offset, maxCount, std::forward<Args>(args)...};
1334 { a.allocate(size) } -> std::same_as<void*>;
1335 { a.deallocate(void_ptr) }
noexcept -> std::same_as<void>;
1338struct printf_selector {
1339 template<
typename K,
typename... T>
requires (is_one_of_std_char_v<K>)
1340 static int snprintf(K* buffer,
size_t count,
const K* format, T&&... args) {
1341 if constexpr (
sizeof(K) == 1) {
1343 return std::snprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), std::forward<T>(args)...);
1347 return _sprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1351 return std::swprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1355 return _swprintf_p(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args...);
1359 template<
typename K>
requires (is_one_of_std_char_v<K>)
1360 static int vsnprintf(K* buffer,
size_t count,
const K* format, va_list args) {
1361 if constexpr (std::is_same_v<K, u8s>) {
1363 return std::vsnprintf(buffer, count, format, args);
1367 return _vsprintf_p(buffer, count, format, args);
1371 return std::vswprintf(to_one_of_std_char(buffer), count, to_one_of_std_char(format), args);
1375 return _vswprintf_p(buffer, count, format, args);
1381inline size_t grow2(
size_t ret,
size_t currentCapacity) {
1382 return ret <= currentCapacity ? ret : ret * 2;
1386struct to_std_char_type : std::type_identity<K>{};
1389struct to_std_char_type<char8_t>{
1394struct to_std_char_type<char16_t>{
1395 using type = std::conditional_t<
sizeof(char16_t) ==
sizeof(
wchar_t), wchar_t,
void>;
1399struct to_std_char_type<char32_t>{
1400 using type = std::conditional_t<
sizeof(char32_t) ==
sizeof(
wchar_t), wchar_t,
void>;
1441template<
typename K,
typename Impl>
1444 using my_type = Impl;
1448 return *
static_cast<Impl*
>(
this);
1450 const Impl& d()
const {
1451 return *
static_cast<const Impl*
>(
this);
1453 size_t _len()
const noexcept {
1454 return d().length();
1456 const K* _str()
const noexcept {
1457 return d().symbols();
1460 using symb_type = K;
1461 using traits = ch_traits<K>;
1462 using uni = unicode_traits<K>;
1463 using uns_type = std::make_unsigned_t<K>;
1465 template<
typename Op>
1466 Impl& make_trim_op(
const Op& op) {
1467 str_piece me = d(), pos = op(me);
1468 if (me.
length() != pos.length()) {
1469 if (me.
symbols() != pos.symbols())
1470 traits::move(
const_cast<K*
>(me.
symbols()), pos.symbols(), pos.length());
1471 d().set_size(pos.length());
1477 Impl& commonChangeCase() {
1478 size_t len = _len();
1480 Op(_str(), len,
str());
1487 template<
typename T,
bool Dummy = true>
1489 static Impl&
upper(Impl& obj) {
1490 return obj.template commonChangeCase<unicode_traits<K>::upper>();
1492 static Impl&
lower(Impl& obj) {
1493 return obj.template commonChangeCase<unicode_traits<K>::lower>();
1498 Impl& utf8CaseChange() {
1501 size_t len = _len();
1503 u8s* writePos =
str();
1504 const u8s *startData = writePos, *readPos = writePos;
1505 size_t newLen = Op(readPos, len, writePos, len);
1509 d().set_size(newLen);
1510 }
else if (newLen > len) {
1513 size_t readed =
static_cast<size_t>(readPos - startData);
1514 size_t writed =
static_cast<size_t>(writePos - startData);
1515 d().set_size(newLen);
1517 readPos = startData + readed;
1518 writePos =
const_cast<u8s*
>(startData) + writed;
1519 Op(readPos, len - readed, writePos, newLen - writed);
1524 template<
bool Dummy>
1525 struct CaseTraits<u8s, Dummy> {
1526 static Impl&
upper(Impl& obj) {
1527 return obj.template utf8CaseChange<unicode_traits<u8s>::upper>();
1529 static Impl&
lower(Impl& obj) {
1530 return obj.template utf8CaseChange<unicode_traits<u8s>::lower>();
1534 template<TrimSides S, bool withSpaces, typename T, size_t N = const_lit_for<K, T>::Count>
1535 Impl& makeTrim(T&& pattern) {
1536 return make_trim_op(trim_operator<S, K, N - 1, withSpaces>{pattern});
1539 template<TrimS
ides S,
bool withSpaces>
1540 Impl& makeTrim(str_piece pattern) {
1541 return make_trim_op(trim_operator<S, K, 0, withSpaces>{{pattern}});
1560 explicit operator K*()
noexcept {
1570 return make_trim_op(SimpleTrim<TrimSides::TrimAll, K>{});
1579 return make_trim_op(SimpleTrim<TrimSides::TrimLeft, K>{});
1588 return make_trim_op(SimpleTrim<TrimSides::TrimRight, K>{});
1598 template<typename T, size_t N = const_lit_for<K, T>::Count>
1599 requires is_const_pattern<N>
1601 return makeTrim<TrimSides::TrimAll, false>(pattern);
1611 template<typename T, size_t N = const_lit_for<K, T>::Count>
1612 requires is_const_pattern<N>
1614 return makeTrim<TrimSides::TrimLeft, false>(pattern);
1624 template<typename T, size_t N = const_lit_for<K, T>::Count>
1625 requires is_const_pattern<N>
1627 return makeTrim<TrimSides::TrimRight, false>(pattern);
1637 template<typename T, size_t N = const_lit_for<K, T>::Count>
1638 requires is_const_pattern<N>
1640 return makeTrim<TrimSides::TrimAll, true>(pattern);
1650 template<typename T, size_t N = const_lit_for<K, T>::Count>
1651 requires is_const_pattern<N>
1653 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1663 template<typename T, size_t N = const_lit_for<K, T>::Count>
1664 requires is_const_pattern<N>
1666 return makeTrim<TrimSides::TrimRight, true>(pattern);
1677 return pattern.
length() ? makeTrim<TrimSides::TrimAll, false>(pattern) : d();
1688 return pattern.
length() ? makeTrim<TrimSides::TrimLeft, false>(pattern) : d();
1699 return pattern.
length() ? makeTrim<TrimSides::TrimRight, false>(pattern) : d();
1710 return makeTrim<TrimSides::TrimAll, true>(pattern);
1721 return makeTrim<TrimSides::TrimLeft, true>(pattern);
1732 return makeTrim<TrimSides::TrimRight, true>(pattern);
1742 for (
size_t i = 0, l = _len(); i < l; i++, ptr++) {
1744 if (isAsciiLower(s))
1757 for (
size_t i = 0, l = _len(); i < l; i++, ptr++) {
1759 if (isAsciiUpper(s))
1777 return CaseTraits<K>::upper(d());
1792 return CaseTraits<K>::lower(d());
1796 template<
typename T>
1797 Impl& changeImpl(
size_t from,
size_t len, T expr) {
1798 size_t myLen = _len();
1802 if (from + len > myLen) {
1806 size_t otherLen = expr.length();
1807 if (len == otherLen) {
1808 expr.place(buffer + from);
1810 size_t tailLen = myLen - from - len;
1811 if (len > otherLen) {
1812 expr.place(buffer + from);
1813 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1814 d().set_size(myLen - (len - otherLen));
1816 buffer = d().set_size(myLen + otherLen - len);
1817 traits::move(buffer + from + otherLen, buffer + from + len, tailLen);
1818 expr.place(buffer + from);
1824 template<
typename T>
1825 Impl& appendImpl(T expr) {
1826 if (
size_t len = expr.length(); len) {
1827 size_t size = _len();
1828 expr.place(d().set_size(size + len) + size);
1833 template<
typename T>
1834 Impl& appendFromImpl(
size_t pos, T expr) {
1837 if (
size_t len = expr.length())
1838 expr.place(d().set_size(pos + len) + pos);
1845 inline static constexpr bool is_str_mutable =
true;
1855 return appendImpl<str_piece>(other);
1865 template<StrExprForType<K> A>
1867 return appendImpl<const A&>(expr);
1878 return appendImpl<str_piece>(other);
1888 template<StrExprForType<K> A>
1890 return appendImpl<const A&>(expr);
1907 return appendFromImpl<str_piece>(pos, other);
1923 template<StrExprForType<K> A>
1925 return appendFromImpl<const A&>(pos, expr);
1939 Impl&
change(
size_t from,
size_t len, str_piece other) {
1940 return changeImpl<str_piece>(from, len, other);
1954 template<StrExprForType<K> A>
1955 Impl&
change(
size_t from,
size_t len,
const A& expr) {
1956 return changeImpl<const A&>(from, len, expr);
1969 return changeImpl<str_piece>(to, 0, other);
1981 template<StrExprForType<K> A>
1983 return changeImpl<const A&>(to, 0, expr);
1996 return changeImpl<const empty_expr<K>&>(from, len, {});
2007 return changeImpl<str_piece>(0, 0, other);
2017 template<StrExprForType<K> A>
2019 return changeImpl<const A&>(0, 0, expr);
2035 Impl&
replace(str_piece pattern, str_piece repl,
size_t offset = 0,
size_t maxCount = 0) {
2036 offset = d().find(pattern, offset);
2037 if (offset == str::npos) {
2042 size_t replLength = repl.
length(), patternLength = pattern.
length();
2044 if (patternLength == replLength) {
2048 while (maxCount--) {
2049 traits::copy(ptr + offset, repl.
symbols(), replLength);
2050 offset = d().find(pattern, offset + replLength);
2051 if (offset == str::npos)
2054 }
else if (patternLength > replLength) {
2058 traits::copy(ptr + offset, repl.
symbols(), replLength);
2059 size_t posWrite = offset + replLength;
2060 offset += patternLength;
2062 while (--maxCount) {
2063 size_t idx = d().find(pattern, offset);
2064 if (idx == str::npos)
2066 size_t lenOfPiece = idx - offset;
2067 traits::move(ptr + posWrite, ptr + offset, lenOfPiece);
2068 posWrite += lenOfPiece;
2069 traits::copy(ptr + posWrite, repl.
symbols(), replLength);
2070 posWrite += replLength;
2071 offset = idx + patternLength;
2073 size_t tailLen = _len() - offset;
2074 traits::move(ptr + posWrite, ptr + offset, tailLen);
2075 d().set_size(posWrite + tailLen);
2077 struct replace_grow_helper {
2078 replace_grow_helper(my_type& src, str_piece p, str_piece r,
size_t mc,
size_t d)
2079 : source(src), pattern(p), repl(r), maxCount(mc), delta(d) {}
2081 const str_piece pattern;
2082 const str_piece repl;
2086 K* reserve_for_copy{};
2087 size_t end_of_piece{};
2088 size_t total_length{};
2091 size_t found[16] = {offset};
2093 offset += pattern.
length();
2096 for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) {
2097 found[idx] = source.find(pattern, offset);
2098 if (found[idx] == str::npos) {
2101 offset = found[idx] + pattern.
length();
2104 if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) {
2109 if (!reserve_for_copy) {
2112 end_of_piece = source.length();
2113 total_length = end_of_piece + all_delta;
2114 reserve_for_copy = source.alloc_for_copy(total_length);
2116 K* dst_start = reserve_for_copy;
2117 const K* src_start = source.symbols();
2119 size_t pos = found[idx] + pattern.
length();
2120 size_t lenOfPiece = end_of_piece - pos;
2121 ch_traits<K>::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece);
2122 ch_traits<K>::copy(dst_start + pos + all_delta - repl.
length(), repl.
symbols(), repl.
length());
2124 end_of_piece = found[idx];
2126 if (!all_delta && reserve_for_copy != src_start) {
2127 ch_traits<K>::copy(dst_start, src_start, found[0]);
2130 } helper(d(), pattern, repl, maxCount, repl.
length() - pattern.
length());
2131 helper.replace(offset);
2132 d().set_from_copy(helper.reserve_for_copy, helper.total_length);
2152 template<StrType<K> From>
2153 Impl&
replace_from(
const From& f, str_piece pattern, str_piece repl,
size_t offset = 0,
size_t maxCount = 0) {
2155 K* dst = d().reserve_no_preserve(f.length());
2156 const K* src = f.symbols();
2158 if (maxCount == 0) {
2161 size_t src_length = f.length(), start = 0;
2162 while (maxCount--) {
2163 offset = f.find(pattern, offset);
2164 if (offset == str::npos) {
2167 size_t piece_len = offset - start;
2169 ch_traits<K>::copy(dst, src + start, piece_len);
2177 offset += pattern.
length();
2180 if (start < src_length) {
2181 ch_traits<K>::copy(dst, src + start, src_length - start);
2183 d().set_size(src_length - delta);
2186 replace(pattern, repl, offset, maxCount);
2214 template<
typename Op>
2215 Impl&
fill(
size_t from,
const Op& fillFunction) {
2216 size_t size = _len();
2219 size_t capacity = d().capacity();
2223 size_t needSize = (size_t)fillFunction(ptr + from, capacity);
2224 if (capacity >= needSize) {
2225 d().set_size(from + needSize);
2228 ptr = from == 0 ? d().reserve_no_preserve(needSize) : d().set_size(from + needSize);
2229 capacity = d().capacity() - from;
2241 template<
typename Op>
2242 requires std::is_invocable_v<Op, K*, size_t>
2244 return fill(0, fillFunction);
2254 template<
typename Op>
2255 requires std::is_invocable_v<Op, K*, size_t>
2257 return fill(_len(), fillFunction);
2267 template<
typename Op>
2268 requires std::is_invocable_v<Op, my_type&>
2287 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2289 size_t size = _len();
2292 size_t capacity = d().capacity();
2303 if constexpr (
sizeof(K) == 1 && !isWindowsOs) {
2304 result = printf_selector::snprintf(ptr + from, capacity + 1,
format, std::forward<T>(args)...);
2305 if (result > (
int)capacity) {
2306 ptr = from == 0 ? d().reserve_no_preserve(result) : d().set_size(from + result);
2307 result = printf_selector::snprintf(ptr + from, result + 1,
format, std::forward<T>(args)...);
2311 result = printf_selector::snprintf(ptr + from, capacity + 1,
format, std::forward<T>(args)...);
2318 ptr = from == 0 ? d().reserve_no_preserve(capacity) : d().set_size(from + capacity);
2324 d().set_size(
static_cast<size_t>(traits::length(_str())));
2326 d().set_size(from + result);
2341 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2357 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2368 inline static K pad;
2369 K& operator*()
const {
2372 writer& operator++() {
2373 if (writed < max_write) {
2376 size_t l = ptr - store->begin();
2378 ptr = store->set_size(l + std::min(l / 2,
size_t(8192))) + l;
2386 writer operator++(
int) {
2392 writer(my_type& s, K* p, K* e,
size_t ml) : store(&s), ptr(p), end(e), max_write(ml) {}
2394 writer(
const writer&) =
delete;
2395 writer& operator=(
const writer&)
noexcept =
delete;
2396 writer(writer&&) noexcept = default;
2397 writer& operator=(writer&&) noexcept = default;
2398 using difference_type =
int;
2400 using fmt_type = typename to_std_char_type<K>::type;
2415 template<typename... T> requires (is_one_of_std_char_v<K>)
2417 size_t size = _len();
2420 size_t capacity = d().capacity();
2423 auto result = std::format_to(writer{d(), ptr + from, ptr + capacity, size_t(-1)},
2424 std::forward<decltype(format)>(
format), std::forward<T>(args)...);
2425 d().set_size(result.ptr - _str());
2444 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2446 size_t size = _len();
2449 size_t capacity = d().capacity();
2452 if constexpr (std::is_same_v<K, u8s>) {
2453 auto result = std::vformat_to(
2454 writer{d(), ptr + from, ptr + capacity, max_write},
2455 std::basic_string_view<K>{
format.symbols(),
format.length()},
2456 std::make_format_args(args...));
2457 d().set_size(result.ptr - _str());
2459 auto result = std::vformat_to(
2460 writer{d(), to_one_of_std_char(ptr + from), ptr + capacity, max_write},
2461 std::basic_string_view<wchar_t>{to_one_of_std_char(
format.symbols()),
format.length()},
2462 std::make_wformat_args(std::forward<T>(args)...));
2463 d().set_size(result.ptr - _str());
2479 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2480 Impl&
format(
const FmtString<fmt_type, T...>& pattern, T&&... args) {
2481 return format_from(0, pattern, std::forward<T>(args)...);
2495 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2511 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2527 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2545 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2563 template<
typename... T>
requires (is_one_of_std_char_v<K>)
2577 template<
typename Op,
typename... Args>
2578 Impl&
with(
const Op& fillFunction, Args&&... args) {
2579 fillFunction(d(), std::forward<Args>(args)...);
2585struct SharedStringData {
2586 std::atomic_size_t ref_;
2588 SharedStringData() {
2592 return (K*)(
this + 1);
2595 ref_.fetch_add(1, std::memory_order_relaxed);
2597 void decr(Allocatorable
auto& allocator) {
2598 size_t val = ref_.fetch_sub(1, std::memory_order_relaxed);
2600 allocator.deallocate(
this);
2603 static SharedStringData<K>* create(
size_t l, Allocatorable
auto& allocator) {
2604 size_t size =
sizeof(SharedStringData<K>) + (l + 1) *
sizeof(K);
2605 return new (allocator.allocate(size)) SharedStringData();
2607 static SharedStringData<K>* from_str(
const K* p) {
2608 return (SharedStringData<K>*)p - 1;
2610 K* place(K* p,
size_t len) {
2611 ch_traits<K>::copy(p, str(), len);
2618class string_common_allocator {
2620 void* allocate(
size_t bytes) {
2621 return new char[bytes];
2623 void deallocate(
void* address)
noexcept {
2624 delete []
static_cast<char*
>(address);
2628string_common_allocator default_string_allocator_selector(...);
2635using allocator_string =
decltype(default_string_allocator_selector(
int(0)));
2637template<
typename K, Allocatorable Allocator>
2669template<
typename K,
size_t N,
bool forShared = false, Allocatorable Allocator = allocator_
string>
2671 public str_algs<K, simple_str<K>, lstring<K, N, forShared, Allocator>, true>,
2672 public str_mutable<K, lstring<K, N, forShared, Allocator>>,
2673 public str_storable<K, lstring<K, N, forShared, Allocator>, Allocator>,
2674 public null_terminated<K, lstring<K, N, forShared, Allocator>>,
2675 public from_utf_convertible<K, lstring<K, N, forShared, Allocator>> {
2677 using symb_type = K;
2679 using allocator_t = Allocator;
2688 extra = forShared ?
sizeof(SharedStringData<K>) : 0,
2691 using base_algs = str_algs<K, simple_str<K>, my_type,
true>;
2692 using base_storable = str_storable<K, my_type, Allocator>;
2693 using base_mutable = str_mutable<K, my_type>;
2694 using base_utf = from_utf_convertible<K, my_type>;
2695 using traits = ch_traits<K>;
2696 using s_str = base_storable::s_str;
2698 friend base_storable;
2699 friend base_mutable;
2701 friend class sstring<K, Allocator>;
2710 K local_[LocalCapacity + 1];
2713 constexpr void create_empty() {
2718 constexpr static size_t calc_capacity(
size_t s) {
2719 size_t real_need = (s + 1) *
sizeof(K) + extra;
2720 size_t aligned_alloced = (real_need +
alignof(std::max_align_t) - 1) /
alignof(std::max_align_t) *
alignof(std::max_align_t);
2721 return (aligned_alloced - extra) /
sizeof(K) - 1;
2724 constexpr K* init(
size_t s) {
2726 if (size_ > LocalCapacity) {
2727 s = calc_capacity(s);
2728 data_ = alloc_place(s);
2736 constexpr bool is_alloced() const noexcept {
2737 return data_ != local_;
2740 constexpr void dealloc() {
2742 base_storable::allocator().deallocate(to_real_address(data_));
2747 constexpr static K* to_real_address(
void* ptr) {
2748 return reinterpret_cast<K*
>(
reinterpret_cast<u8s*
>(ptr) - extra);
2750 constexpr static K* from_real_address(
void* ptr) {
2751 return reinterpret_cast<K*
>(
reinterpret_cast<u8s*
>(ptr) + extra);
2754 constexpr K* alloc_place(
size_t newSize) {
2755 return from_real_address(base_storable::allocator().allocate((newSize + 1) *
sizeof(K) + extra));
2759 constexpr K* alloc_for_copy(
size_t newSize) {
2760 if (capacity() >= newSize) {
2765 return alloc_place(calc_capacity(newSize));
2769 constexpr void set_from_copy(K* ptr,
size_t newSize) {
2775 capacity_ = calc_capacity(newSize);
2782 using base_utf::base_utf;
2790 template<
typename... Args>
2791 requires (std::is_constructible_v<allocator_t, Args...> &&
sizeof...(Args) > 0)
2792 constexpr lstring(Args&&... args)
noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
2793 : base_storable(std::forward<Args>(args)...) {
2805 template<
typename... Args>
2806 requires std::is_constructible_v<allocator_t, Args...>
2807 constexpr lstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2820 template<
typename... Args>
2821 requires std::is_constructible_v<allocator_t, Args...>
2822 constexpr lstring(
size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2835 template<
typename... Args>
2836 requires std::is_constructible_v<allocator_t, Args...>
2837 constexpr lstring(
size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2854 template<
typename... Args>
2855 requires std::is_constructible_v<allocator_t, Args...>
2875 template<StrType<K> From,
typename... Args>
2876 requires std::is_constructible_v<allocator_t, Args...>
2877 constexpr lstring(
const From& f, s_str pattern, s_str repl,
size_t offset = 0,
size_t maxCount = 0, Args&&... args)
2878 : base_storable(std::forward<Args>(args)...) {
2886 constexpr ~lstring() {
2898 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2909 template<
typename... Args>
2910 requires(
sizeof...(Args) > 0 && std::is_convertible_v<allocator_t, Args...>)
2911 constexpr lstring(
const my_type& other, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2913 traits::copy(init(other.size_), other.symbols(), other.size_ + 1);
2924 template<typename T, size_t I = const_lit_for<K, T>::Count,
typename... Args>
2925 requires std::is_constructible_v<allocator_t, Args...>
2926 constexpr lstring(T&& value, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2927 if constexpr (I > 1) {
2928 K* ptr = init(I - 1);
2929 traits::copy(ptr, value, I - 1);
2940 constexpr lstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
2942 size_ = other.size_;
2943 if (other.is_alloced()) {
2944 data_ = other.data_;
2945 capacity_ = other.capacity_;
2948 traits::copy(local_, other.local_, size_ + 1);
2950 other.data_ = other.local_;
2952 other.local_[0] = 0;
2963 template<
typename Op,
typename... Args>
2964 requires(std::is_constructible_v<Allocator, Args...> && (std::is_invocable_v<Op, my_type&> || std::is_invocable_v<Op, K*, size_t>))
2965 lstring(
const Op& op, Args&&... args) : base_storable(std::forward<Args>(args)...) {
2986 if (&other !=
this) {
2988 size_ = other.size_;
3003 if (&other !=
this) {
3005 if (other.is_alloced()) {
3006 data_ = other.data_;
3007 capacity_ = other.capacity_;
3009 traits::copy(data_, other.local_, other.size_ + 1);
3012 size_ = other.size_;
3013 other.create_empty();
3018 my_type& assign(
const K* other,
size_t len) {
3020 bool isIntersect = other >= data_ && other + len <= data_ + size_;
3026 if (other > data_) {
3027 traits::move(data_, other, len);
3030 traits::copy(reserve_no_preserve(len), other, len);
3046 return assign(other.str, other.len);
3056 template<typename T, size_t S = const_lit_for<K, T>::Count>
3058 return assign(other, S - 1);
3071 size_t newLen = expr.
length();
3117 newSize = calc_capacity(newSize);
3118 K* newData = alloc_place(newSize);
3121 capacity_ = newSize;
3139 newSize = calc_capacity(newSize);
3140 K* newData = alloc_place(newSize);
3141 traits::copy(newData, data_, size_);
3144 capacity_ = newSize;
3162 if (newSize > cap) {
3163 size_t needPlace = newSize;
3164 if (needPlace < (cap + 1) * 2) {
3165 needPlace = (cap + 1) * 2 - 1;
3178 return !is_alloced();
3188 for (
size_t i = 0; i < cap; i++) {
3189 if (data_[i] == 0) {
3204 size_t need_capacity = calc_capacity(size_);
3205 if (is_alloced() && capacity_ > need_capacity) {
3206 K* newData = size_ <=
LocalCapacity ? local_ : alloc_place(need_capacity);
3207 traits::copy(newData, data_, size_ + 1);
3212 capacity_ = need_capacity;
3228template<
size_t N = 15>
3229using lstringa = lstring<u8s, N>;
3230template<
size_t N = 15>
3231using lstringb = lstring<ubs, N>;
3232template<
size_t N = 15>
3233using lstringw = lstring<wchar_t, N>;
3234template<
size_t N = 15>
3235using lstringu = lstring<u16s, N>;
3236template<
size_t N = 15>
3237using lstringuu = lstring<u32s, N>;
3239template<
size_t N = 15>
3240using lstringsa = lstring<u8s, N, true>;
3241template<
size_t N = 15>
3242using lstringsb = lstring<ubs, N, true>;
3243template<
size_t N = 15>
3244using lstringsw = lstring<wchar_t, N, true>;
3245template<
size_t N = 15>
3246using lstringsu = lstring<u16s, N, true>;
3247template<
size_t N = 15>
3248using lstringsuu = lstring<u32s, N, true>;
3251template<typename T, typename K = typename const_lit<T>::symb_type>
3252auto getLiteralType(T&&) {
3256template<
size_t Arch,
size_t L>
3257inline constexpr const size_t _local_count = 0;
3260inline constexpr const size_t _local_count<8, 1> = 23;
3262inline constexpr const size_t _local_count<8, 2> = 15;
3264inline constexpr const size_t _local_count<8, 4> = 7;
3266inline constexpr const size_t _local_count<4, 1> = 15;
3268inline constexpr const size_t _local_count<4, 2> = 11;
3270inline constexpr const size_t _local_count<4, 4> = 5;
3273constexpr const size_t local_count = _local_count<
sizeof(size_t),
sizeof(T)>;
3327template<
typename K, Allocatorable Allocator = allocator_
string>
3328class decl_empty_bases sstring :
3329 public str_algs<K, simple_str<K>, sstring<K, Allocator>, false>,
3330 public str_storable<K, sstring<K, Allocator>, Allocator>,
3331 public null_terminated<K, sstring<K, Allocator>>,
3332 public from_utf_convertible<K, sstring<K, Allocator>> {
3334 using symb_type = K;
3335 using uns_type = std::make_unsigned_t<K>;
3336 using my_type = sstring<K, Allocator>;
3337 using allocator_t = Allocator;
3339 enum { LocalCount = local_count<K> };
3342 using base_algs = str_algs<K, simple_str<K>, my_type,
false>;
3344 using base_utf = from_utf_convertible<K, my_type>;
3345 using traits = ch_traits<K>;
3346 using uni = unicode_traits<K>;
3347 using s_str = base_storable::s_str;
3349 friend base_storable;
3352 enum Types { Local, Constant, Shared };
3365 uns_type localRemain_ :
sizeof(uns_type) * CHAR_BIT - 2;
3377 uns_type pad_[LocalCount - (
sizeof(
const K*) +
sizeof(
size_t)) /
sizeof(K)];
3378 uns_type blocalRemain_ :
sizeof(uns_type) * CHAR_BIT - 2;
3379 uns_type btype_ : 2;
3383 constexpr void create_empty() {
3385 localRemain_ = LocalCount;
3389 if (s > LocalCount) {
3397 localRemain_ = LocalCount - s;
3402 K* set_size(
size_t newSize) {
3408 if (newSize !=
size) {
3409 if (type_ == Constant) {
3412 if (newSize <= LocalCount) {
3413 if (type_ == Shared) {
3414 SharedStringData<K>* str_buf = SharedStringData<K>::from_str(sstr_);
3415 traits::copy(buf_, sstr_, newSize);
3419 localRemain_ = LocalCount - newSize;
3421 if (type_ == Shared) {
3422 if (newSize >
size || (newSize > 64 && newSize <=
size * 3 / 4)) {
3424 traits::copy(newStr, sstr_, newSize);
3428 }
else if (type_ == Local) {
3431 traits::copy(newStr, buf_,
size);
3440 K*
str = type_ == Local ? buf_ : (K*)sstr_;
3446 using base_utf::base_utf;
3458 template<
typename... Args>
3459 requires (std::is_constructible_v<allocator_t, Args...> &&
sizeof...(Args) > 0)
3460 sstring(Args&&... args)
noexcept(std::is_nothrow_constructible_v<allocator_t, Args...>)
3461 : base_storable(std::forward<Args>(args)...) {
3473 template<
typename... Args>
3474 requires std::is_constructible_v<allocator_t, Args...>
3475 sstring(s_str other, Args&&... args) : base_storable(std::forward<Args>(args)...), buf_{0} {
3488 template<
typename... Args>
3489 requires std::is_constructible_v<allocator_t, Args...>
3490 sstring(
size_t repeat, s_str pattern, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3503 template<
typename... Args>
3504 requires std::is_constructible_v<allocator_t, Args...>
3505 sstring(
size_t count, K pad, Args&&... args) : base_storable(std::forward<Args>(args)...) {
3522 template<
typename... Args>
3523 requires std::is_constructible_v<allocator_t, Args...>
3543 template<StrType<K> From,
typename... Args>
3544 requires std::is_constructible_v<allocator_t, Args...>
3545 sstring(
const From& f, s_str pattern, s_str repl,
size_t offset = 0,
size_t maxCount = 0, Args&&... args)
3546 : base_storable(std::forward<Args>(args)...) {
3553 if (btype_ == Shared) {
3563 constexpr sstring(
const my_type& other) noexcept : base_storable(other.allocator()) {
3564 memcpy(buf_, other.buf_,
sizeof(buf_) +
sizeof(K));
3565 if (type_ == Shared)
3566 SharedStringData<K>::from_str(sstr_)->incr();
3574 constexpr sstring(my_type&& other) noexcept : base_storable(std::move(other.allocator())) {
3575 memcpy(buf_, other.buf_,
sizeof(buf_) +
sizeof(K));
3576 other.create_empty();
3591 size_t size = src.length();
3593 if (src.is_alloced()) {
3597 if (
size > LocalCount) {
3604 new (SharedStringData<K>::from_str(
str)) SharedStringData<K>();
3609 localRemain_ = LocalCount -
size;
3610 traits::copy(buf_,
str,
size + 1);
3618 K*
str = init(src.size_);
3619 traits::copy(
str, src.symbols(),
size + 1);
3636 template<typename T, size_t N = const_lit_for<K, T>::Count,
typename... Args>
3637 requires std::is_constructible_v<allocator_t, Args...>
3638 constexpr sstring(T&& s, Args&&... args) : base_storable(std::forward<Args>(args)...)
3647 constexpr void swap(my_type&& other)
noexcept {
3648 char buf[
sizeof(buf_) +
sizeof(K)];
3649 memcpy(buf, buf_,
sizeof(buf));
3650 memcpy(buf_, other.buf_,
sizeof(buf));
3651 memcpy(other.buf_, buf,
sizeof(buf));
3653 std::swap(base_storable::allocator(), other.allocator());
3664 swap(std::move(other));
3686 template<typename T, size_t N = const_lit_for<K, T>::Count>
3698 template<
size_t N,
bool forShared,
typename A>
3712 return operator=(my_type{std::move(other)});
3734 if (type_ == Shared)
3741 return btype_ == Local ? buf_ : cstr_;
3745 return btype_ == Local ? LocalCount - blocalRemain_ : bigLen_;
3767 template<
typename... T>
3768 static my_type
printf(
const K* pattern, T&&... args) {
3781 template<
typename... T>
3782 static my_type
format(
const FmtString<
typename to_std_char_type<K>::type, T...>& fmtString, T&&... args) {
3795 template<
typename... T>
3801template<
typename K, Allocatorable Allocator>
3802inline const sstring<K> sstring<K, Allocator>::empty_str{};
3805consteval simple_str_nt<K> select_str(simple_str_nt<u8s> s8, simple_str_nt<uws> sw, simple_str_nt<u16s> s16, simple_str_nt<u32s> s32) {
3806 if constexpr (std::is_same_v<K, u8s>)
3808 if constexpr (std::is_same_v<K, uws>)
3810 if constexpr (std::is_same_v<K, u16s>)
3812 if constexpr (std::is_same_v<K, u32s>)
3816#define uni_string(K, p) select_str<K>(p, L##p, u##p, U##p)
3818template<
typename K,
typename H>
3822 char node[
sizeof(sstring<K>)];
3824 const simple_str_nt<K>& to_nt() const noexcept {
3825 return static_cast<const simple_str_nt<K>&
>(str);
3827 const sstring<K>& to_str() const noexcept {
3828 return *
reinterpret_cast<const sstring<K>*
>(node);
3834 static inline constexpr size_t basis =
static_cast<size_t>(14695981039346656037ULL);
3835 static inline constexpr size_t prime =
static_cast<size_t>(1099511628211ULL);
3839struct fnv_const<false> {
3840 static inline constexpr size_t basis =
static_cast<size_t>(2166136261U);
3841 static inline constexpr size_t prime =
static_cast<size_t>(16777619U);
3844using fnv = fnv_const<
sizeof(size_t) == 8>;
3847inline constexpr size_t fnv_hash(
const K* ptr,
size_t l) {
3848 size_t h = fnv::basis;
3849 for (
size_t i = 0; i < l; i++) {
3850 h = (h ^ (std::make_unsigned_t<K>)ptr[i]) * fnv::prime;
3856inline constexpr size_t fnv_hash_ia(
const K* ptr,
size_t l) {
3857 size_t h = fnv::basis;
3858 for (
size_t i = 0; i < l; i++) {
3859 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)ptr[i];
3860 h = (h ^ (s >=
'A' && s <=
'Z' ? s | 0x20 : s)) * fnv::prime;
3865template<typename T, typename K = typename const_lit<T>::symb_type,
size_t N = const_lit<T>::Count>
3866inline constexpr size_t fnv_hash(T&& value) {
3867 size_t h = fnv::basis;
3868 for (
size_t i = 0; i < N - 1; i++) {
3869 h = (h ^ (std::make_unsigned_t<K>)value[i]) * fnv::prime;
3874template<typename T, typename K = typename const_lit<T>::symb_type,
size_t N = const_lit<T>::Count>
3875inline constexpr size_t fnv_hash_ia(T&& value) {
3876 size_t h = fnv::basis;
3877 for (
size_t i = 0; i < N - 1; i++) {
3878 std::make_unsigned_t<K> s = (std::make_unsigned_t<K>)value[i];
3879 h = (h ^ (s >=
'A' && s <=
'Z' ? s | 0x20 : s)) * fnv::prime;
3885inline consteval size_t fnv_hash_compile(
const K* ptr,
size_t l) {
3886 return fnv_hash(ptr, l);
3890inline consteval size_t fnv_hash_ia_compile(
const K* ptr,
size_t l) {
3891 return fnv_hash_ia(ptr, l);
3894static_assert(std::is_trivially_copyable_v<StoreType<u8s, int>>,
"Store type must be trivially copyable");
3995template<
typename K,
typename T,
typename H = strhash<K>,
typename E = streql<K>>
3996class hashStrMap :
public std::unordered_map<StoreType<K, H>, T, H, E> {
3998 using InStore = StoreType<K, H>;
4001 using my_type = hashStrMap<K, T, H, E>;
4002 using hash_t = std::unordered_map<InStore, T, H, E>;
4005 hashStrMap() =
default;
4006 hashStrMap(
const my_type& other) : hash_t(other) {
4007 for (
const auto& [k, v] : *
this) {
4008 InStore& stored =
const_cast<InStore&
>(k);
4010 new (stored.node)
sstring<K>(std::move(tmp));
4011 stored.str.str = stored.to_str().symbols();
4015 for (
auto& k: *
this)
4019 hashStrMap(my_type&& o) =
default;
4021 my_type& operator=(
const my_type& other) {
4022 hash_t::operator=(other);
4023 for (
const auto& [k, v] : *
this) {
4024 InStore& stored =
const_cast<InStore&
>(k);
4026 new (stored.node)
sstring<K>(std::move(tmp));
4027 stored.str.str = stored.to_str().symbols();
4031 my_type& operator=(my_type&&) =
default;
4033 hashStrMap(std::initializer_list<std::pair<const InStore, T>>&& init) {
4034 for (
const auto& e: init)
4035 emplace(e.first, e.second);
4038 using init_str = std::initializer_list<std::pair<const sstring<K>, T>>;
4040 hashStrMap(init_str&& init) {
4041 for (
const auto& e: init)
4042 emplace(e.first, e.second);
4047 template<
typename... ValArgs>
4048 auto try_emplace(
const InStore& key, ValArgs&&... args) {
4049 auto it = hash_t::try_emplace(key, std::forward<ValArgs>(args)...);
4051 InStore& stored =
const_cast<InStore&
>(it.first->first);
4053 stored.str.str = stored.to_str().symbols();
4059 return {key, H{}(key)};
4062 template<
typename Key,
typename... ValArgs>
4063 requires(std::is_convertible_v<Key, simple_str<K>>)
4064 auto try_emplace(Key&& key, ValArgs&&... args) {
4065 auto it = hash_t::try_emplace(toStoreType(key), std::forward<ValArgs>(args)...);
4067 InStore& stored =
const_cast<InStore&
>(it.first->first);
4068 new (stored.node)
sstring<K>(std::forward<Key>(key));
4069 stored.str.str = stored.to_str().symbols();
4074 template<
typename... ValArgs>
4075 auto emplace(
const InStore& key, ValArgs&&... args) {
4076 auto it = try_emplace(key, std::forward<ValArgs>(args)...);
4078 it.first->second = T(std::forward<ValArgs>(args)...);
4083 template<
typename Key,
typename... ValArgs>
4084 requires(std::is_convertible_v<Key, simple_str<K>>)
4085 auto emplace(Key&& key, ValArgs&&... args) {
4086 auto it = try_emplace(std::forward<Key>(key), std::forward<ValArgs>(args)...);
4088 it.first->second = T(std::forward<ValArgs>(args)...);
4093 auto& operator[](
const InStore& key) {
4094 return try_emplace(key).first->second;
4097 template<
typename Key>
4098 requires(std::is_convertible_v<Key, simple_str<K>>)
4099 auto&
operator[](Key&& key) {
4100 return try_emplace(std::forward<Key>(key)).first->second;
4103 decltype(
auto) at(
const InStore& key) {
4104 return hash_t::at(key);
4106 decltype(
auto) at(
const InStore& key)
const {
4107 return hash_t::at(key);
4111 return hash_t::at(toStoreType(key));
4114 return hash_t::at(toStoreType(key));
4117 auto find(
const InStore& key)
const {
4118 return hash_t::find(key);
4122 return find(toStoreType(key));
4125 auto find(
const InStore& key) {
4126 return hash_t::find(key);
4130 return find(toStoreType(key));
4133 auto erase(
typename hash_t::const_iterator it) {
4134 if (it != hash_t::end()) {
4137 return hash_t::erase(it);
4140 auto erase(
const InStore& key) {
4141 auto it = hash_t::find(key);
4142 if (it != hash_t::end()) {
4151 return erase(toStoreType(key));
4155 auto it = find(txt);
4156 if (it != hash_t::end()) {
4164 for (
auto& k: *
this)
4168 bool contains(
const InStore& key)
const {
4169 return hash_t::find(key) != this->end();
4173 return find(toStoreType(key)) != this->end();
4179 template<
typename H>
4180 bool operator()(
const StoreType<K, H>& _Left,
const StoreType<K, H>& _Right)
const {
4181 return _Left.hash == _Right.hash && _Left.str == _Right.str;
4187 size_t operator()(simple_str<K> _Keyval)
const {
4188 return fnv_hash(_Keyval.symbols(), _Keyval.length());
4190 size_t operator()(
const StoreType<K, strhash<K>>& _Keyval)
const {
4191 return _Keyval.hash;
4197 template<
typename H>
4198 bool operator()(
const StoreType<K, H>& _Left,
const StoreType<K, H>& _Right)
const {
4199 return _Left.hash == _Right.hash && _Left.str.equal_ia(_Right.str);
4205 size_t operator()(simple_str<K> _Keyval)
const {
4206 return fnv_hash_ia(_Keyval.symbols(), _Keyval.length());
4208 size_t operator()(
const StoreType<K, strhashia<K>>& _Keyval)
const {
4209 return _Keyval.hash;
4215 template<
typename H>
4216 bool operator()(
const StoreType<K, H>& _Left,
const StoreType<K, H>& _Right)
const {
4217 return _Left.hash == _Right.hash && _Left.str.equal_iu(_Right.str);
4223 size_t operator()(simple_str<K> _Keyval)
const {
4224 return unicode_traits<K>::hashiu(_Keyval.symbols(), _Keyval.length());
4226 size_t operator()(
const StoreType<K, strhashiu<K>>& _Keyval)
const {
4227 return _Keyval.hash;
4247 using chunk_t = std::pair<std::unique_ptr<K[]>,
size_t>;
4248 std::vector<chunk_t> chunks;
4255 using my_type = chunked_string_builder<K>;
4256 using symb_type = K;
4257 chunked_string_builder() =
default;
4258 chunked_string_builder(
size_t a) : align(a){};
4259 chunked_string_builder(
const my_type&) =
delete;
4260 chunked_string_builder(my_type&& other) noexcept
4261 : chunks(std::move(other.chunks)), write(other.write), len(other.len), remain(other.remain), align(other.align) {
4262 other.len = other.remain = 0;
4263 other.write =
nullptr;
4265 my_type& operator=(my_type other)
noexcept {
4266 chunks.swap(other.chunks);
4267 write = other.write;
4269 remain = other.remain;
4270 align = other.align;
4271 other.len = other.remain = 0;
4272 other.write =
nullptr;
4280 if (
data.len <= remain) {
4283 ch_traits<K>::copy(write,
data.str,
data.len);
4285 chunks.back().second +=
data.len;
4292 ch_traits<K>::copy(write,
data.str, remain);
4295 chunks.back().second += remain;
4301 size_t blockSize = (
data.len + align - 1) / align * align;
4302 chunks.emplace_back(std::make_unique<K[]>(blockSize),
data.len);
4303 write = chunks.back().first.get();
4304 ch_traits<K>::copy(write,
data.str,
data.len);
4306 remain = blockSize -
data.len;
4313 size_t l = expr.
length();
4316 write = expr.place(write);
4317 chunks.back().second += l;
4320 }
else if (!remain) {
4321 size_t blockSize = (l + align - 1) / align * align;
4322 chunks.emplace_back(std::make_unique<K[]>(blockSize), l);
4323 write = expr.place(chunks.back().first.get());
4325 remain = blockSize - l;
4327 auto store = std::make_unique<K[]>(l);
4328 expr.place(store.get());
4335 template<
typename T>
4337 requires std::is_same_v<T, K>
4339 return operator<<(expr_char<K>(
data));
4347 if (chunks.empty()) {
4350 if (chunks.size() > 1) {
4354 remain += chunks[0].second;
4355 chunks[0].second = 0;
4357 write = chunks[0].first.get();
4360 constexpr K* place(K* p)
const noexcept {
4361 for (
const auto& block: chunks) {
4362 ch_traits<K>::copy(p, block.first.get(), block.second);
4375 template<
typename Op>
4377 for (
const auto& block: chunks)
4378 o(block.first.get(), block.second);
4385 if (chunks.size()) {
4386 const K* ptr = chunks.front().first.get();
4387 for (
const auto& chunk: chunks) {
4388 if (chunk.first.get() != ptr)
4390 ptr += chunk.second;
4402 return chunks.size() ? chunks.front().first.get() : simple_str_nt<K>::empty_str.str;
4419 typename decltype(chunks)::const_iterator it, end;
4420 size_t writedFromCurrentChunk;
4440 while (size && !
is_end()) {
4441 size_t remain = it->second - writedFromCurrentChunk;
4442 size_t write = std::min(size, remain);
4443 ch_traits<K>::copy(buffer, it->first.get() + writedFromCurrentChunk, write);
4449 writedFromCurrentChunk = 0;
4451 writedFromCurrentChunk += write;
4463 return {chunks.begin(), chunks.end(), 0};
4476using stringa = sstring<u8s>;
4477using stringb = sstring<ubs>;
4478using stringw = sstring<wchar_t>;
4479using stringu = sstring<u16s>;
4480using stringuu = sstring<u32s>;
4481static_assert(
sizeof(stringa) == (
sizeof(
void*) == 8 ? 24 : 16),
"Bad size of sstring");
4563inline namespace literals {
4632template<
typename K>
using HashKey = StoreType<K, strhash<K>>;
4633template<
typename K>
using HashKeyIA = StoreType<K, strhashia<K>>;
4634template<
typename K>
using HashKeyIU = StoreType<K, strhashiu<K>>;
4646consteval HashKey<u8s>
operator""_h(
const u8s* ptr,
size_t l) {
4647 return HashKey<u8s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4660consteval HashKeyIA<u8s>
operator""_ia(
const u8s* ptr,
size_t l) {
4661 return HashKeyIA<u8s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4674inline HashKeyIU<u8s>
operator""_iu(
const u8s* ptr,
size_t l) {
4675 return HashKeyIU<u8s>{{ptr, l}, strhashiu<u8s>{}(
simple_str<u8s>{ptr, l})};
4688consteval HashKey<u16s>
operator""_h(
const u16s* ptr,
size_t l) {
4689 return HashKey<u16s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4702consteval HashKeyIA<u16s>
operator""_ia(
const u16s* ptr,
size_t l) {
4703 return HashKeyIA<u16s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4716inline HashKeyIU<u16s>
operator""_iu(
const u16s* ptr,
size_t l) {
4717 return HashKeyIU<u16s>{{ptr, l}, strhashiu<u16s>{}(
simple_str<u16s>{ptr, l})};
4730consteval HashKey<u32s>
operator""_h(
const u32s* ptr,
size_t l) {
4731 return HashKey<u32s>{{ptr, l}, fnv_hash_compile(ptr, l)};
4744consteval HashKeyIA<u32s>
operator""_ia(
const u32s* ptr,
size_t l) {
4745 return HashKeyIA<u32s>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4758inline HashKeyIU<u32s>
operator""_iu(
const u32s* ptr,
size_t l) {
4759 return HashKeyIU<u32s>{{ptr, l}, strhashiu<u32s>{}(
simple_str<u32s>{ptr, l})};
4772consteval HashKey<uws>
operator""_h(
const uws* ptr,
size_t l) {
4773 return HashKey<uws>{{ptr, l}, fnv_hash_compile(ptr, l)};
4786consteval HashKeyIA<uws>
operator""_ia(
const uws* ptr,
size_t l) {
4787 return HashKeyIA<uws>{{ptr, l}, fnv_hash_ia_compile(ptr, l)};
4800inline HashKeyIU<uws>
operator""_iu(
const uws* ptr,
size_t l) {
4801 return HashKeyIU<uws>{{ptr, l}, strhashiu<uws>{}(
simple_str<uws>{ptr, l})};
4816 return stream << std::string_view{text.
symbols(), text.
length()};
4829inline std::wostream&
operator<<(std::wostream& stream, ssw text) {
4830 return stream << std::wstring_view{text.
symbols(), text.
length()};
4844 return stream << std::wstring_view{from_w(text.
symbols()), text.
length()};
4857inline std::ostream&
operator<<(std::ostream& stream,
const stringa& text) {
4858 return stream << std::string_view{text.
symbols(), text.
length()};
4871inline std::wostream&
operator<<(std::wostream& stream,
const stringw& text) {
4872 return stream << std::wstring_view{text.
symbols(), text.
length()};
4886 return stream << std::wstring_view{from_w(text.symbols()), text.length()};
4899template<
size_t N,
bool S, simstr::Allocatorable A>
4901 return stream << std::string_view{text.
symbols(), text.
length()};
4914template<
size_t N,
bool S, simstr::Allocatorable A>
4916 return stream << std::wstring_view{text.
symbols(), text.
length()};
4929template<
size_t N,
bool S, simstr::Allocatorable A>
4931 return stream << std::wstring_view{from_w(text.
symbols()), text.
length()};
4941struct std::formatter<
simstr::simple_str<K>, K> : std::formatter<std::basic_string_view<K>, K> {
4943 template<
typename FormatContext>
4945 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
4954struct std::formatter<
simstr::simple_str_nt<K>, K> : std::formatter<std::basic_string_view<K>, K> {
4956 template<
typename FormatContext>
4958 return std::formatter<std::basic_string_view<K>, K>::format({t.str, t.len}, fc);
4967struct std::formatter<
simstr::sstring<K>, K> : std::formatter<std::basic_string_view<K>, K> {
4969 template<
typename FormatContext>
4971 return std::formatter<std::basic_string_view<K>, K>::format({t.
symbols(), t.
length()}, fc);
4979template<
typename K,
size_t N,
bool S,
typename A>
4980struct std::formatter<
simstr::lstring<K, N, S, A>, K> : std::formatter<std::basic_string_view<K>, K> {
4982 template<
typename FormatContext>
4984 return std::formatter<std::basic_string_view<K>, K>::format({t.
symbols(), t.
length()}, fc);
4993struct std::formatter<
simstr::simple_str<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
4995 template<
typename FormatContext>
4997 return std::formatter<std::basic_string_view<char>,
char>::format({(
const char*)t.str, t.len}, fc);
5006struct std::formatter<
simstr::simple_str_nt<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5008 template<
typename FormatContext>
5010 return std::formatter<std::basic_string_view<char>,
char>::format({(
const char*)t.str, t.len}, fc);
5019struct std::formatter<
simstr::sstring<char8_t>, char> : std::formatter<std::basic_string_view<char>, char> {
5021 template<
typename FormatContext>
5023 return std::formatter<std::basic_string_view<char>,
char>::format({(
const char*)t.
symbols(), t.
length()}, fc);
5031template<
size_t N,
bool S,
typename A>
5032struct std::formatter<
simstr::lstring<char8_t, N, S, A>, char> : std::formatter<std::basic_string_view<char>, char> {
5034 template<
typename FormatContext>
5036 return std::formatter<std::basic_string_view<char>,
char>::format({(
const char*)t.
symbols(), t.
length()}, fc);
5045struct std::formatter<
simstr::simple_str<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5047 template<
typename FormatContext>
5049 return std::formatter<std::basic_string_view<wchar_t>,
wchar_t>::format({(
const wchar_t*)t.str, t.len}, fc);
5058struct std::formatter<
simstr::simple_str_nt<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5060 template<
typename FormatContext>
5062 return std::formatter<std::basic_string_view<wchar_t>,
wchar_t>::format({(
const wchar_t*)t.str, t.len}, fc);
5071struct std::formatter<
simstr::sstring<simstr::wchar_type>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5073 template<
typename FormatContext>
5075 return std::formatter<std::basic_string_view<wchar_t>,
wchar_t>::format({(
const wchar_t*)t.
symbols(), t.
length()}, fc);
5083template<
size_t N,
bool S,
typename A>
5084struct std::formatter<
simstr::lstring<simstr::wchar_type, N, S, A>, wchar_t> : std::formatter<std::basic_string_view<wchar_t>, wchar_t> {
5086 template<
typename FormatContext>
5088 return std::formatter<std::basic_string_view<wchar_t>,
wchar_t>::format({(
const wchar_t*)t.
symbols(), t.
length()}, fc);
Class for sequentially obtaining substrings by a given delimiter.
Definition strexpr.h:2351
my_type & operator<<(simple_str< K > data)
Adding a piece of data.
Definition sstring.h:4277
portion_store get_portion() const
Get a portion_store through which data can be sequentially retrieved into an external buffer.
Definition sstring.h:4462
constexpr size_t length() const noexcept
Length of the saved text.
Definition sstring.h:4342
my_type & operator<<(T data)
Adding a symbol.
Definition sstring.h:4336
void reset()
Resets the contents, but does not delete the first buffer in order to avoid allocation later.
Definition sstring.h:4346
void clear()
Clear the object, freeing all allocated buffers.
Definition sstring.h:4408
my_type & operator<<(const StrExprForType< K > auto &expr)
Adding a string expression.
Definition sstring.h:4312
bool is_continuous() const
Checks whether all text is located in one contiguous chunk in memory.
Definition sstring.h:4384
void out(const Op &o) const
Applies a functor to each stored buffer.
Definition sstring.h:4376
const auto & data() const
Get internal data buffers.
Definition sstring.h:4471
const K * begin() const
Get a pointer to the beginning of the first buffer. It makes sense to apply only if is_continuous tru...
Definition sstring.h:4401
Container for more efficient searching by string keys.
Definition sstring.h:3996
The mutable, owning string class. Contains an internal buffer for text of a given size.
Definition sstring.h:2675
constexpr void define_size()
Determine the length of the string. Searches for the character 0 in the string buffer to its capacity...
Definition sstring.h:3186
constexpr lstring(T &&value, Args &&... args)
String literal constructor.
Definition sstring.h:2926
my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3057
constexpr void reset()
Makes the string empty and frees the external buffer, if there was one.
Definition sstring.h:3221
@ LocalCapacity
Definition sstring.h:2683
constexpr bool is_local() const noexcept
Find out whether a local or external buffer is used for characters.
Definition sstring.h:3177
my_type & operator=(my_type &&other) noexcept
Assignment operator by moving from a string of the same type.
Definition sstring.h:3000
constexpr size_t length() const noexcept
String length.
Definition sstring.h:3080
constexpr lstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:2807
constexpr lstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:2877
constexpr lstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:2822
constexpr lstring(const my_type &other)
Copy from another string of the same type.
Definition sstring.h:2896
constexpr lstring(const my_type &other, Args &&... args)
Copy from another string of the same type, but with a different allocator.
Definition sstring.h:2911
my_type & operator=(const StrExprForType< K > auto &expr)
String expression appending operator.
Definition sstring.h:3070
constexpr K * reserve_no_preserve(size_t newSize)
Definition sstring.h:3115
my_type & operator=(const my_type &other)
Copy assignment operator from a string of the same type.
Definition sstring.h:2983
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3092
lstring(const Op &op, Args &&... args)
A fill constructor using a functor (see str_mutable::fill).
Definition sstring.h:2965
constexpr K * set_size(size_t newSize)
Sets the size of the current string, allocating space if necessary.
Definition sstring.h:3160
constexpr lstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:2837
constexpr lstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:2856
constexpr const K * symbols() const noexcept
Pointer to constant characters.
Definition sstring.h:3084
constexpr void clear()
Makes a string empty without changing the string buffer.
Definition sstring.h:3217
my_type & operator=(simple_str< K > other)
Assignment operator from simple_str.
Definition sstring.h:3045
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3096
constexpr void shrink_to_fit()
Reduces the size of the external buffer to the smallest possible size to hold the string....
Definition sstring.h:3203
constexpr lstring(my_type &&other) noexcept
Constructor for moving from a string of the same type.
Definition sstring.h:2940
constexpr K * reserve(size_t newSize)
Allocate a buffer large enough to hold newSize characters plus a terminating null.
Definition sstring.h:3137
constexpr K * str() noexcept
Pointer to a string buffer.
Definition sstring.h:3088
constexpr size_t capacity() const noexcept
Current row buffer capacity.
Definition sstring.h:3100
constexpr lstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Create an empty object.
Definition sstring.h:2792
Immutable owning string class.
Definition sstring.h:3332
constexpr my_type & operator=(const lstring< K, N, forShared, A > &other)
Assignment operator to another string of type lstring.
Definition sstring.h:3699
constexpr bool empty() const noexcept
Whether the string is empty, for compatibility with std::string.
Definition sstring.h:3752
constexpr bool is_empty() const noexcept
Is the string empty?
Definition sstring.h:3748
sstring(s_str other, Args &&... args)
A constructor from another string object.
Definition sstring.h:3475
sstring(size_t count, K pad, Args &&... args)
Character repetition constructor.
Definition sstring.h:3505
constexpr const K * symbols() const noexcept
Pointer to characters in the string.
Definition sstring.h:3740
constexpr sstring(const my_type &other) noexcept
String copy constructor.
Definition sstring.h:3563
constexpr my_type & operator=(T &&other)
String literal assignment operator.
Definition sstring.h:3687
sstring(size_t repeat, s_str pattern, Args &&... args)
String repetition constructor.
Definition sstring.h:3490
constexpr my_type & operator=(my_type other) noexcept
Assignment operator to another string of the same type.
Definition sstring.h:3663
static my_type printf(const K *pattern, T &&... args)
Get a string formatted with std::sprintf.
Definition sstring.h:3768
constexpr sstring(T &&s, Args &&... args)
Initialize from a string literal.
Definition sstring.h:3638
constexpr sstring(lstring< K, N, true, Allocator > &&src)
A move constructor from lstring with an sstring-compatible external buffer.
Definition sstring.h:3590
constexpr size_t length() const noexcept
Line length.
Definition sstring.h:3744
constexpr sstring(my_type &&other) noexcept
Move constructor.
Definition sstring.h:3574
static my_type format(const FmtString< typename to_std_char_type< K >::type, T... > &fmtString, T &&... args)
Get a string formatted with std::format.
Definition sstring.h:3782
static my_type vformat(simple_str< K > fmtString, T &&... args)
Get a string formatted with std::vformat.
Definition sstring.h:3796
constexpr my_type & operator=(simple_str< K > other)
Assignment operator to another string of a different type.
Definition sstring.h:3675
constexpr my_type & make_empty() noexcept
Make the string empty.
Definition sstring.h:3733
constexpr my_type & operator=(const StrExprForType< K > auto &expr)
String expression assignment operator.
Definition sstring.h:3724
constexpr my_type & operator=(lstring< K, N, true, Allocator > &&other)
Assignment operator to a movable string of type lstring with a compatible buffer.
Definition sstring.h:3711
constexpr sstring(const StrExprForType< K > auto &expr, Args &&... args)
Constructor from a string expression.
Definition sstring.h:3524
sstring(Args &&... args) noexcept(std::is_nothrow_constructible_v< allocator_t, Args... >)
Constructor for the empty string.
Definition sstring.h:3460
constexpr ~sstring()
String destructor.
Definition sstring.h:3552
sstring(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Constructor from string source with replacement.
Definition sstring.h:3545
A class with additional constant string algorithms.
Definition sstring.h:168
bool starts_with_iu(str_piece prefix) const noexcept
Whether the string starts with the given substring, case-insensitive Unicode characters of the first ...
Definition sstring.h:241
int compare_iu(str_piece text) const noexcept
Compare strings character by character without taking into account the case of Unicode characters of ...
Definition sstring.h:205
bool less_iu(str_piece text) const noexcept
Whether a string is smaller than another string, character-by-character-insensitive,...
Definition sstring.h:227
constexpr bool ends_with_iu(str_piece suffix) const noexcept
Whether the string ends with the specified substring, case-insensitive Unicode characters of the firs...
Definition sstring.h:256
R upperred() const
Get a copy of the string in upper case Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:268
R lowered() const
Get a copy of the string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:280
std::optional< double > to_double() const noexcept
Convert string to double.
Definition sstring.h:290
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition sstring.h:348
bool equal_iu(str_piece text) const noexcept
Whether a string is equal to another string, character-by-character-insensitive, of the Unicode chara...
Definition sstring.h:216
void as_number(double &t) const
Convert string to double.
Definition sstring.h:331
Base class for working with mutable strings.
Definition sstring.h:1442
Impl & insert(size_t to, const A &expr)
Insert a string expression at the specified position.
Definition sstring.h:1982
Impl & operator<<=(const Op &fillFunction)
Fills a string with the fill method after the end of the string.
Definition sstring.h:2256
Impl & append(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:1866
Impl & operator<<(const Op &fillFunction)
Calls the passed functor, passing a reference to itself.
Definition sstring.h:2269
Impl & trim(str_piece pattern)
Remove characters included in the passed string at the beginning and end of the line.
Definition sstring.h:1676
Impl & upper_only_ascii()
Convert ASCII characters to uppercase.
Definition sstring.h:1740
Impl & lower_only_ascii()
Convert ASCII characters to lowercase.
Definition sstring.h:1755
Impl & trim_left()
Remove whitespace at the beginning of a line.
Definition sstring.h:1578
Impl & append_printf(const K *format, T &&... args)
Appends sprintf formatted output to the end of the line.
Definition sstring.h:2358
Impl & trim_right_with_wpaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the end of a string.
Definition sstring.h:1665
Impl & trim_left(str_piece pattern)
Remove characters included in the passed string at the beginning of the line.
Definition sstring.h:1687
Impl & trim_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning and end of th...
Definition sstring.h:1639
Impl & prepend(str_piece other)
Add another line to the beginning of the line.
Definition sstring.h:2006
Impl & append_formatted(const FmtString< fmt_type, T... > &format, T &&... args)
Appends std::format-formatted output to the end of the line.
Definition sstring.h:2496
my_type & format(const FmtString< fmt_type, T... > &pattern, T &&... args)
Definition sstring.h:2480
Impl & printf(const K *format, T &&... args)
Formats a string using sprintf.
Definition sstring.h:2342
Impl & with(const Op &fillFunction, Args &&... args)
Call a functor with a string and passed arguments.
Definition sstring.h:2578
Impl & append_vformatted_n(size_t max_write, str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line, writing no more than the specified numb...
Definition sstring.h:2564
Impl & operator<<(const Op &fillFunction)
Fills a string with the fill method from position zero.
Definition sstring.h:2243
Impl & vformat(str_piece format, T &&... args)
Formats a string using std::vformat.
Definition sstring.h:2512
Impl & change(size_t from, size_t len, const A &expr)
Replace a piece of string with a string expression.
Definition sstring.h:1955
Impl & fill(size_t from, const Op &fillFunction)
Fill a string buffer using a functor.
Definition sstring.h:2215
Impl & change(size_t from, size_t len, str_piece other)
Replace a piece of string with another string.
Definition sstring.h:1939
Impl & remove(size_t from, size_t len)
Remove part of a line.
Definition sstring.h:1995
Impl & trim_left_with_spaces(T &&pattern)
Remove characters included in a string literal, as well as whitespace, at the beginning of a line.
Definition sstring.h:1652
Impl & trim_left(T &&pattern)
Remove characters included in a string literal at the beginning of the line.
Definition sstring.h:1613
Impl & trim_right(T &&pattern)
Remove characters included in a string literal at the end of the line.
Definition sstring.h:1626
Impl & trim_right_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace at the end of the string.
Definition sstring.h:1731
Impl & append_in(size_t pos, str_piece other)
Add another line starting at the given position.
Definition sstring.h:1906
Impl & vformat_from(size_t from, size_t max_write, str_piece format, T &&... args)
Appends std::vformat formatted output starting at the specified position.
Definition sstring.h:2445
Impl & trim_left_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace, at the beginning of the strin...
Definition sstring.h:1720
Impl & insert(size_t to, str_piece other)
Insert a line at the specified position.
Definition sstring.h:1968
Impl & vformat_n(size_t max_write, str_piece format, T &&... args)
Formats a string using std::vformat up to the specified size.
Definition sstring.h:2546
Impl & printf_from(size_t from, const K *format, T &&... args)
Appends sprintf formatted output starting at the specified position.
Definition sstring.h:2288
Impl & operator+=(str_piece other)
Add another line to the end of the line.
Definition sstring.h:1877
Impl & trim_right()
Remove whitespace from the end of a line.
Definition sstring.h:1587
Impl & append(str_piece other)
Add another line to the end of the line.
Definition sstring.h:1854
Impl & trim_right(str_piece pattern)
Remove characters included in the passed string from the end of the string.
Definition sstring.h:1698
Impl & trim_with_spaces(str_piece pattern)
Remove characters included in the passed string, as well as whitespace characters,...
Definition sstring.h:1709
K * str() noexcept
Get a pointer to the string buffer.
Definition sstring.h:1551
Impl & prepend(const A &expr)
Add a string expression to the beginning of a line.
Definition sstring.h:2018
Impl & trim()
Remove whitespace from the beginning and end of a line.
Definition sstring.h:1569
Impl & lower()
Convert first plane characters (<0xFFFF) to lowercase Unicode.
Definition sstring.h:1789
Impl & replace(str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Replace occurrences of a substring with another string.
Definition sstring.h:2035
Impl & upper()
Convert first plane characters (<0xFFFF) to uppercase Unicode.
Definition sstring.h:1774
Impl & replace_from(const From &f, str_piece pattern, str_piece repl, size_t offset=0, size_t maxCount=0)
Copy the source string, replacing occurrences of substrings with another string.
Definition sstring.h:2153
Impl & append_vformatted(str_piece format, T &&... args)
Appends std::vformat-formatted output to the end of the line.
Definition sstring.h:2528
Impl & append_in(size_t pos, const A &expr)
Add a string expression starting at the given position.
Definition sstring.h:1924
my_type & format_from(size_t from, const FmtString< fmt_type, T... > &format, T &&... args)
Definition sstring.h:2416
Impl & trim(T &&pattern)
Remove characters included in a string literal at the beginning and end of the line.
Definition sstring.h:1600
Impl & operator+=(const A &expr)
Add a string expression to the end of the line.
Definition sstring.h:1889
constexpr str_piece to_str() const noexcept
Convert itself to a "string chunk" that includes the entire string.
Definition strexpr.h:2539
constexpr size_t size() const
Definition strexpr.h:2480
constexpr void as_number(T &t) const
Convert a string to an integer.
Definition strexpr.h:3323
constexpr str_storable(Args &&... args)
Create an empty object.
Definition sstring.h:937
void init_replaced(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0)
Initialization from string source with replacement.
Definition sstring.h:1024
static my_type upperred_from(const From &f, Args &&... args)
Create a copy of the passed string in uppercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1285
constexpr void init_from_str_other(s_str other)
Initialization from another string object.
Definition sstring.h:945
constexpr allocator_t & allocator()
Get the allocator.
Definition sstring.h:914
constexpr void init_symb_repeat(size_t count, K pad)
Character repetition initialization.
Definition sstring.h:981
constexpr void init_str_expr(const A &expr)
Initialization from a string expression.
Definition sstring.h:1002
static my_type upperred_only_ascii_from(const From &f, Args &&... args)
Create a string copy of the passed in uppercase ASCII characters.
Definition sstring.h:1255
static my_type lowered_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase Unicode characters of the first plane (<0xFFFF).
Definition sstring.h:1302
constexpr void init_str_repeat(size_t repeat, s_str pattern)
String repetition initialization.
Definition sstring.h:961
static my_type lowered_only_ascii_from(const From &f, Args &&... args)
Create a copy of the passed string in lowercase ASCII characters.
Definition sstring.h:1268
static my_type join(const T &strings, s_str delimiter, bool tail=false, bool skip_empty=false, Args &&... args)
Concatenate strings from the container into one string.
Definition sstring.h:1204
static my_type replaced_from(const From &f, s_str pattern, s_str repl, size_t offset=0, size_t maxCount=0, Args &&... args)
Create a copy of the passed string with substrings replaced.
Definition sstring.h:1323
constexpr s_str_nt to_nts(size_t from=0) const
Get simple_str_nt starting at the given character.
Definition sstring.h:1148
Concept of a memory management type.
Definition sstring.h:1333
The concept of a string expression compatible with a given character type.
Definition strexpr.h:418
A type concept that cannot modify a stored string.
Definition sstring.h:856
A type concept that can modify a stored string.
Definition sstring.h:849
A type concept that can store a string.
Definition sstring.h:839
Small namespace for standard string methods.
Definition strexpr.h:1393
Library namespace.
Definition sstring.cpp:12
hashStrMap< u16s, T, strhash< u16s >, streql< u16s > > hashStrMapU
Hash dictionary type for char16_t strings, case sensitive search.
Definition sstring.h:4528
hashStrMap< u8s, T, strhashia< u8s >, streqlia< u8s > > hashStrMapAIA
Type of hash dictionary for char strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4494
hashStrMap< u32s, T, strhashiu< u32s >, streqliu< u32s > > hashStrMapUUIU
Hash dictionary type for char32_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4559
hashStrMap< u32s, T, strhashia< u32s >, streqlia< u32s > > hashStrMapUUIA
Hash dictionary type for char32_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4553
hashStrMap< wchar_t, T, strhashiu< wchar_t >, streqliu< wchar_t > > hashStrMapWIU
Hash dictionary type for wchar_t strings, case insensitive search for Unicode characters up to 0xFFFF...
Definition sstring.h:4521
hashStrMap< wchar_t, T, strhashia< wchar_t >, streqlia< wchar_t > > hashStrMapWIA
Hash dictionary type for wchar_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4514
expr_utf< From, To > e_utf(simple_str< From > from)
Returns a string expression that converts a string of one character type to another type,...
Definition sstring.h:830
hashStrMap< u8s, T, strhash< u8s >, streql< u8s > > hashStrMapA
Type of hash dictionary for char strings, case sensitive search.
Definition sstring.h:4488
hashStrMap< u16s, T, strhashiu< u16s >, streqliu< u16s > > hashStrMapUIU
Hash dictionary type for char16_t strings, case insensitive search for Unicode characters up to 0xFFF...
Definition sstring.h:4540
hashStrMap< u16s, T, strhashia< u16s >, streqlia< u16s > > hashStrMapUIA
Hash dictionary type for char16_t strings, case-insensitive lookup for ASCII characters.
Definition sstring.h:4534
hashStrMap< wchar_t, T, strhash< wchar_t >, streql< wchar_t > > hashStrMapW
Hash dictionary type for wchar_t strings, case sensitive search.
Definition sstring.h:4507
hashStrMap< u8s, T, strhashiu< u8s >, streqliu< u8s > > hashStrMapAIU
Hash dictionary type for char strings, case-insensitive search for Unicode characters up to 0xFFFF.
Definition sstring.h:4500
hashStrMap< u32s, T, strhash< u32s >, streql< u32s > > hashStrMapUU
Hash dictionary type for char32_t strings, case sensitive search.
Definition sstring.h:4547
An object that allows you to sequentially copy content into a buffer of a given size.
Definition sstring.h:4418
bool is_end()
Check that the data has not yet run out.
Definition sstring.h:4425
size_t store(K *buffer, size_t size)
Save the next portion of data to the buffer.
Definition sstring.h:4438
Base class for converting string expressions to standard strings.
Definition strexpr.h:468
String expression to convert strings to different UTF types.
Definition sstring.h:801
A class that claims to refer to a null-terminated string.
Definition sstring.h:511
constexpr my_type to_nts(size_t from)
Get a null-terminated string by shifting the start by the specified number of characters.
Definition sstring.h:574
constexpr simple_str_nt(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:563
constexpr simple_str_nt(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:545
constexpr simple_str_nt(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:551
constexpr simple_str_nt(T &&p) noexcept
Explicit constructor from C-string.
Definition sstring.h:536
The simplest immutable non-owning string class.
Definition sstring.h:375
constexpr simple_str(T &&v) noexcept
Constructor from a string literal.
Definition sstring.h:391
constexpr K operator[](size_t idx) const
Get the character from the specified position. Bounds checking is not performed.
Definition sstring.h:455
constexpr my_type & remove_suffix(size_t delta)
Shortens the string by the specified number of characters.
Definition sstring.h:479
constexpr size_t length() const noexcept
Get the length of the string.
Definition sstring.h:412
constexpr const symb_type * symbols() const noexcept
Get a pointer to a constant buffer containing string characters.
Definition sstring.h:419
constexpr my_type & remove_prefix(size_t delta)
Shifts the start of a line by the specified number of characters.
Definition sstring.h:466
constexpr simple_str(const std::basic_string_view< K, std::char_traits< K > > &s) noexcept
Constructor from std::basic_string_view.
Definition sstring.h:407
constexpr simple_str(const K *p, size_t l) noexcept
Constructor from pointer and length.
Definition sstring.h:396
constexpr simple_str(const std::basic_string< K, std::char_traits< K >, A > &s) noexcept
Constructor from std::basic_string.
Definition sstring.h:402
constexpr bool is_empty() const noexcept
Check if a string is empty.
Definition sstring.h:426
constexpr bool is_same(simple_str< K > other) const noexcept
Check if two objects point to the same string.
Definition sstring.h:435
constexpr bool is_part_of(simple_str< K > other) const noexcept
Check if a string is part of another string.
Definition sstring.h:444
The simplest class of an immutable non-owning string.
Definition strexpr.h:3924