78 using allocator_traits = std::allocator_traits<Allocator>;
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>;
95 constexpr static size_type npos =
static_cast<size_t>(-1);
98 constexpr static auto _terminator = value_type();
100 PLUGIFY_NO_UNIQUE_ADDRESS
105#if defined(__clang__)
108#elif defined(__GNUC__)
110#elif defined(_MSC_VER)
115 template<
typename CharT, std::
size_t = sizeof(CharT)>
120 template<
typename CharT>
121 struct padding<
CharT, 1> {
128 struct sso_size : padding<value_type> {
129 PLUGIFY_PACK(
struct {
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 [[
nodiscard]]
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 [[
nodiscard]]
constexpr auto& get_long_data()
noexcept {
188 return _storage._long.data;
191 [[
nodiscard]]
constexpr const auto& get_long_data()
const noexcept {
192 return _storage._long.data;
195 [[
nodiscard]]
constexpr auto& get_short_data()
noexcept {
196 return _storage._short.data;
199 [[
nodiscard]]
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 [[
nodiscard]]
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 [[
nodiscard]]
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 [[
nodiscard]]
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 [[
nodiscard]]
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 [[
nodiscard]]
constexpr sview_type view()
const noexcept {
247 return sview_type(data(), size());
254 auto old_len = get_long_size();
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) {
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);
299 constexpr void null_terminate() {
303 Traits::assign(
buffer[size()], _terminator);
306 [[
nodiscard]]
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();
332 constexpr void internal_replace(size_type pos, const_pointer
str, size_type
oldcount, size_type
count) {
333 if (addr_in_range(
str)) {
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) {
349 auto cap = capacity();
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)) {
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) {
380 auto cap = capacity();
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);
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)
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)) {
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) {
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)
462 PLUGIFY_ASSERT(
count <= max_size(),
"plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
468 PLUGIFY_ASSERT(pos <=
str.size(),
"plg::basic_string::basic_string(): pos out of range", std::out_of_range);
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);
478 PLUGIFY_ASSERT(
count <= max_size(),
"plg::basic_string::basic_string(): constructed string size would exceed max_size()", std::length_error);
485 template<PLUGIFY_INPUT_ITERATOR InputIterator>
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);
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);
503 : _allocator(std::move(
str._allocator)), _storage(std::move(
str._storage)) {
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());
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#if __cpp_impl_three_way_comparison
530 template<
typename Type>
531 requires(std::is_convertible_v<const Type&, sview_type>)
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);
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);
543 template<
typename Type>
545 requires(std::is_convertible_v<const Type&, sview_type> &&
546 !std::is_convertible_v<const Type&, const Char*>)
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);
558 PLUGIFY_ASSERT(pos <=
str.size(),
"plg::basic_string::basic_string(): pos out of range", std::out_of_range);
565#if __cplusplus > 202002L
569#if PLUGIFY_STRING_CONTAINERS_RANGES
570 template<detail::
string_compatible_range<Char> Range>
575#if __cpp_constexpr >= 201907L
587 allocator_traits::propagate_on_container_move_assignment::value ||
588 allocator_traits::is_always_equal::value) {
589 return assign(std::move(
str));
593 return assign(
str, Traits::length(
str));
597 return assign(std::addressof(
ch), 1);
601 return assign(
list.begin(),
list.size());
604 template<
typename Type>
606 requires(std::is_convertible_v<const Type&, sview_type> &&
607 !std::is_convertible_v<const Type&, const Char*>)
614#if __cplusplus > 202002L
615 constexpr basic_string& operator=(std::nullptr_t) =
delete;
619 PLUGIFY_ASSERT(
count <= max_size(),
"plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
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));
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()) {
641 _allocator =
str._allocator;
644 internal_assign(
str.data(),
str.size());
649 allocator_traits::propagate_on_container_move_assignment::value ||
650 allocator_traits::is_always_equal::value) {
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()) {
661 _allocator = std::move(
str._allocator);
664 if constexpr (allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value) {
667 std::swap(_storage,
str._storage);
669 if (get_allocator() ==
str.get_allocator()) {
672 std::swap(_storage,
str._storage);
674 internal_assign(
str.data(),
str.size());
682 PLUGIFY_ASSERT(
count <= max_size(),
"plg::basic_string::assign(): resulted string size would exceed max_size()", std::length_error);
688 return assign(
str, Traits::length(
str));
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);
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);
706 template<
typename Type>
708 requires(std::is_convertible_v<const Type&, sview_type> &&
709 !std::is_convertible_v<const Type&, const Char*>)
713 return assign(
sv.data(),
sv.length());
716 template<
typename Type>
718 requires(std::is_convertible_v<const Type&, sview_type> &&
719 !std::is_convertible_v<const Type&, const Char*>)
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);
728#if PLUGIFY_STRING_CONTAINERS_RANGES
729 template<detail::
string_compatible_range<Char> 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));
741 [[
nodiscard]]
constexpr reference operator[](size_type pos) {
745 [[
nodiscard]]
constexpr const_reference operator[](size_type pos)
const {
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);
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);
759 [[
nodiscard]]
constexpr reference front() {
760 PLUGIFY_ASSERT(!empty(),
"plg::basic_string::front(): vector is empty", std::length_error);
764 [[
nodiscard]]
constexpr const_reference front()
const {
765 PLUGIFY_ASSERT(!empty(),
"plg::basic_string::front(): vector is empty", std::length_error);
769 [[
nodiscard]]
constexpr reference back() {
770 PLUGIFY_ASSERT(!empty(),
"plg::basic_string::back(): vector is empty", std::length_error);
771 return data()[size() - 1];
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];
779 [[
nodiscard]]
constexpr const value_type* data()
const noexcept {
780 return is_long() ? get_long_data() : get_short_data();
783 [[
nodiscard]]
constexpr value_type* data()
noexcept {
784 return is_long() ? get_long_data() : get_short_data();
787 [[
nodiscard]]
constexpr const value_type* c_str()
const noexcept {
791 [[
nodiscard]]
constexpr operator sview_type()
const noexcept {
795 [[
nodiscard]]
constexpr iterator begin()
noexcept {
799 [[
nodiscard]]
constexpr const_iterator begin()
const noexcept {
803 [[
nodiscard]]
constexpr const_iterator cbegin()
const noexcept {
807 [[
nodiscard]]
constexpr iterator end()
noexcept {
808 return data() + size();
811 [[
nodiscard]]
constexpr const_iterator end()
const noexcept {
812 return data() + size();
815 [[
nodiscard]]
constexpr const_iterator cend()
const noexcept {
816 return data() + size();
819 [[
nodiscard]]
constexpr reverse_iterator rbegin()
noexcept {
820 return reverse_iterator(end());
823 [[
nodiscard]]
constexpr const_reverse_iterator rbegin()
const noexcept {
824 return const_reverse_iterator(end());
827 [[
nodiscard]]
constexpr const_reverse_iterator crbegin()
const noexcept {
828 return const_reverse_iterator(cend());
831 [[
nodiscard]]
constexpr reverse_iterator rend()
noexcept {
832 return reverse_iterator(begin());
835 [[
nodiscard]]
constexpr const_reverse_iterator rend()
const noexcept {
836 return const_reverse_iterator(begin());
839 [[
nodiscard]]
constexpr const_reverse_iterator crend()
const noexcept {
840 return const_reverse_iterator(cbegin());
843 [[
nodiscard]]
constexpr bool empty()
const noexcept {
847 [[
nodiscard]]
constexpr size_type size()
const noexcept {
848 return is_long() ? get_long_size() : get_short_size();
851 [[
nodiscard]]
constexpr size_type length()
const noexcept {
855 [[
nodiscard]]
constexpr size_type max_size()
const noexcept {
862 return (allocator_traits::max_size(_allocator) - 1) / 2;
865 [[
nodiscard]]
constexpr size_type capacity()
const noexcept {
866 return is_long() ? get_long_cap() : min_cap;
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())
874 auto new_cap = std::max(cap, size());
885 constexpr void shrink_to_fit() {
886 if (is_long() ==
false)
889 reallocate(size(),
true);
892 constexpr void clear()
noexcept {
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);
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);
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);
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());
926 PLUGIFY_ASSERT(pos <= size() &&
pos_str <=
str.size(),
"plg::basic_string::insert(): pos or pos_str out of range", std::out_of_range);
928 PLUGIFY_ASSERT(size() +
count <= max_size(),
"plg::basic_string::insert(): resulted string size would exceed max_size()", std::length_error);
932 constexpr iterator insert(const_iterator pos, value_type
ch) {
933 return insert(pos, 1,
ch);
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);
940 return std::next(begin(),
spos);
943 template<PLUGIFY_INPUT_ITERATOR InputIterator>
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);
949 return std::next(begin(),
spos);
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);
959 template<
typename Type>
961 requires(std::is_convertible_v<const Type&, sview_type> &&
962 !std::is_convertible_v<const Type&, const Char*>)
965 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::insert(): pos out of range", std::out_of_range);
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());
972 template<
typename Type>
974 requires(std::is_convertible_v<const Type&, sview_type> &&
975 !std::is_convertible_v<const Type&, const Char*>)
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);
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());
986#if PLUGIFY_STRING_CONTAINERS_RANGES
987 template<detail::
string_compatible_range<Char> 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);
999 PLUGIFY_ASSERT(pos <=
sz,
"plg::basic_string::erase(): pos out of range", std::out_of_range);
1014 constexpr iterator erase(const_iterator
position) {
1015 auto pos = std::distance(cbegin(),
position);
1017 return begin() + pos;
1020 constexpr iterator erase(const_iterator
first, const_iterator
last) {
1021 auto pos = std::distance(cbegin(),
first);
1024 return begin() + pos;
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);
1032 constexpr void pop_back() {
1037 PLUGIFY_ASSERT(size() +
count <= max_size(),
"plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
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());
1049 PLUGIFY_ASSERT(pos <=
str.size(),
"plg::basic_string::append(): pos out of range", std::out_of_range);
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());
1057 PLUGIFY_ASSERT(size() +
count <= max_size(),
"plg::basic_string::append(): resulted string size would exceed max_size()", std::length_error);
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);
1068 template<PLUGIFY_INPUT_ITERATOR InputIterator>
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);
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());
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*>)
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());
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*>)
1101 PLUGIFY_ASSERT(pos <=
sv.length(),
"plg::basic_string::append(): pos out of range", std::out_of_range);
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());
1108#if PLUGIFY_STRING_CONTAINERS_RANGES
1109 template<detail::
string_compatible_range<Char> 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);
1130 constexpr basic_string& operator+=(std::initializer_list<value_type>
list) {
1131 return append(
list);
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*>)
1140 return append(sview_type(
t));
1144 return view().compare(
str.view());
1155 [[
nodiscard]]
constexpr int compare(
const value_type*
str)
const {
1156 return view().compare(
str);
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*>)
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));
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*>)
1182 return view().compare(
pos1,
count1, sview_type(
t));
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*>)
1194 [[
nodiscard]]
constexpr bool starts_with(sview_type
sv)
const noexcept {
1195 return view().starts_with(
sv);
1199 return view().starts_with(
ch);
1203 return view().starts_with(
str);
1206 [[
nodiscard]]
constexpr bool ends_with(sview_type
sv)
const noexcept {
1207 return view().ends_with(
sv);
1211 return view().ends_with(
ch);
1215 return view().ends_with(
str);
1218 [[
nodiscard]]
constexpr bool contains(sview_type
sv)
const noexcept {
1219 return view().contains(
sv);
1223 return view().contains(
ch);
1227 return view().contains(
str);
1231 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::replace(): pos out of range", std::out_of_range);
1236 auto pos = std::distance(cbegin(),
first);
1242 PLUGIFY_ASSERT(pos <= size() &&
pos2 <=
str.size(),
"plg::basic_string::replace(): pos or pos_str out of range", std::out_of_range);
1245 return replace(pos,
count,
ssv.data(),
ssv.length());
1248 template<PLUGIFY_INPUT_ITERATOR InputIterator>
1254 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::replace(): pos out of range", std::out_of_range);
1256 PLUGIFY_ASSERT(size() -
count +
count2 <= max_size(),
"plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1262 auto pos = std::distance(cbegin(),
first);
1268 return replace(pos,
count,
str, Traits::length(
str));
1276 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::replace(): pos out of range", std::out_of_range);
1278 PLUGIFY_ASSERT(size() -
count +
count2 <= max_size(),
"plg::basic_string::replace(): resulted string size would exceed max_size()", std::length_error);
1284 auto pos = std::distance(cbegin(),
first);
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);
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*>)
1303 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::replace(): pos out of range", std::out_of_range);
1305 return replace(pos,
count,
sv.data(),
sv.length());
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*>)
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*>)
1324 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::replace(): pos out of range", std::out_of_range);
1326 return replace(pos,
count,
sv.data(),
sv.length());
1329#if PLUGIFY_STRING_CONTAINERS_RANGES
1330 template<detail::
string_compatible_range<Char> Range>
1332 auto str =
basic_string(std::from_range, std::forward<Range>(range), _allocator);
1338 PLUGIFY_ASSERT(pos <= size(),
"plg::basic_string::substr(): pos out of range", std::out_of_range);
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);
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();
1362 constexpr void resize(size_type
count) {
1363 resize(
count, _terminator);
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!");
1371 constexpr void swap(
basic_string&
other)
noexcept(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value) {
1373 if constexpr (allocator_traits::propagate_on_container_swap::value) {
1374 swap(_allocator,
other._allocator);
1376 swap(_storage,
other._storage);
1380 return view().find(sview_type(
str), pos);
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);
1387 [[
nodiscard]]
constexpr size_type find(
const value_type*
str, size_type pos = 0)
const noexcept {
1388 return view().find(
str, pos);
1391 [[
nodiscard]]
constexpr size_type find(value_type
ch, size_type pos = 0)
const noexcept {
1392 return view().find(
ch, pos);
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*>)
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);
1405 return view().rfind(sview_type(
str), pos);
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);
1412 [[
nodiscard]]
constexpr size_type rfind(
const value_type*
str, size_type pos = npos)
const noexcept {
1413 return view().rfind(
str, pos);
1416 [[
nodiscard]]
constexpr size_type rfind(value_type
ch, size_type pos = npos)
const noexcept {
1417 return view().rfind(
ch, pos);
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*>)
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);
1430 return view().find_first_of(sview_type(
str), pos);
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);
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);
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);
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*>)
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);
1455 return view().find_last_not_of(sview_type(
str), pos);
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);
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);
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);
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*>)
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);
1480 return view().find_last_of(sview_type(
str), pos);
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);
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);
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);
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*>)
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);
1505 return view().find_last_not_of(sview_type(
str), pos);
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);
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);
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);
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*>)
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);
1533 auto buffer = ret.data();
1536 ret.null_terminate();
1541 return std::move(
lhs.append(
rhs));
1545 return std::move(
rhs.insert(0,
lhs));
1549 return std::move(
lhs.append(
rhs));
1556 auto buffer = ret.data();
1559 ret.null_terminate();
1564 return std::move(
rhs.insert(0,
lhs));
1570 auto buffer = ret.data();
1573 ret.null_terminate();
1579 return std::move(
rhs);
1586 auto buffer = ret.data();
1589 ret.null_terminate();
1594 return std::move(
lhs.append(
rhs));
1600 auto buffer = ret.data();
1603 ret.null_terminate();
1609 return std::move(
lhs);