plugify 1.2.6
Loading...
Searching...
No Matches
string.hpp
1#pragma once
2
3// Just in case, because we can't ignore some warnings from `-Wpedantic` (about zero size arrays and anonymous structs when gnu extensions are disabled) on gcc
4#if PLUGIFY_COMPILER_CLANG
5# pragma clang system_header
6#elif PLUGIFY_COMPILER_GCC
7# pragma GCC system_header
8#endif
9
10#include <initializer_list> // for std::initializer_list
11#include <string_view> // for std::basic_string_view
12#include <type_traits> // for std::is_constant_evaluated, std::declval, std::false_type
13#include <algorithm> // for std::min, std::max
14#include <concepts> // for std::unsigned_integral, std::signed_integral
15#include <iterator> // for std::distance, std::next, std::iterator_traits, std::input_iterator
16#include <utility> // for std::move, std::hash
17#include <compare> // for std::strong_ordering
18#include <memory> // for std::allocator, std::swap, std::allocator_traits
19#include <limits> // for std::numeric_limits
20#include <charconv> // for std::to_chars
21
22#include <cstdint>
23#include <cstddef>
24#include <cstdarg>
25
26#if PLUGIFY_STRING_CONTAINERS_RANGES && (__cplusplus <= 202002L || !__has_include(<ranges>) || !defined(__cpp_lib_containers_ranges))
27# undef PLUGIFY_STRING_CONTAINERS_RANGES
28# define PLUGIFY_STRING_CONTAINERS_RANGES 0
29#endif
30
31#if PLUGIFY_STRING_CONTAINERS_RANGES
32# include <ranges>
33#endif
34
35#ifndef PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
36# include <cstdlib>
37#endif
38
39#ifndef PLUGIFY_STRING_NO_STD_FORMAT
40# include "compat_format.hpp"
41#endif
42
43#include "allocator.hpp"
44
45namespace plg {
46 namespace detail {
47 template<typename Allocator, typename = void>
48 struct is_allocator : std::false_type {};
49
50 template<typename Allocator>
51 struct is_allocator<Allocator, std::void_t<typename Allocator::value_type, decltype(std::declval<Allocator&>().allocate(std::size_t{}))>> : std::true_type {};
52
53 template<typename Allocator>
54 constexpr bool is_allocator_v = is_allocator<Allocator>::value;
55
56 template<typename Traits>
57 using is_traits = std::conjunction<std::is_integral<typename Traits::char_type>, std::is_integral<typename Traits::char_traits::char_type>>;
58
59 template<typename Traits>
60 constexpr bool is_traits_v = is_traits<Traits>::value;
61
63
64 template<typename>
65 constexpr bool dependent_false = false;
66
67#if PLUGIFY_STRING_CONTAINERS_RANGES
68 template<typename Range, typename Type>
69 concept string_compatible_range = std::ranges::input_range<Range> && std::convertible_to<std::ranges::range_reference_t<Range>, Type>;
70#endif // PLUGIFY_STRING_CONTAINERS_RANGES
71 }// namespace detail
72
73 // basic_string
74 // based on implementations from libc++, libstdc++ and Microsoft STL
75 template<typename Char, typename Traits = std::char_traits<Char>, typename Allocator = plg::allocator<Char>> requires (detail::is_traits_v<Traits> && detail::is_allocator_v<Allocator>)
77 private:
78 using allocator_traits = std::allocator_traits<Allocator>;
79 public:
80 using traits_type = Traits;
81 using value_type = typename traits_type::char_type;
83 using size_type = typename allocator_traits::size_type;
84 using difference_type = typename allocator_traits::difference_type;
85 using reference = value_type&;
86 using const_reference = const value_type&;
87 using pointer = typename allocator_traits::pointer;
88 using const_pointer = typename allocator_traits::const_pointer;
89 using iterator = pointer;
90 using const_iterator = const_pointer;
91 using reverse_iterator = std::reverse_iterator<iterator>;
92 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
93 using sview_type = std::basic_string_view<Char, Traits>;
94
95 constexpr static size_type npos = static_cast<size_t>(-1);
96
97 private:
98 constexpr static auto _terminator = value_type();
99
100 PLUGIFY_NO_UNIQUE_ADDRESS
101 allocator_type _allocator;
102
104
105#if PLUGIFY_COMPILER_CLANG
106 PLUGIFY_WARN_IGNORE("-Wgnu-anonymous-struct")
107 PLUGIFY_WARN_IGNORE("-Wzero-length-array")
108#elif PLUGIFY_COMPILER_GCC
109 PLUGIFY_WARN_IGNORE("-Wpedantic")
110#elif PLUGIFY_COMPILER_MSVC
113#endif
114
115 template<typename CharT, std::size_t = sizeof(CharT)>
116 struct padding {
117 [[maybe_unused]] uint8_t pad[sizeof(CharT) - 1];
118 };
119
120 template<typename CharT>
121 struct padding<CharT, 1> {
122 // template specialization to remove the padding structure to avoid warnings on zero length arrays
123 // also, this allows us to take advantage of the empty-base-class optimization.
124 };
125
126 // size must correspond to the last byte of long_data.cap, so we don't want the compiler to insert
127 // padding after size if sizeof(value_type) != 1; Also ensures both layouts are the same size.
128 struct sso_size : padding<value_type> {
129 PLUGIFY_PACK(struct {
130 uint8_t spare_size : 7;
131 uint8_t is_long : 1;
132 });
133 };
134
135 static constexpr int char_bit = std::numeric_limits<uint8_t>::digits + std::numeric_limits<uint8_t>::is_signed;
136 static_assert(char_bit == 8, "assumes an 8 bit byte.");
137
138 struct long_data {
139 pointer data;
140 size_type size;
141 PLUGIFY_PACK(struct {
142 size_type cap : sizeof(size_type) * char_bit - 1;
143 size_type is_long : 1;
144 });
145 };
146
147 static constexpr size_type min_cap = (sizeof(long_data) - 1) / sizeof(value_type) > 2 ? (sizeof(long_data) - 1) / sizeof(value_type) : 2;
148
149 struct short_data {
150 value_type data[min_cap];
151 sso_size size;
152 };
153
155
156 static_assert(sizeof(short_data) == (sizeof(value_type) * (min_cap + 1)), "short has an unexpected size.");
157 static_assert(sizeof(short_data) == sizeof(long_data), "short and long layout structures must be the same size");
158
159 union {
160 long_data _long;
161 short_data _short{};
162 } _storage;
163
164 constexpr static bool fits_in_sso(size_type size) noexcept {
165 return size < min_cap;
166 }
167
168 constexpr void long_init() noexcept {
169 set_long(true);
170 set_long_data(nullptr);
171 set_long_size(0);
172 set_long_cap(0);
173 }
174
175 constexpr void short_init() noexcept {
176 set_long(false);
177 set_short_size(0);
178 }
179
180 constexpr void default_init(size_type size) noexcept {
181 if (fits_in_sso(size))
182 short_init();
183 else
184 long_init();
185 }
186
187 constexpr auto& get_long_data() noexcept {
188 return _storage._long.data;
189 }
190
191 constexpr const auto& get_long_data() const noexcept {
192 return _storage._long.data;
193 }
194
195 constexpr auto& get_short_data() noexcept {
196 return _storage._short.data;
197 }
198
199 constexpr const auto& get_short_data() const noexcept {
200 return _storage._short.data;
201 }
202
203 constexpr void set_short_size(size_type size) noexcept {
204 _storage._short.size.spare_size = min_cap - (size & 0x7F);
205 }
206
207 constexpr size_type get_short_size() const noexcept {
208 return min_cap - _storage._short.size.spare_size;
209 }
210
211 constexpr void set_long_size(size_type size) noexcept {
212 _storage._long.size = size;
213 }
214
215 constexpr size_type get_long_size() const noexcept {
216 return _storage._long.size;
217 }
218
219 constexpr void set_long_cap(size_type cap) noexcept {
220 _storage._long.cap = (cap & 0x7FFFFFFFFFFFFFFF);
221 }
222
223 constexpr size_type get_long_cap() const noexcept {
224 return _storage._long.cap;
225 }
226
227 constexpr void set_long_data(value_type* data) noexcept {
228 _storage._long.data = data;
229 }
230
231 constexpr bool is_long() const noexcept {
232 return _storage._long.is_long == true;
233 }
234
235 constexpr void set_long(bool is_long) noexcept {
236 _storage._long.is_long = is_long;
237 }
238
239 constexpr void set_size(size_type size) noexcept {
240 if (is_long())
241 set_long_size(size);
242 else
243 set_short_size(size);
244 }
245
246 constexpr sview_type view() const noexcept {
247 return sview_type(data(), size());
248 }
249
250 constexpr void reallocate(size_type new_cap, bool copy_old) {
251 if (new_cap == get_long_cap())
252 return;
253
254 auto old_len = get_long_size();
255 auto old_cap = get_long_cap();
256 auto& old_buffer = get_long_data();
257
258 auto new_len = std::min(new_cap, old_len);
259 auto new_data = allocator_traits::allocate(_allocator, new_cap + 1);
260
261 if (old_buffer != nullptr) {
262 if (old_len != 0 && copy_old)
263 Traits::copy(new_data, old_buffer, new_len);
264 allocator_traits::deallocate(_allocator, old_buffer, old_cap + 1);
265 }
266
267 set_long_data(new_data);
268 set_long_size(new_len);
269 set_long_cap(new_cap);
270 }
271
272 constexpr void deallocate() {
273 if (is_long()) {
274 if (auto& buffer = get_long_data(); buffer != nullptr) {
275 allocator_traits::deallocate(_allocator, buffer, get_long_cap() + 1);
276 buffer = nullptr;
277 }
278 }
279 }
280
281 constexpr void grow_to(size_type new_cap) {
282 if (is_long() == true) {
283 reallocate(new_cap, true);
284 return;
285 }
286
287 auto buffer = allocator_traits::allocate(_allocator, new_cap + 1);
288 auto len = get_short_size();
289
290 Traits::copy(buffer, get_short_data(), len);
291 Traits::assign(buffer[len], _terminator);
292
293 long_init();
294 set_long_data(buffer);
295 set_long_size(len);
296 set_long_cap(new_cap);
297 }
298
299 constexpr void null_terminate() {
300 auto buffer = data();
301 if (buffer == nullptr) [[unlikely]]
302 return;
303 Traits::assign(buffer[size()], _terminator);
304 }
305
306 constexpr bool addr_in_range(const_pointer ptr) const noexcept {
307 if (std::is_constant_evaluated())
308 return false;
309 else
310 return data() <= ptr && ptr <= data() + size();
311 }
312
313 template<typename F>
314 constexpr void internal_replace_impl(const F& func, size_type pos, size_type oldcount, size_type count) {
315 auto cap = capacity();
316 auto sz = size();
317
318 auto rsz = sz - oldcount + count;
319
320 if (cap < rsz)
321 grow_to(rsz);
322
323 if (oldcount != count)
324 Traits::move(data() + pos + count, data() + pos + oldcount, sz - pos - oldcount);
325
326 func();
327
328 set_size(rsz);
329 null_terminate();
330 }
331
332 constexpr void internal_replace(size_type pos, const_pointer str, size_type oldcount, size_type count) {
333 if (addr_in_range(str)) {
335 internal_replace_impl([&]() { Traits::copy(data() + pos, rstr.data(), count); }, pos, oldcount, count);
336 } else
337 internal_replace_impl([&]() { Traits::copy(data() + pos, str, count); }, pos, oldcount, count);
338 }
339
340 constexpr void internal_replace(size_type pos, value_type ch, size_type oldcount, size_type count) {
341 internal_replace_impl([&]() { Traits::assign(data() + pos, count, ch); }, pos, oldcount, count);
342 }
343
344 template<typename F>
345 constexpr void internal_insert_impl(const F& func, size_type pos, size_type count) {
346 if (count == 0) [[unlikely]]
347 return;
348
349 auto cap = capacity();
350 auto sz = size();
351 auto rsz = sz + count;
352
353 if (cap < rsz)
354 grow_to(rsz);
355
356 Traits::move(data() + pos + count, data() + pos, sz - pos);
357 func();
358
359 set_size(rsz);
360 null_terminate();
361 }
362
363 constexpr void internal_insert(size_type pos, const_pointer str, size_type count) {
364 if (addr_in_range(str)) {
366 internal_insert_impl([&]() { Traits::copy(data() + pos, rstr.data(), count); }, pos, count);
367 } else
368 internal_insert_impl([&]() { Traits::copy(data() + pos, str, count); }, pos, count);
369 }
370
371 constexpr void internal_insert(size_type pos, value_type ch, size_type count) {
372 internal_insert_impl([&]() { Traits::assign(data() + pos, count, ch); }, pos, count);
373 }
374
375 template<typename F>
376 constexpr void internal_append_impl(const F& func, size_type count) {
377 if (count == 0) [[unlikely]]
378 return;
379
380 auto cap = capacity();
381 auto sz = size();
382 auto rsz = sz + count;
383
384 if (cap < rsz)
385 grow_to(rsz);
386
387 func(sz);
388 set_size(rsz);
389 null_terminate();
390 }
391
392 constexpr void internal_append(const_pointer str, size_type count) {
393 if (addr_in_range(str)) {
395 internal_append_impl([&](size_type pos) { Traits::copy(data() + pos, rstr.data(), count); }, count);
396 } else
397 internal_append_impl([&](size_type pos) { Traits::copy(data() + pos, str, count); }, count);
398 }
399
400 constexpr void internal_append(value_type ch, size_type count) {
401 internal_append_impl([&](size_type pos) { Traits::assign(data() + pos, count, ch); }, count);
402 }
403
404 template<typename F>
405 constexpr void internal_assign_impl(const F& func, size_type size, bool copy_old) {
406 if (fits_in_sso(size)) {
407 if (is_long() == true) {
408 deallocate();
409 short_init();
410 }
411
412 set_short_size(size);
413 func(get_short_data());
414 null_terminate();
415 } else {
416 if (is_long() == false)
417 long_init();
418 if (get_long_cap() < size)
419 reallocate(size, copy_old);
420
421 func(get_long_data());
422 set_long_size(size);
423 null_terminate();
424 }
425 }
426
427 constexpr void internal_assign(const_pointer str, size_type size, bool copy_old = false) {
428 if (addr_in_range(str)) {
429 basic_string rstr(str, size);
430 internal_assign_impl([&](auto data) { Traits::copy(data, rstr.data(), size); }, size, copy_old);
431 } else
432 internal_assign_impl([&](auto data) { Traits::copy(data, str, size); }, size, copy_old);
433 }
434
435 constexpr void internal_assign(value_type ch, size_type count, bool copy_old = false) {
436 internal_assign_impl([&](auto data) { Traits::assign(data, count, ch); }, count, copy_old);
437 }
438
439 public:
440 explicit constexpr basic_string(detail::uninitialized_size_tag, size_type size, const Allocator& allocator)
441 : _allocator(allocator) {
442 PLUGIFY_ASSERT(size <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
443 if (fits_in_sso(size))
444 short_init();
445 else {
446 long_init();
447 reallocate(size, false);
448 }
449 set_size(size);
450 }
451
452 constexpr basic_string() noexcept(std::is_nothrow_default_constructible<Allocator>::value)
453 : basic_string(Allocator()) {}
454
455 explicit constexpr basic_string(const Allocator& allocator) noexcept
456 : _allocator(allocator) {
457 short_init();
458 }
459
460 constexpr basic_string(size_type count, value_type ch, const Allocator& allocator = Allocator())
461 : _allocator(allocator) {
462 PLUGIFY_ASSERT(count <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
463 internal_assign(ch, count);
464 }
465
466 constexpr basic_string(const basic_string& str, size_type pos, size_type count, const Allocator& allocator = Allocator())
467 : _allocator(allocator) {
468 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::basic_string(): pos out of range", std::out_of_range);
469 auto len = std::min(count, str.size() - pos);
470 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
471 internal_assign(str.data() + pos, len);
472 }
473 constexpr basic_string(const basic_string& str, size_type pos, const Allocator& allocator = Allocator())
474 : basic_string(str, pos, npos, allocator) {}
475
476 constexpr basic_string(const value_type* str, size_type count, const Allocator& allocator = Allocator())
477 : _allocator(allocator) {
478 PLUGIFY_ASSERT(count <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
479 internal_assign(str, count);
480 }
481
482 constexpr basic_string(const value_type* str, const Allocator& allocator = Allocator())
483 : basic_string(str, Traits::length(str), allocator) {}
484
485 template<std::input_iterator InputIterator>
487 : _allocator(allocator) {
488 auto len = size_type(std::distance(first, last));
489 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
490 internal_assign(const_pointer(first), len);
491 }
492
493 constexpr basic_string(const basic_string& str, const Allocator& allocator)
494 : _allocator(allocator) {
495 auto len = str.length();
496 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
497 internal_assign(str.data(), len);
498 }
499 constexpr basic_string(const basic_string& str)
500 : basic_string(str, str.get_allocator()) {}
501
502 constexpr basic_string(basic_string&& str) noexcept(std::is_nothrow_move_constructible<Allocator>::value)
503 : _allocator(std::move(str._allocator)), _storage(std::move(str._storage)) {
504 str.short_init();
505 }
506
507 constexpr basic_string(basic_string&& str, const Allocator& allocator)
508 : _allocator(allocator) {
509 if constexpr (allocator_traits::is_always_equal::value) {
510 std::swap(_storage, str._storage);
511 } else {
512 if (!str.is_long() || get_allocator() == str.get_allocator()) {
513 std::swap(_storage, str._storage);
514 } else {
515 internal_assign(str.data(), str.size());
516 str.deallocate();
517 }
518 }
519 str.short_init();
520 }
521
522 constexpr basic_string(std::initializer_list<value_type> list, const Allocator& allocator = Allocator())
523 : _allocator(allocator) {
524 auto len = list.size();
525 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
526 internal_assign(const_pointer(list.begin()), len);
527 }
528
529 template<typename Type>
530 requires (std::is_convertible_v<const Type&, sview_type>)
531 constexpr basic_string(const Type& t, size_type pos, size_type count, const Allocator& allocator = Allocator())
532 : _allocator(allocator) {
533 auto sv = sview_type(t);
534 PLUGIFY_ASSERT(pos <= sv.length(), "plg::basic_string::basic_string(): pos out of range", std::out_of_range);
535 auto ssv = sv.substr(pos, count);
536 auto len = ssv.length();
537 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
538 internal_assign(ssv.data(), len);
539 }
540
541 template<typename Type>
542 requires (std::is_convertible_v<const Type&, sview_type> &&
543 !std::is_convertible_v<const Type&, const Char*>)
544 constexpr basic_string(const Type& t, const Allocator& allocator = Allocator())
545 : _allocator(allocator) {
546 sview_type sv(t);
547 auto len = sv.length();
548 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
549 internal_assign(sv.data(), len);
550 }
551
552 constexpr basic_string(basic_string&& str, size_type pos, size_type count, const Allocator& allocator = Allocator())
553 : basic_string(std::move(str), allocator) {
554 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::basic_string(): pos out of range", std::out_of_range);
555 erase(pos, count);
556 }
557
558 constexpr basic_string(basic_string&& str, size_type pos, const Allocator& allocator = Allocator())
559 : basic_string(std::move(str), pos, npos, allocator) {}
560
561#if __cplusplus > 202002L
562 basic_string(std::nullptr_t) = delete;
563#endif
564
565#if PLUGIFY_STRING_CONTAINERS_RANGES
566 template<detail::string_compatible_range<Char> Range>
567 constexpr basic_string(std::from_range_t, Range&& range, const Allocator& allocator = Allocator())
568 : basic_string(std::ranges::begin(range), std::ranges::end(range), allocator) {}
569#endif // PLUGIFY_STRING_CONTAINERS_RANGES
570
571 constexpr ~basic_string() {
572 deallocate();
573 }
574
575 constexpr basic_string& operator=(const basic_string& str) {
576 return assign(str);
577 }
578
579 constexpr basic_string& operator=(basic_string&& str) noexcept(
580 allocator_traits::propagate_on_container_move_assignment::value ||
581 allocator_traits::is_always_equal::value) {
582 return assign(std::move(str));
583 }
584
585 constexpr basic_string& operator=(const value_type* str) {
586 return assign(str, Traits::length(str));
587 }
588
589 constexpr basic_string& operator=(value_type ch) {
590 return assign(std::addressof(ch), 1);
591 }
592
593 constexpr basic_string& operator=(std::initializer_list<value_type> list) {
594 return assign(list.begin(), list.size());
595 }
596
597 template<typename Type>
598 requires (std::is_convertible_v<const Type&, sview_type> &&
599 !std::is_convertible_v<const Type&, const Char*>)
600 constexpr basic_string& operator=(const Type& t) {
601 sview_type sv(t);
602 return assign(sv);
603 }
604
605#if __cplusplus > 202002L
606 constexpr basic_string& operator=(std::nullptr_t) = delete;
607#endif
608
609 constexpr basic_string& assign(size_type count, value_type ch) {
610 PLUGIFY_ASSERT(count <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
611 internal_assign(ch, count);
612 return *this;
613 }
614
615 constexpr basic_string& assign(const basic_string& str, size_type pos, size_type count = npos) {
616 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::assign(): pos out of range", std::out_of_range);
617 internal_assign(str.data(), std::min(count, str.size() - pos));
618 return *this;
619 }
620
621 constexpr basic_string& assign(const basic_string& str) {
622 if (this == &str) [[unlikely]]
623 return *this;
624
625 if constexpr (allocator_traits::propagate_on_container_copy_assignment::value) {
626 if constexpr (!allocator_traits::is_always_equal::value) {
627 if (get_allocator() != str.get_allocator()) {
628 deallocate();
629 short_init();
630 }
631 }
632 _allocator = str._allocator;
633 }
634
635 internal_assign(str.data(), str.size());
636 return *this;
637 }
638
639 constexpr basic_string& assign(basic_string&& str) noexcept(
640 allocator_traits::propagate_on_container_move_assignment::value ||
641 allocator_traits::is_always_equal::value) {
642 if (this == &str) [[unlikely]]
643 return *this;
644
645 if constexpr (allocator_traits::propagate_on_container_move_assignment::value) {
646 if constexpr (!allocator_traits::is_always_equal::value) {
647 if (get_allocator() != str.get_allocator()) {
648 deallocate();
649 short_init();
650 }
651 }
652 _allocator = std::move(str._allocator);
653 }
654
655 if constexpr (allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) {
656 deallocate();
657 short_init();
658 std::swap(_storage, str._storage);
659 } else {
660 if (get_allocator() == str.get_allocator()) {
661 deallocate();
662 short_init();
663 std::swap(_storage, str._storage);
664 } else {
665 internal_assign(str.data(), str.size());
666 }
667 }
668
669 return *this;
670 }
671
672 constexpr basic_string& assign(const value_type* str, size_type count) {
673 PLUGIFY_ASSERT(count <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
674 internal_assign(str, count);
675 return *this;
676 }
677
678 constexpr basic_string& assign(const value_type* str) {
679 return assign(str, Traits::length(str));
680 }
681
682 template<std::input_iterator InputIterator>
684 auto len = static_cast<size_type>(std::distance(first, last));
685 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
686 internal_assign(const_pointer(first), len);
687 return *this;
688 }
689
690 constexpr basic_string& assign(std::initializer_list<value_type> list) {
691 auto len = list.size();
692 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
693 internal_assign(const_pointer(list.begin()), len);
694 return *this;
695 }
696
697 template<typename Type>
698 requires (std::is_convertible_v<const Type&, sview_type> &&
699 !std::is_convertible_v<const Type&, const Char*>)
700 constexpr basic_string& assign(const Type& t) {
701 sview_type sv(t);
702 return assign(sv.data(), sv.length());
703 }
704
705 template<typename Type>
706 requires (std::is_convertible_v<const Type&, sview_type> &&
707 !std::is_convertible_v<const Type&, const Char*>)
708 constexpr basic_string& assign(const Type& t, size_type pos, size_type count = npos) {
709 auto sv = sview_type(t).substr(pos, count);
710 auto len = sv.length();
711 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
712 return assign(sv.data(), len);
713 }
714
715#if PLUGIFY_STRING_CONTAINERS_RANGES
716 template<detail::string_compatible_range<Char> Range>
717 constexpr basic_string& assign_range(Range&& range) {
718 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
719 PLUGIFY_ASSERT(str.size() <= max_size(), "plg::basic_string::assign_range(): resulted string size would exceed max_size()", std::length_error);
720 return assign(std::move(str));
721 }
722#endif // PLUGIFY_STRING_CONTAINERS_RANGES
723
724 constexpr allocator_type get_allocator() const noexcept {
725 return _allocator;
726 }
727
728 constexpr reference operator[](size_type pos) {
729 return data()[pos];
730 }
731
732 constexpr const_reference operator[](size_type pos) const {
733 return data()[pos];
734 }
735
736 constexpr reference at(size_type pos) {
737 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::at(): pos out of range", std::out_of_range);
738 return data()[pos];
739 }
740
741 constexpr const_reference at(size_type pos) const {
742 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::at(): pos out of range", std::out_of_range);
743 return data()[pos];
744 }
745
746 constexpr reference front() {
747 PLUGIFY_ASSERT(!empty(), "plg::basic_string::front(): vector is empty", std::length_error);
748 return data()[0];
749 }
750
751 constexpr const_reference front() const {
752 PLUGIFY_ASSERT(!empty(), "plg::basic_string::front(): vector is empty", std::length_error);
753 return data()[0];
754 }
755
756 constexpr reference back() {
757 PLUGIFY_ASSERT(!empty(), "plg::basic_string::back(): vector is empty", std::length_error);
758 return data()[size() - 1];
759 }
760
761 constexpr const_reference back() const {
762 PLUGIFY_ASSERT(!empty(), "plg::basic_string::back(): vector is empty", std::length_error);
763 return data()[size() - 1];
764 }
765
766 constexpr const value_type* data() const noexcept {
767 return is_long() ? get_long_data() : get_short_data();
768 }
769
770 constexpr value_type* data() noexcept {
771 return is_long() ? get_long_data() : get_short_data();
772 }
773
774 constexpr const value_type* c_str() const noexcept {
775 return data();
776 }
777
778 constexpr operator sview_type() const noexcept {
779 return view();
780 }
781
782 constexpr iterator begin() noexcept {
783 return data();
784 }
785
786 constexpr const_iterator begin() const noexcept {
787 return data();
788 }
789
790 constexpr const_iterator cbegin() const noexcept {
791 return data();
792 }
793
794 constexpr iterator end() noexcept {
795 return data() + size();
796 }
797
798 constexpr const_iterator end() const noexcept {
799 return data() + size();
800 }
801
802 constexpr const_iterator cend() const noexcept {
803 return data() + size();
804 }
805
806 constexpr reverse_iterator rbegin() noexcept {
807 return reverse_iterator(end());
808 }
809
810 constexpr const_reverse_iterator rbegin() const noexcept {
811 return const_reverse_iterator(end());
812 }
813
814 constexpr const_reverse_iterator crbegin() const noexcept {
815 return const_reverse_iterator(cend());
816 }
817
818 constexpr reverse_iterator rend() noexcept {
819 return reverse_iterator(begin());
820 }
821
822 constexpr const_reverse_iterator rend() const noexcept {
823 return const_reverse_iterator(begin());
824 }
825
826 constexpr const_reverse_iterator crend() const noexcept {
827 return const_reverse_iterator(cbegin());
828 }
829
830 constexpr bool empty() const noexcept {
831 return size() == 0;
832 }
833
834 constexpr size_type size() const noexcept {
835 return is_long() ? get_long_size() : get_short_size();
836 }
837
838 constexpr size_type length() const noexcept {
839 return size();
840 }
841
842 constexpr size_type max_size() const noexcept {
843 // const size_type alignment = 16;
844 // size_type m = allocator_traits::max_size(_allocator);
845 // if (m <= std::numeric_limits<size_type>::max() / 2)
846 // return m - alignment;
847 // else
848 //= return (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) ? m - alignment : (m / 2) - alignment;
849 return (allocator_traits::max_size(_allocator) - 1) / 2;
850 }
851
852 constexpr size_type capacity() const noexcept {
853 return is_long() ? get_long_cap() : min_cap;
854 }
855
856 constexpr void reserve(size_type cap) {
857 PLUGIFY_ASSERT(cap <= max_size(), "plg::basic_string::reserve(): allocated memory size would exceed max_size()", std::length_error);
858 if (cap <= capacity())
859 return;
860
861 auto new_cap = std::max(cap, size());
862 if (new_cap == capacity())
863 return;
864
865 grow_to(new_cap);
866 }
867
868 void reserve() {
869 shrink_to_fit();
870 }
871
872 constexpr void shrink_to_fit() {
873 if (is_long() == false)
874 return;
875
876 reallocate(size(), true);
877 }
878
879 constexpr void clear() noexcept {
880 set_size(0);
881 }
882
883 constexpr basic_string& insert(size_type pos, size_type count, value_type ch) {
884 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
885 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
886 insert(std::next(cbegin(), pos), count, ch);
887 return *this;
888 }
889
890 constexpr basic_string& insert(size_type pos, const value_type* str) {
891 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
892 auto len = Traits::length(str);
893 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
894 internal_insert(pos, str, len);
895 return *this;
896 }
897
898 constexpr basic_string& insert(size_type pos, const value_type* str, size_type count) {
899 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
900 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
901 internal_insert(pos, str, count);
902 return *this;
903 }
904
905 constexpr basic_string& insert(size_type pos, const basic_string& str) {
906 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
907 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
908 internal_insert(pos, const_pointer(str.data()), str.size());
909 return *this;
910 }
911
912 constexpr basic_string& insert(size_type pos, const basic_string& str, size_type pos_str, size_type count = npos) {
913 PLUGIFY_ASSERT(pos <= size() && pos_str <= str.size(), "plg::basic_string::insert(): pos or pos_str out of range", std::out_of_range);
914 count = std::min(count, str.length() - pos_str);
915 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
916 return insert(pos, str.data() + pos_str, count);
917 }
918
919 constexpr iterator insert(const_iterator pos, value_type ch) {
920 return insert(pos, 1, ch);
921 }
922
923 constexpr iterator insert(const_iterator pos, size_type count, value_type ch) {
924 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
925 auto spos = std::distance(cbegin(), pos);
926 internal_insert(spos, ch, count);
927 return std::next(begin(), spos);
928 }
929
930 template<std::input_iterator InputIterator>
931 constexpr iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
932 auto spos = std::distance(cbegin(), pos);
933 auto len = static_cast<size_type>(std::distance(first, last));
934 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
935 internal_insert(spos, const_pointer(first), len);
936 return std::next(begin(), spos);
937 }
938
939 constexpr iterator insert(const_iterator pos, std::initializer_list<value_type> list) {
940 PLUGIFY_ASSERT(size() + list.size() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
941 auto spos = std::distance(cbegin(), pos);
942 internal_insert(spos, const_pointer(list.begin()), list.size());
943 return std::next(begin(), spos);
944 }
945
946 template<typename Type>
947 requires (std::is_convertible_v<const Type&, sview_type> &&
948 !std::is_convertible_v<const Type&, const Char*>)
949 constexpr basic_string& insert(size_type pos, const Type& t) {
950 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
951 sview_type sv(t);
952 PLUGIFY_ASSERT(size() + sv.length() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
953 internal_insert(pos, const_pointer(sv.data()), sv.length());
954 return *this;
955 }
956
957 template<typename Type>
958 requires (std::is_convertible_v<const Type&, sview_type> &&
959 !std::is_convertible_v<const Type&, const Char*>)
960 constexpr basic_string& insert(size_type pos, const Type& t, size_type pos_str, size_type count = npos) {
961 auto sv = sview_type(t);
962 PLUGIFY_ASSERT(pos <= size() && pos_str <= sv.length(), "plg::basic_string::insert(): pos or pos_str out of range", std::out_of_range);
963 auto ssv = sv.substr(pos_str, count);
964 PLUGIFY_ASSERT(size() + ssv.length() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
965 internal_insert(pos, const_pointer(ssv.data()), ssv.length());
966 return *this;
967 }
968
969#if PLUGIFY_STRING_CONTAINERS_RANGES
970 template<detail::string_compatible_range<Char> Range>
971 constexpr iterator insert_range(const_iterator pos, Range&& range) {
972 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
973 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::insert_range(): resulted string size would exceed max_size()", std::length_error);
974 return insert(pos - begin(), str);
975 }
976#endif // PLUGIFY_STRING_CONTAINERS_RANGES
977
978 constexpr basic_string& erase(size_type pos = 0, size_type count = npos) {
979 auto sz = size();
980 auto buffer = data();
981
982 PLUGIFY_ASSERT(pos <= sz, "plg::basic_string::erase(): pos out of range", std::out_of_range);
983
984 count = std::min(count, sz - pos);
985
986 auto left = sz - (pos + count);
987 if (left != 0)
988 Traits::move(buffer + pos, buffer + pos + count, left);
989
990 auto new_size = pos + left;
991 set_size(new_size);
992 null_terminate();
993
994 return *this;
995 }
996
997 constexpr iterator erase(const_iterator position) {
998 auto pos = std::distance(cbegin(), position);
999 erase(pos, 1);
1000 return begin() + pos;
1001 }
1002
1003 constexpr iterator erase(const_iterator first, const_iterator last) {
1004 auto pos = std::distance(cbegin(), first);
1005 auto len = std::distance(first, last);
1006 erase(pos, len);
1007 return begin() + pos;
1008 }
1009
1010 constexpr void push_back(value_type ch) {
1011 PLUGIFY_ASSERT(size() + 1 <= max_size(), "plg::basic_string::push_back(): resulted string size would exceed max_size()", std::length_error);
1012 append(1, ch);
1013 }
1014
1015 constexpr void pop_back() {
1016 erase(end() - 1);
1017 }
1018
1019 constexpr basic_string& append(size_type count, value_type ch) {
1020 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1021 internal_append(ch, count);
1022 return *this;
1023 }
1024
1025 constexpr basic_string& append(const basic_string& str) {
1026 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1027 internal_append(str.data(), str.size());
1028 return *this;
1029 }
1030
1031 constexpr basic_string& append(const basic_string& str, size_type pos, size_type count = npos) {
1032 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::append(): pos out of range", std::out_of_range);
1033 auto ssv = sview_type(str).substr(pos, count);
1034 PLUGIFY_ASSERT(size() + ssv.length() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1035 internal_append(ssv.data(), ssv.length());
1036 return *this;
1037 }
1038
1039 constexpr basic_string& append(const value_type* str, size_type count) {
1040 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1041 internal_append(str, count);
1042 return *this;
1043 }
1044
1045 constexpr basic_string& append(const value_type* str) {
1046 auto len = Traits::length(str);
1047 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1048 return append(str, len);
1049 }
1050
1051 template<std::input_iterator InputIterator>
1052 constexpr basic_string& append(InputIterator first, InputIterator last) {
1053 auto len = static_cast<size_type>(std::distance(first, last));
1054 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1055 internal_append(const_pointer(first), len);
1056 return *this;
1057 }
1058
1059 constexpr basic_string& append(std::initializer_list<value_type> list) {
1060 PLUGIFY_ASSERT(size() + list.size() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1061 internal_append(const_pointer(list.begin()), list.size());
1062 return *this;
1063 }
1064
1065 template<typename Type>
1066 requires (std::is_convertible_v<const Type&, sview_type> &&
1067 !std::is_convertible_v<const Type&, const Char*>)
1068 constexpr basic_string& append(const Type& t) {
1069 sview_type sv(t);
1070 PLUGIFY_ASSERT(size() + sv.length() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1071 internal_append(sv.data(), sv.size());
1072 return *this;
1073 }
1074
1075 template<typename Type>
1076 requires (std::is_convertible_v<const Type&, sview_type> &&
1077 !std::is_convertible_v<const Type&, const Char*>)
1078 constexpr basic_string& append(const Type& t, size_type pos, size_type count = npos) {
1079 sview_type sv(t);
1080 PLUGIFY_ASSERT(pos <= sv.length(), "plg::basic_string::append(): pos out of range", std::out_of_range);
1081 auto ssv = sv.substr(pos, count);
1082 PLUGIFY_ASSERT(size() + ssv.length() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1083 internal_append(ssv.data(), ssv.length());
1084 return *this;
1085 }
1086
1087#if PLUGIFY_STRING_CONTAINERS_RANGES
1088 template<detail::string_compatible_range<Char> Range>
1089 constexpr basic_string& append_range(Range&& range) {
1090 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
1091 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::insert_range(): resulted string size would exceed max_size()", std::length_error);
1092 return append(str);
1093 }
1094#endif // PLUGIFY_STRING_CONTAINERS_RANGES
1095
1096 constexpr basic_string& operator+=(const basic_string& str) {
1097 return append(str);
1098 }
1099
1100 constexpr basic_string& operator+=(value_type ch) {
1101 push_back(ch);
1102 return *this;
1103 }
1104
1105 constexpr basic_string& operator+=(const value_type* str) {
1106 return append(str);
1107 }
1108
1109 constexpr basic_string& operator+=(std::initializer_list<value_type> list) {
1110 return append(list);
1111 }
1112
1113 template<typename Type>
1114 requires (std::is_convertible_v<const Type&, sview_type> &&
1115 !std::is_convertible_v<const Type&, const Char*>)
1116 constexpr basic_string& operator+=(const Type& t) {
1117 return append(sview_type(t));
1118 }
1119
1120 constexpr int compare(const basic_string& str) const noexcept {
1121 return view().compare(str.view());
1122 }
1123
1124 constexpr int compare(size_type pos1, size_type count1, const basic_string& str) const {
1125 return view().compare(pos1, count1, str.view());
1126 }
1127
1128 constexpr int compare(size_type pos1, size_type count1, const basic_string& str, size_type pos2, size_type count2 = npos) const {
1129 return view().compare(pos1, count1, str.view(), pos2, count2);
1130 }
1131
1132 constexpr int compare(const value_type* str) const {
1133 return view().compare(str);
1134 }
1135
1136 constexpr int compare(size_type pos1, size_type count1, const value_type* str) const {
1137 return view().compare(pos1, count1, str);
1138 }
1139
1140 constexpr int compare(size_type pos1, size_type count1, const value_type* str, size_type count2) const {
1141 return view().compare(pos1, count1, str, count2);
1142 }
1143
1144 template<typename Type>
1145 requires (std::is_convertible_v<const Type&, sview_type> &&
1146 !std::is_convertible_v<const Type&, const Char*>)
1147 constexpr int compare(const Type& t) const noexcept(noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>)) {
1148 return view().compare(sview_type(t));
1149 }
1150
1151 template<typename Type>
1152 requires (std::is_convertible_v<const Type&, sview_type> &&
1153 !std::is_convertible_v<const Type&, const Char*>)
1154 constexpr int compare(size_type pos1, size_type count1, const Type& t) const {
1155 return view().compare(pos1, count1, sview_type(t));
1156 }
1157
1158 template<typename Type>
1159 requires (std::is_convertible_v<const Type&, sview_type> &&
1160 !std::is_convertible_v<const Type&, const Char*>)
1161 constexpr int compare(size_type pos1, size_type count1, const Type& t, size_type pos2, size_type count2 = npos) const {
1162 return view().compare(pos1, count1, sview_type(t), pos2, count2);
1163 }
1164
1165 constexpr bool starts_with(sview_type sv) const noexcept {
1166 return view().starts_with(sv);
1167 }
1168
1169 constexpr bool starts_with(Char ch) const noexcept {
1170 return view().starts_with(ch);
1171 }
1172
1173 constexpr bool starts_with(const Char* str) const {
1174 return view().starts_with(str);
1175 }
1176
1177 constexpr bool ends_with(sview_type sv) const noexcept {
1178 return view().ends_with(sv);
1179 }
1180
1181 constexpr bool ends_with(Char ch) const noexcept {
1182 return view().ends_with(ch);
1183 }
1184
1185 constexpr bool ends_with(const Char* str) const {
1186 return view().ends_with(str);
1187 }
1188
1189 constexpr bool contains(sview_type sv) const noexcept {
1190 return view().contains(sv);
1191 }
1192
1193 constexpr bool contains(Char ch) const noexcept {
1194 return view().contains(ch);
1195 }
1196
1197 constexpr bool contains(const Char* str) const {
1198 return view().contains(str);
1199 }
1200
1201 constexpr basic_string& replace(size_type pos, size_type count, const basic_string& str) {
1202 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1203 return replace(pos, count, str, 0, str.length());
1204 }
1205
1206 constexpr basic_string& replace(const_iterator first, const_iterator last, const basic_string& str) {
1207 auto pos = std::distance(cbegin(), first);
1208 auto count = std::distance(first, last);
1209 return replace(pos, count, str, 0, str.length());
1210 }
1211
1212 constexpr basic_string& replace(size_type pos, size_type count, const basic_string& str, size_type pos2, size_type count2 = npos) {
1213 PLUGIFY_ASSERT(pos <= size() && pos2 <= str.size(), "plg::basic_string::replace(): pos or pos_str out of range", std::out_of_range);
1214 count2 = std::min(count2, str.length() - pos2);
1215 auto ssv = sview_type(str).substr(pos2, count2);
1216 return replace(pos, count, ssv.data(), ssv.length());
1217 }
1218
1219 template<std::input_iterator InputIterator>
1220 constexpr basic_string& replace(const_iterator first, const_iterator last, InputIterator first2, InputIterator last2) {
1221 return replace(first, last, const_pointer(first2), std::distance(first2, last2));
1222 }
1223
1224 constexpr basic_string& replace(size_type pos, size_type count, const value_type* str, size_type count2) {
1225 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1226 count = std::min(count, length() - pos);
1227 PLUGIFY_ASSERT(size() - count + count2 <= max_size(), "plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1228 internal_replace(pos, const_pointer(str), count, count2);
1229 return *this;
1230 }
1231
1232 constexpr basic_string& replace(const_iterator first, const_iterator last, const value_type* str, size_type count2) {
1233 auto pos = std::distance(cbegin(), first);
1234 auto count = std::distance(first, last);
1235 return replace(pos, count, str, count2);
1236 }
1237
1238 constexpr basic_string& replace(size_type pos, size_type count, const value_type* str) {
1239 return replace(pos, count, str, Traits::length(str));
1240 }
1241
1242 constexpr basic_string& replace(const_iterator first, const_iterator last, const value_type* str) {
1243 return replace(first, last, str, Traits::length(str));
1244 }
1245
1246 constexpr basic_string& replace(size_type pos, size_type count, size_type count2, value_type ch) {
1247 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1248 count = std::min(count, length() - pos);
1249 PLUGIFY_ASSERT(size() - count + count2 <= max_size(), "plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1250 internal_replace(pos, ch, count, count2);
1251 return *this;
1252 }
1253
1254 constexpr basic_string& replace(const_iterator first, const_iterator last, size_type count2, value_type ch) {
1255 auto pos = std::distance(cbegin(), first);
1256 auto count = std::distance(first, last);
1257
1258 PLUGIFY_ASSERT(size() - count + count2 <= max_size(), "plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1259 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1260 internal_replace(pos, ch, count, count2);
1261 return *this;
1262 }
1263
1264 constexpr basic_string& replace(const_iterator first, const_iterator last, std::initializer_list<value_type> list) {
1265 return replace(first, last, const_pointer(list.begin()), list.size());
1266 }
1267
1268 template<typename Type>
1269 requires (std::is_convertible_v<const Type&, sview_type> &&
1270 !std::is_convertible_v<const Type&, const Char*>)
1271 constexpr basic_string& replace(size_type pos, size_type count, const Type& t) {
1272 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1273 sview_type sv(t);
1274 return replace(pos, count, sv.data(), sv.length());
1275 }
1276
1277 template<typename Type>
1278 requires (std::is_convertible_v<const Type&, sview_type> &&
1279 !std::is_convertible_v<const Type&, const Char*>)
1280 constexpr basic_string& replace(const_iterator first, const_iterator last, const Type& t) {
1281 sview_type sv(t);
1282 return replace(first, last, sv.data(), sv.length());
1283 }
1284
1285 template<typename Type>
1286 requires (std::is_convertible_v<const Type&, sview_type> &&
1287 !std::is_convertible_v<const Type&, const Char*>)
1288 constexpr basic_string& replace(size_type pos, size_type count, const Type& t, size_type pos2, size_type count2 = npos) {
1289 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1290 auto sv = sview_type(t).substr(pos2, count2);
1291 return replace(pos, count, sv.data(), sv.length());
1292 }
1293
1294#if PLUGIFY_STRING_CONTAINERS_RANGES
1295 template<detail::string_compatible_range<Char> Range>
1296 constexpr iterator replace_with_range(const_iterator first, const_iterator last, Range&& range) {
1297 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
1298 return replace(first, last, str);// replace checks for max_size()
1299 }
1300#endif // PLUGIFY_STRING_CONTAINERS_RANGES
1301
1302 constexpr basic_string substr(size_type pos = 0, size_type count = npos) const {
1303 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::substr(): pos out of range", std::out_of_range);
1304 return basic_string(*this, pos, count);
1305 }
1306
1307 constexpr size_type copy(value_type* str, size_type count, size_type pos = 0) const {
1308 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::copy(): pos out of range", std::out_of_range);
1309 return view().copy(str, count, pos);
1310 }
1311
1312 constexpr void resize(size_type count, value_type ch) {
1313 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::resize(): resulted string size would exceed max_size()", std::length_error);
1314 auto cap = capacity();
1315 auto sz = size();
1316 auto rsz = count + sz;
1317
1318 if (sz < rsz) {
1319 if (cap < rsz)
1320 grow_to(rsz);
1321 Traits::assign(data() + sz, count, ch);
1322 }
1323 set_size(rsz);
1324 null_terminate();
1325 }
1326
1327 constexpr void resize(size_type count) {
1328 resize(count, _terminator);
1329 }
1330
1331 template<typename Operation>
1332 constexpr void resize_and_overwrite(size_type, Operation) {
1333 static_assert(detail::dependent_false<Char>, "plg::basic_string::resize_and_overwrite(count, op) not implemented!");
1334 }
1335
1336 constexpr void swap(basic_string& other) noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value) {
1337 using std::swap;
1338 if constexpr (allocator_traits::propagate_on_container_swap::value) {
1339 swap(_allocator, other._allocator);
1340 }
1341 swap(_storage, other._storage);
1342 }
1343
1344 constexpr size_type find(const basic_string& str, size_type pos = 0) const noexcept {
1345 return view().find(sview_type(str), pos);
1346 }
1347
1348 constexpr size_type find(const value_type* str, size_type pos, size_type count) const noexcept {
1349 return view().find(str, pos, count);
1350 }
1351
1352 constexpr size_type find(const value_type* str, size_type pos = 0) const noexcept {
1353 return view().find(str, pos);
1354 }
1355
1356 constexpr size_type find(value_type ch, size_type pos = 0) const noexcept {
1357 return view().find(ch, pos);
1358 }
1359
1360 template<typename Type>
1361 requires (std::is_convertible_v<const Type&, sview_type> &&
1362 !std::is_convertible_v<const Type&, const Char*>)
1363 constexpr size_type find(const Type& t, size_type pos = 0) const noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>) {
1364 return view().find(sview_type(t), pos);
1365 }
1366
1367 constexpr size_type rfind(const basic_string& str, size_type pos = npos) const noexcept {
1368 return view().rfind(sview_type(str), pos);
1369 }
1370
1371 constexpr size_type rfind(const value_type* str, size_type pos, size_type count) const noexcept {
1372 return view().rfind(str, pos, count);
1373 }
1374
1375 constexpr size_type rfind(const value_type* str, size_type pos = npos) const noexcept {
1376 return view().rfind(str, pos);
1377 }
1378
1379 constexpr size_type rfind(value_type ch, size_type pos = npos) const noexcept {
1380 return view().rfind(ch, pos);
1381 }
1382
1383 template<typename Type>
1384 requires (std::is_convertible_v<const Type&, sview_type> &&
1385 !std::is_convertible_v<const Type&, const Char*>)
1386 constexpr size_type rfind(const Type& t, size_type pos = npos) const noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>) {
1387 return view().rfind(sview_type(t), pos);
1388 }
1389
1390 constexpr size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept {
1391 return view().find_first_of(sview_type(str), pos);
1392 }
1393
1394 constexpr size_type find_first_of(const value_type* str, size_type pos, size_type count) const noexcept {
1395 return view().find_first_of(str, pos, count);
1396 }
1397
1398 constexpr size_type find_first_of(const value_type* str, size_type pos = 0) const noexcept {
1399 return view().find_first_of(str, pos);
1400 }
1401
1402 constexpr size_type find_first_of(value_type ch, size_type pos = 0) const noexcept {
1403 return view().find_first_of(ch, pos);
1404 }
1405
1406 template<typename Type>
1407 requires (std::is_convertible_v<const Type&, sview_type> &&
1408 !std::is_convertible_v<const Type&, const Char*>)
1409 constexpr size_type find_first_of(const Type& t, size_type pos = 0) const noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>) {
1410 return view().find_first_of(sview_type(t), pos);
1411 }
1412
1413 constexpr size_type find_first_not_of(const basic_string& str, size_type pos = 0) const noexcept {
1414 return view().find_first_not_of(sview_type(str), pos);
1415 }
1416
1417 constexpr size_type find_first_not_of(const value_type* str, size_type pos, size_type count) const noexcept {
1418 return view().find_first_not_of(str, pos, count);
1419 }
1420
1421 constexpr size_type find_first_not_of(const value_type* str, size_type pos = 0) const noexcept {
1422 return view().find_first_not_of(str, pos);
1423 }
1424
1425 constexpr size_type find_first_not_of(value_type ch, size_type pos = 0) const noexcept {
1426 return view().find_first_not_of(ch, pos);
1427 }
1428
1429 template<typename Type>
1430 requires (std::is_convertible_v<const Type&, sview_type> &&
1431 !std::is_convertible_v<const Type&, const Char*>)
1432 constexpr size_type find_first_not_of(const Type& t, size_type pos = 0) const noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>) {
1433 return view().find_first_not_of(sview_type(t), pos);
1434 }
1435
1436 constexpr size_type find_last_of(const basic_string& str, size_type pos = npos) const noexcept {
1437 return view().find_last_of(sview_type(str), pos);
1438 }
1439
1440 constexpr size_type find_last_of(const value_type* str, size_type pos, size_type count) const noexcept {
1441 return view().find_last_of(str, pos, count);
1442 }
1443
1444 constexpr size_type find_last_of(const value_type* str, size_type pos = npos) const noexcept {
1445 return view().find_last_of(str, pos);
1446 }
1447
1448 constexpr size_type find_last_of(value_type ch, size_type pos = npos) const noexcept {
1449 return view().find_last_of(ch, pos);
1450 }
1451
1452 template<typename Type>
1453 requires (std::is_convertible_v<const Type&, sview_type> &&
1454 !std::is_convertible_v<const Type&, const Char*>)
1455 constexpr size_type find_last_of(const Type& t, size_type pos = npos) const noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>) {
1456 return view().find_last_of(sview_type(t), pos);
1457 }
1458
1459 constexpr size_type find_last_not_of(const basic_string& str, size_type pos = npos) const noexcept {
1460 return view().find_last_not_of(sview_type(str), pos);
1461 }
1462
1463 constexpr size_type find_last_not_of(const value_type* str, size_type pos, size_type count) const noexcept {
1464 return view().find_last_not_of(str, pos, count);
1465 }
1466
1467 constexpr size_type find_last_not_of(const value_type* str, size_type pos = npos) const noexcept {
1468 return view().find_last_not_of(str, pos);
1469 }
1470
1471 constexpr size_type find_last_not_of(value_type ch, size_type pos = npos) const noexcept {
1472 return view().find_last_not_of(ch, pos);
1473 }
1474
1475 template<typename Type>
1476 requires (std::is_convertible_v<const Type&, sview_type> &&
1477 !std::is_convertible_v<const Type&, const Char*>)
1478 constexpr size_type find_last_not_of(const Type& t, size_type pos = npos) const noexcept(std::is_nothrow_convertible_v<const Type&, sview_type>) {
1479 return view().find_last_not_of(sview_type(t), pos);
1480 }
1481
1482 friend constexpr basic_string operator+(const basic_string& lhs, const basic_string& rhs) {
1483 auto lhs_sz = lhs.size();
1484 auto rhs_sz = rhs.size();
1485 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + rhs_sz, basic_string::allocator_traits::select_on_container_copy_construction(lhs._allocator));
1486 auto buffer = ret.data();
1487 Traits::copy(buffer, lhs.data(), lhs_sz);
1488 Traits::copy(buffer + lhs_sz, rhs.data(), rhs_sz);
1489 ret.null_terminate();
1490 return ret;
1491 }
1492
1493 friend constexpr basic_string operator+(basic_string&& lhs, const basic_string& rhs) {
1494 return std::move(lhs.append(rhs));
1495 }
1496
1497 friend constexpr basic_string operator+(const basic_string& lhs, basic_string&& rhs) {
1498 return std::move(rhs.insert(0, lhs));
1499 }
1500
1501 friend constexpr basic_string operator+(basic_string&& lhs, basic_string&& rhs) {
1502 return std::move(lhs.append(rhs));
1503 }
1504
1505 friend constexpr basic_string operator+(const Char* lhs, const basic_string& rhs) {
1506 auto lhs_sz = Traits::length(lhs);
1507 auto rhs_sz = rhs.size();
1508 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + rhs_sz, basic_string::allocator_traits::select_on_container_copy_construction(rhs._allocator));
1509 auto buffer = ret.data();
1510 Traits::copy(buffer, lhs, lhs_sz);
1511 Traits::copy(buffer + lhs_sz, rhs.data(), rhs_sz);
1512 ret.null_terminate();
1513 return ret;
1514 }
1515
1516 friend constexpr basic_string operator+(const Char* lhs, basic_string&& rhs) {
1517 return std::move(rhs.insert(0, lhs));
1518 }
1519
1520 friend constexpr basic_string operator+(Char lhs, const basic_string& rhs) {
1521 auto rhs_sz = rhs.size();
1522 basic_string ret(detail::uninitialized_size_tag(), rhs_sz + 1, basic_string::allocator_traits::select_on_container_copy_construction(rhs._allocator));
1523 auto buffer = ret.data();
1524 Traits::assign(buffer, 1, lhs);
1525 Traits::copy(buffer + 1, rhs.data(), rhs_sz);
1526 ret.null_terminate();
1527 return ret;
1528 }
1529
1530 friend constexpr basic_string operator+(Char lhs, basic_string&& rhs) {
1531 rhs.insert(rhs.begin(), lhs);
1532 return std::move(rhs);
1533 }
1534
1535 friend constexpr basic_string operator+(const basic_string& lhs, const Char* rhs) {
1536 auto lhs_sz = lhs.size();
1537 auto rhs_sz = Traits::length(rhs);
1538 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + rhs_sz, basic_string::allocator_traits::select_on_container_copy_construction(lhs._allocator));
1539 auto buffer = ret.data();
1540 Traits::copy(buffer, lhs.data(), lhs_sz);
1541 Traits::copy(buffer + lhs_sz, rhs, rhs_sz);
1542 ret.null_terminate();
1543 return ret;
1544 }
1545
1546 friend constexpr basic_string operator+(basic_string&& lhs, const Char* rhs) {
1547 return std::move(lhs.append(rhs));
1548 }
1549
1550 friend constexpr basic_string operator+(const basic_string& lhs, Char rhs) {
1551 auto lhs_sz = lhs.size();
1552 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + 1, basic_string::allocator_traits::select_on_container_copy_construction(lhs._allocator));
1553 auto buffer = ret.data();
1554 Traits::copy(buffer, lhs.data(), lhs_sz);
1555 Traits::assign(buffer + lhs_sz, 1, rhs);
1556 ret.null_terminate();
1557 return ret;
1558 }
1559
1560 friend constexpr basic_string operator+(basic_string&& lhs, Char rhs) {
1561 lhs.push_back(rhs);
1562 return std::move(lhs);
1563 }
1564 };
1565
1566 template<typename Char, typename Traits, typename Allocator>
1567 constexpr bool operator==(const basic_string<Char, Traits, Allocator>& lhs, const basic_string<Char, Traits, Allocator>& rhs) noexcept {
1568 return lhs.compare(rhs) == 0;
1569 }
1570
1571 template<typename Char, typename Traits, typename Allocator>
1572 constexpr bool operator==(const basic_string<Char, Traits, Allocator>& lhs, const Char* rhs) {
1573 return lhs.compare(rhs) == 0;
1574 }
1575
1576 template<typename Char, typename Traits, typename Allocator>
1577 constexpr std::strong_ordering operator<=>(const basic_string<Char, Traits, Allocator>& lhs, const basic_string<Char, Traits, Allocator>& rhs) noexcept {
1578 return lhs.compare(rhs) <=> 0;
1579 }
1580
1581 template<typename Char, typename Traits, typename Allocator>
1582 constexpr std::strong_ordering operator<=>(const basic_string<Char, Traits, Allocator>& lhs, const Char* rhs) {
1583 return lhs.compare(rhs) <=> 0;
1584 }
1585
1586 // swap
1587 template<typename Char, typename Traits, typename Allocator>
1588 constexpr void swap(basic_string<Char, Traits, Allocator>& lhs, basic_string<Char, Traits, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
1589 lhs.swap(rhs);
1590 }
1591
1592 // erasure
1593 template<typename Char, typename Traits, typename Allocator, typename U>
1594 constexpr typename basic_string<Char, Traits, Allocator>::size_type erase(basic_string<Char, Traits, Allocator>& c, const U& value) {
1595 auto it = std::remove(c.begin(), c.end(), value);
1596 auto r = std::distance(it, c.end());
1597 c.erase(it, c.end());
1598 return r;
1599 }
1600
1601 template<typename Char, typename Traits, typename Allocator, typename Pred>
1602 constexpr typename basic_string<Char, Traits, Allocator>::size_type erase_if(basic_string<Char, Traits, Allocator>& c, Pred pred) {
1603 auto it = std::remove_if(c.begin(), c.end(), pred);
1604 auto r = std::distance(it, c.end());
1605 c.erase(it, c.end());
1606 return r;
1607 }
1608
1609 // deduction guides
1610 template<typename InputIterator, typename Allocator = plg::allocator<typename std::iterator_traits<InputIterator>::value_type>>
1611 basic_string(InputIterator, InputIterator, Allocator = Allocator()) -> basic_string<typename std::iterator_traits<InputIterator>::value_type, std::char_traits<typename std::iterator_traits<InputIterator>::value_type>, Allocator>;
1612
1613 template<typename Char, typename Traits, typename Allocator = plg::allocator<Char>>
1614 explicit basic_string(std::basic_string_view<Char, Traits>, const Allocator& = Allocator()) -> basic_string<Char, Traits, Allocator>;
1615
1616 template<typename Char, typename Traits, typename Allocator = plg::allocator<Char>>
1617 basic_string(std::basic_string_view<Char, Traits>, typename basic_string<Char, Traits, Allocator>::size_type, typename basic_string<Char, Traits, Allocator>::size_type, const Allocator& = Allocator()) -> basic_string<Char, Traits, Allocator>;
1618
1619#if PLUGIFY_STRING_CONTAINERS_RANGES
1620 template<std::ranges::input_range Range, typename Allocator = plg::allocator<std::ranges::range_value_t<Range>>>
1621 basic_string(std::from_range_t, Range&&, Allocator = Allocator()) -> basic_string<std::ranges::range_value_t<Range>, std::char_traits<std::ranges::range_value_t<Range>>, Allocator>;
1622#endif // PLUGIFY_STRING_CONTAINERS_RANGES
1623
1624 // basic_string typedef-names
1625 using string = basic_string<char>;
1626 using u8string = basic_string<char8_t>;
1627 using u16string = basic_string<char16_t>;
1628 using u32string = basic_string<char32_t>;
1629 using wstring = basic_string<wchar_t>;
1630
1631#ifndef PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
1632 // numeric conversions
1633 inline int stoi(const string& str, std::size_t* pos = nullptr, int base = 10) {
1634 auto cstr = str.c_str();
1635 char* ptr = const_cast<char*>(cstr);
1636
1637 auto ret = strtol(cstr, &ptr, base);
1638 if (pos != nullptr)
1639 *pos = static_cast<size_t>(cstr - ptr);
1640
1641 return static_cast<int>(ret);
1642 }
1643
1644 inline long stol(const string& str, std::size_t* pos = nullptr, int base = 10) {
1645 auto cstr = str.c_str();
1646 char* ptr = const_cast<char*>(cstr);
1647
1648 auto ret = strtol(cstr, &ptr, base);
1649 if (pos != nullptr)
1650 *pos = static_cast<size_t>(cstr - ptr);
1651
1652 return ret;
1653 }
1654
1655 inline long long stoll(const string& str, std::size_t* pos = nullptr, int base = 10) {
1656 auto cstr = str.c_str();
1657 char* ptr = const_cast<char*>(cstr);
1658
1659 auto ret = strtoll(cstr, &ptr, base);
1660 if (pos != nullptr)
1661 *pos = static_cast<size_t>(cstr - ptr);
1662
1663 return ret;
1664 }
1665
1666 inline unsigned long stoul(const string& str, std::size_t* pos = nullptr, int base = 10) {
1667 auto cstr = str.c_str();
1668 char* ptr = const_cast<char*>(cstr);
1669
1670 auto ret = strtoul(cstr, &ptr, base);
1671 if (pos != nullptr)
1672 *pos = static_cast<size_t>(cstr - ptr);
1673
1674 return ret;
1675 }
1676
1677 inline unsigned long long stoull(const string& str, std::size_t* pos = nullptr, int base = 10) {
1678 auto cstr = str.c_str();
1679 char* ptr = const_cast<char*>(cstr);
1680
1681 auto ret = strtoull(cstr, &ptr, base);
1682 if (pos != nullptr)
1683 *pos = static_cast<size_t>(cstr - ptr);
1684
1685 return ret;
1686 }
1687
1688 inline float stof(const string& str, std::size_t* pos = nullptr) {
1689 auto cstr = str.c_str();
1690 char* ptr = const_cast<char*>(cstr);
1691
1692 auto ret = strtof(cstr, &ptr);
1693 if (pos != nullptr)
1694 *pos = static_cast<size_t>(cstr - ptr);
1695
1696 return ret;
1697 }
1698
1699 inline double stod(const string& str, std::size_t* pos = nullptr) {
1700 auto cstr = str.c_str();
1701 char* ptr = const_cast<char*>(cstr);
1702
1703 auto ret = strtod(cstr, &ptr);
1704 if (pos != nullptr)
1705 *pos = static_cast<size_t>(cstr - ptr);
1706
1707 return ret;
1708 }
1709
1710 inline long double stold(const string& str, std::size_t* pos = nullptr) {
1711 auto cstr = str.c_str();
1712 char* ptr = const_cast<char*>(cstr);
1713
1714 auto ret = strtold(cstr, &ptr);
1715 if (pos != nullptr)
1716 *pos = static_cast<size_t>(cstr - ptr);
1717
1718 return ret;
1719 }
1720
1721 namespace detail {
1722 template<typename S, typename V>
1723 PLUGIFY_FORCE_INLINE constexpr S to_string(V v) {
1724 // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
1725 // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented),
1726 // so we need +1 here.
1727 constexpr std::size_t bufSize = std::numeric_limits<V>::digits10 + 2; // +1 for minus, +1 for digits10
1728 char buf[bufSize];
1729 const auto res = std::to_chars(buf, buf + bufSize, v);
1730 return S(buf, res.ptr);
1731 }
1732
1733 typedef int (*wide_printf)(wchar_t* __restrict, std::size_t, const wchar_t* __restrict, ...);
1734
1735#if PLUGIFY_COMPILER_MSVC
1736 inline int truncate_snwprintf(wchar_t* __restrict buffer, std::size_t count, const wchar_t* __restrict format, ...) {
1737 int r;
1738 va_list args;
1739 va_start(args, format);
1740 r = _vsnwprintf_s(buffer, count, _TRUNCATE, format, args);
1741 va_end(args);
1742 return r;
1743 }
1744#endif
1745
1746 PLUGIFY_FORCE_INLINE constexpr wide_printf get_swprintf() noexcept {
1747#if PLUGIFY_COMPILER_MSVC
1748 return static_cast<int(__cdecl*)(wchar_t* __restrict, std::size_t, const wchar_t* __restrict, ...)>(truncate_snwprintf);
1749#else
1750 return swprintf;
1751#endif
1752 }
1753
1754 template<typename S, typename P, typename V>
1755 PLUGIFY_FORCE_INLINE constexpr S as_string(P sprintf_like, const typename S::value_type* fmt, V v) {
1756 typedef typename S::size_type size_type;
1757 S s;
1758 s.resize(s.capacity());
1759 size_type available = s.size();
1760 while (true) {
1761 int status = sprintf_like(&s[0], available + 1, fmt, v);
1762 if (status >= 0) {
1763 auto used = static_cast<size_type>(status);
1764 if (used <= available) {
1765 s.resize(used);
1766 break;
1767 }
1768 available = used; // Assume this is advice of how much space we need.
1769 } else {
1770 available = available * 2 + 1;
1771 }
1772 s.resize(available);
1773 }
1774 return s;
1775 }
1776 }// namespace detail
1777
1778 inline string to_string(int val) { return detail::to_string<string>(val); }
1779 inline string to_string(unsigned val) { return detail::to_string<string>(val); }
1780 inline string to_string(long val) { return detail::to_string<string>(val); }
1781 inline string to_string(unsigned long val) { return detail::to_string<string>(val); }
1782 inline string to_string(long long val) { return detail::to_string<string>(val); }
1783 inline string to_string(unsigned long long val) { return detail::to_string<string>(val); }
1784 inline string to_string(float val) { return detail::as_string<string>(snprintf, "%f", val); }
1785 inline string to_string(double val) { return detail::as_string<string>(snprintf, "%f", val); }
1786 inline string to_string(long double val) { return detail::as_string<string>(snprintf, "%Lf", val); }
1787
1788 inline wstring to_wstring(int val) { return detail::to_string<wstring>(val); }
1789 inline wstring to_wstring(unsigned val) { return detail::to_string<wstring>(val); }
1790 inline wstring to_wstring(long val) { return detail::to_string<wstring>(val); }
1791 inline wstring to_wstring(unsigned long val) { return detail::to_string<wstring>(val); }
1792 inline wstring to_wstring(long long val) { return detail::to_string<wstring>(val); }
1793 inline wstring to_wstring(unsigned long long val) { return detail::to_string<wstring>(val); }
1794 inline wstring to_wstring(float val) { return detail::as_string<wstring>(detail::get_swprintf(), L"%f", val); }
1795 inline wstring to_wstring(double val) { return detail::as_string<wstring>(detail::get_swprintf(), L"%f", val); }
1796 inline wstring to_wstring(long double val) { return detail::as_string<wstring>(detail::get_swprintf(), L"%Lf", val); }
1797#endif // PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
1798
1799#ifndef PLUGIFY_STRING_NO_STD_HASH
1800 // hash support
1801 namespace detail {
1802 template<typename Char, typename Allocator, typename String = basic_string<Char, std::char_traits<Char>, Allocator>>
1804 constexpr std::size_t operator()(const String& str) const noexcept {
1805 return std::hash<typename String::sview_type>{}(typename String::sview_type(str));
1806 }
1807 };
1808 }// namespace detail
1809#endif // PLUGIFY_STRING_NO_STD_HASH
1810
1811#ifndef PLUGIFY_STRING_NO_STD_FORMAT
1812 // format support
1813 namespace detail {
1814 template<typename Char>
1815 static constexpr const Char* format_string() {
1816 if constexpr (std::is_same_v<Char, char> || std::is_same_v<Char, char8_t>)
1817 return "{}";
1818 if constexpr (std::is_same_v<Char, wchar_t>)
1819 return L"{}";
1820 if constexpr (std::is_same_v<Char, char16_t>)
1821 return u"{}";
1822 if constexpr (std::is_same_v<Char, char32_t>)
1823 return U"{}";
1824 return "";
1825 }
1826
1827 template<typename Char, typename Allocator, typename String = basic_string<Char, std::char_traits<Char>, Allocator>>
1829 constexpr auto parse(std::format_parse_context& ctx) {
1830 return ctx.begin();
1831 }
1832
1833 template<class FormatContext>
1834 auto format(const String& str, FormatContext& ctx) const {
1835 return std::format_to(ctx.out(), format_string<Char>(), str.c_str());
1836 }
1837 };
1838 }
1839#endif // PLUGIFY_STRING_NO_STD_FORMAT
1840
1841 inline namespace literals {
1842 inline namespace string_literals {
1844
1845#if PLUGIFY_COMPILER_CLANG
1846 PLUGIFY_WARN_IGNORE("-Wuser-defined-literals")
1847#elif PLUGIFY_COMPILER_GCC
1848 PLUGIFY_WARN_IGNORE("-Wliteral-suffix")
1849#elif PLUGIFY_COMPILER_MSVC
1851#endif
1852 // suffix for basic_string literals
1853 constexpr string operator""s(const char* str, std::size_t len) { return string{str, len}; }
1854 constexpr u8string operator""s(const char8_t* str, std::size_t len) { return u8string{str, len}; }
1855 constexpr u16string operator""s(const char16_t* str, std::size_t len) { return u16string{str, len}; }
1856 constexpr u32string operator""s(const char32_t* str, std::size_t len) { return u32string{str, len}; }
1857 constexpr wstring operator""s(const wchar_t* str, std::size_t len) { return wstring{str, len}; }
1858
1859 PLUGIFY_WARN_POP()
1860 }// namespace string_literals
1861 }// namespace literals
1862}// namespace plg
1863
1864#ifndef PLUGIFY_STRING_NO_STD_HASH
1865// hash support
1866namespace std {
1867 template<typename Allocator>
1868 struct hash<plg::basic_string<char, std::char_traits<char>, Allocator>> : plg::detail::string_hash_base<char, Allocator> {};
1869
1870 template<typename Allocator>
1871 struct hash<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>> : plg::detail::string_hash_base<char8_t, Allocator> {};
1872
1873 template<typename Allocator>
1874 struct hash<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>> : plg::detail::string_hash_base<char16_t, Allocator> {};
1875
1876 template<typename Allocator>
1877 struct hash<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>> : plg::detail::string_hash_base<char32_t, Allocator> {};
1878
1879 template<typename Allocator>
1880 struct hash<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>> : plg::detail::string_hash_base<wchar_t, Allocator> {};
1881}// namespace std
1882#endif // PLUGIFY_STRING_NO_STD_HASH
1883
1884#ifndef PLUGIFY_STRING_NO_STD_FORMAT
1885// format support
1886#ifdef FMT_HEADER_ONLY
1887namespace fmt {
1888#else
1889namespace std {
1890#endif
1891 template<typename Allocator>
1892 struct formatter<plg::basic_string<char, std::char_traits<char>, Allocator>> : plg::detail::string_formatter_base<char, Allocator> {};
1893
1894 template<typename Allocator>
1895 struct formatter<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>> : plg::detail::string_formatter_base<char8_t, Allocator> {};
1896
1897 template<typename Allocator>
1898 struct formatter<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>> : plg::detail::string_formatter_base<char16_t, Allocator> {};
1899
1900 template<typename Allocator>
1901 struct formatter<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>> : plg::detail::string_formatter_base<char32_t, Allocator> {};
1902
1903 template<typename Allocator>
1904 struct formatter<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>> : plg::detail::string_formatter_base<wchar_t, Allocator> {};
1905}// namespace std
1906#endif // PLUGIFY_STRING_NO_STD_FORMAT