591#include <string_view>
592#include <type_traits>
593#include <initializer_list>
605#include "plg/allocator.hpp"
606#include "plg/guards.hpp"
607#include "plg/concepts.hpp"
609#ifndef PLUGIFY_STRING_NO_STD_FORMAT
610#include "plg/format.hpp"
614#if PLUGIFY_COMPILER_CLANG
615# pragma clang system_header
616#elif PLUGIFY_COMPILER_GCC
617# pragma GCC system_header
623 template <
class CharT,
class Traits,
class Allocator>
626 template <
class CharT,
class Traits,
class Allocator>
627 constexpr basic_string<CharT, Traits, Allocator> concatenate_strings(
628 const Allocator& alloc,
629 std::type_identity_t<std::basic_string_view<CharT, Traits>> str1,
630 std::type_identity_t<std::basic_string_view<CharT, Traits>> str2
633 template <
class Iter>
634 inline const bool string_is_trivial_iterator_v =
false;
639 template <
class T,
class CharT,
class Traits>
640 concept string_view_convertible = std::is_convertible_v<const T&, std::basic_string_view<CharT, Traits>> && !std::is_convertible_v<const T&, const CharT*>;
643 template <
class T,
class CharT,
class Traits,
class Allocator>
646 template <
typename T>
648 { std::string_view(v) };
663 template <
class CharT,
class Traits = std::
char_traits<CharT>,
class Allocator = allocator<CharT>>
667 using self_view = std::basic_string_view<CharT, Traits>;
669 using value_type =
CharT;
671 using alloc_traits = std::allocator_traits<allocator_type>;
672 using size_type =
typename alloc_traits::size_type;
673 using difference_type =
typename alloc_traits::difference_type;
676 using pointer =
typename alloc_traits::pointer;
677 using const_pointer =
typename alloc_traits::const_pointer;
690#if PLUGIFY_INSTRUMENTED_WITH_ASAN
700 using trivially_relocatable =
void;
702 using trivially_relocatable = std::conditional_t<
709#if PLUGIFY_INSTRUMENTED_WITH_ASAN && __has_cpp_attribute(__no_sanitize__)
710# define PLUGIFY_INTERNAL_MEMORY_ACCESS __attribute__((__no_sanitize__("address")))
717# define PLUGIFY_INTERNAL_MEMORY_ACCESS
720#if PLUGIFY_INSTRUMENTED_WITH_ASAN
722 if (std::is_constant_evaluated()) {
728 return const_cast<pointer&
>(
copy_ptr);
732 if (std::is_constant_evaluated()) {
736 volatile const_pointer
copy_ptr = ptr;
738 return const_cast<const_pointer&
>(
copy_ptr);
741# define PLUGIFY_ASAN_VOLATILE_WRAPPER(PTR) asan_volatile_wrapper(PTR)
743# define PLUGIFY_ASAN_VOLATILE_WRAPPER(PTR) PTR
746 static_assert(!std::is_array_v<value_type>,
"Character type of basic_string must not be an array");
748 std::is_standard_layout_v<value_type>,
749 "Character type of basic_string must be standard-layout"
752 std::is_trivially_default_constructible_v<value_type>,
753 "Character type of basic_string must be trivially default constructible"
756 std::is_trivially_copyable_v<value_type>,
757 "Character type of basic_string must be trivially copyable"
760 std::is_same_v<CharT, typename traits_type::char_type>,
761 "traits_type::char_type must be the same type as CharT"
764 std::is_same_v<typename allocator_type::value_type, value_type>,
765 "Allocator::value_type must be same type as value_type"
769 using iterator = pointer;
770 using const_iterator = const_pointer;
771 using reverse_iterator = std::reverse_iterator<iterator>;
772 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
777 static constexpr int char_bit = std::numeric_limits<uint8_t>::digits + std::numeric_limits<uint8_t>::is_signed;
778 static_assert(char_bit == 8,
"This implementation assumes that one byte contains 8 bits");
781 constexpr long_() =
default;
786 , cap_(
alloc.count / endian_factor)
788 PLUGIFY_ASSERT(!fits_in_sso(
alloc.count),
"Long capacity should always be larger than the SSO");
793 size_type cap_ :
sizeof(size_type) * char_bit - 1;
794 size_type is_long_ : 1;
797 static constexpr size_type min_cap = ((
sizeof(long_) - 1) /
sizeof(value_type) > 2 ? (
sizeof(long_) - 1) /
sizeof(value_type) : 2) + 1;
802 , spare_size_(min_cap - 1)
806 value_type data_[min_cap - 1];
807 PLUGIFY_NO_UNIQUE_ADDRESS
padding<
sizeof(value_type) - 1> padding_;
825 static constexpr size_type endian_factor = std::endian::native == std::endian::big ? 2 : 1;
828 sizeof(short_) == (
sizeof(value_type) * min_cap),
829 "short has an unexpected size."
832 sizeof(short_) ==
sizeof(long_),
833 "short and long layout structures must be the same size"
840 PLUGIFY_NO_UNIQUE_ADDRESS
841 allocator_type alloc_;
845 struct annotate_new_size {
852 constexpr void operator()() {
853 str_.annotate_new(str_.size());
862 init_internal_buffer(size);
865 template <
class Iter,
class Sent>
868 init_with_sentinel(std::move(
first), std::move(
last));
871 constexpr iterator make_iterator(pointer
p) {
875 constexpr const_iterator make_const_iterator(const_pointer
p)
const {
876 return const_iterator(
p);
880 static const size_type npos =
static_cast<size_type
>(-1);
882 constexpr basic_string()
noexcept(std::is_nothrow_default_constructible_v<allocator_type>)
894 : alloc_(alloc_traits::select_on_container_copy_construction(str.alloc_)) {
895 if (!str.is_long()) {
897 annotate_new(get_short_size());
899 init_copy_ctor_external(std::to_address(str.get_long_pointer()), str.get_long_size());
903 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS
906 if (!str.is_long()) {
908 annotate_new(get_short_size());
910 init_copy_ctor_external(std::to_address(str.get_long_pointer()), str.get_long_size());
923 return std::move(s.rep_);
925 , alloc_(std::move(str.alloc_)) {
929 annotate_new(size());
933 constexpr basic_string(basic_string&& str,
const allocator_type& a)
935 if (str.is_long() && a != str.alloc_) {
936 init(std::to_address(str.get_long_pointer()), str.get_long_size());
938 if (std::is_constant_evaluated()) {
941 if (!str.is_long()) {
942 str.annotate_delete();
947 if (!is_long() &&
this != std::addressof(str)) {
948 annotate_new(size());
953 constexpr basic_string(
const CharT* PLUGIFY_NO_NULL s)
954 requires(is_allocator<Allocator>)
956 PLUGIFY_ASSERT(s !=
nullptr,
"basic_string(const char*) detected nullptr");
957 init(s, traits_type::length(s));
960 constexpr basic_string(
const CharT* PLUGIFY_NO_NULL s,
const Allocator& a)
961 requires(is_allocator<Allocator>)
963 PLUGIFY_ASSERT(s !=
nullptr,
"basic_string(const char*, allocator) detected nullptr");
964 init(s, traits_type::length(s));
967 basic_string(std::nullptr_t) =
delete;
969 constexpr basic_string(
const CharT* s, size_type n) {
970 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"basic_string(const char*, n) detected nullptr");
974 constexpr basic_string(
const CharT* s, size_type n,
const Allocator& a)
977 n == 0 || s !=
nullptr,
978 "basic_string(const char*, n, allocator) detected nullptr"
983 constexpr basic_string(size_type n, CharT c) {
987 constexpr basic_string(basic_string&& str, size_type pos,
const Allocator& alloc = Allocator())
988 : basic_string(std::move(str), pos, npos, alloc) {
991 constexpr basic_string(
995 const Allocator& alloc = Allocator()
998 if (pos > str.size()) {
999 this->throw_out_of_range();
1002 auto len = std::min<size_type>(n, str.size() - pos);
1003 if (alloc_traits::is_always_equal::value || alloc == str.alloc_) {
1004 move_assign(std::move(str), pos, len);
1007 init(str.data() + pos, len);
1011 constexpr basic_string(size_type n, CharT c,
const Allocator& a)
1012 requires(is_allocator<Allocator>)
1017 constexpr basic_string(
1018 const basic_string& str,
1021 const Allocator& a = Allocator()
1024 size_type str_sz = str.size();
1026 this->throw_out_of_range();
1028 init(str.data() + pos, std::min(n, str_sz - pos));
1031 constexpr basic_string(
const basic_string& str, size_type pos,
const Allocator& a = Allocator())
1033 size_type str_sz = str.size();
1035 this->throw_out_of_range();
1037 init(str.data() + pos, str_sz - pos);
1040 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1041 constexpr basic_string(
1045 const allocator_type& a = allocator_type()
1049 self_view sv = sv0.substr(pos, n);
1050 init(sv.data(), sv.size());
1053 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1054 constexpr basic_string(
const T& t) {
1056 init(sv.data(), sv.size());
1059 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1060 constexpr basic_string(
const T& t,
const allocator_type& a)
1063 init(sv.data(), sv.size());
1066 template <std::input_iterator InputIterator>
1067 constexpr basic_string(InputIterator first, InputIterator last) {
1071 template <std::input_iterator InputIterator>
1072 constexpr basic_string(InputIterator first, InputIterator last,
const allocator_type& a)
1077#if PLUGIFY_HAS_CXX23
1078 template <container_compatible_range<CharT> Range>
1079 constexpr basic_string(std::from_range_t, Range&& range,
const allocator_type& a = allocator_type())
1081 if constexpr (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) {
1083 std::ranges::begin(range),
1084 std::ranges::end(range),
1085 std::ranges::distance(range)
1088 init_with_sentinel(std::ranges::begin(range), std::ranges::end(range));
1093 constexpr basic_string(std::initializer_list<CharT> il) {
1094 init(il.begin(), il.end());
1097 constexpr basic_string(std::initializer_list<CharT> il,
const Allocator& a)
1099 init(il.begin(), il.end());
1102 inline constexpr ~basic_string() {
1103 reset_internal_buffer();
1106 constexpr operator self_view() const noexcept {
1107 return self_view(begin(), end());
1110 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS basic_string& operator=(
const basic_string& str);
1112 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1113 constexpr basic_string& operator=(
const T& t) {
1118 constexpr basic_string& operator=(basic_string&& str
1119 )
noexcept(alloc_traits::propagate_on_container_move_assignment::value) {
1122 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
1127 constexpr basic_string& operator=(std::initializer_list<value_type> il) {
1128 return assign(il.begin(), il.size());
1131 constexpr basic_string& operator=(
const value_type* PLUGIFY_NO_NULL s) {
1135 basic_string& operator=(std::nullptr_t) =
delete;
1136 constexpr basic_string& operator=(value_type c);
1138 constexpr iterator begin() noexcept {
1139 return make_iterator(get_pointer());
1142 constexpr const_iterator begin() const noexcept {
1143 return make_const_iterator(get_pointer());
1146 constexpr iterator end() noexcept {
1147 return make_iterator(get_pointer() + size());
1150 constexpr const_iterator end() const noexcept {
1151 return make_const_iterator(get_pointer() + size());
1154 constexpr reverse_iterator rbegin() noexcept {
1155 return reverse_iterator(end());
1158 constexpr const_reverse_iterator rbegin() const noexcept {
1159 return const_reverse_iterator(end());
1162 constexpr reverse_iterator rend() noexcept {
1163 return reverse_iterator(begin());
1166 constexpr const_reverse_iterator rend() const noexcept {
1167 return const_reverse_iterator(begin());
1170 constexpr const_iterator cbegin() const noexcept {
1174 constexpr const_iterator cend() const noexcept {
1178 constexpr const_reverse_iterator crbegin() const noexcept {
1182 constexpr const_reverse_iterator crend() const noexcept {
1186 constexpr size_type size() const noexcept {
1187 return is_long() ? get_long_size() : get_short_size();
1190 constexpr size_type length() const noexcept {
1194 constexpr size_type max_size() const noexcept {
1195 constexpr bool uses_lsb = endian_factor == 2;
1197 if (size_type m = alloc_traits::max_size(alloc_);
1198 m <= std::numeric_limits<size_type>::max() / 2) {
1199 size_type res = m - alignment;
1204 if constexpr (uses_lsb) {
1205 res &= ~size_type(1);
1212 return uses_lsb ? m - alignment - 1 : (m / 2) - alignment - 1;
1216 constexpr size_type capacity() const noexcept {
1217 return (is_long() ? get_long_cap() : min_cap) - 1;
1220 constexpr void resize(size_type n, value_type c);
1222 constexpr void resize(size_type n) {
1223 resize(n, value_type());
1226 constexpr void reserve(size_type requested_capacity);
1228#if PLUGIFY_HAS_CXX23
1243 constexpr void shrink_to_fit() noexcept;
1244 constexpr
void clear() noexcept;
1246 [[nodiscard]] constexpr
bool empty() const noexcept {
1250 constexpr const_reference operator[](size_type pos)
const noexcept {
1251 PLUGIFY_ASSERT(pos <= size(),
"string index out of bounds");
1252 if (__builtin_constant_p(pos) && !fits_in_sso(pos)) {
1253 return *(get_long_pointer() + pos);
1255 return *(data() + pos);
1258 constexpr reference operator[](size_type pos)
noexcept {
1259 PLUGIFY_ASSERT(pos <= size(),
"string index out of bounds");
1260 if (__builtin_constant_p(pos) && !fits_in_sso(pos)) {
1261 return *(get_long_pointer() + pos);
1263 return *(get_pointer() + pos);
1266 constexpr const_reference at(size_type n)
const;
1267 constexpr reference at(size_type n);
1269 constexpr basic_string& operator+=(
const basic_string& str) {
1273 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1274 constexpr basic_string& operator+=(
const T& t) {
1279 constexpr basic_string& operator+=(
const value_type* PLUGIFY_NO_NULL s) {
1283 constexpr basic_string& operator+=(value_type c) {
1288 constexpr basic_string& operator+=(std::initializer_list<value_type> il) {
1292 constexpr basic_string& append(
const basic_string& str) {
1293 return append(str.data(), str.size());
1296 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1297 constexpr basic_string& append(
const T& t) {
1299 return append(sv.data(), sv.size());
1302 constexpr basic_string& append(
const basic_string& str, size_type pos, size_type n = npos);
1304 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1305 constexpr basic_string& append(
const T& t, size_type pos, size_type n = npos) {
1307 size_type sz = sv.size();
1309 throw_out_of_range();
1311 return append(sv.data() + pos, std::min(n, sz - pos));
1314 constexpr basic_string& append(
const value_type* s, size_type n);
1315 constexpr basic_string& append(
const value_type* PLUGIFY_NO_NULL s);
1316 constexpr basic_string& append(size_type n, value_type c);
1318 template <std::input_iterator InputIterator>
1319 constexpr basic_string& append(InputIterator first, InputIterator last) {
1320 const basic_string temp(first, last, alloc_);
1321 append(temp.data(), temp.size());
1325 template <std::forward_iterator ForwardIterator>
1326 constexpr basic_string& append(ForwardIterator first, ForwardIterator last) {
1327 size_type sz = size();
1328 size_type cap = capacity();
1329 size_type n =
static_cast<size_type
>(std::distance(first, last));
1334 if (string_is_trivial_iterator_v<ForwardIterator> && !addr_in_range(*first)) {
1336 grow_by_without_replace(cap, sz + n - cap, sz, sz, 0);
1338 annotate_increase(n);
1339 auto end = copy_non_overlapping_range(first, last, std::to_address(get_pointer() + sz));
1340 traits_type::assign(*end, value_type());
1344 const basic_string temp(first, last, alloc_);
1345 return append(temp.data(), temp.size());
1349#if PLUGIFY_HAS_CXX23
1350 template <container_compatible_range<CharT> Range>
1351 constexpr basic_string& append_range(Range&& range) {
1352 insert_range(end(), std::forward<Range>(range));
1357 constexpr basic_string& append(std::initializer_list<value_type> il) {
1358 return append(il.begin(), il.size());
1361 constexpr void push_back(value_type c);
1362 constexpr void pop_back();
1364 constexpr reference front() noexcept {
1365 PLUGIFY_ASSERT(!empty(),
"string::front(): string is empty");
1366 return *get_pointer();
1369 constexpr const_reference front() const noexcept {
1370 PLUGIFY_ASSERT(!empty(),
"string::front(): string is empty");
1374 constexpr reference back() noexcept {
1375 PLUGIFY_ASSERT(!empty(),
"string::back(): string is empty");
1376 return *(get_pointer() + size() - 1);
1379 constexpr const_reference back() const noexcept {
1380 PLUGIFY_ASSERT(!empty(),
"string::back(): string is empty");
1381 return *(data() + size() - 1);
1384 template <
string_view_convertible<CharT, Traits> T>
1385 constexpr basic_string& assign(
const T& t) {
1387 return assign(sv.data(), sv.size());
1390 constexpr void move_assign(basic_string&& str, size_type pos, size_type len) {
1392 PLUGIFY_ASSERT(alloc_ == str.alloc_,
"move_assign called with wrong allocator");
1393 size_type old_sz = str.size();
1394 if (!str.is_long()) {
1395 str.annotate_delete();
1399 str.annotate_new(0);
1401 Traits::move(data(), data() + pos, len);
1403 Traits::assign(data()[len], value_type());
1407 }
else if (old_sz > len) {
1408 annotate_shrink(old_sz);
1412 constexpr basic_string& assign(
const basic_string& str) {
1416 constexpr basic_string&
1417 assign(basic_string&& str)
noexcept(alloc_traits::propagate_on_container_move_assignment::value
1419 *
this = std::move(str);
1423 constexpr basic_string& assign(
const basic_string& str, size_type pos, size_type n = npos);
1425 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1426 constexpr basic_string& assign(
const T& t, size_type pos, size_type n = npos) {
1428 size_type sz = sv.size();
1430 throw_out_of_range();
1432 return assign(sv.data() + pos, std::min(n, sz - pos));
1435 constexpr basic_string& assign(
const value_type* s, size_type n);
1436 constexpr basic_string& assign(
const value_type* PLUGIFY_NO_NULL s);
1437 constexpr basic_string& assign(size_type n, value_type c);
1439 template <std::input_iterator InputIterator>
1440 constexpr basic_string& assign(InputIterator first, InputIterator last) {
1441 assign_with_sentinel(first, last);
1445 template <std::forward_iterator ForwardIterator>
1446 constexpr basic_string& assign(ForwardIterator first, ForwardIterator last) {
1447 if (string_is_trivial_iterator_v<ForwardIterator>) {
1448 size_type n =
static_cast<size_type
>(std::distance(first, last));
1449 assign_trivial(first, last, n);
1451 assign_with_sentinel(first, last);
1457#if PLUGIFY_HAS_CXX23
1458 template <container_compatible_range<CharT> Range>
1459 constexpr basic_string& assign_range(Range&& range) {
1460 if constexpr (string_is_trivial_iterator_v<std::ranges::iterator_t<Range>>
1461 && (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) ) {
1462 size_type n =
static_cast<size_type
>(std::ranges::distance(range));
1463 assign_trivial(std::ranges::begin(range), std::ranges::end(range), n);
1466 assign_with_sentinel(std::ranges::begin(range), std::ranges::end(range));
1473 constexpr basic_string& assign(std::initializer_list<value_type> il) {
1474 return assign(il.begin(), il.size());
1477 constexpr basic_string& insert(size_type pos1,
const basic_string& str) {
1478 return insert(pos1, str.data(), str.size());
1481 template <
string_view_convertible<CharT, Traits> T>
1482 constexpr basic_string& insert(size_type pos1,
const T& t) {
1484 return insert(pos1, sv.data(), sv.size());
1487 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1488 constexpr basic_string&
1489 insert(size_type pos1,
const T& t, size_type pos2, size_type n = npos) {
1491 size_type str_sz = sv.size();
1492 if (pos2 > str_sz) {
1493 throw_out_of_range();
1495 return insert(pos1, sv.data() + pos2, std::min(n, str_sz - pos2));
1498 constexpr basic_string&
1499 insert(size_type pos1,
const basic_string& str, size_type pos2, size_type n = npos);
1500 constexpr basic_string& insert(size_type pos,
const value_type* s, size_type n);
1501 constexpr basic_string& insert(size_type pos,
const value_type* PLUGIFY_NO_NULL s);
1502 constexpr basic_string& insert(size_type pos, size_type n, value_type c);
1503 constexpr iterator insert(const_iterator pos, value_type c);
1505#if PLUGIFY_HAS_CXX23
1506 template <container_compatible_range<CharT> Range>
1507 constexpr iterator insert_range(const_iterator position, Range&& range) {
1508 if constexpr (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) {
1509 auto n =
static_cast<size_type
>(std::ranges::distance(range));
1510 return insert_with_size(position, std::ranges::begin(range), std::ranges::end(range), n);
1513 basic_string temp(std::from_range, std::forward<Range>(range), alloc_);
1514 return insert(position, temp.data(), temp.data() + temp.size());
1519 constexpr iterator insert(const_iterator pos, size_type n, value_type c) {
1520 difference_type p = pos - begin();
1521 insert(
static_cast<size_type
>(p), n, c);
1525 template <std::input_iterator InputIterator>
1526 constexpr iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
1527 const basic_string temp(first, last, alloc_);
1528 return insert(pos, temp.data(), temp.data() + temp.size());
1531 template <std::forward_iterator ForwardIterator>
1532 constexpr iterator insert(const_iterator pos, ForwardIterator first, ForwardIterator last) {
1533 auto n =
static_cast<size_type
>(std::distance(first, last));
1534 return insert_with_size(pos, first, last, n);
1537 constexpr iterator insert(const_iterator pos, std::initializer_list<value_type> il) {
1538 return insert(pos, il.begin(), il.end());
1541 constexpr basic_string& erase(size_type pos = 0, size_type n = npos);
1542 constexpr iterator erase(const_iterator pos);
1543 constexpr iterator erase(const_iterator first, const_iterator last);
1545 constexpr basic_string& replace(size_type pos1, size_type n1,
const basic_string& str) {
1546 return replace(pos1, n1, str.data(), str.size());
1549 template <
string_view_convertible<CharT, Traits> T>
1550 constexpr basic_string& replace(size_type pos1, size_type n1,
const T& t) {
1552 return replace(pos1, n1, sv.data(), sv.size());
1555 constexpr basic_string&
1556 replace(size_type pos1, size_type n1,
const basic_string& str, size_type pos2, size_type n2 = npos);
1558 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1559 constexpr basic_string&
1560 replace(size_type pos1, size_type n1,
const T& t, size_type pos2, size_type n2 = npos) {
1562 size_type str_sz = sv.size();
1563 if (pos2 > str_sz) {
1564 throw_out_of_range();
1566 return replace(pos1, n1, sv.data() + pos2, std::min(n2, str_sz - pos2));
1569 constexpr basic_string&
1570 replace(size_type pos, size_type n1,
const value_type* s, size_type n2);
1571 constexpr basic_string&
1572 replace(size_type pos, size_type n1,
const value_type* PLUGIFY_NO_NULL s);
1573 constexpr basic_string& replace(size_type pos, size_type n1, size_type n2, value_type c);
1575 constexpr basic_string&
1576 replace(const_iterator i1, const_iterator i2,
const basic_string& str) {
1578 static_cast<size_type
>(i1 - begin()),
1579 static_cast<size_type
>(i2 - i1),
1585 template <
string_view_convertible<CharT, Traits> T>
1586 constexpr basic_string& replace(const_iterator i1, const_iterator i2,
const T& t) {
1588 return replace(i1 - begin(), i2 - i1, sv);
1591 constexpr basic_string&
1592 replace(const_iterator i1, const_iterator i2,
const value_type* s, size_type n) {
1593 return replace(
static_cast<size_type
>(i1 - begin()),
static_cast<size_type
>(i2 - i1), s, n);
1596 constexpr basic_string& replace(const_iterator i1, const_iterator i2,
const value_type* s) {
1597 return replace(
static_cast<size_type
>(i1 - begin()),
static_cast<size_type
>(i2 - i1), s);
1600 constexpr basic_string&
1601 replace(const_iterator i1, const_iterator i2, size_type n, value_type c) {
1602 return replace(
static_cast<size_type
>(i1 - begin()),
static_cast<size_type
>(i2 - i1), n, c);
1605 template <std::input_iterator InputIterator>
1606 constexpr basic_string&
1607 replace(const_iterator i1, const_iterator i2, InputIterator j1, InputIterator j2) {
1608 const basic_string temp(j1, j2, alloc_);
1609 return replace(i1, i2, temp);
1612#if PLUGIFY_HAS_CXX23
1613 template <container_compatible_range<CharT> Range>
1614 constexpr basic_string&
1615 replace_with_range(const_iterator i1, const_iterator i2, Range&& range) {
1616 basic_string temp(std::from_range, std::forward<Range>(range), alloc_);
1617 return replace(i1, i2, temp);
1621 constexpr basic_string&
1622 replace(const_iterator i1, const_iterator i2, std::initializer_list<value_type> il) {
1623 return replace(i1, i2, il.begin(), il.end());
1626 constexpr size_type copy(value_type* s, size_type n, size_type pos = 0)
const;
1628 constexpr basic_string substr(size_type pos = 0, size_type n = npos)
const& {
1629 return basic_string(*
this, pos, n);
1632 constexpr basic_string substr(size_type pos = 0, size_type n = npos) && {
1633 return basic_string(std::move(*
this), pos, n);
1636 constexpr void swap(basic_string& str)
noexcept;
1641 constexpr const value_type* c_str() const noexcept {
1645 constexpr const value_type* data() const noexcept {
1646 return std::to_address(get_pointer());
1649 constexpr value_type* data() noexcept {
1650 return std::to_address(get_pointer());
1653 constexpr allocator_type get_allocator() const noexcept {
1659 constexpr size_type find(
const basic_string& str, size_type pos = 0) const noexcept {
1660 return operator self_view().find(str.data(), pos, str.size());
1663 template <
string_view_convertible<CharT, Traits> T>
1664 constexpr size_type find(
const T& t, size_type pos = 0) const noexcept {
1666 return operator self_view().find(sv.data(), pos, sv.size());
1669 constexpr size_type find(
const value_type* s, size_type pos, size_type n)
const noexcept {
1670 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::find(): received nullptr");
1671 return operator self_view().find(s, pos, n);
1675 find(
const value_type* PLUGIFY_NO_NULL s, size_type pos = 0) const noexcept {
1676 PLUGIFY_ASSERT(s !=
nullptr,
"string::find(): received nullptr");
1677 return operator self_view().find(s, pos, traits_type::length(s));
1680 constexpr size_type find(value_type c, size_type pos = 0) const noexcept {
1681 return operator self_view().find(c, pos);
1686 constexpr size_type rfind(
const basic_string& str, size_type pos = npos)
const noexcept {
1687 return operator self_view().rfind(str.data(), pos, str.size());
1690 template <
string_view_convertible<CharT, Traits> T>
1691 constexpr size_type rfind(
const T& t, size_type pos = npos)
const noexcept {
1693 return operator self_view().rfind(sv.data(), pos, sv.size());
1696 constexpr size_type rfind(
const value_type* s, size_type pos, size_type n)
const noexcept {
1697 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::rfind(): received nullptr");
1698 return operator self_view().rfind(s, pos, n);
1702 rfind(
const value_type* PLUGIFY_NO_NULL s, size_type pos = npos)
const noexcept {
1703 PLUGIFY_ASSERT(s !=
nullptr,
"string::rfind(): received nullptr");
1704 return operator self_view().rfind(s, pos, traits_type::length(s));
1707 constexpr size_type rfind(value_type c, size_type pos = npos)
const noexcept {
1708 return operator self_view().rfind(c, pos);
1713 constexpr size_type find_first_of(
const basic_string& str, size_type pos = 0) const noexcept {
1714 return operator self_view().find_first_of(str.data(), pos, str.size());
1717 template <
string_view_convertible<CharT, Traits> T>
1718 constexpr size_type find_first_of(
const T& t, size_type pos = 0) const noexcept {
1720 return operator self_view().find_first_of(sv.data(), pos, sv.size());
1724 find_first_of(
const value_type* s, size_type pos, size_type n)
const noexcept {
1725 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::find_first_of(): received nullptr");
1726 return operator self_view().find_first_of(s, pos, n);
1730 find_first_of(
const value_type* PLUGIFY_NO_NULL s, size_type pos = 0) const noexcept {
1731 PLUGIFY_ASSERT(s !=
nullptr,
"string::find_first_of(): received nullptr");
1732 return operator self_view().find_first_of(s, pos, traits_type::length(s));
1735 constexpr size_type find_first_of(value_type c, size_type pos = 0) const noexcept {
1736 return find(c, pos);
1742 find_last_of(
const basic_string& str, size_type pos = npos)
const noexcept {
1743 return operator self_view().find_last_of(str.data(), pos, str.size());
1746 template <
string_view_convertible<CharT, Traits> T>
1747 constexpr size_type find_last_of(
const T& t, size_type pos = npos)
const noexcept {
1749 return operator self_view().find_last_of(sv.data(), pos, sv.size());
1753 find_last_of(
const value_type* s, size_type pos, size_type n)
const noexcept {
1754 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::find_last_of(): received nullptr");
1755 return operator self_view().find_last_of(s, pos, n);
1759 find_last_of(
const value_type* PLUGIFY_NO_NULL s, size_type pos = npos)
const noexcept {
1760 PLUGIFY_ASSERT(s !=
nullptr,
"string::find_last_of(): received nullptr");
1761 return operator self_view().find_last_of(s, pos, traits_type::length(s));
1764 constexpr size_type find_last_of(value_type c, size_type pos = npos)
const noexcept {
1765 return rfind(c, pos);
1771 find_first_not_of(
const basic_string& str, size_type pos = 0) const noexcept {
1772 return operator self_view().find_first_not_of(str.data(), pos, str.size());
1775 template <
string_view_convertible<CharT, Traits> T>
1776 constexpr size_type find_first_not_of(
const T& t, size_type pos = 0) const noexcept {
1778 return operator self_view().find_first_not_of(sv.data(), pos, sv.size());
1782 find_first_not_of(
const value_type* s, size_type pos, size_type n)
const noexcept {
1783 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::find_first_not_of(): received nullptr");
1784 return operator self_view().find_first_not_of(s, pos, n);
1788 find_first_not_of(
const value_type* PLUGIFY_NO_NULL s, size_type pos = 0) const noexcept {
1789 PLUGIFY_ASSERT(s !=
nullptr,
"string::find_first_not_of(): received nullptr");
1790 return operator self_view().find_first_not_of(s, pos, traits_type::length(s));
1793 constexpr size_type find_first_not_of(value_type c, size_type pos = 0) const noexcept {
1794 return operator self_view().find_first_not_of(c, pos);
1800 find_last_not_of(
const basic_string& str, size_type pos = npos)
const noexcept {
1801 return operator self_view().find_last_not_of(str.data(), pos, str.size());
1804 template <
string_view_convertible<CharT, Traits> T>
1805 constexpr size_type find_last_not_of(
const T& t, size_type pos = npos)
const noexcept {
1807 return operator self_view().find_last_not_of(sv.data(), pos, sv.size());
1811 find_last_not_of(
const value_type* s, size_type pos, size_type n)
const noexcept {
1812 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::find_last_not_of(): received nullptr");
1813 return operator self_view().find_last_not_of(s, pos, n);
1817 find_last_not_of(
const value_type* PLUGIFY_NO_NULL s, size_type pos = npos)
const noexcept {
1818 PLUGIFY_ASSERT(s !=
nullptr,
"string::find_last_not_of(): received nullptr");
1819 return operator self_view().find_last_not_of(s, pos, traits_type::length(s));
1822 constexpr size_type find_last_not_of(value_type c, size_type pos = npos)
const noexcept {
1823 return operator self_view().find_last_not_of(c, pos);
1828 constexpr int compare(
const basic_string& str)
const noexcept {
1829 return compare(self_view(str));
1832 template <
string_view_convertible<CharT, Traits> T>
1833 constexpr int compare(
const T& t)
const noexcept {
1835 size_t lhs_sz = size();
1836 size_t rhs_sz = sv.size();
1837 int result = traits_type::compare(data(), sv.data(), std::min(lhs_sz, rhs_sz));
1841 if (lhs_sz < rhs_sz) {
1844 if (lhs_sz > rhs_sz) {
1850 template <
string_view_convertible<CharT, Traits> T>
1851 constexpr int compare(size_type pos1, size_type n1,
const T& t)
const {
1853 return compare(pos1, n1, sv.data(), sv.size());
1856 constexpr int compare(size_type pos1, size_type n1,
const basic_string& str)
const {
1857 return compare(pos1, n1, str.data(), str.size());
1860 constexpr int compare(
1863 const basic_string& str,
1867 return compare(pos1, n1, self_view(str), pos2, n2);
1870 template <
string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1871 inline constexpr int
1872 compare(size_type pos1, size_type n1,
const T& t, size_type pos2, size_type n2 = npos)
const {
1874 return self_view(*this).substr(pos1, n1).compare(sv.substr(pos2, n2));
1877 constexpr int compare(
const value_type* PLUGIFY_NO_NULL s)
const noexcept {
1878 PLUGIFY_ASSERT(s !=
nullptr,
"string::compare(): received nullptr");
1879 return compare(0, npos, s, traits_type::length(s));
1883 compare(size_type pos1, size_type n1,
const value_type* PLUGIFY_NO_NULL s)
const {
1884 PLUGIFY_ASSERT(s !=
nullptr,
"string::compare(): received nullptr");
1885 return compare(pos1, n1, s, traits_type::length(s));
1888 constexpr int compare(size_type pos1, size_type n1,
const value_type* s, size_type n2)
const;
1892 constexpr bool starts_with(self_view sv)
const noexcept {
1893 return self_view(
typename self_view::assume_valid(), data(), size()).starts_with(sv);
1896 constexpr bool starts_with(value_type c)
const noexcept {
1897 return !empty() && Traits::eq(front(), c);
1900 constexpr bool starts_with(
const value_type* PLUGIFY_NO_NULL s)
const noexcept {
1901 return starts_with(self_view(s));
1906 constexpr bool ends_with(self_view sv)
const noexcept {
1907 return self_view(
typename self_view::assume_valid(), data(), size()).ends_with(sv);
1910 constexpr bool ends_with(value_type c)
const noexcept {
1911 return !empty() && Traits::eq(back(), c);
1914 constexpr bool ends_with(
const value_type* PLUGIFY_NO_NULL s)
const noexcept {
1915 return ends_with(self_view(s));
1920 constexpr bool contains(self_view sv)
const noexcept {
1921 return self_view(
typename self_view::assume_valid(), data(), size()).contains(sv);
1924 constexpr bool contains(value_type c)
const noexcept {
1925 return self_view(
typename self_view::assume_valid(), data(), size()).contains(c);
1928 constexpr bool contains(
const value_type* PLUGIFY_NO_NULL s)
const {
1929 return self_view(
typename self_view::assume_valid(), data(), size()).contains(s);
1932 [[nodiscard]]
constexpr bool invariants()
const;
1935 [[nodiscard]]
constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS
bool is_long() const noexcept {
1936 if (std::is_constant_evaluated() && __builtin_constant_p(rep_.l.is_long_)) {
1937 return rep_.l.is_long_;
1939 return rep_.s.is_long_;
1942 static constexpr bool fits_in_sso(size_type sz) {
1943 return sz < min_cap;
1946 template <
class Iterator,
class Sentinel>
1947 constexpr void assign_trivial(Iterator first, Sentinel last, size_type n);
1949 template <
class Iterator,
class Sentinel>
1950 constexpr void assign_with_sentinel(Iterator first, Sentinel last);
1954 template <
class ForwardIter,
class Sent>
1955 static constexpr value_type*
1956 copy_non_overlapping_range(ForwardIter first, Sent last, value_type* dest) {
1957 if constexpr (std::contiguous_iterator<ForwardIter>
1958 && std::is_same_v<value_type, std::remove_cvref_t<
decltype(*first)>>
1959 && std::is_same_v<ForwardIter, Sent>) {
1961 !is_overlapping_range(std::to_address(first), std::to_address(last), dest),
1962 "copy_non_overlapping_range called with an overlapping range!"
1964 traits_type::copy(dest, std::to_address(first), last - first);
1965 return dest + (last - first);
1967 for (; first != last; ++first) {
1968 traits_type::assign(*dest++, *first);
1974 template <
class ForwardIterator,
class Sentinel>
1976 insert_from_safe_copy(size_type n, size_type ip, ForwardIterator first, Sentinel last) {
1977 size_type sz = size();
1978 size_type cap = capacity();
1980 if (cap - sz >= n) {
1981 annotate_increase(n);
1982 p = std::to_address(get_pointer());
1983 size_type n_move = sz - ip;
1985 traits_type::move(p + ip + n, p + ip, n_move);
1988 grow_by_without_replace(cap, sz + n - cap, sz, ip, 0, n);
1989 p = std::to_address(get_long_pointer());
1993 traits_type::assign(p[sz], value_type());
1994 copy_non_overlapping_range(std::move(first), std::move(last), p + ip);
1996 return begin() + ip;
1999 template <
class Iterator,
class Sentinel>
2001 insert_with_size(const_iterator pos, Iterator first, Sentinel last, size_type n);
2006 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS
void set_short_size(size_type s)
noexcept {
2009 "s should never be greater than or equal to the short string capacity"
2011 rep_.s.spare_size_ = (min_cap - 1) - s;
2012 rep_.s.is_long_ =
false;
2015 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS size_type get_short_size() const noexcept {
2016 PLUGIFY_ASSERT(!rep_.s.is_long_,
"String has to be short when trying to get the short size");
2017 return (min_cap - 1) - rep_.s.spare_size_;
2020 constexpr void set_long_size(size_type s)
noexcept {
2024 constexpr size_type get_long_size() const noexcept {
2025 PLUGIFY_ASSERT(rep_.l.is_long_,
"String has to be long when trying to get the long size");
2026 return rep_.l.size_;
2029 constexpr void set_size(size_type s)
noexcept {
2037 constexpr size_type get_long_cap() const noexcept {
2038 PLUGIFY_ASSERT(rep_.l.is_long_,
"String has to be long when trying to get the long capacity");
2039 return rep_.l.cap_ * endian_factor;
2042 constexpr pointer get_long_pointer() noexcept {
2043 PLUGIFY_ASSERT(rep_.l.is_long_,
"String has to be long when trying to get the long pointer");
2044 return PLUGIFY_ASAN_VOLATILE_WRAPPER(rep_.l.data_);
2047 constexpr const_pointer get_long_pointer() const noexcept {
2048 PLUGIFY_ASSERT(rep_.l.is_long_,
"String has to be long when trying to get the long pointer");
2049 return PLUGIFY_ASAN_VOLATILE_WRAPPER(rep_.l.data_);
2052 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS pointer get_short_pointer() noexcept {
2053 return PLUGIFY_ASAN_VOLATILE_WRAPPER(
2054 std::pointer_traits<pointer>::pointer_to(rep_.s.data_[0])
2058 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS const_pointer get_short_pointer() const noexcept {
2059 return PLUGIFY_ASAN_VOLATILE_WRAPPER(
2060 std::pointer_traits<const_pointer>::pointer_to(rep_.s.data_[0])
2064 constexpr pointer get_pointer() noexcept {
2065 return is_long() ? get_long_pointer() : get_short_pointer();
2068 constexpr const_pointer get_pointer() const noexcept {
2069 return is_long() ? get_long_pointer() : get_short_pointer();
2080 static constexpr long_ allocate_long_buffer(Allocator& alloc, size_type capacity) {
2081 auto buffer = allocate_at_least(alloc, recommend(capacity) + 1);
2083 if (std::is_constant_evaluated()) {
2084 for (size_type i = 0; i != buffer.count; ++i) {
2085 std::construct_at(std::addressof(buffer.ptr[i]));
2089 return long_(buffer, capacity);
2094 constexpr void reset_internal_buffer() {
2097 alloc_traits::deallocate(alloc_, get_long_pointer(), get_long_cap());
2103 constexpr void replace_internal_buffer(long_ alloc) {
2104 reset_internal_buffer();
2110 constexpr pointer init_internal_buffer(size_type size) {
2111 if (std::is_constant_evaluated()) {
2115 if (size > max_size()) {
2116 throw_length_error();
2119 if (fits_in_sso(size)) {
2120 set_short_size(size);
2122 return get_short_pointer();
2124 rep_.l = allocate_long_buffer(alloc_, size);
2126 return get_long_pointer();
2134 constexpr void annotate_contiguous_container(
2135 const void* old_mid,
2144 data() + capacity() + 1,
2150 constexpr void annotate_new(size_type current_size)
const noexcept {
2151 annotate_contiguous_container(data() + capacity() + 1, data() + current_size + 1);
2154 constexpr void annotate_delete() const noexcept {
2155 annotate_contiguous_container(data() + size() + 1, data() + capacity() + 1);
2158 constexpr void annotate_increase(size_type n)
const noexcept {
2159 annotate_contiguous_container(data() + size() + 1, data() + size() + 1 + n);
2162 constexpr void annotate_shrink(size_type old_size)
const noexcept {
2163 annotate_contiguous_container(data() + old_size + 1, data() + size() + 1);
2168 struct [[nodiscard]] annotation_guard {
2169 annotation_guard(
const annotation_guard&) =
delete;
2170 annotation_guard& operator=(
const annotation_guard&) =
delete;
2172 constexpr annotation_guard(basic_string& str)
2174 str.annotate_delete();
2177 constexpr ~annotation_guard() {
2178 str.annotate_new(str.size());
2184 template <
size_type a>
2185 static constexpr size_type align_it(size_type s)
noexcept {
2186 return (s + (a - 1)) & ~(a - 1);
2189 enum { alignment = 8 };
2191 static constexpr size_type recommend(size_type s)
noexcept {
2195 const size_type boundary =
sizeof(value_type) < alignment ? alignment /
sizeof(value_type) : endian_factor;
2196 size_type guess = align_it<boundary>(s + 1) - 1;
2197 if (guess == min_cap) {
2198 guess += endian_factor;
2201 PLUGIFY_ASSERT(guess >= s,
"recommendation is below the requested size");
2205 inline constexpr void init(
const value_type* s, size_type sz);
2206 inline constexpr void init(size_type n, value_type c);
2216 PLUGIFY_NOINLINE
constexpr void init_copy_ctor_external(
const value_type* s, size_type sz);
2218 template <std::input_iterator InputIterator>
2219 inline constexpr void init(InputIterator first, InputIterator last);
2221 template <std::forward_iterator ForwardIterator>
2222 inline constexpr void init(ForwardIterator first, ForwardIterator last);
2224 template <
class InputIterator,
class Sentinel>
2225 constexpr void init_with_sentinel(InputIterator first, Sentinel last);
2226 template <
class InputIterator,
class Sentinel>
2227 constexpr void init_with_size(InputIterator first, Sentinel last, size_type sz);
2229 constexpr void grow_by_without_replace(
2231 size_type delta_cap,
2237 constexpr void grow_by_and_replace(
2239 size_type delta_cap,
2244 const value_type* p_new_stuff
2250 template <
bool is_
short>
2251 PLUGIFY_NOINLINE
constexpr basic_string& assign_no_alias(
const value_type* s, size_type n);
2253 constexpr void erase_to_end(size_type pos) {
2256 "Trying to erase at position outside the strings capacity!"
2258 null_terminate_at(std::to_address(get_pointer()), pos);
2263 PLUGIFY_NOINLINE
constexpr void erase_external_with_move(size_type pos, size_type n);
2265 constexpr void copy_assign_alloc(
const basic_string& str) {
2268 std::integral_constant<bool, alloc_traits::propagate_on_container_copy_assignment::value>()
2272 constexpr void copy_assign_alloc(
const basic_string& str, std::true_type) {
2273 if (alloc_ == str.alloc_) {
2274 alloc_ = str.alloc_;
2276 if (!str.is_long()) {
2277 reset_internal_buffer();
2278 alloc_ = str.alloc_;
2281 [[maybe_unused]]
auto guard = make_scope_guard(annotate_new_size(*
this));
2282 auto alloc = str.alloc_;
2283 replace_internal_buffer(allocate_long_buffer(alloc, str.size()));
2284 alloc_ = std::move(alloc);
2289 constexpr void copy_assign_alloc(
const basic_string&, std::false_type)
noexcept {
2293 move_assign(basic_string& str, std::false_type)
noexcept(alloc_traits::is_always_equal::value);
2294 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS
void
2295 move_assign(basic_string& str, std::true_type)
noexcept;
2297 constexpr void move_assign_alloc(
2299 )
noexcept(!alloc_traits::propagate_on_container_move_assignment::value || std::is_nothrow_move_assignable_v<allocator_type>) {
2302 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
2306 constexpr void move_assign_alloc(
2309 )
noexcept(std::is_nothrow_move_assignable_v<allocator_type>) {
2310 alloc_ = std::move(c.alloc_);
2313 constexpr void move_assign_alloc(basic_string&, std::false_type)
noexcept {
2316 PLUGIFY_NOINLINE
constexpr basic_string& assign_external(
const value_type* s);
2317 PLUGIFY_NOINLINE
constexpr basic_string& assign_external(
const value_type* s, size_type n);
2320 inline constexpr basic_string& assign_short(
const value_type* s, size_type n) {
2321 size_type old_size = size();
2323 annotate_increase(n - old_size);
2328 p = get_long_pointer();
2331 p = get_short_pointer();
2333 traits_type::move(std::to_address(p), s, n);
2334 traits_type::assign(p[n], value_type());
2336 annotate_shrink(old_size);
2341 constexpr basic_string& null_terminate_at(value_type* p, size_type newsz) {
2342 size_type old_size = size();
2343 if (newsz > old_size) {
2344 annotate_increase(newsz - old_size);
2347 traits_type::assign(p[newsz], value_type());
2348 if (old_size > newsz) {
2349 annotate_shrink(old_size);
2355 constexpr bool addr_in_range(
const T& v)
const {
2356 return is_pointer_in_range(data(), data() + size() + 1, std::addressof(v));
2359 [[noreturn]]
static void throw_length_error() {
2360 PLUGIFY_THROW(
"constructed string size would exceed max_size()", std::length_error);
2363 [[noreturn]]
static void throw_out_of_range() {
2364 PLUGIFY_THROW(
"input index is out of bounds", std::out_of_range);
2367 friend constexpr basic_string
2368 concatenate_strings<>(
const Allocator&, std::type_identity_t<self_view>, std::type_identity_t<self_view>);
2370 template <
class CharT2,
class Traits2,
class Allocator2>
2371 friend inline constexpr bool
2372 operator==(
const basic_string<CharT2, Traits2, Allocator2>&,
const CharT2*)
noexcept;
2376 std::input_iterator InputIterator,
2377 class CharT = std::iter_value_t<InputIterator>,
2378 is_allocator Allocator = allocator<CharT>>
2379 basic_string(InputIterator, InputIterator, Allocator = Allocator())
2380 -> basic_string<CharT, std::char_traits<CharT>, Allocator>;
2382 template <
class CharT, is_
char_traits Traits, is_allocator Allocator = allocator<CharT>>
2383 explicit basic_string(std::basic_string_view<CharT, Traits>,
const Allocator& = Allocator())
2384 -> basic_string<CharT, Traits, Allocator>;
2388 is_char_traits Traits,
2389 is_allocator Allocator = allocator<CharT>,
2390 class Sz =
typename std::allocator_traits<Allocator>::size_type>
2391 basic_string(std::basic_string_view<CharT, Traits>, Sz, Sz,
const Allocator& = Allocator())
2392 -> basic_string<CharT, Traits, Allocator>;
2394#if PLUGIFY_HAS_CXX23
2396 std::ranges::input_range Range,
2397 is_allocator Allocator = std::allocator<std::ranges::range_value_t<Range>>>
2398 basic_string(std::from_range_t, Range&&, Allocator = Allocator()) -> basic_string<
2399 std::ranges::range_value_t<Range>,
2400 std::char_traits<std::ranges::range_value_t<Range>>,
2404 template <
class CharT,
class Traits,
class Allocator>
2405 constexpr void basic_string<CharT, Traits, Allocator>::init(
const value_type* s, size_type sz) {
2406 pointer p = init_internal_buffer(sz);
2407 traits_type::copy(std::to_address(p), s, sz);
2408 traits_type::assign(p[sz], value_type());
2411 template <
class CharT,
class Traits,
class Allocator>
2412 PLUGIFY_NOINLINE
constexpr void
2413 basic_string<CharT, Traits, Allocator>::init_copy_ctor_external(
const value_type* s, size_type sz) {
2414 pointer p = init_internal_buffer(sz);
2415 traits_type::copy(std::to_address(p), s, sz + 1);
2418 template <
class CharT,
class Traits,
class Allocator>
2419 constexpr void basic_string<CharT, Traits, Allocator>::init(size_type n, value_type c) {
2420 pointer p = init_internal_buffer(n);
2421 traits_type::assign(std::to_address(p), n, c);
2422 traits_type::assign(p[n], value_type());
2425 template <
class CharT,
class Traits,
class Allocator>
2426 template <std::input_iterator InputIterator>
2428 basic_string<CharT, Traits, Allocator>::init(InputIterator first, InputIterator last) {
2429 init_with_sentinel(std::move(first), std::move(last));
2432 template <
class CharT,
class Traits,
class Allocator>
2433 template <
class InputIterator,
class Sentinel>
2435 basic_string<CharT, Traits, Allocator>::init_with_sentinel(InputIterator first, Sentinel last) {
2439#if PLUGIFY_HAS_EXCEPTIONS
2442 for (; first != last; ++first) {
2445#if PLUGIFY_HAS_EXCEPTIONS
2447 reset_internal_buffer();
2453 template <
class CharT,
class Traits,
class Allocator>
2454 template <std::forward_iterator ForwardIterator>
2456 basic_string<CharT, Traits, Allocator>::init(ForwardIterator first, ForwardIterator last) {
2457 size_type sz =
static_cast<size_type
>(std::distance(first, last));
2458 init_with_size(first, last, sz);
2461 template <
class CharT,
class Traits,
class Allocator>
2462 template <
class InputIterator,
class Sentinel>
2463 constexpr void basic_string<CharT, Traits, Allocator>::init_with_size(
2464 InputIterator first,
2468 pointer p = init_internal_buffer(sz);
2470#if PLUGIFY_HAS_EXCEPTIONS
2473 auto end = copy_non_overlapping_range(std::move(first), std::move(last), std::to_address(p));
2474 traits_type::assign(*end, value_type());
2475#if PLUGIFY_HAS_EXCEPTIONS
2477 reset_internal_buffer();
2483 template <
class CharT,
class Traits,
class Allocator>
2484 constexpr void basic_string<CharT, Traits, Allocator>::grow_by_and_replace(
2486 size_type delta_cap,
2491 const value_type* p_new_stuff
2493 size_type ms = max_size();
2494 if (delta_cap > ms - old_cap) {
2495 throw_length_error();
2497 pointer old_p = get_pointer();
2498 size_type cap = old_cap < ms / 2 - alignment
2499 ? recommend(std::max(old_cap + delta_cap, 2 * old_cap))
2502 [[maybe_unused]]
auto guard = make_scope_guard(annotate_new_size(*
this));
2503 long_ buffer = allocate_long_buffer(alloc_, cap);
2505 traits_type::copy(std::to_address(buffer.data_), std::to_address(old_p), n_copy);
2508 traits_type::copy(std::to_address(buffer.data_) + n_copy, p_new_stuff, n_add);
2510 size_type sec_cp_sz = old_sz - n_del - n_copy;
2511 if (sec_cp_sz != 0) {
2513 std::to_address(buffer.data_) + n_copy + n_add,
2514 std::to_address(old_p) + n_copy + n_del,
2518 buffer.size_ = n_copy + n_add + sec_cp_sz;
2519 traits_type::assign(buffer.data_[buffer.size_], value_type());
2520 replace_internal_buffer(buffer);
2523 template <
class CharT,
class Traits,
class Allocator>
2524 constexpr void basic_string<CharT, Traits, Allocator>::grow_by_without_replace(
2526 size_type delta_cap,
2533 [[maybe_unused]]
auto guard = make_scope_guard(annotate_new_size(*
this));
2534 size_type ms = max_size();
2535 if (delta_cap > ms - old_cap) {
2536 this->throw_length_error();
2538 pointer old_p = get_pointer();
2539 size_type cap = old_cap < ms / 2 - alignment
2540 ? recommend(std::max(old_cap + delta_cap, 2 * old_cap))
2542 long_ buffer = allocate_long_buffer(alloc_, cap);
2544 traits_type::copy(std::to_address(buffer.data_), std::to_address(old_p), n_copy);
2546 size_type sec_cp_sz = old_sz - n_del - n_copy;
2547 if (sec_cp_sz != 0) {
2549 std::to_address(buffer.data_) + n_copy + n_add,
2550 std::to_address(old_p) + n_copy + n_del,
2557 buffer.size_ = npos;
2558 replace_internal_buffer(buffer);
2559 set_long_size(old_sz - n_del + n_add);
2564 template <
class CharT,
class Traits,
class Allocator>
2565 template <
bool is_
short>
2566 PLUGIFY_NOINLINE
constexpr basic_string<CharT, Traits, Allocator>&
2567 basic_string<CharT, Traits, Allocator>::assign_no_alias(
const value_type* s, size_type n) {
2568 const auto cap = is_short ? min_cap : get_long_cap();
2569 const auto size = is_short ? get_short_size() : get_long_size();
2571 grow_by_and_replace(cap - 1, n - cap + 1, size, 0, size, n, s);
2576 [[maybe_unused]]
auto guard = make_scope_guard(annotate_new_size(*
this));
2579 p = get_short_pointer();
2582 p = get_long_pointer();
2585 traits_type::copy(std::to_address(p), s, n);
2586 traits_type::assign(p[n], value_type());
2590 template <
class CharT,
class Traits,
class Allocator>
2591 PLUGIFY_NOINLINE
constexpr basic_string<CharT, Traits, Allocator>&
2592 basic_string<CharT, Traits, Allocator>::assign_external(
const value_type* s, size_type n) {
2593 const auto cap = capacity();
2594 const auto sz = size();
2597 annotate_increase(n - sz);
2599 value_type* p = std::to_address(get_pointer());
2600 traits_type::move(p, s, n);
2601 return null_terminate_at(p, n);
2603 grow_by_and_replace(cap, n - cap, sz, 0, sz, n, s);
2608 template <
class CharT,
class Traits,
class Allocator>
2609 constexpr basic_string<CharT, Traits, Allocator>&
2610 basic_string<CharT, Traits, Allocator>::assign(
const value_type* s, size_type n) {
2611 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::assign received nullptr");
2612 return (__builtin_constant_p(n) && fits_in_sso(n)) ? assign_short(s, n)
2613 : assign_external(s, n);
2616 template <
class CharT,
class Traits,
class Allocator>
2617 constexpr basic_string<CharT, Traits, Allocator>&
2618 basic_string<CharT, Traits, Allocator>::assign(size_type n, value_type c) {
2619 size_type cap = capacity();
2620 size_type old_size = size();
2622 grow_by_without_replace(cap, n - cap, old_size, 0, old_size);
2623 annotate_increase(n);
2624 }
else if (n > old_size) {
2625 annotate_increase(n - old_size);
2627 value_type* p = std::to_address(get_pointer());
2628 traits_type::assign(p, n, c);
2629 return null_terminate_at(p, n);
2632 template <
class CharT,
class Traits,
class Allocator>
2633 constexpr basic_string<CharT, Traits, Allocator>&
2634 basic_string<CharT, Traits, Allocator>::operator=(value_type c) {
2635 size_type old_size = size();
2636 if (old_size == 0) {
2637 annotate_increase(1);
2641 p = get_long_pointer();
2644 p = get_short_pointer();
2647 traits_type::assign(*p, c);
2648 traits_type::assign(*++p, value_type());
2650 annotate_shrink(old_size);
2655 template <
class CharT,
class Traits,
class Allocator>
2656 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS basic_string<CharT, Traits, Allocator>&
2657 basic_string<CharT, Traits, Allocator>::operator=(
const basic_string& str) {
2658 if (
this == std::addressof(str)) {
2662 copy_assign_alloc(str);
2665 return assign_no_alias<false>(str.data(), str.size());
2668 if (str.is_long()) {
2669 return assign_no_alias<true>(str.data(), str.size());
2673 [[maybe_unused]]
auto guard = make_scope_guard(annotate_new_size(*
this));
2679 template <
class CharT,
class Traits,
class Allocator>
2680 inline constexpr void
2681 basic_string<CharT, Traits, Allocator>::move_assign(basic_string& str, std::false_type)
noexcept(
2682 alloc_traits::is_always_equal::value
2684 if (alloc_ != str.alloc_) {
2687 move_assign(str, std::true_type());
2691 template <
class CharT,
class Traits,
class Allocator>
2692 inline constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS
void
2693 basic_string<CharT, Traits, Allocator>::move_assign(basic_string& str, std::true_type)
noexcept {
2696 reset_internal_buffer();
2698 size_type str_old_size = str.size();
2699 bool str_was_short = !str.is_long();
2701 move_assign_alloc(str);
2703 str.set_short_size(0);
2704 traits_type::assign(str.get_short_pointer()[0], value_type());
2706 if (str_was_short &&
this != std::addressof(str)) {
2707 str.annotate_shrink(str_old_size);
2711 str.annotate_new(0);
2721 if (!is_long() && std::addressof(str) !=
this) {
2723 annotate_new(get_short_size());
2727 template <
class CharT,
class Traits,
class Allocator>
2728 template <
class InputIterator,
class Sentinel>
2730 basic_string<CharT, Traits, Allocator>::assign_with_sentinel(InputIterator first, Sentinel last) {
2731 const basic_string temp(init_with_sentinel_tag(), std::move(first), std::move(last), alloc_);
2732 assign(temp.data(), temp.size());
2735 template <
class CharT,
class Traits,
class Allocator>
2736 template <
class Iterator,
class Sentinel>
2738 basic_string<CharT, Traits, Allocator>::assign_trivial(Iterator first, Sentinel last, size_type n) {
2740 string_is_trivial_iterator_v<Iterator>,
2741 "The iterator type given to `assign_trivial` must be trivial"
2744 size_type old_size = size();
2745 size_type cap = capacity();
2755 size_type sz = size();
2756 grow_by_without_replace(cap, n - cap, sz, 0, sz);
2757 annotate_increase(n);
2758 }
else if (n > old_size) {
2759 annotate_increase(n - old_size);
2761 pointer p = get_pointer();
2762 for (; first != last; ++p, (void) ++first) {
2763 traits_type::assign(*p, *first);
2765 traits_type::assign(*p, value_type());
2768 annotate_shrink(old_size);
2772 template <
class CharT,
class Traits,
class Allocator>
2773 constexpr basic_string<CharT, Traits, Allocator>&
2774 basic_string<CharT, Traits, Allocator>::assign(
const basic_string& str, size_type pos, size_type n) {
2775 size_type sz = str.size();
2777 this->throw_out_of_range();
2779 return assign(str.data() + pos, std::min(n, sz - pos));
2782 template <
class CharT,
class Traits,
class Allocator>
2783 PLUGIFY_NOINLINE
constexpr basic_string<CharT, Traits, Allocator>&
2784 basic_string<CharT, Traits, Allocator>::assign_external(
const value_type* s) {
2785 return assign_external(s, traits_type::length(s));
2788 template <
class CharT,
class Traits,
class Allocator>
2789 constexpr basic_string<CharT, Traits, Allocator>&
2790 basic_string<CharT, Traits, Allocator>::assign(
const value_type* s) {
2791 PLUGIFY_ASSERT(s !=
nullptr,
"string::assign received nullptr");
2792 if (
auto len = traits_type::length(s); __builtin_constant_p(len) && fits_in_sso(len)) {
2793 return assign_short(s, len);
2795 return assign_external(s);
2800 template <
class CharT,
class Traits,
class Allocator>
2801 constexpr basic_string<CharT, Traits, Allocator>&
2802 basic_string<CharT, Traits, Allocator>::append(
const value_type* s, size_type n) {
2803 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::append received nullptr");
2804 size_type cap = capacity();
2805 size_type sz = size();
2807 grow_by_and_replace(cap, sz + n - cap, sz, sz, 0, n, s);
2815 annotate_increase(n);
2816 value_type* p = std::to_address(get_pointer());
2817 traits_type::copy(p + sz, s, n);
2820 traits_type::assign(p[sz], value_type());
2824 template <
class CharT,
class Traits,
class Allocator>
2825 constexpr basic_string<CharT, Traits, Allocator>&
2826 basic_string<CharT, Traits, Allocator>::append(size_type n, value_type c) {
2831 size_type cap = capacity();
2832 size_type sz = size();
2834 grow_by_without_replace(cap, sz + n - cap, sz, sz, 0);
2836 annotate_increase(n);
2837 pointer p = get_pointer();
2838 traits_type::assign(std::to_address(p) + sz, n, c);
2841 traits_type::assign(p[sz], value_type());
2845 template <
class CharT,
class Traits,
class Allocator>
2846 constexpr void basic_string<CharT, Traits, Allocator>::push_back(value_type c) {
2847 bool is_short = !is_long();
2852 sz = get_short_size();
2854 cap = get_long_cap() - 1;
2855 sz = get_long_size();
2858 grow_by_without_replace(cap, 1, sz, sz, 0);
2861 annotate_increase(1);
2864 p = get_short_pointer() + sz;
2865 set_short_size(sz + 1);
2867 p = get_long_pointer() + sz;
2868 set_long_size(sz + 1);
2870 traits_type::assign(*p, c);
2871 traits_type::assign(*++p, value_type());
2874 template <
class CharT,
class Traits,
class Allocator>
2875 constexpr basic_string<CharT, Traits, Allocator>&
2876 basic_string<CharT, Traits, Allocator>::append(
const basic_string& str, size_type pos, size_type n) {
2877 size_type sz = str.size();
2879 this->throw_out_of_range();
2881 return append(str.data() + pos, std::min(n, sz - pos));
2884 template <
class CharT,
class Traits,
class Allocator>
2885 constexpr basic_string<CharT, Traits, Allocator>&
2886 basic_string<CharT, Traits, Allocator>::append(
const value_type* s) {
2887 PLUGIFY_ASSERT(s !=
nullptr,
"string::append received nullptr");
2888 return append(s, traits_type::length(s));
2893 template <
class CharT,
class Traits,
class Allocator>
2894 constexpr basic_string<CharT, Traits, Allocator>&
2895 basic_string<CharT, Traits, Allocator>::insert(size_type pos,
const value_type* s, size_type n) {
2896 PLUGIFY_ASSERT(n == 0 || s !=
nullptr,
"string::insert received nullptr");
2897 size_type sz = size();
2899 this->throw_out_of_range();
2901 size_type cap = capacity();
2904 grow_by_and_replace(cap, sz + n - cap, sz, pos, 0, n, s);
2912 annotate_increase(n);
2913 value_type* p = std::to_address(get_pointer());
2914 size_type n_move = sz - pos;
2916 if (is_pointer_in_range(p + pos, p + sz, s)) {
2919 traits_type::move(p + pos + n, p + pos, n_move);
2921 traits_type::move(p + pos, s, n);
2924 traits_type::assign(p[sz], value_type());
2928 template <
class CharT,
class Traits,
class Allocator>
2929 constexpr basic_string<CharT, Traits, Allocator>&
2930 basic_string<CharT, Traits, Allocator>::insert(size_type pos, size_type n, value_type c) {
2931 size_type sz = size();
2933 this->throw_out_of_range();
2940 size_type cap = capacity();
2942 if (cap - sz >= n) {
2943 annotate_increase(n);
2944 p = std::to_address(get_pointer());
2945 size_type n_move = sz - pos;
2947 traits_type::move(p + pos + n, p + pos, n_move);
2950 grow_by_without_replace(cap, sz + n - cap, sz, pos, 0, n);
2951 p = std::to_address(get_long_pointer());
2953 traits_type::assign(p + pos, n, c);
2956 traits_type::assign(p[sz], value_type());
2960 template <
class CharT,
class Traits,
class Allocator>
2961 template <
class Iterator,
class Sentinel>
2962 constexpr typename basic_string<CharT, Traits, Allocator>::iterator
2963 basic_string<CharT, Traits, Allocator>::insert_with_size(
2969 size_type ip =
static_cast<size_type
>(pos - begin());
2971 return begin() + ip;
2974 if (string_is_trivial_iterator_v<Iterator> && !addr_in_range(*first)) {
2975 return insert_from_safe_copy(n, ip, std::move(first), std::move(last));
2977 const basic_string temp(init_with_sentinel_tag(), std::move(first), std::move(last), alloc_);
2978 return insert_from_safe_copy(n, ip, temp.begin(), temp.end());
2982 template <
class CharT,
class Traits,
class Allocator>
2983 constexpr basic_string<CharT, Traits, Allocator>& basic_string<CharT, Traits, Allocator>::insert(
2985 const basic_string& str,
2989 size_type str_sz = str.size();
2990 if (pos2 > str_sz) {
2991 this->throw_out_of_range();
2993 return insert(pos1, str.data() + pos2, std::min(n, str_sz - pos2));
2996 template <
class CharT,
class Traits,
class Allocator>
2997 constexpr basic_string<CharT, Traits, Allocator>&
2998 basic_string<CharT, Traits, Allocator>::insert(size_type pos,
const value_type* s) {
2999 PLUGIFY_ASSERT(s !=
nullptr,
"string::insert received nullptr");
3000 return insert(pos, s, traits_type::length(s));
3003 template <
class CharT,
class Traits,
class Allocator>
3004 constexpr typename basic_string<CharT, Traits, Allocator>::iterator
3005 basic_string<CharT, Traits, Allocator>::insert(const_iterator pos, value_type c) {
3006 size_type ip =
static_cast<size_type
>(pos - begin());
3007 size_type sz = size();
3008 size_type cap = capacity();
3011 grow_by_without_replace(cap, 1, sz, ip, 0, 1);
3012 p = std::to_address(get_long_pointer());
3014 annotate_increase(1);
3015 p = std::to_address(get_pointer());
3016 size_type n_move = sz - ip;
3018 traits_type::move(p + ip + 1, p + ip, n_move);
3021 traits_type::assign(p[ip], c);
3022 traits_type::assign(p[++sz], value_type());
3024 return begin() +
static_cast<difference_type
>(ip);
3029 template <
class CharT,
class Traits,
class Allocator>
3030 constexpr basic_string<CharT, Traits, Allocator>&
3031 basic_string<CharT, Traits, Allocator>::replace(
3034 const value_type* s,
3037 PLUGIFY_ASSERT(n2 == 0 || s !=
nullptr,
"string::replace received nullptr");
3038 size_type sz = size();
3040 this->throw_out_of_range();
3042 n1 = std::min(n1, sz - pos);
3043 size_type cap = capacity();
3044 if (cap - sz + n1 < n2) {
3045 grow_by_and_replace(cap, sz - n1 + n2 - cap, sz, pos, n1, n2, s);
3049 value_type* p = std::to_address(get_pointer());
3052 annotate_increase(n2 - n1);
3054 size_type n_move = sz - pos - n1;
3057 traits_type::move(p + pos, s, n2);
3058 traits_type::move(p + pos + n2, p + pos + n1, n_move);
3059 return null_terminate_at(p, sz + (n2 - n1));
3061 if (is_pointer_in_range(p + pos + 1, p + sz, s)) {
3062 if (p + pos + n1 <= s) {
3065 traits_type::move(p + pos, s, n1);
3072 traits_type::move(p + pos + n2, p + pos + n1, n_move);
3075 traits_type::move(p + pos, s, n2);
3076 return null_terminate_at(p, sz + (n2 - n1));
3079 template <
class CharT,
class Traits,
class Allocator>
3080 constexpr basic_string<CharT, Traits, Allocator>&
3081 basic_string<CharT, Traits, Allocator>::replace(size_type pos, size_type n1, size_type n2, value_type c) {
3082 size_type sz = size();
3084 this->throw_out_of_range();
3086 n1 = std::min(n1, sz - pos);
3087 size_type cap = capacity();
3089 if (cap - sz + n1 >= n2) {
3090 p = std::to_address(get_pointer());
3093 annotate_increase(n2 - n1);
3095 size_type n_move = sz - pos - n1;
3097 traits_type::move(p + pos + n2, p + pos + n1, n_move);
3101 grow_by_without_replace(cap, sz - n1 + n2 - cap, sz, pos, n1, n2);
3102 p = std::to_address(get_long_pointer());
3104 traits_type::assign(p + pos, n2, c);
3105 return null_terminate_at(p, sz - (n1 - n2));
3108 template <
class CharT,
class Traits,
class Allocator>
3109 constexpr basic_string<CharT, Traits, Allocator>&
3110 basic_string<CharT, Traits, Allocator>::replace(
3113 const basic_string& str,
3117 size_type str_sz = str.size();
3118 if (pos2 > str_sz) {
3119 this->throw_out_of_range();
3121 return replace(pos1, n1, str.data() + pos2, std::min(n2, str_sz - pos2));
3124 template <
class CharT,
class Traits,
class Allocator>
3125 constexpr basic_string<CharT, Traits, Allocator>&
3126 basic_string<CharT, Traits, Allocator>::replace(size_type pos, size_type n1,
const value_type* s) {
3127 PLUGIFY_ASSERT(s !=
nullptr,
"string::replace received nullptr");
3128 return replace(pos, n1, s, traits_type::length(s));
3135 template <
class CharT,
class Traits,
class Allocator>
3136 PLUGIFY_NOINLINE
constexpr void
3137 basic_string<CharT, Traits, Allocator>::erase_external_with_move(size_type pos, size_type n) {
3142 size_type sz = size();
3143 value_type* p = std::to_address(get_pointer());
3144 n = std::min(n, sz - pos);
3145 size_type n_move = sz - pos - n;
3147 traits_type::move(p + pos, p + pos + n, n_move);
3149 null_terminate_at(p, sz - n);
3152 template <
class CharT,
class Traits,
class Allocator>
3153 constexpr basic_string<CharT, Traits, Allocator>&
3154 basic_string<CharT, Traits, Allocator>::erase(size_type pos, size_type n) {
3156 this->throw_out_of_range();
3161 erase_external_with_move(pos, n);
3166 template <
class CharT,
class Traits,
class Allocator>
3167 inline constexpr typename basic_string<CharT, Traits, Allocator>::iterator
3168 basic_string<CharT, Traits, Allocator>::erase(const_iterator pos) {
3169 PLUGIFY_ASSERT(pos != end(),
"string::erase(iterator) called with a non-dereferenceable iterator");
3170 iterator b = begin();
3171 size_type r =
static_cast<size_type
>(pos - b);
3173 return b +
static_cast<difference_type
>(r);
3176 template <
class CharT,
class Traits,
class Allocator>
3177 inline constexpr typename basic_string<CharT, Traits, Allocator>::iterator
3178 basic_string<CharT, Traits, Allocator>::erase(const_iterator first, const_iterator last) {
3179 PLUGIFY_ASSERT(first <= last,
"string::erase(first, last) called with invalid range");
3180 iterator b = begin();
3181 size_type r =
static_cast<size_type
>(first - b);
3182 erase(r,
static_cast<size_type
>(last - first));
3183 return b +
static_cast<difference_type
>(r);
3186 template <
class CharT,
class Traits,
class Allocator>
3187 inline constexpr void basic_string<CharT, Traits, Allocator>::pop_back() {
3188 PLUGIFY_ASSERT(!empty(),
"string::pop_back(): string is already empty");
3189 erase_to_end(size() - 1);
3192 template <
class CharT,
class Traits,
class Allocator>
3193 inline constexpr void basic_string<CharT, Traits, Allocator>::clear() noexcept {
3196 old_size = get_long_size();
3197 traits_type::assign(*get_long_pointer(), value_type());
3200 old_size = get_short_size();
3201 traits_type::assign(*get_short_pointer(), value_type());
3204 annotate_shrink(old_size);
3207 template <
class CharT,
class Traits,
class Allocator>
3208 constexpr void basic_string<CharT, Traits, Allocator>::resize(size_type n, value_type c) {
3209 size_type sz = size();
3217 template <
class CharT,
class Traits,
class Allocator>
3218 constexpr void basic_string<CharT, Traits, Allocator>::reserve(size_type requested_capacity) {
3219 if (requested_capacity > max_size()) {
3220 this->throw_length_error();
3226 if (requested_capacity <= capacity()) {
3230 [[maybe_unused]] annotation_guard g(*
this);
3231 long_ buffer = allocate_long_buffer(alloc_, requested_capacity);
3232 buffer.size_ = size();
3233 traits_type::copy(std::to_address(buffer.data_), data(), buffer.size_ + 1);
3234 replace_internal_buffer(buffer);
3237 template <
class CharT,
class Traits,
class Allocator>
3238 inline constexpr void basic_string<CharT, Traits, Allocator>::shrink_to_fit() noexcept {
3239 size_type target_capacity = recommend(size());
3240 if (target_capacity == capacity()) {
3244 PLUGIFY_ASSERT(is_long(),
"Trying to shrink small string");
3247 const auto ptr = get_long_pointer();
3248 const auto size = get_long_size();
3249 const auto cap = get_long_cap();
3251 if (fits_in_sso(target_capacity)) {
3252 [[maybe_unused]] annotation_guard g(*
this);
3253 set_short_size(size);
3254 traits_type::copy(std::to_address(get_short_pointer()), std::to_address(ptr), size + 1);
3255 alloc_traits::deallocate(alloc_, ptr, cap);
3259#if PLUGIFY_HAS_EXCEPTIONS
3262 [[maybe_unused]] annotation_guard g(*
this);
3263 long_ buffer = allocate_long_buffer(alloc_, size);
3268 if (buffer.cap_ * endian_factor - 1 >= capacity()) {
3269 alloc_traits::deallocate(alloc_, buffer.data_, buffer.cap_ * endian_factor);
3274 std::to_address(buffer.data_),
3275 std::to_address(get_long_pointer()),
3278 replace_internal_buffer(buffer);
3279#if PLUGIFY_HAS_EXCEPTIONS
3286 template <
class CharT,
class Traits,
class Allocator>
3287 constexpr typename basic_string<CharT, Traits, Allocator>::const_reference
3288 basic_string<CharT, Traits, Allocator>::at(size_type n)
const {
3290 this->throw_out_of_range();
3295 template <
class CharT,
class Traits,
class Allocator>
3296 constexpr typename basic_string<CharT, Traits, Allocator>::reference
3297 basic_string<CharT, Traits, Allocator>::at(size_type n) {
3299 this->throw_out_of_range();
3304 template <
class CharT,
class Traits,
class Allocator>
3305 constexpr typename basic_string<CharT, Traits, Allocator>::size_type
3306 basic_string<CharT, Traits, Allocator>::copy(value_type* s, size_type n, size_type pos)
const {
3307 size_type sz = size();
3309 this->throw_out_of_range();
3311 size_type rlen = std::min(n, sz - pos);
3312 traits_type::copy(s, data() + pos, rlen);
3316 template <
class CharT,
class Traits,
class Allocator>
3317 inline constexpr void basic_string<CharT, Traits, Allocator>::swap(basic_string& str)
noexcept {
3319 alloc_traits::propagate_on_container_swap::value || alloc_traits::is_always_equal::value || alloc_ == str.alloc_,
3320 "swapping non-equal allocators"
3325 if (
this != std::addressof(str) && !str.is_long()) {
3326 str.annotate_delete();
3328 std::swap(rep_, str.rep_);
3329 swap_allocator(alloc_, str.alloc_);
3331 annotate_new(get_short_size());
3333 if (
this != std::addressof(str) && !str.is_long()) {
3334 str.annotate_new(str.get_short_size());
3340 template <
class CharT,
class Traits,
class Allocator>
3341 inline constexpr int basic_string<CharT, Traits, Allocator>::compare(
3344 const value_type* s,
3347 PLUGIFY_ASSERT(n2 == 0 || s !=
nullptr,
"string::compare(): received nullptr");
3348 size_type sz = size();
3349 if (pos1 > sz || n2 == npos) {
3350 this->throw_out_of_range();
3352 size_type rlen = std::min(n1, sz - pos1);
3353 int r = traits_type::compare(data() + pos1, s, std::min(rlen, n2));
3357 }
else if (rlen > n2) {
3366 template <
class CharT,
class Traits,
class Allocator>
3367 inline constexpr bool basic_string<CharT, Traits, Allocator>::invariants()
const {
3368 if (size() > capacity()) {
3371 if (capacity() < min_cap - 1) {
3374 if (data() ==
nullptr) {
3377 if (!Traits::eq(data()[size()], value_type())) {
3385 template <
class CharT,
class Traits,
class Allocator>
3386 inline constexpr bool operator==(
3387 const basic_string<CharT, Traits, Allocator>& lhs,
3388 const basic_string<CharT, Traits, Allocator>& rhs
3390 size_t lhs_sz = lhs.size();
3391 return lhs_sz == rhs.size() && Traits::compare(lhs.data(), rhs.data(), lhs_sz) == 0;
3394 template <
class CharT,
class Traits,
class Allocator>
3395 inline constexpr bool operator==(
3396 const basic_string<CharT, Traits, Allocator>& lhs,
3397 const CharT* PLUGIFY_NO_NULL rhs
3399 PLUGIFY_ASSERT(rhs !=
nullptr,
"operator==(basic_string, char*): received nullptr");
3401 using String = basic_string<CharT, Traits, Allocator>;
3403 size_t rhs_len = Traits::length(rhs);
3404 if (__builtin_constant_p(rhs_len) && !String::fits_in_sso(rhs_len)) {
3405 if (!lhs.is_long()) {
3409 if (rhs_len != lhs.size()) {
3412 return lhs.compare(0, String::npos, rhs, rhs_len) == 0;
3415 template <
class CharT,
class Traits,
class Allocator>
3416 constexpr auto operator<=>(
3417 const basic_string<CharT, Traits, Allocator>& lhs,
3418 const basic_string<CharT, Traits, Allocator>& rhs
3420 return std::basic_string_view<CharT, Traits>(lhs)
3421 <=> std::basic_string_view<CharT, Traits>(rhs);
3424 template <
class CharT,
class Traits,
class Allocator>
3425 constexpr auto operator<=>(
const basic_string<CharT, Traits, Allocator>& lhs,
const CharT* rhs) {
3426 return std::basic_string_view<CharT, Traits>(lhs)
3427 <=> std::basic_string_view<CharT, Traits>(rhs);
3432 template <
class CharT,
class Traits,
class Allocator>
3433 constexpr basic_string<CharT, Traits, Allocator> concatenate_strings(
3434 const Allocator& alloc,
3435 std::type_identity_t<std::basic_string_view<CharT, Traits>> str1,
3436 std::type_identity_t<std::basic_string_view<CharT, Traits>> str2
3438 using String = basic_string<CharT, Traits, Allocator>;
3440 uninitialized_size_tag(),
3441 str1.size() + str2.size(),
3442 String::alloc_traits::select_on_container_copy_construction(alloc)
3444 auto ptr = std::to_address(r.get_pointer());
3445 Traits::copy(ptr, str1.data(), str1.size());
3446 Traits::copy(ptr + str1.size(), str2.data(), str2.size());
3447 Traits::assign(ptr[str1.size() + str2.size()], CharT());
3451 template <
class CharT,
class Traits,
class Allocator>
3452 constexpr basic_string<CharT, Traits, Allocator> operator+(
3453 const basic_string<CharT, Traits, Allocator>& lhs,
3454 const basic_string<CharT, Traits, Allocator>& rhs
3456 return concatenate_strings<CharT, Traits>(lhs.get_allocator(), lhs, rhs);
3459 template <
class CharT,
class Traits,
class Allocator>
3460 constexpr basic_string<CharT, Traits, Allocator>
3461 operator+(
const CharT* lhs,
const basic_string<CharT, Traits, Allocator>& rhs) {
3462 return concatenate_strings<CharT, Traits>(rhs.get_allocator(), lhs, rhs);
3468 template <
class CharT,
class Traits,
class Allocator>
3469 constexpr basic_string<CharT, Traits, Allocator>
3470 operator+(CharT lhs,
const basic_string<CharT, Traits, Allocator>& rhs) {
3471 return concatenate_strings<CharT, Traits>(
3472 rhs.get_allocator(),
3473 std::basic_string_view<CharT, Traits>(std::addressof(lhs), 1),
3478 template <
class CharT,
class Traits,
class Allocator>
3479 constexpr basic_string<CharT, Traits, Allocator>
3480 operator+(
const basic_string<CharT, Traits, Allocator>& lhs,
const CharT* rhs) {
3481 return concatenate_strings<CharT, Traits>(lhs.get_allocator(), lhs, rhs);
3484 template <
class CharT,
class Traits,
class Allocator>
3485 constexpr basic_string<CharT, Traits, Allocator>
3486 operator+(
const basic_string<CharT, Traits, Allocator>& lhs, CharT rhs) {
3487 return concatenate_strings<CharT, Traits>(
3488 lhs.get_allocator(),
3490 std::basic_string_view<CharT, Traits>(std::addressof(rhs), 1)
3493#if PLUGIFY_HAS_CXX26
3495 template <
class CharT,
class Traits,
class Allocator>
3496 constexpr basic_string<CharT, Traits, Allocator> operator+(
3497 const basic_string<CharT, Traits, Allocator>& lhs,
3498 std::type_identity_t<std::basic_string_view<CharT, Traits>> rhs
3500 return concatenate_strings<CharT, Traits>(lhs.get_allocator(), lhs, rhs);
3503 template <
class CharT,
class Traits,
class Allocator>
3504 constexpr basic_string<CharT, Traits, Allocator> operator+(
3505 std::type_identity_t<std::basic_string_view<CharT, Traits>> lhs,
3506 const basic_string<CharT, Traits, Allocator>& rhs
3508 return concatenate_strings<CharT, Traits>(rhs.get_allocator(), lhs, rhs);
3513 template <
class CharT,
class Traits,
class Allocator>
3514 inline constexpr basic_string<CharT, Traits, Allocator> operator+(
3515 basic_string<CharT, Traits, Allocator>&& lhs,
3516 const basic_string<CharT, Traits, Allocator>& rhs
3518 return std::move(lhs.append(rhs));
3521 template <
class CharT,
class Traits,
class Allocator>
3522 inline constexpr basic_string<CharT, Traits, Allocator> operator+(
3523 const basic_string<CharT, Traits, Allocator>& lhs,
3524 basic_string<CharT, Traits, Allocator>&& rhs
3526 return std::move(rhs.insert(0, lhs));
3529 template <
class CharT,
class Traits,
class Allocator>
3530 inline constexpr basic_string<CharT, Traits, Allocator>
3531 operator+(basic_string<CharT, Traits, Allocator>&& lhs, basic_string<CharT, Traits, Allocator>&& rhs) {
3532 return std::move(lhs.append(rhs));
3535 template <
class CharT,
class Traits,
class Allocator>
3536 inline constexpr basic_string<CharT, Traits, Allocator>
3537 operator+(
const CharT* lhs, basic_string<CharT, Traits, Allocator>&& rhs) {
3538 return std::move(rhs.insert(0, lhs));
3541 template <
class CharT,
class Traits,
class Allocator>
3542 inline constexpr basic_string<CharT, Traits, Allocator>
3543 operator+(CharT lhs, basic_string<CharT, Traits, Allocator>&& rhs) {
3544 rhs.insert(rhs.begin(), lhs);
3545 return std::move(rhs);
3548 template <
class CharT,
class Traits,
class Allocator>
3549 inline constexpr basic_string<CharT, Traits, Allocator>
3550 operator+(basic_string<CharT, Traits, Allocator>&& lhs,
const CharT* rhs) {
3551 return std::move(lhs.append(rhs));
3554 template <
class CharT,
class Traits,
class Allocator>
3555 inline constexpr basic_string<CharT, Traits, Allocator>
3556 operator+(basic_string<CharT, Traits, Allocator>&& lhs, CharT rhs) {
3558 return std::move(lhs);
3561#if PLUGIFY_HAS_CXX26
3563 template <
class CharT,
class Traits,
class Allocator>
3564 constexpr basic_string<CharT, Traits, Allocator> operator+(
3565 basic_string<CharT, Traits, Allocator>&& lhs,
3566 std::type_identity_t<std::basic_string_view<CharT, Traits>> rhs
3568 return std::move(lhs.append(rhs));
3571 template <
class CharT,
class Traits,
class Allocator>
3572 constexpr basic_string<CharT, Traits, Allocator> operator+(
3573 std::type_identity_t<std::basic_string_view<CharT, Traits>> lhs,
3574 basic_string<CharT, Traits, Allocator>&& rhs
3576 return std::move(rhs.insert(0, lhs));
3583 template <
class CharT,
class Traits,
class Allocator>
3584 inline constexpr void swap(
3585 basic_string<CharT, Traits, Allocator>& lhs,
3586 basic_string<CharT, Traits, Allocator>& rhs
3587 )
noexcept(
noexcept(lhs.swap(rhs))) {
3591 template <
class CharT,
class Traits,
class Allocator,
class Up>
3592 inline constexpr typename basic_string<CharT, Traits, Allocator>::size_type
3593 erase(basic_string<CharT, Traits, Allocator>& str,
const Up& v) {
3594 auto old_size = str.size();
3595 str.erase(std::remove(str.begin(), str.end(), v), str.end());
3596 return old_size - str.size();
3599 template <
class CharT,
class Traits,
class Allocator,
class Predicate>
3600 inline constexpr typename basic_string<CharT, Traits, Allocator>::size_type
3601 erase_if(basic_string<CharT, Traits, Allocator>& str, Predicate pred) {
3602 auto old_size = str.size();
3603 str.erase(std::remove_if(str.begin(), str.end(), pred), str.end());
3604 return old_size - str.size();
3608 using string = basic_string<char>;
3609 using u8string = basic_string<char8_t>;
3610 using u16string = basic_string<char16_t>;
3611 using u32string = basic_string<char32_t>;
3612 using wstring = basic_string<wchar_t>;
3614#ifndef PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
3616 inline int stoi(
const string& str, std::size_t* pos =
nullptr,
int base = 10) {
3617 auto cstr = str.c_str();
3618 char* ptr =
const_cast<char*
>(cstr);
3620 auto ret = strtol(cstr, &ptr, base);
3621 if (pos !=
nullptr) {
3622 *pos =
static_cast<size_t>(cstr - ptr);
3625 return static_cast<int>(ret);
3628 inline long stol(
const string& str, std::size_t* pos =
nullptr,
int base = 10) {
3629 auto cstr = str.c_str();
3630 char* ptr =
const_cast<char*
>(cstr);
3632 auto ret = strtol(cstr, &ptr, base);
3633 if (pos !=
nullptr) {
3634 *pos =
static_cast<size_t>(cstr - ptr);
3640 inline long long stoll(
const string& str, std::size_t* pos =
nullptr,
int base = 10) {
3641 auto cstr = str.c_str();
3642 char* ptr =
const_cast<char*
>(cstr);
3644 auto ret = strtoll(cstr, &ptr, base);
3645 if (pos !=
nullptr) {
3646 *pos =
static_cast<size_t>(cstr - ptr);
3652 inline unsigned long stoul(
const string& str, std::size_t* pos =
nullptr,
int base = 10) {
3653 auto cstr = str.c_str();
3654 char* ptr =
const_cast<char*
>(cstr);
3656 auto ret = strtoul(cstr, &ptr, base);
3657 if (pos !=
nullptr) {
3658 *pos =
static_cast<size_t>(cstr - ptr);
3664 inline unsigned long long stoull(
const string& str, std::size_t* pos =
nullptr,
int base = 10) {
3665 auto cstr = str.c_str();
3666 char* ptr =
const_cast<char*
>(cstr);
3668 auto ret = strtoull(cstr, &ptr, base);
3669 if (pos !=
nullptr) {
3670 *pos =
static_cast<size_t>(cstr - ptr);
3676 inline float stof(
const string& str, std::size_t* pos =
nullptr) {
3677 auto cstr = str.c_str();
3678 char* ptr =
const_cast<char*
>(cstr);
3680 auto ret = strtof(cstr, &ptr);
3681 if (pos !=
nullptr) {
3682 *pos =
static_cast<size_t>(cstr - ptr);
3688 inline double stod(
const string& str, std::size_t* pos =
nullptr) {
3689 auto cstr = str.c_str();
3690 char* ptr =
const_cast<char*
>(cstr);
3692 auto ret = strtod(cstr, &ptr);
3693 if (pos !=
nullptr) {
3694 *pos =
static_cast<size_t>(cstr - ptr);
3700 inline long double stold(
const string& str, std::size_t* pos =
nullptr) {
3701 auto cstr = str.c_str();
3702 char* ptr =
const_cast<char*
>(cstr);
3704 auto ret = strtold(cstr, &ptr);
3705 if (pos !=
nullptr) {
3706 *pos =
static_cast<size_t>(cstr - ptr);
3713 template <
typename S,
typename V>
3714 constexpr S to_string(V v) {
3718 constexpr std::size_t bufSize = std::numeric_limits<V>::digits10 + 2;
3722 const auto res = std::to_chars(buf, buf + bufSize, v);
3723 return S(buf, res.ptr);
3726 typedef int (*wide_printf)(
wchar_t* __restrict, std::size_t,
const wchar_t* __restrict, ...);
3728#if PLUGIFY_COMPILER_MSVC
3729 inline int truncate_snwprintf(
3730 wchar_t* __restrict buffer,
3732 const wchar_t* __restrict format,
3737 va_start(args, format);
3738 r = _vsnwprintf_s(buffer, count, _TRUNCATE, format, args);
3744 constexpr wide_printf get_swprintf() noexcept {
3745#if PLUGIFY_COMPILER_MSVC
3747 int(__cdecl*)(
wchar_t* __restrict, std::size_t,
const wchar_t* __restrict, ...)
>(
3755 template <
typename S,
typename P,
typename V>
3756 constexpr S as_string(P sprintf_like,
const typename S::value_type* fmt, V v) {
3757 typedef typename S::size_type size_type;
3759 s.resize(s.capacity());
3760 size_type available = s.size();
3762 int status = sprintf_like(&s[0], available + 1, fmt, v);
3764 auto used =
static_cast<size_type
>(status);
3765 if (used <= available) {
3771 available = available * 2 + 1;
3773 s.resize(available);
3779 inline string to_string(
int val) {
return detail::to_string<string>(val); }
3780 inline string to_string(
unsigned val) {
return detail::to_string<string>(val); }
3781 inline string to_string(
long val) {
return detail::to_string<string>(val); }
3782 inline string to_string(
unsigned long val) {
return detail::to_string<string>(val); }
3783 inline string to_string(
long long val) {
return detail::to_string<string>(val); }
3784 inline string to_string(
unsigned long long val) {
return detail::to_string<string>(val); }
3785 inline string to_string(
float val) {
return detail::as_string<string>(snprintf,
"%f", val); }
3786 inline string to_string(
double val) {
return detail::as_string<string>(snprintf,
"%f", val); }
3787 inline string to_string(
long double val) {
return detail::as_string<string>(snprintf,
"%Lf", val); }
3789 inline wstring to_wstring(
int val) {
return detail::to_string<wstring>(val); }
3790 inline wstring to_wstring(
unsigned val) {
return detail::to_string<wstring>(val); }
3791 inline wstring to_wstring(
long val) {
return detail::to_string<wstring>(val); }
3792 inline wstring to_wstring(
unsigned long val) {
return detail::to_string<wstring>(val); }
3793 inline wstring to_wstring(
long long val) {
return detail::to_string<wstring>(val); }
3794 inline wstring to_wstring(
unsigned long long val) {
return detail::to_string<wstring>(val); }
3795 inline wstring to_wstring(
float val) {
return detail::as_string<wstring>(detail::get_swprintf(), L
"%f", val); }
3796 inline wstring to_wstring(
double val) {
return detail::as_string<wstring>(detail::get_swprintf(), L
"%f", val); }
3797 inline wstring to_wstring(
long double val) {
return detail::as_string<wstring>(detail::get_swprintf(), L
"%Lf", val); }
3800#ifndef PLUGIFY_STRING_NO_STD_HASH
3806 typename String = basic_string<Char, std::char_traits<Char>, Allocator>
3808 struct string_hash_base {
3809 constexpr std::size_t operator()(
const String& str)
const noexcept {
3810 return std::hash<typename String::self_view>{}(
typename String::self_view(str));
3816#ifndef PLUGIFY_STRING_NO_STD_FORMAT
3819 template <
typename Char>
3820 static constexpr const Char* format_string() {
3821 if constexpr (std::is_same_v<Char, char> || std::is_same_v<Char, char8_t>) {
3824 if constexpr (std::is_same_v<Char, wchar_t>) {
3827 if constexpr (std::is_same_v<Char, char16_t>) {
3830 if constexpr (std::is_same_v<Char, char32_t>) {
3839 typename String = basic_string<Char, std::char_traits<Char>, Allocator>
3842 constexpr auto parse(std::format_parse_context&
ctx) {
3846 template <
class FormatContext>
3854 inline namespace literals {
3855 inline namespace string_literals {
3858#if PLUGIFY_COMPILER_CLANG
3860#elif PLUGIFY_COMPILER_GCC
3862#elif PLUGIFY_COMPILER_MSVC
3867 constexpr string operator""s(
const char* str, std::size_t
len) {
return string{str,
len}; }
3868 constexpr u8string
operator""s(
const char8_t* str, std::size_t len) {
return u8string{str, len}; }
3869 constexpr u16string
operator""s(
const char16_t* str, std::size_t len) {
return u16string{str, len}; }
3870 constexpr u32string
operator""s(
const char32_t* str, std::size_t len) {
return u32string{str, len}; }
3871 constexpr wstring
operator""s(
const wchar_t* str, std::size_t len) {
return wstring{str, len}; }
3878#ifndef PLUGIFY_STRING_NO_STD_HASH
3881 template <
typename Allocator>
3882 struct hash<plg::basic_string<char, std::char_traits<char>, Allocator>>
3883 : plg::detail::string_hash_base<char, Allocator> {};
3885 template <
typename Allocator>
3886 struct hash<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>>
3887 : plg::detail::string_hash_base<char8_t, Allocator> {};
3889 template <
typename Allocator>
3890 struct hash<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>>
3891 : plg::detail::string_hash_base<char16_t, Allocator> {};
3893 template <
typename Allocator>
3894 struct hash<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>>
3895 : plg::detail::string_hash_base<char32_t, Allocator> {};
3897 template <
typename Allocator>
3898 struct hash<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>>
3899 : plg::detail::string_hash_base<wchar_t, Allocator> {};
3903#ifndef PLUGIFY_STRING_NO_STD_FORMAT
3905#ifdef FMT_HEADER_ONLY
3910 template <
typename Allocator>
3911 struct formatter<plg::basic_string<char, std::char_traits<char>, Allocator>>
3914 template <
typename Allocator>
3915 struct formatter<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>>
3918 template <
typename Allocator>
3919 struct formatter<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>>
3922 template <
typename Allocator>
3923 struct formatter<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>>
3926 template <
typename Allocator>
3927 struct formatter<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>>
3932template <
typename Char,
typename Traits,
typename Alloc>
3938#ifndef PLUGIFY_STRING_NO_STD_FORMAT
3939#include <functional>
3942 template <
typename Range>
3943 constexpr string join(
const Range& range, std::string_view separator) {
3946 auto it = range.cbegin();
3947 auto end = range.cend();
3954 size_t total_size = 0;
3957 for (
auto tmp = it; tmp != end; ++tmp) {
3958 using elem = std::decay_t<
decltype(*tmp)>;
3959 if constexpr (string_like<elem>) {
3960 total_size += std::string_view(*tmp).size();
3962 total_size += std::formatted_size(
"{}", *tmp);
3967 total_size += (count - 1) * separator.size();
3969 result.reserve(total_size);
3971 auto in = std::back_inserter(result);
3974 { std::format_to(in,
"{}", *it++); }
3976 std::format_to(in,
"{}{}", separator, *it++);
3982 template <
typename Range,
typename Proj>
3983 constexpr string join(
const Range& range, Proj&& proj, std::string_view separator) {
3986 auto it = range.cbegin();
3987 auto end = range.cend();
3994 size_t total_size = 0;
3997 for (
auto tmp = it; tmp != end; ++tmp) {
3998 auto&& projected = std::invoke(std::forward<Proj>(proj), *tmp);
3999 using elem = std::decay_t<
decltype(projected)>;
4000 if constexpr (string_like<elem>) {
4001 total_size += std::string_view(*projected).size();
4003 total_size += std::formatted_size(
"{}", projected);
4008 total_size += (count - 1) * separator.size();
4010 result.reserve(total_size);
4012 auto out = std::back_inserter(result);
4016 auto&& projected = std::invoke(std::forward<Proj>(proj), *it++);
4017 std::format_to(out,
"{}", projected);
4020 auto&& projected = std::invoke(std::forward<Proj>(proj), *it++);
4021 std::format_to(out,
"{}{}", separator, projected);