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