plugify 1.0.0.0
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 defined(__clang__)
5# pragma clang system_header
6#elif defined(__GNUC__)
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 "macro.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 inline 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 = std::allocator<Char>>
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 defined(__clang__)
106 PLUGIFY_WARN_IGNORE("-Wgnu-anonymous-struct")
107 PLUGIFY_WARN_IGNORE("-Wzero-length-array")
108#elif defined(__GNUC__)
109 PLUGIFY_WARN_IGNORE("-Wpedantic")
110#elif defined(_MSC_VER)
113#endif
114
115 template<typename CharT, std::size_t = sizeof(CharT)>
116 struct padding {
117 [[maybe_unused]] uint8_t padding[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 [[nodiscard]] 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 [[nodiscard]] constexpr auto& get_long_data() noexcept {
188 return _storage._long.data;
189 }
190
191 [[nodiscard]] constexpr const auto& get_long_data() const noexcept {
192 return _storage._long.data;
193 }
194
195 [[nodiscard]] constexpr auto& get_short_data() noexcept {
196 return _storage._short.data;
197 }
198
199 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] 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 [[nodiscard]] 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<PLUGIFY_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#if __cpp_impl_three_way_comparison
530 template<typename Type>
531 requires(std::is_convertible_v<const Type&, sview_type>)
532 constexpr basic_string(const Type& t, size_type pos, size_type count, const Allocator& allocator = Allocator())
533 : _allocator(allocator) {
534 auto sv = sview_type(t);
535 PLUGIFY_ASSERT(pos <= sv.length(), "plg::basic_string::basic_string(): pos out of range", std::out_of_range);
536 auto ssv = sv.substr(pos, count);
537 auto len = ssv.length();
538 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
539 internal_assign(ssv.data(), len);
540 }
541#endif // __cpp_impl_three_way_comparison
542
543 template<typename Type>
544#if __cpp_concepts
545 requires(std::is_convertible_v<const Type&, sview_type> &&
546 !std::is_convertible_v<const Type&, const Char*>)
547#endif // __cpp_concepts
548 constexpr basic_string(const Type& t, const Allocator& allocator = Allocator())
549 : _allocator(allocator) {
550 sview_type sv(t);
551 auto len = sv.length();
552 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
553 internal_assign(sv.data(), len);
554 }
555
556 constexpr basic_string(basic_string&& str, size_type pos, size_type count, const Allocator& allocator = Allocator())
557 : basic_string(std::move(str), allocator) {
558 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::basic_string(): pos out of range", std::out_of_range);
559 erase(pos, count);
560 }
561
562 constexpr basic_string(basic_string&& str, size_type pos, const Allocator& allocator = Allocator())
563 : basic_string(std::move(str), pos, npos, allocator) {}
564
565#if __cplusplus > 202002L
566 basic_string(std::nullptr_t) = delete;
567#endif // __cplusplus > 202002L
568
569#if PLUGIFY_STRING_CONTAINERS_RANGES
570 template<detail::string_compatible_range<Char> Range>
571 constexpr basic_string(std::from_range_t, Range&& range, const Allocator& allocator = Allocator())
572 : basic_string(std::ranges::begin(range), std::ranges::end(range), allocator) {}
573#endif // PLUGIFY_STRING_CONTAINERS_RANGES
574
575#if __cpp_constexpr >= 201907L
576 constexpr
577#endif // __cpp_constexpr >= 201907L
578 ~basic_string() {
579 deallocate();
580 }
581
582 constexpr basic_string& operator=(const basic_string& str) {
583 return assign(str);
584 }
585
586 constexpr basic_string& operator=(basic_string&& str) noexcept(
587 allocator_traits::propagate_on_container_move_assignment::value ||
588 allocator_traits::is_always_equal::value) {
589 return assign(std::move(str));
590 }
591
592 constexpr basic_string& operator=(const value_type* str) {
593 return assign(str, Traits::length(str));
594 }
595
596 constexpr basic_string& operator=(value_type ch) {
597 return assign(std::addressof(ch), 1);
598 }
599
600 constexpr basic_string& operator=(std::initializer_list<value_type> list) {
601 return assign(list.begin(), list.size());
602 }
603
604 template<typename Type>
605#if __cpp_concepts
606 requires(std::is_convertible_v<const Type&, sview_type> &&
607 !std::is_convertible_v<const Type&, const Char*>)
608#endif // __cpp_concepts
609 constexpr basic_string& operator=(const Type& t) {
610 sview_type sv(t);
611 return assign(sv);
612 }
613
614#if __cplusplus > 202002L
615 constexpr basic_string& operator=(std::nullptr_t) = delete;
616#endif
617
618 constexpr basic_string& assign(size_type count, value_type ch) {
619 PLUGIFY_ASSERT(count <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
620 internal_assign(ch, count);
621 return *this;
622 }
623
624 constexpr basic_string& assign(const basic_string& str, size_type pos, size_type count = npos) {
625 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::assign(): pos out of range", std::out_of_range);
626 internal_assign(str.data(), std::min(count, str.size() - pos));
627 return *this;
628 }
629
630 constexpr basic_string& assign(const basic_string& str) {
631 if (this == &str) [[unlikely]]
632 return *this;
633
634 if constexpr (allocator_traits::propagate_on_container_copy_assignment::value) {
635 if constexpr (!allocator_traits::is_always_equal::value) {
636 if (get_allocator() != str.get_allocator()) {
637 deallocate();
638 short_init();
639 }
640 }
641 _allocator = str._allocator;
642 }
643
644 internal_assign(str.data(), str.size());
645 return *this;
646 }
647
648 constexpr basic_string& assign(basic_string&& str) noexcept(
649 allocator_traits::propagate_on_container_move_assignment::value ||
650 allocator_traits::is_always_equal::value) {
651 if (this == &str) [[unlikely]]
652 return *this;
653
654 if constexpr (allocator_traits::propagate_on_container_move_assignment::value) {
655 if constexpr (!allocator_traits::is_always_equal::value) {
656 if (get_allocator() != str.get_allocator()) {
657 deallocate();
658 short_init();
659 }
660 }
661 _allocator = std::move(str._allocator);
662 }
663
664 if constexpr (allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) {
665 deallocate();
666 short_init();
667 std::swap(_storage, str._storage);
668 } else {
669 if (get_allocator() == str.get_allocator()) {
670 deallocate();
671 short_init();
672 std::swap(_storage, str._storage);
673 } else {
674 internal_assign(str.data(), str.size());
675 }
676 }
677
678 return *this;
679 }
680
681 constexpr basic_string& assign(const value_type* str, size_type count) {
682 PLUGIFY_ASSERT(count <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
683 internal_assign(str, count);
684 return *this;
685 }
686
687 constexpr basic_string& assign(const value_type* str) {
688 return assign(str, Traits::length(str));
689 }
690
691 template<PLUGIFY_INPUT_ITERATOR InputIterator>
693 auto len = static_cast<size_type>(std::distance(first, last));
694 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
695 internal_assign(const_pointer(first), len);
696 return *this;
697 }
698
699 constexpr basic_string& assign(std::initializer_list<value_type> list) {
700 auto len = list.size();
701 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
702 internal_assign(const_pointer(list.begin()), len);
703 return *this;
704 }
705
706 template<typename Type>
707#if __cpp_concepts
708 requires(std::is_convertible_v<const Type&, sview_type> &&
709 !std::is_convertible_v<const Type&, const Char*>)
710#endif // __cpp_concepts
711 constexpr basic_string& assign(const Type& t) {
712 sview_type sv(t);
713 return assign(sv.data(), sv.length());
714 }
715
716 template<typename Type>
717#if __cpp_concepts
718 requires(std::is_convertible_v<const Type&, sview_type> &&
719 !std::is_convertible_v<const Type&, const Char*>)
720#endif // __cpp_concepts
721 constexpr basic_string& assign(const Type& t, size_type pos, size_type count = npos) {
722 auto sv = sview_type(t).substr(pos, count);
723 auto len = sv.length();
724 PLUGIFY_ASSERT(len <= max_size(), "plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
725 return assign(sv.data(), len);
726 }
727
728#if PLUGIFY_STRING_CONTAINERS_RANGES
729 template<detail::string_compatible_range<Char> Range>
730 constexpr basic_string& assign_range(Range&& range) {
731 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
732 PLUGIFY_ASSERT(str.size() <= max_size(), "plg::basic_string::assign_range(): resulted string size would exceed max_size()", std::length_error);
733 return assign(std::move(str));
734 }
735#endif // PLUGIFY_STRING_CONTAINERS_RANGES
736
737 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
738 return _allocator;
739 }
740
741 [[nodiscard]] constexpr reference operator[](size_type pos) {
742 return data()[pos];
743 }
744
745 [[nodiscard]] constexpr const_reference operator[](size_type pos) const {
746 return data()[pos];
747 }
748
749 [[nodiscard]] constexpr reference at(size_type pos) {
750 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::at(): pos out of range", std::out_of_range);
751 return data()[pos];
752 }
753
754 [[nodiscard]] constexpr const_reference at(size_type pos) const {
755 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::at(): pos out of range", std::out_of_range);
756 return data()[pos];
757 }
758
759 [[nodiscard]] constexpr reference front() {
760 PLUGIFY_ASSERT(!empty(), "plg::basic_string::front(): vector is empty", std::length_error);
761 return data()[0];
762 }
763
764 [[nodiscard]] constexpr const_reference front() const {
765 PLUGIFY_ASSERT(!empty(), "plg::basic_string::front(): vector is empty", std::length_error);
766 return data()[0];
767 }
768
769 [[nodiscard]] constexpr reference back() {
770 PLUGIFY_ASSERT(!empty(), "plg::basic_string::back(): vector is empty", std::length_error);
771 return data()[size() - 1];
772 }
773
774 [[nodiscard]] constexpr const_reference back() const {
775 PLUGIFY_ASSERT(!empty(), "plg::basic_string::back(): vector is empty", std::length_error);
776 return data()[size() - 1];
777 }
778
779 [[nodiscard]] constexpr const value_type* data() const noexcept {
780 return is_long() ? get_long_data() : get_short_data();
781 }
782
783 [[nodiscard]] constexpr value_type* data() noexcept {
784 return is_long() ? get_long_data() : get_short_data();
785 }
786
787 [[nodiscard]] constexpr const value_type* c_str() const noexcept {
788 return data();
789 }
790
791 [[nodiscard]] constexpr operator sview_type() const noexcept {
792 return view();
793 }
794
795 [[nodiscard]] constexpr iterator begin() noexcept {
796 return data();
797 }
798
799 [[nodiscard]] constexpr const_iterator begin() const noexcept {
800 return data();
801 }
802
803 [[nodiscard]] constexpr const_iterator cbegin() const noexcept {
804 return data();
805 }
806
807 [[nodiscard]] constexpr iterator end() noexcept {
808 return data() + size();
809 }
810
811 [[nodiscard]] constexpr const_iterator end() const noexcept {
812 return data() + size();
813 }
814
815 [[nodiscard]] constexpr const_iterator cend() const noexcept {
816 return data() + size();
817 }
818
819 [[nodiscard]] constexpr reverse_iterator rbegin() noexcept {
820 return reverse_iterator(end());
821 }
822
823 [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept {
824 return const_reverse_iterator(end());
825 }
826
827 [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept {
828 return const_reverse_iterator(cend());
829 }
830
831 [[nodiscard]] constexpr reverse_iterator rend() noexcept {
832 return reverse_iterator(begin());
833 }
834
835 [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept {
836 return const_reverse_iterator(begin());
837 }
838
839 [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept {
840 return const_reverse_iterator(cbegin());
841 }
842
843 [[nodiscard]] constexpr bool empty() const noexcept {
844 return size() == 0;
845 }
846
847 [[nodiscard]] constexpr size_type size() const noexcept {
848 return is_long() ? get_long_size() : get_short_size();
849 }
850
851 [[nodiscard]] constexpr size_type length() const noexcept {
852 return size();
853 }
854
855 [[nodiscard]] constexpr size_type max_size() const noexcept {
856 // const size_type alignment = 16;
857 // size_type m = allocator_traits::max_size(_allocator);
858 // if (m <= std::numeric_limits<size_type>::max() / 2)
859 // return m - alignment;
860 // else
861 //= return (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) ? m - alignment : (m / 2) - alignment;
862 return (allocator_traits::max_size(_allocator) - 1) / 2;
863 }
864
865 [[nodiscard]] constexpr size_type capacity() const noexcept {
866 return is_long() ? get_long_cap() : min_cap;
867 }
868
869 constexpr void reserve(size_type cap) {
870 PLUGIFY_ASSERT(cap <= max_size(), "plg::basic_string::reserve(): allocated memory size would exceed max_size()", std::length_error);
871 if (cap <= capacity())
872 return;
873
874 auto new_cap = std::max(cap, size());
875 if (new_cap == capacity())
876 return;
877
878 grow_to(new_cap);
879 }
880
881 void reserve() {
882 shrink_to_fit();
883 }
884
885 constexpr void shrink_to_fit() {
886 if (is_long() == false)
887 return;
888
889 reallocate(size(), true);
890 }
891
892 constexpr void clear() noexcept {
893 set_size(0);
894 }
895
896 constexpr basic_string& insert(size_type pos, size_type count, value_type ch) {
897 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
898 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
899 insert(std::next(cbegin(), pos), count, ch);
900 return *this;
901 }
902
903 constexpr basic_string& insert(size_type pos, const value_type* str) {
904 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
905 auto len = Traits::length(str);
906 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
907 internal_insert(pos, str, len);
908 return *this;
909 }
910
911 constexpr basic_string& insert(size_type pos, const value_type* str, size_type count) {
912 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
913 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
914 internal_insert(pos, str, count);
915 return *this;
916 }
917
918 constexpr basic_string& insert(size_type pos, const basic_string& str) {
919 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
920 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
921 internal_insert(pos, const_pointer(str.data()), str.size());
922 return *this;
923 }
924
925 constexpr basic_string& insert(size_type pos, const basic_string& str, size_type pos_str, size_type count = npos) {
926 PLUGIFY_ASSERT(pos <= size() && pos_str <= str.size(), "plg::basic_string::insert(): pos or pos_str out of range", std::out_of_range);
927 count = std::min(count, str.length() - pos_str);
928 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
929 return insert(pos, str.data() + pos_str, count);
930 }
931
932 constexpr iterator insert(const_iterator pos, value_type ch) {
933 return insert(pos, 1, ch);
934 }
935
936 constexpr iterator insert(const_iterator pos, size_type count, value_type ch) {
937 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
938 auto spos = std::distance(cbegin(), pos);
939 internal_insert(spos, ch, count);
940 return std::next(begin(), spos);
941 }
942
943 template<PLUGIFY_INPUT_ITERATOR InputIterator>
944 constexpr iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
945 auto spos = std::distance(cbegin(), pos);
946 auto len = static_cast<size_type>(std::distance(first, last));
947 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
948 internal_insert(spos, const_pointer(first), len);
949 return std::next(begin(), spos);
950 }
951
952 constexpr iterator insert(const_iterator pos, std::initializer_list<value_type> list) {
953 PLUGIFY_ASSERT(size() + list.size() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
954 auto spos = std::distance(cbegin(), pos);
955 internal_insert(spos, const_pointer(list.begin()), list.size());
956 return std::next(begin(), spos);
957 }
958
959 template<typename Type>
960#ifdef __cpp_concepts
961 requires(std::is_convertible_v<const Type&, sview_type> &&
962 !std::is_convertible_v<const Type&, const Char*>)
963#endif // __cpp_concepts
964 constexpr basic_string& insert(size_type pos, const Type& t) {
965 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::insert(): pos out of range", std::out_of_range);
966 sview_type sv(t);
967 PLUGIFY_ASSERT(size() + sv.length() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
968 internal_insert(pos, const_pointer(sv.data()), sv.length());
969 return *this;
970 }
971
972 template<typename Type>
973#ifdef __cpp_concepts
974 requires(std::is_convertible_v<const Type&, sview_type> &&
975 !std::is_convertible_v<const Type&, const Char*>)
976#endif // __cpp_concepts
977 constexpr basic_string& insert(size_type pos, const Type& t, size_type pos_str, size_type count = npos) {
978 auto sv = sview_type(t);
979 PLUGIFY_ASSERT(pos <= size() && pos_str <= sv.length(), "plg::basic_string::insert(): pos or pos_str out of range", std::out_of_range);
980 auto ssv = sv.substr(pos_str, count);
981 PLUGIFY_ASSERT(size() + ssv.length() <= max_size(), "plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
982 internal_insert(pos, const_pointer(ssv.data()), ssv.length());
983 return *this;
984 }
985
986#if PLUGIFY_STRING_CONTAINERS_RANGES
987 template<detail::string_compatible_range<Char> Range>
988 constexpr iterator insert_range(const_iterator pos, Range&& range) {
989 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
990 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::insert_range(): resulted string size would exceed max_size()", std::length_error);
991 return insert(pos - begin(), str);
992 }
993#endif // PLUGIFY_STRING_CONTAINERS_RANGES
994
995 constexpr basic_string& erase(size_type pos = 0, size_type count = npos) {
996 auto sz = size();
997 auto buffer = data();
998
999 PLUGIFY_ASSERT(pos <= sz, "plg::basic_string::erase(): pos out of range", std::out_of_range);
1000
1001 count = std::min(count, sz - pos);
1002
1003 auto left = sz - (pos + count);
1004 if (left != 0)
1005 Traits::move(buffer + pos, buffer + pos + count, left);
1006
1007 auto new_size = pos + left;
1008 set_size(new_size);
1009 null_terminate();
1010
1011 return *this;
1012 }
1013
1014 constexpr iterator erase(const_iterator position) {
1015 auto pos = std::distance(cbegin(), position);
1016 erase(pos, 1);
1017 return begin() + pos;
1018 }
1019
1020 constexpr iterator erase(const_iterator first, const_iterator last) {
1021 auto pos = std::distance(cbegin(), first);
1022 auto len = std::distance(first, last);
1023 erase(pos, len);
1024 return begin() + pos;
1025 }
1026
1027 constexpr void push_back(value_type ch) {
1028 PLUGIFY_ASSERT(size() + 1 <= max_size(), "plg::basic_string::push_back(): resulted string size would exceed max_size()", std::length_error);
1029 append(1, ch);
1030 }
1031
1032 constexpr void pop_back() {
1033 erase(end() - 1);
1034 }
1035
1036 constexpr basic_string& append(size_type count, value_type ch) {
1037 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1038 internal_append(ch, count);
1039 return *this;
1040 }
1041
1042 constexpr basic_string& append(const basic_string& str) {
1043 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1044 internal_append(str.data(), str.size());
1045 return *this;
1046 }
1047
1048 constexpr basic_string& append(const basic_string& str, size_type pos, size_type count = npos) {
1049 PLUGIFY_ASSERT(pos <= str.size(), "plg::basic_string::append(): pos out of range", std::out_of_range);
1050 auto ssv = sview_type(str).substr(pos, count);
1051 PLUGIFY_ASSERT(size() + ssv.length() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1052 internal_append(ssv.data(), ssv.length());
1053 return *this;
1054 }
1055
1056 constexpr basic_string& append(const value_type* str, size_type count) {
1057 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1058 internal_append(str, count);
1059 return *this;
1060 }
1061
1062 constexpr basic_string& append(const value_type* str) {
1063 auto len = Traits::length(str);
1064 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1065 return append(str, len);
1066 }
1067
1068 template<PLUGIFY_INPUT_ITERATOR InputIterator>
1069 constexpr basic_string& append(InputIterator first, InputIterator last) {
1070 auto len = static_cast<size_type>(std::distance(first, last));
1071 PLUGIFY_ASSERT(size() + len <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1072 internal_append(const_pointer(first), len);
1073 return *this;
1074 }
1075
1076 constexpr basic_string& append(std::initializer_list<value_type> list) {
1077 PLUGIFY_ASSERT(size() + list.size() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1078 internal_append(const_pointer(list.begin()), list.size());
1079 return *this;
1080 }
1081
1082 template<typename Type>
1083#ifdef __cpp_concepts
1084 requires(std::is_convertible_v<const Type&, sview_type> &&
1085 !std::is_convertible_v<const Type&, const Char*>)
1086#endif // __cpp_concepts
1087 constexpr basic_string& append(const Type& t) {
1088 sview_type sv(t);
1089 PLUGIFY_ASSERT(size() + sv.length() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1090 internal_append(sv.data(), sv.size());
1091 return *this;
1092 }
1093
1094 template<typename Type>
1095#ifdef __cpp_concepts
1096 requires(std::is_convertible_v<const Type&, sview_type> &&
1097 !std::is_convertible_v<const Type&, const Char*>)
1098#endif // __cpp_concepts
1099 constexpr basic_string& append(const Type& t, size_type pos, size_type count = npos) {
1100 sview_type sv(t);
1101 PLUGIFY_ASSERT(pos <= sv.length(), "plg::basic_string::append(): pos out of range", std::out_of_range);
1102 auto ssv = sv.substr(pos, count);
1103 PLUGIFY_ASSERT(size() + ssv.length() <= max_size(), "plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
1104 internal_append(ssv.data(), ssv.length());
1105 return *this;
1106 }
1107
1108#if PLUGIFY_STRING_CONTAINERS_RANGES
1109 template<detail::string_compatible_range<Char> Range>
1110 constexpr basic_string& append_range(Range&& range) {
1111 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
1112 PLUGIFY_ASSERT(size() + str.size() <= max_size(), "plg::basic_string::insert_range(): resulted string size would exceed max_size()", std::length_error);
1113 return append(str);
1114 }
1115#endif // PLUGIFY_STRING_CONTAINERS_RANGES
1116
1117 constexpr basic_string& operator+=(const basic_string& str) {
1118 return append(str);
1119 }
1120
1121 constexpr basic_string& operator+=(value_type ch) {
1122 push_back(ch);
1123 return *this;
1124 }
1125
1126 constexpr basic_string& operator+=(const value_type* str) {
1127 return append(str);
1128 }
1129
1130 constexpr basic_string& operator+=(std::initializer_list<value_type> list) {
1131 return append(list);
1132 }
1133
1134 template<typename Type>
1135#ifdef __cpp_concepts
1136 requires(std::is_convertible_v<const Type&, sview_type> &&
1137 !std::is_convertible_v<const Type&, const Char*>)
1138#endif // __cpp_concepts
1139 constexpr basic_string& operator+=(const Type& t) {
1140 return append(sview_type(t));
1141 }
1142
1143 [[nodiscard]] constexpr int compare(const basic_string& str) const noexcept {
1144 return view().compare(str.view());
1145 }
1146
1147 [[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const basic_string& str) const {
1148 return view().compare(pos1, count1, str.view());
1149 }
1150
1151 [[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const basic_string& str, size_type pos2, size_type count2 = npos) const {
1152 return view().compare(pos1, count1, str.view(), pos2, count2);
1153 }
1154
1155 [[nodiscard]] constexpr int compare(const value_type* str) const {
1156 return view().compare(str);
1157 }
1158
1159 [[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const value_type* str) const {
1160 return view().compare(pos1, count1, str);
1161 }
1162
1163 [[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const value_type* str, size_type count2) const {
1164 return view().compare(pos1, count1, str, count2);
1165 }
1166
1167 template<typename Type>
1168#ifdef __cpp_concepts
1169 requires(std::is_convertible_v<const Type&, sview_type> &&
1170 !std::is_convertible_v<const Type&, const Char*>)
1171#endif // __cpp_concepts
1172 [[nodiscard]] constexpr int compare(const Type& t) const noexcept(noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type))) {
1173 return view().compare(sview_type(t));
1174 }
1175
1176 template<typename Type>
1177#ifdef __cpp_concepts
1178 requires(std::is_convertible_v<const Type&, sview_type> &&
1179 !std::is_convertible_v<const Type&, const Char*>)
1180#endif // __cpp_concepts
1181 [[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const Type& t) const {
1182 return view().compare(pos1, count1, sview_type(t));
1183 }
1184
1185 template<typename Type>
1186#ifdef __cpp_concepts
1187 requires(std::is_convertible_v<const Type&, sview_type> &&
1188 !std::is_convertible_v<const Type&, const Char*>)
1189#endif // __cpp_concepts
1190 [[nodiscard]] constexpr int compare(size_type pos1, size_type count1, const Type& t, size_type pos2, size_type count2 = npos) const {
1191 return view().compare(pos1, count1, sview_type(t), pos2, count2);
1192 }
1193
1194 [[nodiscard]] constexpr bool starts_with(sview_type sv) const noexcept {
1195 return view().starts_with(sv);
1196 }
1197
1198 [[nodiscard]] constexpr bool starts_with(Char ch) const noexcept {
1199 return view().starts_with(ch);
1200 }
1201
1202 [[nodiscard]] constexpr bool starts_with(const Char* str) const {
1203 return view().starts_with(str);
1204 }
1205
1206 [[nodiscard]] constexpr bool ends_with(sview_type sv) const noexcept {
1207 return view().ends_with(sv);
1208 }
1209
1210 [[nodiscard]] constexpr bool ends_with(Char ch) const noexcept {
1211 return view().ends_with(ch);
1212 }
1213
1214 [[nodiscard]] constexpr bool ends_with(const Char* str) const {
1215 return view().ends_with(str);
1216 }
1217
1218 [[nodiscard]] constexpr bool contains(sview_type sv) const noexcept {
1219 return view().contains(sv);
1220 }
1221
1222 [[nodiscard]] constexpr bool contains(Char ch) const noexcept {
1223 return view().contains(ch);
1224 }
1225
1226 [[nodiscard]] constexpr bool contains(const Char* str) const {
1227 return view().contains(str);
1228 }
1229
1230 constexpr basic_string& replace(size_type pos, size_type count, const basic_string& str) {
1231 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1232 return replace(pos, count, str, 0, str.length());
1233 }
1234
1235 constexpr basic_string& replace(const_iterator first, const_iterator last, const basic_string& str) {
1236 auto pos = std::distance(cbegin(), first);
1237 auto count = std::distance(first, last);
1238 return replace(pos, count, str, 0, str.length());
1239 }
1240
1241 constexpr basic_string& replace(size_type pos, size_type count, const basic_string& str, size_type pos2, size_type count2 = npos) {
1242 PLUGIFY_ASSERT(pos <= size() && pos2 <= str.size(), "plg::basic_string::replace(): pos or pos_str out of range", std::out_of_range);
1243 count2 = std::min(count2, str.length() - pos2);
1244 auto ssv = sview_type(str).substr(pos2, count2);
1245 return replace(pos, count, ssv.data(), ssv.length());
1246 }
1247
1248 template<PLUGIFY_INPUT_ITERATOR InputIterator>
1249 constexpr basic_string& replace(const_iterator first, const_iterator last, InputIterator first2, InputIterator last2) {
1250 return replace(first, last, const_pointer(first2), std::distance(first2, last2));
1251 }
1252
1253 constexpr basic_string& replace(size_type pos, size_type count, const value_type* str, size_type count2) {
1254 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1255 count = std::min(count, length() - pos);
1256 PLUGIFY_ASSERT(size() - count + count2 <= max_size(), "plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1257 internal_replace(pos, const_pointer(str), count, count2);
1258 return *this;
1259 }
1260
1261 constexpr basic_string& replace(const_iterator first, const_iterator last, const value_type* str, size_type count2) {
1262 auto pos = std::distance(cbegin(), first);
1263 auto count = std::distance(first, last);
1264 return replace(pos, count, str, count2);
1265 }
1266
1267 constexpr basic_string& replace(size_type pos, size_type count, const value_type* str) {
1268 return replace(pos, count, str, Traits::length(str));
1269 }
1270
1271 constexpr basic_string& replace(const_iterator first, const_iterator last, const value_type* str) {
1272 return replace(first, last, str, Traits::length(str));
1273 }
1274
1275 constexpr basic_string& replace(size_type pos, size_type count, size_type count2, value_type ch) {
1276 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1277 count = std::min(count, length() - pos);
1278 PLUGIFY_ASSERT(size() - count + count2 <= max_size(), "plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1279 internal_replace(pos, ch, count, count2);
1280 return *this;
1281 }
1282
1283 constexpr basic_string& replace(const_iterator first, const_iterator last, size_type count2, value_type ch) {
1284 auto pos = std::distance(cbegin(), first);
1285 auto count = std::distance(first, last);
1286
1287 PLUGIFY_ASSERT(size() - count + count2 <= max_size(), "plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1288 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1289 internal_replace(pos, ch, count, count2);
1290 return *this;
1291 }
1292
1293 constexpr basic_string& replace(const_iterator first, const_iterator last, std::initializer_list<value_type> list) {
1294 return replace(first, last, const_pointer(list.begin()), list.size());
1295 }
1296
1297 template<typename Type>
1298#ifdef __cpp_concepts
1299 requires(std::is_convertible_v<const Type&, sview_type> &&
1300 !std::is_convertible_v<const Type&, const Char*>)
1301#endif // __cpp_concepts
1302 constexpr basic_string& replace(size_type pos, size_type count, const Type& t) {
1303 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1304 sview_type sv(t);
1305 return replace(pos, count, sv.data(), sv.length());
1306 }
1307
1308 template<typename Type>
1309#ifdef __cpp_concepts
1310 requires(std::is_convertible_v<const Type&, sview_type> &&
1311 !std::is_convertible_v<const Type&, const Char*>)
1312#endif // __cpp_concepts
1313 constexpr basic_string& replace(const_iterator first, const_iterator last, const Type& t) {
1314 sview_type sv(t);
1315 return replace(first, last, sv.data(), sv.length());
1316 }
1317
1318 template<typename Type>
1319#ifdef __cpp_concepts
1320 requires(std::is_convertible_v<const Type&, sview_type> &&
1321 !std::is_convertible_v<const Type&, const Char*>)
1322#endif // __cpp_concepts
1323 constexpr basic_string& replace(size_type pos, size_type count, const Type& t, size_type pos2, size_type count2 = npos) {
1324 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::replace(): pos out of range", std::out_of_range);
1325 auto sv = sview_type(t).substr(pos2, count2);
1326 return replace(pos, count, sv.data(), sv.length());
1327 }
1328
1329#if PLUGIFY_STRING_CONTAINERS_RANGES
1330 template<detail::string_compatible_range<Char> Range>
1331 constexpr iterator replace_with_range(const_iterator first, const_iterator last, Range&& range) {
1332 auto str = basic_string(std::from_range, std::forward<Range>(range), _allocator);
1333 return replace(first, last, str);// replace checks for max_size()
1334 }
1335#endif // PLUGIFY_STRING_CONTAINERS_RANGES
1336
1337 [[nodiscard]] constexpr basic_string substr(size_type pos = 0, size_type count = npos) const {
1338 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::substr(): pos out of range", std::out_of_range);
1339 return basic_string(*this, pos, count);
1340 }
1341
1342 constexpr size_type copy(value_type* str, size_type count, size_type pos = 0) const {
1343 PLUGIFY_ASSERT(pos <= size(), "plg::basic_string::copy(): pos out of range", std::out_of_range);
1344 return view().copy(str, count, pos);
1345 }
1346
1347 constexpr void resize(size_type count, value_type ch) {
1348 PLUGIFY_ASSERT(size() + count <= max_size(), "plg::basic_string::resize(): resulted string size would exceed max_size()", std::length_error);
1349 auto cap = capacity();
1350 auto sz = size();
1351 auto rsz = count + sz;
1352
1353 if (sz < rsz) {
1354 if (cap < rsz)
1355 grow_to(rsz);
1356 Traits::assign(data() + sz, count, ch);
1357 }
1358 set_size(rsz);
1359 null_terminate();
1360 }
1361
1362 constexpr void resize(size_type count) {
1363 resize(count, _terminator);
1364 }
1365
1366 template<typename Operation>
1367 constexpr void resize_and_overwrite(size_type, Operation) {
1368 static_assert(detail::dependent_false<Char>, "plg::basic_string::resize_and_overwrite(count, op) not implemented!");
1369 }
1370
1371 constexpr void swap(basic_string& other) noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value) {
1372 using std::swap;
1373 if constexpr (allocator_traits::propagate_on_container_swap::value) {
1374 swap(_allocator, other._allocator);
1375 }
1376 swap(_storage, other._storage);
1377 }
1378
1379 [[nodiscard]] constexpr size_type find(const basic_string& str, size_type pos = 0) const noexcept {
1380 return view().find(sview_type(str), pos);
1381 }
1382
1383 [[nodiscard]] constexpr size_type find(const value_type* str, size_type pos, size_type count) const noexcept {
1384 return view().find(str, pos, count);
1385 }
1386
1387 [[nodiscard]] constexpr size_type find(const value_type* str, size_type pos = 0) const noexcept {
1388 return view().find(str, pos);
1389 }
1390
1391 [[nodiscard]] constexpr size_type find(value_type ch, size_type pos = 0) const noexcept {
1392 return view().find(ch, pos);
1393 }
1394
1395 template<typename Type>
1396#ifdef __cpp_concepts
1397 requires(std::is_convertible_v<const Type&, sview_type> &&
1398 !std::is_convertible_v<const Type&, const Char*>)
1399#endif // __cpp_concepts
1400 [[nodiscard]] constexpr size_type find(const Type& t, size_type pos = 0) const noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type)) {
1401 return view().find(sview_type(t), pos);
1402 }
1403
1404 [[nodiscard]] constexpr size_type rfind(const basic_string& str, size_type pos = npos) const noexcept {
1405 return view().rfind(sview_type(str), pos);
1406 }
1407
1408 [[nodiscard]] constexpr size_type rfind(const value_type* str, size_type pos, size_type count) const noexcept {
1409 return view().rfind(str, pos, count);
1410 }
1411
1412 [[nodiscard]] constexpr size_type rfind(const value_type* str, size_type pos = npos) const noexcept {
1413 return view().rfind(str, pos);
1414 }
1415
1416 [[nodiscard]] constexpr size_type rfind(value_type ch, size_type pos = npos) const noexcept {
1417 return view().rfind(ch, pos);
1418 }
1419
1420 template<typename Type>
1421#ifdef __cpp_concepts
1422 requires(std::is_convertible_v<const Type&, sview_type> &&
1423 !std::is_convertible_v<const Type&, const Char*>)
1424#endif // __cpp_concepts
1425 [[nodiscard]] constexpr size_type rfind(const Type& t, size_type pos = npos) const noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type)) {
1426 return view().rfind(sview_type(t), pos);
1427 }
1428
1429 [[nodiscard]] constexpr size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept {
1430 return view().find_first_of(sview_type(str), pos);
1431 }
1432
1433 [[nodiscard]] constexpr size_type find_first_of(const value_type* str, size_type pos, size_type count) const noexcept {
1434 return view().find_first_of(str, pos, count);
1435 }
1436
1437 [[nodiscard]] constexpr size_type find_first_of(const value_type* str, size_type pos = 0) const noexcept {
1438 return view().find_first_of(str, pos);
1439 }
1440
1441 [[nodiscard]] constexpr size_type find_first_of(value_type ch, size_type pos = 0) const noexcept {
1442 return view().find_first_of(ch, pos);
1443 }
1444
1445 template<typename Type>
1446#ifdef __cpp_concepts
1447 requires(std::is_convertible_v<const Type&, sview_type> &&
1448 !std::is_convertible_v<const Type&, const Char*>)
1449#endif // __cpp_concepts
1450 [[nodiscard]] constexpr size_type find_first_of(const Type& t, size_type pos = 0) const noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type)) {
1451 return view().find_first_of(sview_type(t), pos);
1452 }
1453
1454 [[nodiscard]] constexpr size_type find_first_not_of(const basic_string& str, size_type pos = 0) const noexcept {
1455 return view().find_last_not_of(sview_type(str), pos);
1456 }
1457
1458 [[nodiscard]] constexpr size_type find_first_not_of(const value_type* str, size_type pos, size_type count) const noexcept {
1459 return view().find_last_not_of(str, pos, count);
1460 }
1461
1462 [[nodiscard]] constexpr size_type find_first_not_of(const value_type* str, size_type pos = 0) const noexcept {
1463 return view().find_last_not_of(str, pos);
1464 }
1465
1466 [[nodiscard]] constexpr size_type find_first_not_of(value_type ch, size_type pos = 0) const noexcept {
1467 return view().find_first_not_of(ch, pos);
1468 }
1469
1470 template<typename Type>
1471#ifdef __cpp_concepts
1472 requires(std::is_convertible_v<const Type&, sview_type> &&
1473 !std::is_convertible_v<const Type&, const Char*>)
1474#endif // __cpp_concepts
1475 [[nodiscard]] constexpr size_type find_first_not_of(const Type& t, size_type pos = 0) const noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type)) {
1476 return view().find_first_not_of(sview_type(t), pos);
1477 }
1478
1479 [[nodiscard]] constexpr size_type find_last_of(const basic_string& str, size_type pos = npos) const noexcept {
1480 return view().find_last_of(sview_type(str), pos);
1481 }
1482
1483 [[nodiscard]] constexpr size_type find_last_of(const value_type* str, size_type pos, size_type count) const noexcept {
1484 return view().find_last_of(str, pos, count);
1485 }
1486
1487 [[nodiscard]] constexpr size_type find_last_of(const value_type* str, size_type pos = npos) const noexcept {
1488 return view().find_last_of(str, pos);
1489 }
1490
1491 [[nodiscard]] constexpr size_type find_last_of(value_type ch, size_type pos = npos) const noexcept {
1492 return view().find_last_of(ch, pos);
1493 }
1494
1495 template<typename Type>
1496#ifdef __cpp_concepts
1497 requires(std::is_convertible_v<const Type&, sview_type> &&
1498 !std::is_convertible_v<const Type&, const Char*>)
1499#endif // __cpp_concepts
1500 [[nodiscard]] constexpr size_type find_last_of(const Type& t, size_type pos = npos) const noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type)) {
1501 return view().find_last_of(sview_type(t), pos);
1502 }
1503
1504 [[nodiscard]] constexpr size_type find_last_not_of(const basic_string& str, size_type pos = npos) const noexcept {
1505 return view().find_last_not_of(sview_type(str), pos);
1506 }
1507
1508 [[nodiscard]] constexpr size_type find_last_not_of(const value_type* str, size_type pos, size_type count) const noexcept {
1509 return view().find_last_not_of(str, pos, count);
1510 }
1511
1512 [[nodiscard]] constexpr size_type find_last_not_of(const value_type* str, size_type pos = npos) const noexcept {
1513 return view().find_last_not_of(str, pos);
1514 }
1515
1516 [[nodiscard]] constexpr size_type find_last_not_of(value_type ch, size_type pos = npos) const noexcept {
1517 return view().find_last_not_of(ch, pos);
1518 }
1519
1520 template<typename Type>
1521#ifdef __cpp_concepts
1522 requires(std::is_convertible_v<const Type&, sview_type> &&
1523 !std::is_convertible_v<const Type&, const Char*>)
1524#endif // __cpp_concepts
1525 [[nodiscard]] constexpr size_type find_last_not_of(const Type& t, size_type pos = npos) const noexcept(PLUGIFY_NOTTHROW_CONVERTIBLE(const Type&, sview_type)) {
1526 return view().find_last_not_of(sview_type(t), pos);
1527 }
1528
1529 [[nodiscard]] friend constexpr basic_string operator+(const basic_string& lhs, const basic_string& rhs) {
1530 auto lhs_sz = lhs.size();
1531 auto rhs_sz = rhs.size();
1532 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + rhs_sz, basic_string::allocator_traits::select_on_container_copy_construction(lhs._allocator));
1533 auto buffer = ret.data();
1534 Traits::copy(buffer, lhs.data(), lhs_sz);
1535 Traits::copy(buffer + lhs_sz, rhs.data(), rhs_sz);
1536 ret.null_terminate();
1537 return ret;
1538 }
1539
1540 [[nodiscard]] friend constexpr basic_string operator+(basic_string&& lhs, const basic_string& rhs) {
1541 return std::move(lhs.append(rhs));
1542 }
1543
1544 [[nodiscard]] friend constexpr basic_string operator+(const basic_string& lhs, basic_string&& rhs) {
1545 return std::move(rhs.insert(0, lhs));
1546 }
1547
1548 [[nodiscard]] friend constexpr basic_string operator+(basic_string&& lhs, basic_string&& rhs) {
1549 return std::move(lhs.append(rhs));
1550 }
1551
1552 [[nodiscard]] friend constexpr basic_string operator+(const Char* lhs, const basic_string& rhs) {
1553 auto lhs_sz = Traits::length(lhs);
1554 auto rhs_sz = rhs.size();
1555 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + rhs_sz, basic_string::allocator_traits::select_on_container_copy_construction(rhs._allocator));
1556 auto buffer = ret.data();
1557 Traits::copy(buffer, lhs, lhs_sz);
1558 Traits::copy(buffer + lhs_sz, rhs.data(), rhs_sz);
1559 ret.null_terminate();
1560 return ret;
1561 }
1562
1563 [[nodiscard]] friend constexpr basic_string operator+(const Char* lhs, basic_string&& rhs) {
1564 return std::move(rhs.insert(0, lhs));
1565 }
1566
1567 [[nodiscard]] friend constexpr basic_string operator+(Char lhs, const basic_string& rhs) {
1568 auto rhs_sz = rhs.size();
1569 basic_string ret(detail::uninitialized_size_tag(), rhs_sz + 1, basic_string::allocator_traits::select_on_container_copy_construction(rhs._allocator));
1570 auto buffer = ret.data();
1571 Traits::assign(buffer, 1, lhs);
1572 Traits::copy(buffer + 1, rhs.data(), rhs_sz);
1573 ret.null_terminate();
1574 return ret;
1575 }
1576
1577 [[nodiscard]] friend constexpr basic_string operator+(Char lhs, basic_string&& rhs) {
1578 rhs.insert(rhs.begin(), lhs);
1579 return std::move(rhs);
1580 }
1581
1582 [[nodiscard]] friend constexpr basic_string operator+(const basic_string& lhs, const Char* rhs) {
1583 auto lhs_sz = lhs.size();
1584 auto rhs_sz = Traits::length(rhs);
1585 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + rhs_sz, basic_string::allocator_traits::select_on_container_copy_construction(lhs._allocator));
1586 auto buffer = ret.data();
1587 Traits::copy(buffer, lhs.data(), lhs_sz);
1588 Traits::copy(buffer + lhs_sz, rhs, rhs_sz);
1589 ret.null_terminate();
1590 return ret;
1591 }
1592
1593 [[nodiscard]] friend constexpr basic_string operator+(basic_string&& lhs, const Char* rhs) {
1594 return std::move(lhs.append(rhs));
1595 }
1596
1597 [[nodiscard]] friend constexpr basic_string operator+(const basic_string& lhs, Char rhs) {
1598 auto lhs_sz = lhs.size();
1599 basic_string ret(detail::uninitialized_size_tag(), lhs_sz + 1, basic_string::allocator_traits::select_on_container_copy_construction(lhs._allocator));
1600 auto buffer = ret.data();
1601 Traits::copy(buffer, lhs.data(), lhs_sz);
1602 Traits::assign(buffer + lhs_sz, 1, rhs);
1603 ret.null_terminate();
1604 return ret;
1605 }
1606
1607 [[nodiscard]] friend constexpr basic_string operator+(basic_string&& lhs, Char rhs) {
1608 lhs.push_back(rhs);
1609 return std::move(lhs);
1610 }
1611 };
1612
1613 template<typename Char, typename Traits, typename Allocator>
1614 [[nodiscard]] constexpr bool operator==(const basic_string<Char, Traits, Allocator>& lhs, const basic_string<Char, Traits, Allocator>& rhs) noexcept {
1615 return lhs.compare(rhs) == 0;
1616 }
1617
1618 template<typename Char, typename Traits, typename Allocator>
1619 [[nodiscard]] constexpr bool operator==(const basic_string<Char, Traits, Allocator>& lhs, const Char* rhs) {
1620 return lhs.compare(rhs) == 0;
1621 }
1622
1623#if __cpp_impl_three_way_comparison
1624 template<typename Char, typename Traits, typename Allocator>
1625 [[nodiscard]] constexpr std::strong_ordering operator<=>(const basic_string<Char, Traits, Allocator>& lhs, const basic_string<Char, Traits, Allocator>& rhs) noexcept {
1626 return lhs.compare(rhs) <=> 0;
1627 }
1628
1629 template<typename Char, typename Traits, typename Allocator>
1630 [[nodiscard]] constexpr std::strong_ordering operator<=>(const basic_string<Char, Traits, Allocator>& lhs, const Char* rhs) {
1631 return lhs.compare(rhs) <=> 0;
1632 }
1633#endif // __cpp_impl_three_way_comparison
1634
1635 // swap
1636 template<typename Char, typename Traits, typename Allocator>
1637 constexpr void swap(basic_string<Char, Traits, Allocator>& lhs, basic_string<Char, Traits, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
1638 lhs.swap(rhs);
1639 }
1640
1641 // erasure
1642 template<typename Char, typename Traits, typename Allocator, typename U>
1643 constexpr typename basic_string<Char, Traits, Allocator>::size_type erase(basic_string<Char, Traits, Allocator>& c, const U& value) {
1644 auto it = std::remove(c.begin(), c.end(), value);
1645 auto r = std::distance(it, c.end());
1646 c.erase(it, c.end());
1647 return r;
1648 }
1649
1650 template<typename Char, typename Traits, typename Allocator, typename Pred>
1651 constexpr typename basic_string<Char, Traits, Allocator>::size_type erase_if(basic_string<Char, Traits, Allocator>& c, Pred pred) {
1652 auto it = std::remove_if(c.begin(), c.end(), pred);
1653 auto r = std::distance(it, c.end());
1654 c.erase(it, c.end());
1655 return r;
1656 }
1657
1658 // deduction guides
1659 template<typename InputIterator, typename Allocator = std::allocator<typename std::iterator_traits<InputIterator>::value_type>>
1660 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>;
1661
1662 template<typename Char, typename Traits, typename Allocator = std::allocator<Char>>
1663 explicit basic_string(std::basic_string_view<Char, Traits>, const Allocator& = Allocator()) -> basic_string<Char, Traits, Allocator>;
1664
1665 template<typename Char, typename Traits, typename Allocator = std::allocator<Char>>
1666 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>;
1667
1668#if PLUGIFY_STRING_CONTAINERS_RANGES
1669 template<std::ranges::input_range Range, typename Allocator = std::allocator<std::ranges::range_value_t<Range>>>
1670 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>;
1671#endif // PLUGIFY_STRING_CONTAINERS_RANGES
1672
1673 // basic_string typedef-names
1674 using string = basic_string<char>;
1675 using u8string = basic_string<char8_t>;
1676 using u16string = basic_string<char16_t>;
1677 using u32string = basic_string<char32_t>;
1678 using wstring = basic_string<wchar_t>;
1679
1680#ifndef PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
1681 // numeric conversions
1682 [[nodiscard]] inline int stoi(const string& str, std::size_t* pos = nullptr, int base = 10) {
1683 auto cstr = str.c_str();
1684 char* ptr = const_cast<char*>(cstr);
1685
1686 auto ret = strtol(cstr, &ptr, base);
1687 if (pos != nullptr)
1688 *pos = static_cast<size_t>(cstr - ptr);
1689
1690 return static_cast<int>(ret);
1691 }
1692
1693 [[nodiscard]] inline long stol(const string& str, std::size_t* pos = nullptr, int base = 10) {
1694 auto cstr = str.c_str();
1695 char* ptr = const_cast<char*>(cstr);
1696
1697 auto ret = strtol(cstr, &ptr, base);
1698 if (pos != nullptr)
1699 *pos = static_cast<size_t>(cstr - ptr);
1700
1701 return ret;
1702 }
1703
1704 [[nodiscard]] inline long long stoll(const string& str, std::size_t* pos = nullptr, int base = 10) {
1705 auto cstr = str.c_str();
1706 char* ptr = const_cast<char*>(cstr);
1707
1708 auto ret = strtoll(cstr, &ptr, base);
1709 if (pos != nullptr)
1710 *pos = static_cast<size_t>(cstr - ptr);
1711
1712 return ret;
1713 }
1714
1715 [[nodiscard]] inline unsigned long stoul(const string& str, std::size_t* pos = nullptr, int base = 10) {
1716 auto cstr = str.c_str();
1717 char* ptr = const_cast<char*>(cstr);
1718
1719 auto ret = strtoul(cstr, &ptr, base);
1720 if (pos != nullptr)
1721 *pos = static_cast<size_t>(cstr - ptr);
1722
1723 return ret;
1724 }
1725
1726 [[nodiscard]] inline unsigned long long stoull(const string& str, std::size_t* pos = nullptr, int base = 10) {
1727 auto cstr = str.c_str();
1728 char* ptr = const_cast<char*>(cstr);
1729
1730 auto ret = strtoull(cstr, &ptr, base);
1731 if (pos != nullptr)
1732 *pos = static_cast<size_t>(cstr - ptr);
1733
1734 return ret;
1735 }
1736
1737 [[nodiscard]] inline float stof(const string& str, std::size_t* pos = nullptr) {
1738 auto cstr = str.c_str();
1739 char* ptr = const_cast<char*>(cstr);
1740
1741 auto ret = strtof(cstr, &ptr);
1742 if (pos != nullptr)
1743 *pos = static_cast<size_t>(cstr - ptr);
1744
1745 return ret;
1746 }
1747
1748 [[nodiscard]] inline double stod(const string& str, std::size_t* pos = nullptr) {
1749 auto cstr = str.c_str();
1750 char* ptr = const_cast<char*>(cstr);
1751
1752 auto ret = strtod(cstr, &ptr);
1753 if (pos != nullptr)
1754 *pos = static_cast<size_t>(cstr - ptr);
1755
1756 return ret;
1757 }
1758
1759 [[nodiscard]] inline long double stold(const string& str, std::size_t* pos = nullptr) {
1760 auto cstr = str.c_str();
1761 char* ptr = const_cast<char*>(cstr);
1762
1763 auto ret = strtold(cstr, &ptr);
1764 if (pos != nullptr)
1765 *pos = static_cast<size_t>(cstr - ptr);
1766
1767 return ret;
1768 }
1769
1770 namespace detail {
1771 template<typename S, typename V>
1772 PLUGIFY_FORCE_INLINE constexpr S to_string(V v) {
1773 // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
1774 // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented),
1775 // so we need +1 here.
1776 constexpr std::size_t bufSize = std::numeric_limits<V>::digits10 + 2; // +1 for minus, +1 for digits10
1777 char buf[bufSize];
1778 const auto res = std::to_chars(buf, buf + bufSize, v);
1779 return S(buf, res.ptr);
1780 }
1781
1782 typedef int (*wide_printf)(wchar_t* __restrict, std::size_t, const wchar_t* __restrict, ...);
1783
1784#if defined(_MSC_VER)
1785 inline int truncate_snwprintf(wchar_t* __restrict buffer, std::size_t count, const wchar_t* __restrict format, ...) {
1786 int r;
1787 va_list args;
1788 va_start(args, format);
1789 r = _vsnwprintf_s(buffer, count, _TRUNCATE, format, args);
1790 va_end(args);
1791 return r;
1792 }
1793#endif
1794
1795 PLUGIFY_FORCE_INLINE constexpr wide_printf get_swprintf() noexcept {
1796#if defined(_MSC_VER)
1797 return static_cast<int(__cdecl*)(wchar_t* __restrict, std::size_t, const wchar_t* __restrict, ...)>(truncate_snwprintf);
1798#else
1799 return swprintf;
1800#endif
1801 }
1802
1803 template<typename S, typename P, typename V>
1804 PLUGIFY_FORCE_INLINE constexpr S as_string(P sprintf_like, const typename S::value_type* fmt, V v) {
1805 typedef typename S::size_type size_type;
1806 S s;
1807 s.resize(s.capacity());
1808 size_type available = s.size();
1809 while (true) {
1810 int status = sprintf_like(&s[0], available + 1, fmt, v);
1811 if (status >= 0) {
1812 auto used = static_cast<size_type>(status);
1813 if (used <= available) {
1814 s.resize(used);
1815 break;
1816 }
1817 available = used; // Assume this is advice of how much space we need.
1818 } else {
1819 available = available * 2 + 1;
1820 }
1821 s.resize(available);
1822 }
1823 return s;
1824 }
1825 }// namespace detail
1826
1827 [[nodiscard]] inline string to_string(int val) { return detail::to_string<string>(val); }
1828 [[nodiscard]] inline string to_string(unsigned val) { return detail::to_string<string>(val); }
1829 [[nodiscard]] inline string to_string(long val) { return detail::to_string<string>(val); }
1830 [[nodiscard]] inline string to_string(unsigned long val) { return detail::to_string<string>(val); }
1831 [[nodiscard]] inline string to_string(long long val) { return detail::to_string<string>(val); }
1832 [[nodiscard]] inline string to_string(unsigned long long val) { return detail::to_string<string>(val); }
1833 [[nodiscard]] inline string to_string(float val) { return detail::as_string<string>(snprintf, "%f", val); }
1834 [[nodiscard]] inline string to_string(double val) { return detail::as_string<string>(snprintf, "%f", val); }
1835 [[nodiscard]] inline string to_string(long double val) { return detail::as_string<string>(snprintf, "%Lf", val); }
1836
1837 [[nodiscard]] inline wstring to_wstring(int val) { return detail::to_string<wstring>(val); }
1838 [[nodiscard]] inline wstring to_wstring(unsigned val) { return detail::to_string<wstring>(val); }
1839 [[nodiscard]] inline wstring to_wstring(long val) { return detail::to_string<wstring>(val); }
1840 [[nodiscard]] inline wstring to_wstring(unsigned long val) { return detail::to_string<wstring>(val); }
1841 [[nodiscard]] inline wstring to_wstring(long long val) { return detail::to_string<wstring>(val); }
1842 [[nodiscard]] inline wstring to_wstring(unsigned long long val) { return detail::to_string<wstring>(val); }
1843 [[nodiscard]] inline wstring to_wstring(float val) { return detail::as_string<wstring>(detail::get_swprintf(), L"%f", val); }
1844 [[nodiscard]] inline wstring to_wstring(double val) { return detail::as_string<wstring>(detail::get_swprintf(), L"%f", val); }
1845 [[nodiscard]] inline wstring to_wstring(long double val) { return detail::as_string<wstring>(detail::get_swprintf(), L"%Lf", val); }
1846#endif // PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
1847
1848#ifndef PLUGIFY_STRING_NO_STD_HASH
1849 // hash support
1850 namespace detail {
1851 template<typename Char, typename Allocator, typename String = basic_string<Char, std::char_traits<Char>, Allocator>>
1853 [[nodiscard]] constexpr std::size_t operator()(const String& str) const noexcept {
1854 return std::hash<typename String::sview_type>{}(typename String::sview_type(str));
1855 }
1856 };
1857 }// namespace detail
1858#endif // PLUGIFY_STRING_NO_STD_HASH
1859
1860#ifndef PLUGIFY_STRING_NO_STD_FORMAT
1861 // format support
1862 namespace detail {
1863 template<typename Char>
1864 static constexpr const Char* format_string() {
1865 if constexpr (std::is_same_v<Char, char> || std::is_same_v<Char, char8_t>)
1866 return "{}";
1867 if constexpr (std::is_same_v<Char, wchar_t>)
1868 return L"{}";
1869 if constexpr (std::is_same_v<Char, char16_t>)
1870 return u"{}";
1871 if constexpr (std::is_same_v<Char, char32_t>)
1872 return U"{}";
1873 return "";
1874 }
1875
1876 template<typename Char, typename Allocator, typename String = basic_string<Char, std::char_traits<Char>, Allocator>>
1878 constexpr auto parse(std::format_parse_context& ctx) {
1879 return ctx.begin();
1880 }
1881
1882 template<class FormatContext>
1883 auto format(const String& str, FormatContext& ctx) const {
1884 return std::format_to(ctx.out(), format_string<Char>(), str.c_str());
1885 }
1886 };
1887 }
1888#endif // PLUGIFY_STRING_NO_STD_FORMAT
1889
1890 inline namespace literals {
1891 inline namespace string_literals {
1893
1894#if defined(__clang__)
1895 PLUGIFY_WARN_IGNORE("-Wuser-defined-literals")
1896#elif defined(__GNUC__)
1897 PLUGIFY_WARN_IGNORE("-Wliteral-suffix")
1898#elif defined(_MSC_VER)
1900#endif
1901 // suffix for basic_string literals
1902 [[nodiscard]] inline string operator""s(const char* str, std::size_t len) { return string{str, len}; }
1903#ifdef __cpp_char8_t
1904 [[nodiscard]] inline u8string operator""s(const char8_t* str, std::size_t len) { return u8string{str, len}; }
1905#endif // __cpp_char8_t
1906 [[nodiscard]] inline u16string operator""s(const char16_t* str, std::size_t len) { return u16string{str, len}; }
1907 [[nodiscard]] inline u32string operator""s(const char32_t* str, std::size_t len) { return u32string{str, len}; }
1908 [[nodiscard]] inline wstring operator""s(const wchar_t* str, std::size_t len) { return wstring{str, len}; }
1909
1910 PLUGIFY_WARN_POP()
1911 }// namespace string_literals
1912 }// namespace literals
1913}// namespace plg
1914
1915#ifndef PLUGIFY_STRING_NO_STD_HASH
1916// hash support
1917namespace std {
1918 template<typename Allocator>
1919 struct hash<plg::basic_string<char, std::char_traits<char>, Allocator>> : plg::detail::string_hash_base<char, Allocator> {};
1920
1921 template<typename Allocator>
1922 struct hash<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>> : plg::detail::string_hash_base<char8_t, Allocator> {};
1923
1924 template<typename Allocator>
1925 struct hash<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>> : plg::detail::string_hash_base<char16_t, Allocator> {};
1926
1927 template<typename Allocator>
1928 struct hash<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>> : plg::detail::string_hash_base<char32_t, Allocator> {};
1929
1930 template<typename Allocator>
1931 struct hash<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>> : plg::detail::string_hash_base<wchar_t, Allocator> {};
1932}// namespace std
1933#endif // PLUGIFY_STRING_NO_STD_HASH
1934
1935#ifndef PLUGIFY_STRING_NO_STD_FORMAT
1936// format support
1937#ifdef FMT_HEADER_ONLY
1938namespace fmt {
1939#else
1940namespace std {
1941#endif
1942 template<typename Allocator>
1943 struct formatter<plg::basic_string<char, std::char_traits<char>, Allocator>> : plg::detail::string_formatter_base<char, Allocator> {};
1944
1945 template<typename Allocator>
1946 struct formatter<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>> : plg::detail::string_formatter_base<char8_t, Allocator> {};
1947
1948 template<typename Allocator>
1949 struct formatter<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>> : plg::detail::string_formatter_base<char16_t, Allocator> {};
1950
1951 template<typename Allocator>
1952 struct formatter<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>> : plg::detail::string_formatter_base<char32_t, Allocator> {};
1953
1954 template<typename Allocator>
1955 struct formatter<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>> : plg::detail::string_formatter_base<wchar_t, Allocator> {};
1956}// namespace std
1957#endif // PLUGIFY_STRING_NO_STD_FORMAT