5 # pragma clang system_header
6 #elif defined(__GNUC__)
7 # pragma GCC system_header
10 #include <initializer_list>
11 #include <string_view>
12 #include <type_traits>
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
31 #if PLUGIFY_STRING_CONTAINERS_RANGES
35 #ifndef PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
39 #ifndef PLUGIFY_STRING_NO_STD_FORMAT
40 # include <plugify/compat_format.hpp>
43 #include <plugify/macro.hpp>
47 template<
typename Allocator,
typename =
void>
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 {};
53 template<
typename Allocator>
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>>;
59 template<
typename Traits>
60 constexpr
bool is_traits_v = is_traits<Traits>::value;
65 constexpr
bool dependent_false =
false;
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>;
75 template<
typename Char,
typename Traits = std::
char_traits<Char>,
typename Allocator = std::allocator<Char>> requires (detail::is_traits_v<Traits> && detail::is_allocator_v<Allocator>)
78 using allocator_traits = std::allocator_traits<Allocator>;
80 using traits_type = Traits;
81 using value_type =
typename traits_type::char_type;
82 using allocator_type = Allocator;
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>;
95 constexpr
static size_type npos =
static_cast<size_t>(-1);
98 constexpr
static auto _terminator = value_type();
100 PLUGIFY_NO_UNIQUE_ADDRESS
101 allocator_type _allocator;
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)
111 PLUGIFY_WARN_IGNORE(4201)
112 PLUGIFY_WARN_IGNORE(4200)
115 template<
typename CharT, std::
size_t = sizeof(CharT)>
117 [[maybe_unused]] uint8_t padding[
sizeof(CharT) - 1];
120 template<
typename CharT>
121 struct padding<CharT, 1> {
128 struct sso_size : padding<value_type> {
129 PLUGIFY_PACK(
struct {
130 uint8_t spare_size : 7;
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.");
141 PLUGIFY_PACK(
struct {
142 size_type cap :
sizeof(size_type) * char_bit - 1;
143 size_type is_long : 1;
147 static constexpr size_type min_cap = (
sizeof(long_data) - 1) /
sizeof(value_type) > 2 ? (
sizeof(long_data) - 1) /
sizeof(value_type) : 2;
150 value_type data[min_cap];
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");
164 constexpr
static bool fits_in_sso(size_type size) noexcept {
165 return size < min_cap;
168 constexpr
void long_init() noexcept {
170 set_long_data(
nullptr);
175 constexpr
void short_init() noexcept {
180 constexpr
void default_init(size_type size) noexcept {
181 if (fits_in_sso(size))
187 constexpr
auto& get_long_data() noexcept {
188 return _storage._long.data;
191 constexpr
const auto& get_long_data() const noexcept {
192 return _storage._long.data;
195 constexpr
auto& get_short_data() noexcept {
196 return _storage._short.data;
199 constexpr
const auto& get_short_data() const noexcept {
200 return _storage._short.data;
203 constexpr
void set_short_size(size_type size) noexcept {
204 _storage._short.size.spare_size = min_cap - (size & 0x7F);
207 constexpr size_type get_short_size() const noexcept {
208 return min_cap - _storage._short.size.spare_size;
211 constexpr
void set_long_size(size_type size) noexcept {
212 _storage._long.size = size;
215 constexpr size_type get_long_size() const noexcept {
216 return _storage._long.size;
219 constexpr
void set_long_cap(size_type cap) noexcept {
220 _storage._long.cap = (cap & 0x7FFFFFFFFFFFFFFF);
223 constexpr size_type get_long_cap() const noexcept {
224 return _storage._long.cap;
227 constexpr
void set_long_data(value_type* data) noexcept {
228 _storage._long.data = data;
231 constexpr
bool is_long() const noexcept {
232 return _storage._long.is_long ==
true;
235 constexpr
void set_long(
bool is_long) noexcept {
236 _storage._long.is_long = is_long;
239 constexpr
void set_size(size_type size) noexcept {
243 set_short_size(size);
246 constexpr sview_type view() const noexcept {
247 return sview_type(data(), size());
250 constexpr
void reallocate(size_type new_cap,
bool copy_old) {
251 if (new_cap == get_long_cap())
254 auto old_len = get_long_size();
255 auto old_cap = get_long_cap();
256 auto& old_buffer = get_long_data();
258 auto new_len = std::min(new_cap, old_len);
259 auto new_data = allocator_traits::allocate(_allocator, new_cap + 1);
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);
267 set_long_data(new_data);
268 set_long_size(new_len);
269 set_long_cap(new_cap);
272 constexpr
void deallocate() {
274 if (
auto& buffer = get_long_data(); buffer !=
nullptr) {
275 allocator_traits::deallocate(_allocator, buffer, get_long_cap() + 1);
281 constexpr
void grow_to(size_type new_cap) {
282 if (is_long() ==
true) {
283 reallocate(new_cap,
true);
287 auto buffer = allocator_traits::allocate(_allocator, new_cap + 1);
288 auto len = get_short_size();
290 Traits::copy(buffer, get_short_data(), len);
291 Traits::assign(buffer[len], _terminator);
294 set_long_data(buffer);
296 set_long_cap(new_cap);
299 constexpr
void null_terminate() {
300 auto buffer = data();
301 if (buffer ==
nullptr) [[unlikely]]
303 Traits::assign(buffer[size()], _terminator);
306 constexpr
bool addr_in_range(const_pointer ptr)
const noexcept {
307 if (std::is_constant_evaluated())
310 return data() <= ptr && ptr <= data() + size();
314 constexpr
void internal_replace_impl(
const F& func, size_type pos, size_type oldcount, size_type count) {
315 auto cap = capacity();
318 auto rsz = sz - oldcount + count;
323 if (oldcount != count)
324 Traits::move(data() + pos + count, data() + pos + oldcount, sz - pos - oldcount);
332 constexpr
void internal_replace(size_type pos, const_pointer str, size_type oldcount, size_type count) {
333 if (addr_in_range(str)) {
334 basic_string rstr(str, count);
335 internal_replace_impl([&]() { Traits::copy(data() + pos, rstr.data(), count); }, pos, oldcount, count);
337 internal_replace_impl([&]() { Traits::copy(data() + pos, str, count); }, pos, oldcount, count);
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);
345 constexpr
void internal_insert_impl(
const F& func, size_type pos, size_type count) {
346 if (count == 0) [[unlikely]]
349 auto cap = capacity();
351 auto rsz = sz + count;
356 Traits::move(data() + pos + count, data() + pos, sz - pos);
363 constexpr
void internal_insert(size_type pos, const_pointer str, size_type count) {
364 if (addr_in_range(str)) {
365 basic_string rstr(str, count);
366 internal_insert_impl([&]() { Traits::copy(data() + pos, rstr.data(), count); }, pos, count);
368 internal_insert_impl([&]() { Traits::copy(data() + pos, str, count); }, pos, count);
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);
376 constexpr
void internal_append_impl(
const F& func, size_type count) {
377 if (count == 0) [[unlikely]]
380 auto cap = capacity();
382 auto rsz = sz + count;
392 constexpr
void internal_append(const_pointer str, size_type count) {
393 if (addr_in_range(str)) {
394 basic_string rstr(str, count);
395 internal_append_impl([&](size_type pos) { Traits::copy(data() + pos, rstr.data(), count); }, count);
397 internal_append_impl([&](size_type pos) { Traits::copy(data() + pos, str, count); }, count);
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);
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) {
412 set_short_size(size);
413 func(get_short_data());
416 if (is_long() ==
false)
418 if (get_long_cap() < size)
419 reallocate(size, copy_old);
421 func(get_long_data());
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);
432 internal_assign_impl([&](
auto data) { Traits::copy(data, str, size); }, size, copy_old);
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);
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))
447 reallocate(size,
false);
452 constexpr basic_string() noexcept(std::is_nothrow_default_constructible<Allocator>::value)
453 : basic_string(Allocator()) {}
455 explicit constexpr basic_string(
const Allocator& allocator) noexcept
456 : _allocator(allocator) {
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);
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);
473 constexpr basic_string(
const basic_string& str, size_type pos,
const Allocator& allocator = Allocator())
474 : basic_string(str, pos, npos, allocator) {}
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);
482 constexpr basic_string(
const value_type* str,
const Allocator& allocator = Allocator())
483 : basic_string(str, Traits::length(str), allocator) {}
485 template<std::input_iterator InputIterator>
486 constexpr basic_string(InputIterator first, InputIterator last,
const Allocator& allocator = Allocator())
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);
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);
499 constexpr basic_string(
const basic_string& str)
500 : basic_string(str, str.get_allocator()) {}
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)) {
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);
512 if (!str.is_long() || get_allocator() == str.get_allocator()) {
513 std::swap(_storage, str._storage);
515 internal_assign(str.data(), str.size());
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);
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);
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) {
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);
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);
558 constexpr basic_string(basic_string&& str, size_type pos,
const Allocator& allocator = Allocator())
559 : basic_string(std::move(str), pos, npos, allocator) {}
561 #if __cplusplus > 202002L
562 basic_string(std::nullptr_t) =
delete;
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) {}
571 constexpr ~basic_string() {
575 constexpr basic_string& operator=(
const basic_string& str) {
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));
585 constexpr basic_string& operator=(
const value_type* str) {
586 return assign(str, Traits::length(str));
589 constexpr basic_string& operator=(value_type ch) {
590 return assign(std::addressof(ch), 1);
593 constexpr basic_string& operator=(std::initializer_list<value_type> list) {
594 return assign(list.begin(), list.size());
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) {
605 #if __cplusplus > 202002L
606 constexpr basic_string& operator=(std::nullptr_t) =
delete;
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);
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));
621 constexpr basic_string& assign(
const basic_string& str) {
622 if (
this == &str) [[unlikely]]
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()) {
632 _allocator = str._allocator;
635 internal_assign(str.data(), str.size());
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]]
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()) {
652 _allocator = std::move(str._allocator);
655 if constexpr (allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) {
658 std::swap(_storage, str._storage);
660 if (get_allocator() == str.get_allocator()) {
663 std::swap(_storage, str._storage);
665 internal_assign(str.data(), str.size());
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);
678 constexpr basic_string& assign(
const value_type* str) {
679 return assign(str, Traits::length(str));
682 template<std::input_iterator InputIterator>
683 constexpr basic_string& assign(InputIterator first, InputIterator last) {
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);
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);
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) {
702 return assign(sv.data(), sv.length());
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);
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));
724 constexpr allocator_type get_allocator() const noexcept {
728 constexpr reference operator[](size_type pos) {
732 constexpr const_reference operator[](size_type pos)
const {
736 constexpr reference at(size_type pos) {
737 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::at(): pos out of range", std::out_of_range);
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);
746 constexpr reference front() {
747 PLUGIFY_ASSERT(!empty(),
"plg::basic_string::front(): vector is empty", std::length_error);
751 constexpr const_reference front()
const {
752 PLUGIFY_ASSERT(!empty(),
"plg::basic_string::front(): vector is empty", std::length_error);
756 constexpr reference back() {
757 PLUGIFY_ASSERT(!empty(),
"plg::basic_string::back(): vector is empty", std::length_error);
758 return data()[size() - 1];
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];
766 constexpr
const value_type* data() const noexcept {
767 return is_long() ? get_long_data() : get_short_data();
770 constexpr value_type* data() noexcept {
771 return is_long() ? get_long_data() : get_short_data();
774 constexpr
const value_type* c_str() const noexcept {
778 constexpr
operator sview_type() const noexcept {
782 constexpr iterator begin() noexcept {
786 constexpr const_iterator begin() const noexcept {
790 constexpr const_iterator cbegin() const noexcept {
794 constexpr iterator end() noexcept {
795 return data() + size();
798 constexpr const_iterator end() const noexcept {
799 return data() + size();
802 constexpr const_iterator cend() const noexcept {
803 return data() + size();
806 constexpr reverse_iterator rbegin() noexcept {
807 return reverse_iterator(end());
810 constexpr const_reverse_iterator rbegin() const noexcept {
811 return const_reverse_iterator(end());
814 constexpr const_reverse_iterator crbegin() const noexcept {
815 return const_reverse_iterator(cend());
818 constexpr reverse_iterator rend() noexcept {
819 return reverse_iterator(begin());
822 constexpr const_reverse_iterator rend() const noexcept {
823 return const_reverse_iterator(begin());
826 constexpr const_reverse_iterator crend() const noexcept {
827 return const_reverse_iterator(cbegin());
830 constexpr
bool empty() const noexcept {
834 constexpr size_type size() const noexcept {
835 return is_long() ? get_long_size() : get_short_size();
838 constexpr size_type length() const noexcept {
842 constexpr size_type max_size() const noexcept {
849 return (allocator_traits::max_size(_allocator) - 1) / 2;
852 constexpr size_type capacity() const noexcept {
853 return is_long() ? get_long_cap() : min_cap;
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())
861 auto new_cap = std::max(cap, size());
862 if (new_cap == capacity())
872 constexpr
void shrink_to_fit() {
873 if (is_long() ==
false)
876 reallocate(size(),
true);
879 constexpr
void clear() noexcept {
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);
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);
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);
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());
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);
919 constexpr iterator insert(const_iterator pos, value_type ch) {
920 return insert(pos, 1, ch);
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);
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);
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);
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);
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());
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());
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);
978 constexpr basic_string& erase(size_type pos = 0, size_type count = npos) {
980 auto buffer = data();
982 PLUGIFY_ASSERT(pos <= sz,
"plg::basic_string::erase(): pos out of range", std::out_of_range);
984 count = std::min(count, sz - pos);
986 auto left = sz - (pos + count);
988 Traits::move(buffer + pos, buffer + pos + count, left);
990 auto new_size = pos + left;
997 constexpr iterator erase(const_iterator position) {
998 auto pos = std::distance(cbegin(), position);
1000 return begin() + pos;
1003 constexpr iterator erase(const_iterator first, const_iterator last) {
1004 auto pos = std::distance(cbegin(), first);
1005 auto len = std::distance(first, last);
1007 return begin() + pos;
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);
1015 constexpr
void pop_back() {
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);
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());
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());
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);
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);
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);
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());
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) {
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());
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) {
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());
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);
1096 constexpr basic_string& operator+=(
const basic_string& str) {
1100 constexpr basic_string& operator+=(value_type ch) {
1105 constexpr basic_string& operator+=(
const value_type* str) {
1109 constexpr basic_string& operator+=(std::initializer_list<value_type> list) {
1110 return append(list);
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));
1120 constexpr
int compare(
const basic_string& str)
const noexcept {
1121 return view().compare(str.view());
1124 constexpr
int compare(size_type pos1, size_type count1,
const basic_string& str)
const {
1125 return view().compare(pos1, count1, str.view());
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);
1132 constexpr
int compare(
const value_type* str)
const {
1133 return view().compare(str);
1136 constexpr
int compare(size_type pos1, size_type count1,
const value_type* str)
const {
1137 return view().compare(pos1, count1, str);
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);
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));
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));
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);
1165 constexpr
bool starts_with(sview_type sv)
const noexcept {
1166 return view().starts_with(sv);
1169 constexpr
bool starts_with(Char ch)
const noexcept {
1170 return view().starts_with(ch);
1173 constexpr
bool starts_with(
const Char* str)
const {
1174 return view().starts_with(str);
1177 constexpr
bool ends_with(sview_type sv)
const noexcept {
1178 return view().ends_with(sv);
1181 constexpr
bool ends_with(Char ch)
const noexcept {
1182 return view().ends_with(ch);
1185 constexpr
bool ends_with(
const Char* str)
const {
1186 return view().ends_with(str);
1189 constexpr
bool contains(sview_type sv)
const noexcept {
1190 return view().contains(sv);
1193 constexpr
bool contains(Char ch)
const noexcept {
1194 return view().contains(ch);
1197 constexpr
bool contains(
const Char* str)
const {
1198 return view().contains(str);
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());
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());
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());
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));
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);
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);
1238 constexpr basic_string& replace(size_type pos, size_type count,
const value_type* str) {
1239 return replace(pos, count, str, Traits::length(str));
1242 constexpr basic_string& replace(const_iterator first, const_iterator last,
const value_type* str) {
1243 return replace(first, last, str, Traits::length(str));
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);
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);
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);
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());
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);
1274 return replace(pos, count, sv.data(), sv.length());
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) {
1282 return replace(first, last, sv.data(), sv.length());
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());
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);
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);
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);
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();
1316 auto rsz = count + sz;
1321 Traits::assign(data() + sz, count, ch);
1327 constexpr
void resize(size_type count) {
1328 resize(count, _terminator);
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!");
1336 constexpr
void swap(basic_string& other) noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value) {
1338 if constexpr (allocator_traits::propagate_on_container_swap::value) {
1339 swap(_allocator, other._allocator);
1341 swap(_storage, other._storage);
1344 constexpr size_type find(
const basic_string& str, size_type pos = 0) const noexcept {
1345 return view().find(sview_type(str), pos);
1348 constexpr size_type find(
const value_type* str, size_type pos, size_type count)
const noexcept {
1349 return view().find(str, pos, count);
1352 constexpr size_type find(
const value_type* str, size_type pos = 0) const noexcept {
1353 return view().find(str, pos);
1356 constexpr size_type find(value_type ch, size_type pos = 0) const noexcept {
1357 return view().find(ch, pos);
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);
1367 constexpr size_type rfind(
const basic_string& str, size_type pos = npos)
const noexcept {
1368 return view().rfind(sview_type(str), pos);
1371 constexpr size_type rfind(
const value_type* str, size_type pos, size_type count)
const noexcept {
1372 return view().rfind(str, pos, count);
1375 constexpr size_type rfind(
const value_type* str, size_type pos = npos)
const noexcept {
1376 return view().rfind(str, pos);
1379 constexpr size_type rfind(value_type ch, size_type pos = npos)
const noexcept {
1380 return view().rfind(ch, pos);
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);
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);
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);
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);
1402 constexpr size_type find_first_of(value_type ch, size_type pos = 0) const noexcept {
1403 return view().find_first_of(ch, pos);
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);
1413 constexpr size_type find_first_not_of(
const basic_string& str, size_type pos = 0) const noexcept {
1414 return view().find_last_not_of(sview_type(str), pos);
1417 constexpr size_type find_first_not_of(
const value_type* str, size_type pos, size_type count)
const noexcept {
1418 return view().find_last_not_of(str, pos, count);
1421 constexpr size_type find_first_not_of(
const value_type* str, size_type pos = 0) const noexcept {
1422 return view().find_last_not_of(str, pos);
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);
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);
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);
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);
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);
1448 constexpr size_type find_last_of(value_type ch, size_type pos = npos)
const noexcept {
1449 return view().find_last_of(ch, pos);
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);
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);
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);
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);
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);
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);
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();
1493 friend constexpr basic_string operator+(basic_string&& lhs,
const basic_string& rhs) {
1494 return std::move(lhs.append(rhs));
1497 friend constexpr basic_string operator+(
const basic_string& lhs, basic_string&& rhs) {
1498 return std::move(rhs.insert(0, lhs));
1501 friend constexpr basic_string operator+(basic_string&& lhs, basic_string&& rhs) {
1502 return std::move(lhs.append(rhs));
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();
1516 friend constexpr basic_string operator+(
const Char* lhs, basic_string&& rhs) {
1517 return std::move(rhs.insert(0, lhs));
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();
1530 friend constexpr basic_string operator+(Char lhs, basic_string&& rhs) {
1531 rhs.insert(rhs.begin(), lhs);
1532 return std::move(rhs);
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();
1546 friend constexpr basic_string operator+(basic_string&& lhs,
const Char* rhs) {
1547 return std::move(lhs.append(rhs));
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();
1560 friend constexpr basic_string operator+(basic_string&& lhs, Char rhs) {
1562 return std::move(lhs);
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;
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;
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;
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;
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))) {
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());
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());
1610 template<typename InputIterator, typename Allocator = std::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>;
1613 template<
typename Char,
typename Traits,
typename Allocator = std::allocator<Char>>
1614 explicit basic_string(std::basic_string_view<Char, Traits>,
const Allocator& = Allocator()) -> basic_string<Char, Traits, Allocator>;
1616 template<
typename Char,
typename Traits,
typename Allocator = std::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>;
1619 #if PLUGIFY_STRING_CONTAINERS_RANGES
1620 template<std::ranges::input_range Range,
typename Allocator = std::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>;
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>;
1631 #ifndef PLUGIFY_STRING_NO_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);
1637 auto ret = strtol(cstr, &ptr, base);
1639 *pos =
static_cast<size_t>(cstr - ptr);
1641 return static_cast<int>(ret);
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);
1648 auto ret = strtol(cstr, &ptr, base);
1650 *pos =
static_cast<size_t>(cstr - ptr);
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);
1659 auto ret = strtoll(cstr, &ptr, base);
1661 *pos =
static_cast<size_t>(cstr - ptr);
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);
1670 auto ret = strtoul(cstr, &ptr, base);
1672 *pos =
static_cast<size_t>(cstr - ptr);
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);
1681 auto ret = strtoull(cstr, &ptr, base);
1683 *pos =
static_cast<size_t>(cstr - ptr);
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);
1692 auto ret = strtof(cstr, &ptr);
1694 *pos =
static_cast<size_t>(cstr - ptr);
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);
1703 auto ret = strtod(cstr, &ptr);
1705 *pos =
static_cast<size_t>(cstr - ptr);
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);
1714 auto ret = strtold(cstr, &ptr);
1716 *pos =
static_cast<size_t>(cstr - ptr);
1722 template<
typename S,
typename V>
1723 PLUGIFY_FORCE_INLINE constexpr S to_string(V v) {
1727 constexpr std::size_t bufSize = std::numeric_limits<V>::digits10 + 2;
1729 const auto res = std::to_chars(buf, buf + bufSize, v);
1730 return S(buf, res.ptr);
1733 typedef int (*wide_printf)(
wchar_t* __restrict, std::size_t,
const wchar_t* __restrict, ...);
1735 #if defined(_MSC_VER)
1736 inline int truncate_snwprintf(
wchar_t* __restrict buffer, std::size_t count,
const wchar_t* __restrict format, ...) {
1739 va_start(args, format);
1740 r = _vsnwprintf_s(buffer, count, _TRUNCATE, format, args);
1746 PLUGIFY_FORCE_INLINE constexpr wide_printf get_swprintf() noexcept {
1747 #if defined(_MSC_VER)
1748 return static_cast<int(__cdecl*)(
wchar_t* __restrict, std::size_t,
const wchar_t* __restrict, ...)
>(truncate_snwprintf);
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;
1758 s.resize(s.capacity());
1759 size_type available = s.size();
1761 int status = sprintf_like(&s[0], available + 1, fmt, v);
1763 auto used =
static_cast<size_type
>(status);
1764 if (used <= available) {
1770 available = available * 2 + 1;
1772 s.resize(available);
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); }
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); }
1799 #ifndef PLUGIFY_STRING_NO_STD_HASH
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));
1811 #ifndef PLUGIFY_STRING_NO_STD_FORMAT
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>)
1818 if constexpr (std::is_same_v<Char, wchar_t>)
1820 if constexpr (std::is_same_v<Char, char16_t>)
1822 if constexpr (std::is_same_v<Char, char32_t>)
1826 template<
typename Char,
typename Allocator,
typename String = basic_
string<Char, std::
char_traits<Char>, Allocator>>
1828 constexpr
auto parse(std::format_parse_context& ctx) {
1832 template<
class FormatContext>
1833 auto format(
const String& str, FormatContext& ctx)
const {
1834 return std::format_to(ctx.out(), format_string<Char>(), str.c_str());
1840 inline namespace literals {
1841 inline namespace string_literals {
1844 #if defined(__clang__)
1845 PLUGIFY_WARN_IGNORE(
"-Wuser-defined-literals")
1846 #elif defined(__GNUC__)
1847 PLUGIFY_WARN_IGNORE(
"-Wliteral-suffix")
1848 #elif defined(_MSC_VER)
1849 PLUGIFY_WARN_IGNORE(4455)
1852 constexpr
string operator""s(
const char* str, std::size_t len) {
return string{str, len}; }
1853 constexpr u8string
operator""s(
const char8_t* str, std::size_t len) {
return u8string{str, len}; }
1854 constexpr u16string
operator""s(
const char16_t* str, std::size_t len) {
return u16string{str, len}; }
1855 constexpr u32string
operator""s(
const char32_t* str, std::size_t len) {
return u32string{str, len}; }
1856 constexpr wstring
operator""s(
const wchar_t* str, std::size_t len) {
return wstring{str, len}; }
1863 #ifndef PLUGIFY_STRING_NO_STD_HASH
1866 template<
typename Allocator>
1869 template<
typename Allocator>
1872 template<
typename Allocator>
1875 template<
typename Allocator>
1878 template<
typename Allocator>
1883 #ifndef PLUGIFY_STRING_NO_STD_FORMAT
1885 #ifdef FMT_HEADER_ONLY
1890 template<
typename Allocator>
1893 template<
typename Allocator>
1896 template<
typename Allocator>
1899 template<
typename Allocator>
1902 template<
typename Allocator>