plugify 1.2.8
Loading...
Searching...
No Matches
string.hpp
1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#pragma once
11
12// clang-format off
13
14/*
15 string synopsis
16
17#include <compare>
18#include <std::initializer_list>
19
20namespace std
21{
22
23template <class stateT>
24class fpos
25{
26private:
27 stateT st;
28public:
29 fpos(streamoff = streamoff());
30
31 operator streamoff() const;
32
33 stateT state() const;
34 void state(stateT);
35
36 fpos& operator+=(streamoff);
37 fpos operator+ (streamoff) const;
38 fpos& operator-=(streamoff);
39 fpos operator- (streamoff) const;
40};
41
42template <class stateT> streamoff operator-(const fpos<stateT>& x, const fpos<stateT>& y);
43
44template <class stateT> bool operator==(const fpos<stateT>& x, const fpos<stateT>& y);
45template <class stateT> bool operator!=(const fpos<stateT>& x, const fpos<stateT>& y);
46
47template <class charT>
48struct char_traits
49{
50 using char_type = charT;
51 using int_type = ...;
52 using off_type = streamoff;
53 using pos_type = streampos;
54 using state_type = mbstate_t;
55 using comparison_category = strong_ordering; // Since C++20 only for the specializations
56 // char, wchar_t, char8_t, char16_t, and char32_t.
57
58 static void assign(char_type& c1, const char_type& c2) noexcept;
59 static constexpr bool eq(char_type c1, char_type c2) noexcept;
60 static constexpr bool lt(char_type c1, char_type c2) noexcept;
61
62 static int compare(const char_type* s1, const char_type* s2, size_t n);
63 static size_t length(const char_type* s);
64 static const char_type* find(const char_type* s, size_t n, const char_type& a);
65 static char_type* move(char_type* s1, const char_type* s2, size_t n);
66 static char_type* copy(char_type* s1, const char_type* s2, size_t n);
67 static char_type* assign(char_type* s, size_t n, char_type a);
68
69 static constexpr int_type not_eof(int_type c) noexcept;
70 static constexpr char_type to_char_type(int_type c) noexcept;
71 static constexpr int_type to_int_type(char_type c) noexcept;
72 static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept;
73 static constexpr int_type eof() noexcept;
74};
75
76template <> struct char_traits<char>;
77template <> struct char_traits<wchar_t>;
78template <> struct char_traits<char8_t>; // C++20
79template <> struct char_traits<char16_t>;
80template <> struct char_traits<char32_t>;
81
82template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
83class basic_string
84{
85public:
86// types:
87 typedef traits traits_type;
88 typedef typename traits_type::char_type value_type;
89 typedef Allocator allocator_type;
90 typedef typename allocator_type::size_type size_type;
91 typedef typename allocator_type::difference_type difference_type;
92 typedef typename allocator_type::reference reference;
93 typedef typename allocator_type::const_reference const_reference;
94 typedef typename allocator_type::pointer pointer;
95 typedef typename allocator_type::const_pointer const_pointer;
96 typedef implementation-defined iterator;
97 typedef implementation-defined const_iterator;
98 typedef std::reverse_iterator<iterator> reverse_iterator;
99 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
100
101 static const size_type npos = -1;
102
103 basic_string()
104 noexcept(std::is_nothrow_default_constructible<allocator_type>::value); // constexpr since C++20
105 explicit basic_string(const allocator_type& a); // constexpr since C++20
106 basic_string(const basic_string& str); // constexpr since C++20
107 basic_string(basic_string&& str)
108 noexcept(std::is_nothrow_move_constructible<allocator_type>::value); // constexpr since C++20
109 basic_string(const basic_string& str, size_type pos,
110 const allocator_type& a = allocator_type()); // constexpr since C++20
111 basic_string(const basic_string& str, size_type pos, size_type n,
112 const Allocator& a = Allocator()); // constexpr since C++20
113 constexpr basic_string(
114 basic_string&& str, size_type pos, const Allocator& a = Allocator()); // since C++23
115 constexpr basic_string(
116 basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator()); // since C++23
117 template<class T>
118 basic_string(const T& t, size_type pos, size_type n, const Allocator& a = Allocator()); // C++17, constexpr since C++20
119 template <class T>
120 explicit basic_string(const T& t, const Allocator& a = Allocator()); // C++17, constexpr since C++20
121 basic_string(const value_type* s, const allocator_type& a = allocator_type()); // constexpr since C++20
122 basic_string(const value_type* s, size_type n, const allocator_type& a = allocator_type()); // constexpr since C++20
123 basic_string(nullptr_t) = delete; // C++23
124 basic_string(size_type n, value_type c, const allocator_type& a = allocator_type()); // constexpr since C++20
125 template<class InputIterator>
126 basic_string(InputIterator begin, InputIterator end,
127 const allocator_type& a = allocator_type()); // constexpr since C++20
128 template<container-compatible-range<charT> R>
129 constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // since C++23
130 basic_string(std::initializer_list<value_type>, const Allocator& = Allocator()); // constexpr since C++20
131 basic_string(const basic_string&, const Allocator&); // constexpr since C++20
132 basic_string(basic_string&&, const Allocator&); // constexpr since C++20
133
134 ~basic_string(); // constexpr since C++20
135
136 operator std::basic_string_view<charT, traits>() const noexcept; // constexpr since C++20
137
138 basic_string& operator=(const basic_string& str); // constexpr since C++20
139 template <class T>
140 basic_string& operator=(const T& t); // C++17, constexpr since C++20
141 basic_string& operator=(basic_string&& str)
142 noexcept(
143 allocator_type::propagate_on_container_move_assignment::value ||
144 allocator_type::is_always_equal::value ); // C++17, constexpr since C++20
145 basic_string& operator=(const value_type* s); // constexpr since C++20
146 basic_string& operator=(nullptr_t) = delete; // C++23
147 basic_string& operator=(value_type c); // constexpr since C++20
148 basic_string& operator=(std::initializer_list<value_type>); // constexpr since C++20
149
150 iterator begin() noexcept; // constexpr since C++20
151 const_iterator begin() const noexcept; // constexpr since C++20
152 iterator end() noexcept; // constexpr since C++20
153 const_iterator end() const noexcept; // constexpr since C++20
154
155 reverse_iterator rbegin() noexcept; // constexpr since C++20
156 const_reverse_iterator rbegin() const noexcept; // constexpr since C++20
157 reverse_iterator rend() noexcept; // constexpr since C++20
158 const_reverse_iterator rend() const noexcept; // constexpr since C++20
159
160 const_iterator cbegin() const noexcept; // constexpr since C++20
161 const_iterator cend() const noexcept; // constexpr since C++20
162 const_reverse_iterator crbegin() const noexcept; // constexpr since C++20
163 const_reverse_iterator crend() const noexcept; // constexpr since C++20
164
165 size_type size() const noexcept; // constexpr since C++20
166 size_type length() const noexcept; // constexpr since C++20
167 size_type max_size() const noexcept; // constexpr since C++20
168 size_type capacity() const noexcept; // constexpr since C++20
169
170 void resize(size_type n, value_type c); // constexpr since C++20
171 void resize(size_type n); // constexpr since C++20
172
173 template<class Operation>
174 constexpr void resize_and_overwrite(size_type n, Operation op); // since C++23
175
176 void reserve(size_type res_arg); // constexpr since C++20
177 void reserve(); // deprecated in C++20, removed in C++26
178 void shrink_to_fit(); // constexpr since C++20
179 void clear() noexcept; // constexpr since C++20
180 bool empty() const noexcept; // constexpr since C++20
181
182 const_reference operator[](size_type pos) const; // constexpr since C++20
183 reference operator[](size_type pos); // constexpr since C++20
184
185 const_reference at(size_type n) const; // constexpr since C++20
186 reference at(size_type n); // constexpr since C++20
187
188 basic_string& operator+=(const basic_string& str); // constexpr since C++20
189 template <class T>
190 basic_string& operator+=(const T& t); // C++17, constexpr since C++20
191 basic_string& operator+=(const value_type* s); // constexpr since C++20
192 basic_string& operator+=(value_type c); // constexpr since C++20
193 basic_string& operator+=(std::initializer_list<value_type>); // constexpr since C++20
194
195 basic_string& append(const basic_string& str); // constexpr since C++20
196 template <class T>
197 basic_string& append(const T& t); // C++17, constexpr since C++20
198 basic_string& append(const basic_string& str, size_type pos, size_type n=npos); // C++14, constexpr since C++20
199 template <class T>
200 basic_string& append(const T& t, size_type pos, size_type n=npos); // C++17, constexpr since C++20
201 basic_string& append(const value_type* s, size_type n); // constexpr since C++20
202 basic_string& append(const value_type* s); // constexpr since C++20
203 basic_string& append(size_type n, value_type c); // constexpr since C++20
204 template<class InputIterator>
205 basic_string& append(InputIterator first, InputIterator last); // constexpr since C++20
206 template<container-compatible-range<charT> R>
207 constexpr basic_string& append_range(R&& rg); // C++23
208 basic_string& append(std::initializer_list<value_type>); // constexpr since C++20
209
210 void push_back(value_type c); // constexpr since C++20
211 void pop_back(); // constexpr since C++20
212 reference front(); // constexpr since C++20
213 const_reference front() const; // constexpr since C++20
214 reference back(); // constexpr since C++20
215 const_reference back() const; // constexpr since C++20
216
217 basic_string& assign(const basic_string& str); // constexpr since C++20
218 template <class T>
219 basic_string& assign(const T& t); // C++17, constexpr since C++20
220 basic_string& assign(basic_string&& str); // constexpr since C++20
221 basic_string& assign(const basic_string& str, size_type pos, size_type n=npos); // C++14, constexpr since C++20
222 template <class T>
223 basic_string& assign(const T& t, size_type pos, size_type n=npos); // C++17, constexpr since C++20
224 basic_string& assign(const value_type* s, size_type n); // constexpr since C++20
225 basic_string& assign(const value_type* s); // constexpr since C++20
226 basic_string& assign(size_type n, value_type c); // constexpr since C++20
227 template<class InputIterator>
228 basic_string& assign(InputIterator first, InputIterator last); // constexpr since C++20
229 template<container-compatible-range<charT> R>
230 constexpr basic_string& assign_range(R&& rg); // C++23
231 basic_string& assign(std::initializer_list<value_type>); // constexpr since C++20
232
233 basic_string& insert(size_type pos1, const basic_string& str); // constexpr since C++20
234 template <class T>
235 basic_string& insert(size_type pos1, const T& t); // constexpr since C++20
236 basic_string& insert(size_type pos1, const basic_string& str,
237 size_type pos2, size_type n2=npos); // constexpr since C++20
238 template <class T>
239 basic_string& insert(size_type pos1, const T& t, size_type pos2, size_type n=npos); // C++17, constexpr since C++20
240 basic_string& insert(size_type pos, const value_type* s, size_type n=npos); // C++14, constexpr since C++20
241 basic_string& insert(size_type pos, const value_type* s); // constexpr since C++20
242 basic_string& insert(size_type pos, size_type n, value_type c); // constexpr since C++20
243 iterator insert(const_iterator p, value_type c); // constexpr since C++20
244 iterator insert(const_iterator p, size_type n, value_type c); // constexpr since C++20
245 template<class InputIterator>
246 iterator insert(const_iterator p, InputIterator first, InputIterator last); // constexpr since C++20
247 template<container-compatible-range<charT> R>
248 constexpr iterator insert_range(const_iterator p, R&& rg); // C++23
249 iterator insert(const_iterator p, std::initializer_list<value_type>); // constexpr since C++20
250
251 basic_string& erase(size_type pos = 0, size_type n = npos); // constexpr since C++20
252 iterator erase(const_iterator position); // constexpr since C++20
253 iterator erase(const_iterator first, const_iterator last); // constexpr since C++20
254
255 basic_string& replace(size_type pos1, size_type n1, const basic_string& str); // constexpr since C++20
256 template <class T>
257 basic_string& replace(size_type pos1, size_type n1, const T& t); // C++17, constexpr since C++20
258 basic_string& replace(size_type pos1, size_type n1, const basic_string& str,
259 size_type pos2, size_type n2=npos); // C++14, constexpr since C++20
260 template <class T>
261 basic_string& replace(size_type pos1, size_type n1, const T& t,
262 size_type pos2, size_type n2=npos); // C++17, constexpr since C++20
263 basic_string& replace(size_type pos, size_type n1, const value_type* s, size_type n2); // constexpr since C++20
264 basic_string& replace(size_type pos, size_type n1, const value_type* s); // constexpr since C++20
265 basic_string& replace(size_type pos, size_type n1, size_type n2, value_type c); // constexpr since C++20
266 basic_string& replace(const_iterator i1, const_iterator i2, const basic_string& str); // constexpr since C++20
267 template <class T>
268 basic_string& replace(const_iterator i1, const_iterator i2, const T& t); // C++17, constexpr since C++20
269 basic_string& replace(const_iterator i1, const_iterator i2, const value_type* s, size_type n); // constexpr since C++20
270 basic_string& replace(const_iterator i1, const_iterator i2, const value_type* s); // constexpr since C++20
271 basic_string& replace(const_iterator i1, const_iterator i2, size_type n, value_type c); // constexpr since C++20
272 template<class InputIterator>
273 basic_string& replace(const_iterator i1, const_iterator i2, InputIterator j1, InputIterator j2); // constexpr since C++20
274 template<container-compatible-range<charT> R>
275 constexpr basic_string& replace_with_range(const_iterator i1, const_iterator i2, R&& rg); // C++23
276 basic_string& replace(const_iterator i1, const_iterator i2, std::initializer_list<value_type>); // constexpr since C++20
277
278 size_type copy(value_type* s, size_type n, size_type pos = 0) const; // constexpr since C++20
279 basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr in C++20, removed in C++23
280 basic_string substr(size_type pos = 0, size_type n = npos) const&; // since C++23
281 constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&; // since C++23
282 void swap(basic_string& str)
283 noexcept(allocator_traits<allocator_type>::propagate_on_container_swap::value ||
284 allocator_traits<allocator_type>::is_always_equal::value); // C++17, constexpr since C++20
285
286 const value_type* c_str() const noexcept; // constexpr since C++20
287 const value_type* data() const noexcept; // constexpr since C++20
288 value_type* data() noexcept; // C++17, constexpr since C++20
289
290 allocator_type get_allocator() const noexcept; // constexpr since C++20
291
292 size_type find(const basic_string& str, size_type pos = 0) const noexcept; // constexpr since C++20
293 template <class T>
294 size_type find(const T& t, size_type pos = 0) const noexcept; // C++17, noexcept as an extension, constexpr since C++20
295 size_type find(const value_type* s, size_type pos, size_type n) const noexcept; // constexpr since C++20
296 size_type find(const value_type* s, size_type pos = 0) const noexcept; // constexpr since C++20
297 size_type find(value_type c, size_type pos = 0) const noexcept; // constexpr since C++20
298
299 size_type rfind(const basic_string& str, size_type pos = npos) const noexcept; // constexpr since C++20
300 template <class T>
301 size_type rfind(const T& t, size_type pos = npos) const noexcept; // C++17, noexcept as an extension, constexpr since C++20
302 size_type rfind(const value_type* s, size_type pos, size_type n) const noexcept; // constexpr since C++20
303 size_type rfind(const value_type* s, size_type pos = npos) const noexcept; // constexpr since C++20
304 size_type rfind(value_type c, size_type pos = npos) const noexcept; // constexpr since C++20
305
306 size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept; // constexpr since C++20
307 template <class T>
308 size_type find_first_of(const T& t, size_type pos = 0) const noexcept; // C++17, noexcept as an extension, constexpr since C++20
309 size_type find_first_of(const value_type* s, size_type pos, size_type n) const noexcept; // constexpr since C++20
310 size_type find_first_of(const value_type* s, size_type pos = 0) const noexcept; // constexpr since C++20
311 size_type find_first_of(value_type c, size_type pos = 0) const noexcept; // constexpr since C++20
312
313 size_type find_last_of(const basic_string& str, size_type pos = npos) const noexcept; // constexpr since C++20
314 template <class T>
315 size_type find_last_of(const T& t, size_type pos = npos) const noexcept noexcept; // C++17, noexcept as an extension, constexpr since C++20
316 size_type find_last_of(const value_type* s, size_type pos, size_type n) const noexcept; // constexpr since C++20
317 size_type find_last_of(const value_type* s, size_type pos = npos) const noexcept; // constexpr since C++20
318 size_type find_last_of(value_type c, size_type pos = npos) const noexcept; // constexpr since C++20
319
320 size_type find_first_not_of(const basic_string& str, size_type pos = 0) const noexcept; // constexpr since C++20
321 template <class T>
322 size_type find_first_not_of(const T& t, size_type pos = 0) const noexcept; // C++17, noexcept as an extension, constexpr since C++20
323 size_type find_first_not_of(const value_type* s, size_type pos, size_type n) const noexcept; // constexpr since C++20
324 size_type find_first_not_of(const value_type* s, size_type pos = 0) const noexcept; // constexpr since C++20
325 size_type find_first_not_of(value_type c, size_type pos = 0) const noexcept; // constexpr since C++20
326
327 size_type find_last_not_of(const basic_string& str, size_type pos = npos) const noexcept; // constexpr since C++20
328 template <class T>
329 size_type find_last_not_of(const T& t, size_type pos = npos) const noexcept; // C++17, noexcept as an extension, constexpr since C++20
330 size_type find_last_not_of(const value_type* s, size_type pos, size_type n) const noexcept; // constexpr since C++20
331 size_type find_last_not_of(const value_type* s, size_type pos = npos) const noexcept; // constexpr since C++20
332 size_type find_last_not_of(value_type c, size_type pos = npos) const noexcept; // constexpr since C++20
333
334 int compare(const basic_string& str) const noexcept; // constexpr since C++20
335 template <class T>
336 int compare(const T& t) const noexcept; // C++17, noexcept as an extension, constexpr since C++20
337 int compare(size_type pos1, size_type n1, const basic_string& str) const; // constexpr since C++20
338 template <class T>
339 int compare(size_type pos1, size_type n1, const T& t) const; // C++17, constexpr since C++20
340 int compare(size_type pos1, size_type n1, const basic_string& str,
341 size_type pos2, size_type n2=npos) const; // C++14, constexpr since C++20
342 template <class T>
343 int compare(size_type pos1, size_type n1, const T& t,
344 size_type pos2, size_type n2=npos) const; // C++17, constexpr since C++20
345 int compare(const value_type* s) const noexcept; // constexpr since C++20
346 int compare(size_type pos1, size_type n1, const value_type* s) const; // constexpr since C++20
347 int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const; // constexpr since C++20
348
349 constexpr bool starts_with(std::basic_string_view<charT, traits> sv) const noexcept; // C++20
350 constexpr bool starts_with(charT c) const noexcept; // C++20
351 constexpr bool starts_with(const charT* s) const; // C++20
352 constexpr bool ends_with(std::basic_string_view<charT, traits> sv) const noexcept; // C++20
353 constexpr bool ends_with(charT c) const noexcept; // C++20
354 constexpr bool ends_with(const charT* s) const; // C++20
355
356 constexpr bool contains(std::basic_string_view<charT, traits> sv) const noexcept; // C++23
357 constexpr bool contains(charT c) const noexcept; // C++23
358 constexpr bool contains(const charT* s) const; // C++23
359};
360
361template<class InputIterator,
362 class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>>
363basic_string(InputIterator, InputIterator, Allocator = Allocator())
364 -> basic_string<typename iterator_traits<InputIterator>::value_type,
365 char_traits<typename iterator_traits<InputIterator>::value_type>,
366 Allocator>; // C++17
367
368template<ranges::input_range R,
369 class Allocator = allocator<ranges::range_value_t<R>>>
370 basic_string(from_range_t, R&&, Allocator = Allocator())
371 -> basic_string<ranges::range_value_t<R>, char_traits<ranges::range_value_t<R>>,
372 Allocator>; // C++23
373
374template<class charT,
375 class traits,
376 class Allocator = allocator<charT>>
377 explicit basic_string(std::basic_string_view<charT, traits>, const Allocator& = Allocator())
378 -> basic_string<charT, traits, Allocator>; // C++17
379
380template<class charT,
381 class traits,
382 class Allocator = allocator<charT>>
383 basic_string(std::basic_string_view<charT, traits>,
384 typename see below::size_type, typename see below::size_type,
385 const Allocator& = Allocator())
386 -> basic_string<charT, traits, Allocator>; // C++17
387
388template<class charT, class traits, class Allocator>
389basic_string<charT, traits, Allocator>
390operator+(const basic_string<charT, traits, Allocator>& lhs,
391 const basic_string<charT, traits, Allocator>& rhs); // constexpr since C++20
392
393template<class charT, class traits, class Allocator>
394basic_string<charT, traits, Allocator>
395operator+(const charT* lhs , const basic_string<charT,traits,Allocator>&rhs); // constexpr since C++20
396
397template<class charT, class traits, class Allocator>
398basic_string<charT, traits, Allocator>
399operator+(charT lhs, const basic_string<charT,traits,Allocator>& rhs); // constexpr since C++20
400
401template<class charT, class traits, class Allocator>
402basic_string<charT, traits, Allocator>
403operator+(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs); // constexpr since C++20
404
405template<class charT, class traits, class Allocator>
406basic_string<charT, traits, Allocator>
407operator+(const basic_string<charT, traits, Allocator>& lhs, charT rhs); // constexpr since C++20
408
409template<class charT, class traits, class Allocator>
410 constexpr basic_string<charT, traits, Allocator>
411 operator+(const basic_string<charT, traits, Allocator>& lhs,
412 type_identity_t<std::basic_string_view<charT, traits>> rhs); // Since C++26
413template<class charT, class traits, class Allocator>
414 constexpr basic_string<charT, traits, Allocator>
415 operator+(basic_string<charT, traits, Allocator>&& lhs,
416 type_identity_t<std::basic_string_view<charT, traits>> rhs); // Since C++26
417template<class charT, class traits, class Allocator>
418 constexpr basic_string<charT, traits, Allocator>
419 operator+(type_identity_t<std::basic_string_view<charT, traits>> lhs,
420 const basic_string<charT, traits, Allocator>& rhs); // Since C++26
421template<class charT, class traits, class Allocator>
422 constexpr basic_string<charT, traits, Allocator>
423 operator+(type_identity_t<std::basic_string_view<charT, traits>> lhs,
424 basic_string<charT, traits, Allocator>&& rhs); // Since C++26
425
426
427template<class charT, class traits, class Allocator>
428bool operator==(const basic_string<charT, traits, Allocator>& lhs,
429 const basic_string<charT, traits, Allocator>& rhs) noexcept; // constexpr since C++20
430
431template<class charT, class traits, class Allocator>
432bool operator==(const charT* lhs, const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
433
434template<class charT, class traits, class Allocator>
435bool operator==(const basic_string<charT,traits,Allocator>& lhs, const charT* rhs) noexcept; // constexpr since C++20
436
437template<class charT, class traits, class Allocator>
438bool operator!=(const basic_string<charT,traits,Allocator>& lhs,
439 const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
440
441template<class charT, class traits, class Allocator>
442bool operator!=(const charT* lhs, const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
443
444template<class charT, class traits, class Allocator>
445bool operator!=(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs) noexcept; // removed in C++20
446
447template<class charT, class traits, class Allocator>
448bool operator< (const basic_string<charT, traits, Allocator>& lhs,
449 const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
450
451template<class charT, class traits, class Allocator>
452bool operator< (const basic_string<charT, traits, Allocator>& lhs, const charT* rhs) noexcept; // removed in C++20
453
454template<class charT, class traits, class Allocator>
455bool operator< (const charT* lhs, const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
456
457template<class charT, class traits, class Allocator>
458bool operator> (const basic_string<charT, traits, Allocator>& lhs,
459 const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
460
461template<class charT, class traits, class Allocator>
462bool operator> (const basic_string<charT, traits, Allocator>& lhs, const charT* rhs) noexcept; // removed in C++20
463
464template<class charT, class traits, class Allocator>
465bool operator> (const charT* lhs, const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
466
467template<class charT, class traits, class Allocator>
468bool operator<=(const basic_string<charT, traits, Allocator>& lhs,
469 const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
470
471template<class charT, class traits, class Allocator>
472bool operator<=(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs) noexcept; // removed in C++20
473
474template<class charT, class traits, class Allocator>
475bool operator<=(const charT* lhs, const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
476
477template<class charT, class traits, class Allocator>
478bool operator>=(const basic_string<charT, traits, Allocator>& lhs,
479 const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
480
481template<class charT, class traits, class Allocator>
482bool operator>=(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs) noexcept; // removed in C++20
483
484template<class charT, class traits, class Allocator>
485bool operator>=(const charT* lhs, const basic_string<charT, traits, Allocator>& rhs) noexcept; // removed in C++20
486
487template<class charT, class traits, class Allocator> // since C++20
488constexpr see below operator<=>(const basic_string<charT, traits, Allocator>& lhs,
489 const basic_string<charT, traits, Allocator>& rhs) noexcept;
490
491template<class charT, class traits, class Allocator> // since C++20
492constexpr see below operator<=>(const basic_string<charT, traits, Allocator>& lhs,
493 const charT* rhs) noexcept;
494
495template<class charT, class traits, class Allocator>
496void swap(basic_string<charT, traits, Allocator>& lhs,
497 basic_string<charT, traits, Allocator>& rhs)
498 noexcept(noexcept(lhs.swap(rhs))); // constexpr since C++20
499
500template<class charT, class traits, class Allocator>
501std::basic_istream<charT, traits>&
502operator>>(std::basic_istream<charT, traits>& is, basic_string<charT, traits, Allocator>& str);
503
504template<class charT, class traits, class Allocator>
505std::basic_ostream<charT, traits>&
506operator<<(std::basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
507
508template<class charT, class traits, class Allocator>
509std::basic_istream<charT, traits>&
510getline(std::basic_istream<charT, traits>& is, basic_string<charT, traits, Allocator>& str,
511 charT delim);
512
513template<class charT, class traits, class Allocator>
514std::basic_istream<charT, traits>&
515getline(std::basic_istream<charT, traits>& is, basic_string<charT, traits, Allocator>& str);
516
517template<class charT, class traits, class Allocator, class U>
518constexpr typename basic_string<charT, traits, Allocator>::size_type
519erase(basic_string<charT, traits, Allocator>& c, const U& value); // C++20
520template<class charT, class traits, class Allocator, class Predicate>
521constexpr typename basic_string<charT, traits, Allocator>::size_type
522erase_if(basic_string<charT, traits, Allocator>& c, Predicate pred); // C++20
523
524typedef basic_string<char> string;
525typedef basic_string<wchar_t> wstring;
526typedef basic_string<char8_t> u8string; // C++20
527typedef basic_string<char16_t> u16string;
528typedef basic_string<char32_t> u32string;
529
530int stoi (const string& str, size_t* idx = nullptr, int base = 10);
531long stol (const string& str, size_t* idx = nullptr, int base = 10);
532unsigned long stoul (const string& str, size_t* idx = nullptr, int base = 10);
533long long stoll (const string& str, size_t* idx = nullptr, int base = 10);
534unsigned long long stoull(const string& str, size_t* idx = nullptr, int base = 10);
535
536float stof (const string& str, size_t* idx = nullptr);
537double stod (const string& str, size_t* idx = nullptr);
538long double stold(const string& str, size_t* idx = nullptr);
539
540string to_string(int val);
541string to_string(unsigned val);
542string to_string(long val);
543string to_string(unsigned long val);
544string to_string(long long val);
545string to_string(unsigned long long val);
546string to_string(float val);
547string to_string(double val);
548string to_string(long double val);
549
550int stoi (const wstring& str, size_t* idx = nullptr, int base = 10);
551long stol (const wstring& str, size_t* idx = nullptr, int base = 10);
552unsigned long stoul (const wstring& str, size_t* idx = nullptr, int base = 10);
553long long stoll (const wstring& str, size_t* idx = nullptr, int base = 10);
554unsigned long long stoull(const wstring& str, size_t* idx = nullptr, int base = 10);
555
556float stof (const wstring& str, size_t* idx = nullptr);
557double stod (const wstring& str, size_t* idx = nullptr);
558long double stold(const wstring& str, size_t* idx = nullptr);
559
560wstring to_wstring(int val);
561wstring to_wstring(unsigned val);
562wstring to_wstring(long val);
563wstring to_wstring(unsigned long val);
564wstring to_wstring(long long val);
565wstring to_wstring(unsigned long long val);
566wstring to_wstring(float val);
567wstring to_wstring(double val);
568wstring to_wstring(long double val);
569
570template <> struct hash<string>;
571template <> struct hash<u8string>; // C++20
572template <> struct hash<u16string>;
573template <> struct hash<u32string>;
574template <> struct hash<wstring>;
575
576basic_string<char> operator""s( const char *str, size_t len ); // C++14, constexpr since C++20
577basic_string<wchar_t> operator""s( const wchar_t *str, size_t len ); // C++14, constexpr since C++20
578constexpr basic_string<char8_t> operator""s( const char8_t *str, size_t len ); // C++20
579basic_string<char16_t> operator""s( const char16_t *str, size_t len ); // C++14, constexpr since C++20
580basic_string<char32_t> operator""s( const char32_t *str, size_t len ); // C++14, constexpr since C++20
581
582} // std
583
584*/
585
586// clang-format on
587
588#include <bit>
589#include <limits>
590#include <memory>
591#include <string_view>
592#include <type_traits>
593#include <initializer_list>
594#include <algorithm>
595#include <iterator>
596#include <utility>
597#include <compare>
598#include <charconv>
599#include <concepts>
600
601#include <cstdint>
602#include <cstddef>
603#include <cstdarg>
604
605#include "plg/allocator.hpp"
606#include "plg/guards.hpp"
607#include "plg/concepts.hpp"
608
609#ifndef PLUGIFY_STRING_NO_STD_FORMAT
610#include "plg/format.hpp"
611#endif
612
613// 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
614#if PLUGIFY_COMPILER_CLANG
615# pragma clang system_header
616#elif PLUGIFY_COMPILER_GCC
617# pragma GCC system_header
618#endif
619
620// from https://github.com/llvm/llvm-project/blob/main/libcxx/include/string
621namespace plg {
622 // basic_string
623 template <class CharT, class Traits, class Allocator>
624 class basic_string;
625
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
631 );
632
633 template <class Iter>
634 inline const bool string_is_trivial_iterator_v = false;
635
636 template <class T>
637 concept string_is_trivial_iterator = std::is_arithmetic_v<T>;
638
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*>;
641
642 // second concept = the above, but exclude std::basic_string itself
643 template <class T, class CharT, class Traits, class Allocator>
644 concept string_view_convertible_with_exceptiom = string_view_convertible<T, CharT, Traits> && !std::is_same_v<std::remove_cvref_t<T>, basic_string<CharT, Traits, Allocator>>;
645
646 template <typename T>
647 concept string_like = requires(T v) {
648 { std::string_view(v) };
649 };
650
651 template <size_t N>
652 struct padding {
653 char padding[N];
654 };
655
656 template <>
657 struct padding<0> {};
658
660
662
663 template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = allocator<CharT>>
665 public:
666 // using self = std::basic_string<CharT, Traits, Allocator>;
667 using self_view = std::basic_string_view<CharT, Traits>;
668 using traits_type = Traits;
669 using value_type = CharT;
670 using allocator_type = Allocator;
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;
674 using reference = value_type&;
675 using const_reference = const value_type&;
676 using pointer = typename alloc_traits::pointer;
677 using const_pointer = typename alloc_traits::const_pointer;
678
679 // A basic_string contains the following members which may be trivially relocatable:
680 // - pointer: is currently assumed to be trivially relocatable, but is still checked in case
681 // that changes
682 // - size_type: is always trivially relocatable, since it has to be an integral type
683 // - value_type: is always trivially relocatable, since it has to be trivial
684 // - unsigned char: is a fundamental type, so it's trivially relocatable
685 // - allocator_type: may or may not be trivially relocatable, so it's checked
686 //
687 // This string implementation doesn't contain any references into itself. It only contains a
688 // bit that says whether it is in small or large string mode, so the entire structure is
689 // trivially relocatable if its members are.
690#if PLUGIFY_INSTRUMENTED_WITH_ASAN
691 // When compiling with AddressSanitizer (ASan), basic_string cannot be trivially
692 // relocatable. Because the object's memory might be poisoned when its content
693 // is kept inside objects memory (short string optimization), instead of in allocated
694 // external memory. In such cases, the destructor is responsible for unpoisoning
695 // the memory to avoid triggering false positives.
696 // Therefore it's crucial to ensure the destructor is called.
697 //
698 // However, it is replaceable since implementing move-assignment as a destroy +
699 // move-construct will maintain the right ASAN state.
700 using trivially_relocatable = void;
701#else
702 using trivially_relocatable = std::conditional_t<
706 void>;
707#endif // PLUGIFY_INSTRUMENTED_WITH_ASAN
708
709#if PLUGIFY_INSTRUMENTED_WITH_ASAN && __has_cpp_attribute(__no_sanitize__)
710# define PLUGIFY_INTERNAL_MEMORY_ACCESS __attribute__((__no_sanitize__("address")))
711// This macro disables AddressSanitizer (ASan) instrumentation for a specific function,
712// allowing memory accesses that would normally trigger ASan errors to proceed without crashing.
713// This is useful for accessing parts of objects memory, which should not be accessed,
714// such as unused bytes in short strings, that should never be accessed
715// by other parts of the program.
716#else
717# define PLUGIFY_INTERNAL_MEMORY_ACCESS
718#endif // PLUGIFY_INSTRUMENTED_WITH_ASAN
719
720#if PLUGIFY_INSTRUMENTED_WITH_ASAN
721 constexpr pointer asan_volatile_wrapper(pointer const& ptr) const {
722 if (std::is_constant_evaluated()) {
723 return ptr;
724 }
725
726 volatile pointer copy_ptr = ptr;
727
728 return const_cast<pointer&>(copy_ptr);
729 }
730
731 constexpr const_pointer asan_volatile_wrapper(const const_pointer& ptr) const {
732 if (std::is_constant_evaluated()) {
733 return ptr;
734 }
735
736 volatile const_pointer copy_ptr = ptr;
737
738 return const_cast<const_pointer&>(copy_ptr);
739 }
740
741# define PLUGIFY_ASAN_VOLATILE_WRAPPER(PTR) asan_volatile_wrapper(PTR)
742#else
743# define PLUGIFY_ASAN_VOLATILE_WRAPPER(PTR) PTR
744#endif // PLUGIFY_INSTRUMENTED_WITH_ASAN
745
746 static_assert(!std::is_array_v<value_type>, "Character type of basic_string must not be an array");
747 static_assert(
748 std::is_standard_layout_v<value_type>,
749 "Character type of basic_string must be standard-layout"
750 );
751 static_assert(
752 std::is_trivially_default_constructible_v<value_type>,
753 "Character type of basic_string must be trivially default constructible"
754 );
755 static_assert(
756 std::is_trivially_copyable_v<value_type>,
757 "Character type of basic_string must be trivially copyable"
758 );
759 static_assert(
760 std::is_same_v<CharT, typename traits_type::char_type>,
761 "traits_type::char_type must be the same type as CharT"
762 );
763 static_assert(
764 std::is_same_v<typename allocator_type::value_type, value_type>,
765 "Allocator::value_type must be same type as value_type"
766 );
767 // static_assert(std::check_valid_allocator<allocator_type>::value, "");
768
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>;
773
775
776 private:
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");
779
780 struct long_ {
781 constexpr long_() = default;
782
783 constexpr long_(alloc_result alloc, size_type size)
784 : data_(alloc.ptr)
785 , size_(size)
786 , cap_(alloc.count / endian_factor)
787 , is_long_(true) {
788 PLUGIFY_ASSERT(!fits_in_sso(alloc.count), "Long capacity should always be larger than the SSO");
789 }
790
791 pointer data_;
792 size_type size_;
793 size_type cap_ : sizeof(size_type) * char_bit - 1;
794 size_type is_long_ : 1;
795 };
796
797 static constexpr size_type min_cap = ((sizeof(long_) - 1) / sizeof(value_type) > 2 ? (sizeof(long_) - 1) / sizeof(value_type) : 2) + 1;
798
799 struct short_ {
800 constexpr short_()
801 : data_{}
802 , spare_size_(min_cap - 1)
803 , is_long_(false) {
804 }
805
806 value_type data_[min_cap - 1];
807 PLUGIFY_NO_UNIQUE_ADDRESS padding<sizeof(value_type) - 1> padding_;
808 uint8_t spare_size_ : 7;
809 uint8_t is_long_ : 1;
810 };
811
812 // The endian_factor is required because the field we use to store the size
813 // has one fewer bit than it would if it were not a bitfield.
814 //
815 // If the LSB is used to store the short-flag in the short string representation,
816 // we have to multiply the size by two when it is stored and divide it by two when
817 // it is loaded to make sure that we always store an even number. In the long string
818 // representation, we can ignore this because we can assume that we always allocate
819 // an even amount of value_types.
820 //
821 // If the MSB is used for the short-flag, the max_size() is numeric_limits<size_type>::max()
822 // / 2. This does not impact the short string representation, since we never need the MSB
823 // for representing the size of a short string anyway.
824
825 static constexpr size_type endian_factor = std::endian::native == std::endian::big ? 2 : 1;
826
827 static_assert(
828 sizeof(short_) == (sizeof(value_type) * min_cap),
829 "short has an unexpected size."
830 );
831 static_assert(
832 sizeof(short_) == sizeof(long_),
833 "short and long layout structures must be the same size"
834 );
835
836 union rep {
837 short_ s{};
838 long_ l;
839 } rep_;
840 PLUGIFY_NO_UNIQUE_ADDRESS
841 allocator_type alloc_;
842
843 // annotate the string with its size() at scope exit. The string has to be in a valid state
844 // at that point.
845 struct annotate_new_size {
846 basic_string& str_;
847
848 constexpr explicit annotate_new_size(basic_string& str)
849 : str_(str) {
850 }
851
852 constexpr void operator()() {
853 str_.annotate_new(str_.size());
854 }
855 };
856
857 // Construct a string with the given allocator and enough storage to hold `size` characters,
858 // but don't initialize the characters. The contents of the string, including the null
859 // terminator, must be initialized separately.
860 constexpr /*explicit*/ basic_string(uninitialized_size_tag, size_type size, const allocator_type& a)
861 : alloc_(a) {
862 init_internal_buffer(size);
863 }
864
865 template <class Iter, class Sent>
866 constexpr basic_string(init_with_sentinel_tag, Iter first, Sent last, const allocator_type& a)
867 : alloc_(a) {
868 init_with_sentinel(std::move(first), std::move(last));
869 }
870
871 constexpr iterator make_iterator(pointer p) {
872 return iterator(p);
873 }
874
875 constexpr const_iterator make_const_iterator(const_pointer p) const {
876 return const_iterator(p);
877 }
878
879 public:
880 static const size_type npos = static_cast<size_type>(-1);
881
882 constexpr basic_string() noexcept(std::is_nothrow_default_constructible_v<allocator_type>)
883 : rep_(short_()) {
884 annotate_new(0);
885 }
886
887 constexpr /*explicit*/ basic_string(const allocator_type& a) noexcept
888 : rep_(short_())
889 , alloc_(a) {
890 annotate_new(0);
891 }
892
893 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS basic_string(const basic_string& str)
894 : alloc_(alloc_traits::select_on_container_copy_construction(str.alloc_)) {
895 if (!str.is_long()) {
896 rep_ = str.rep_;
897 annotate_new(get_short_size());
898 } else {
899 init_copy_ctor_external(std::to_address(str.get_long_pointer()), str.get_long_size());
900 }
901 }
902
903 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS
904 basic_string(const basic_string& str, const allocator_type& a)
905 : alloc_(a) {
906 if (!str.is_long()) {
907 rep_ = str.rep_;
908 annotate_new(get_short_size());
909 } else {
910 init_copy_ctor_external(std::to_address(str.get_long_pointer()), str.get_long_size());
911 }
912 }
913
914 constexpr basic_string(basic_string&& str) noexcept
915 // Turning off ASan instrumentation for variable initialization with
916 // PLUGIFY_INTERNAL_MEMORY_ACCESS does not work consistently during
917 // initialization of r_, so we instead unpoison str's memory manually first. str's
918 // memory needs to be unpoisoned only in the case where it's a short string.
919 : rep_([](basic_string& s) -> decltype(s.rep_)&& {
920 if (!s.is_long()) {
921 s.annotate_delete();
922 }
923 return std::move(s.rep_);
924 }(str))
925 , alloc_(std::move(str.alloc_)) {
926 str.rep_ = rep();
927 str.annotate_new(0);
928 if (!is_long()) {
929 annotate_new(size());
930 }
931 }
932
933 constexpr basic_string(basic_string&& str, const allocator_type& a)
934 : alloc_(a) {
935 if (str.is_long() && a != str.alloc_) { // copy, not move
936 init(std::to_address(str.get_long_pointer()), str.get_long_size());
937 } else {
938 if (std::is_constant_evaluated()) {
939 rep_ = rep();
940 }
941 if (!str.is_long()) {
942 str.annotate_delete();
943 }
944 rep_ = str.rep_;
945 str.rep_ = rep();
946 str.annotate_new(0);
947 if (!is_long() && this != std::addressof(str)) {
948 annotate_new(size());
949 }
950 }
951 }
952
953 constexpr basic_string(const CharT* PLUGIFY_NO_NULL s)
954 requires(is_allocator<Allocator>)
955 {
956 PLUGIFY_ASSERT(s != nullptr, "basic_string(const char*) detected nullptr");
957 init(s, traits_type::length(s));
958 }
959
960 constexpr basic_string(const CharT* PLUGIFY_NO_NULL s, const Allocator& a)
961 requires(is_allocator<Allocator>)
962 : alloc_(a) {
963 PLUGIFY_ASSERT(s != nullptr, "basic_string(const char*, allocator) detected nullptr");
964 init(s, traits_type::length(s));
965 }
966
967 basic_string(std::nullptr_t) = delete;
968
969 constexpr basic_string(const CharT* s, size_type n) {
970 PLUGIFY_ASSERT(n == 0 || s != nullptr, "basic_string(const char*, n) detected nullptr");
971 init(s, n);
972 }
973
974 constexpr basic_string(const CharT* s, size_type n, const Allocator& a)
975 : alloc_(a) {
976 PLUGIFY_ASSERT(
977 n == 0 || s != nullptr,
978 "basic_string(const char*, n, allocator) detected nullptr"
979 );
980 init(s, n);
981 }
982
983 constexpr basic_string(size_type n, CharT c) {
984 init(n, c);
985 }
986
987 constexpr basic_string(basic_string&& str, size_type pos, const Allocator& alloc = Allocator())
988 : basic_string(std::move(str), pos, npos, alloc) {
989 }
990
991 constexpr basic_string(
992 basic_string&& str,
993 size_type pos,
994 size_type n,
995 const Allocator& alloc = Allocator()
996 )
997 : alloc_(alloc) {
998 if (pos > str.size()) {
999 this->throw_out_of_range();
1000 }
1001
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);
1005 } else {
1006 // Perform a copy because the allocators are not compatible.
1007 init(str.data() + pos, len);
1008 }
1009 }
1010
1011 constexpr basic_string(size_type n, CharT c, const Allocator& a)
1012 requires(is_allocator<Allocator>)
1013 : alloc_(a) {
1014 init(n, c);
1015 }
1016
1017 constexpr basic_string(
1018 const basic_string& str,
1019 size_type pos,
1020 size_type n,
1021 const Allocator& a = Allocator()
1022 )
1023 : alloc_(a) {
1024 size_type str_sz = str.size();
1025 if (pos > str_sz) {
1026 this->throw_out_of_range();
1027 }
1028 init(str.data() + pos, std::min(n, str_sz - pos));
1029 }
1030
1031 constexpr basic_string(const basic_string& str, size_type pos, const Allocator& a = Allocator())
1032 : alloc_(a) {
1033 size_type str_sz = str.size();
1034 if (pos > str_sz) {
1035 this->throw_out_of_range();
1036 }
1037 init(str.data() + pos, str_sz - pos);
1038 }
1039
1040 template <string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1041 constexpr basic_string(
1042 const T& t,
1043 size_type pos,
1044 size_type n,
1045 const allocator_type& a = allocator_type()
1046 )
1047 : alloc_(a) {
1048 self_view sv0 = t;
1049 self_view sv = sv0.substr(pos, n);
1050 init(sv.data(), sv.size());
1051 }
1052
1053 template <string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1054 constexpr /*explicit*/ basic_string(const T& t) {
1055 self_view sv = t;
1056 init(sv.data(), sv.size());
1057 }
1058
1059 template <string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1060 constexpr /*explicit*/ basic_string(const T& t, const allocator_type& a)
1061 : alloc_(a) {
1062 self_view sv = t;
1063 init(sv.data(), sv.size());
1064 }
1065
1066 template <std::input_iterator InputIterator>
1067 constexpr basic_string(InputIterator first, InputIterator last) {
1068 init(first, last);
1069 }
1070
1071 template <std::input_iterator InputIterator>
1072 constexpr basic_string(InputIterator first, InputIterator last, const allocator_type& a)
1073 : alloc_(a) {
1074 init(first, last);
1075 }
1076
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())
1080 : alloc_(a) {
1081 if constexpr (std::ranges::forward_range<Range> || std::ranges::sized_range<Range>) {
1082 init_with_size(
1083 std::ranges::begin(range),
1084 std::ranges::end(range),
1085 std::ranges::distance(range)
1086 );
1087 } else {
1088 init_with_sentinel(std::ranges::begin(range), std::ranges::end(range));
1089 }
1090 }
1091#endif
1092
1093 constexpr basic_string(std::initializer_list<CharT> il) {
1094 init(il.begin(), il.end());
1095 }
1096
1097 constexpr basic_string(std::initializer_list<CharT> il, const Allocator& a)
1098 : alloc_(a) {
1099 init(il.begin(), il.end());
1100 }
1101
1102 inline constexpr ~basic_string() {
1103 reset_internal_buffer();
1104 }
1105
1106 constexpr operator self_view() const noexcept {
1107 return self_view(begin(), end());
1108 }
1109
1110 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS basic_string& operator=(const basic_string& str);
1111
1112 template <string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1113 constexpr basic_string& operator=(const T& t) {
1114 self_view sv = t;
1115 return assign(sv);
1116 }
1117
1118 constexpr basic_string& operator=(basic_string&& str
1119 ) noexcept(alloc_traits::propagate_on_container_move_assignment::value) {
1120 move_assign(
1121 str,
1122 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
1123 );
1124 return *this;
1125 }
1126
1127 constexpr basic_string& operator=(std::initializer_list<value_type> il) {
1128 return assign(il.begin(), il.size());
1129 }
1130
1131 constexpr basic_string& operator=(const value_type* PLUGIFY_NO_NULL s) {
1132 return assign(s);
1133 }
1134
1135 basic_string& operator=(std::nullptr_t) = delete;
1136 constexpr basic_string& operator=(value_type c);
1137
1138 constexpr iterator begin() noexcept {
1139 return make_iterator(get_pointer());
1140 }
1141
1142 constexpr const_iterator begin() const noexcept {
1143 return make_const_iterator(get_pointer());
1144 }
1145
1146 constexpr iterator end() noexcept {
1147 return make_iterator(get_pointer() + size());
1148 }
1149
1150 constexpr const_iterator end() const noexcept {
1151 return make_const_iterator(get_pointer() + size());
1152 }
1153
1154 constexpr reverse_iterator rbegin() noexcept {
1155 return reverse_iterator(end());
1156 }
1157
1158 constexpr const_reverse_iterator rbegin() const noexcept {
1159 return const_reverse_iterator(end());
1160 }
1161
1162 constexpr reverse_iterator rend() noexcept {
1163 return reverse_iterator(begin());
1164 }
1165
1166 constexpr const_reverse_iterator rend() const noexcept {
1167 return const_reverse_iterator(begin());
1168 }
1169
1170 constexpr const_iterator cbegin() const noexcept {
1171 return begin();
1172 }
1173
1174 constexpr const_iterator cend() const noexcept {
1175 return end();
1176 }
1177
1178 constexpr const_reverse_iterator crbegin() const noexcept {
1179 return rbegin();
1180 }
1181
1182 constexpr const_reverse_iterator crend() const noexcept {
1183 return rend();
1184 }
1185
1186 constexpr size_type size() const noexcept {
1187 return is_long() ? get_long_size() : get_short_size();
1188 }
1189
1190 constexpr size_type length() const noexcept {
1191 return size();
1192 }
1193
1194 constexpr size_type max_size() const noexcept {
1195 constexpr bool uses_lsb = endian_factor == 2;
1196
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;
1200
1201 // When the endian_factor == 2, our string representation assumes that the capacity
1202 // (including the null terminator) is always even, so we have to make sure the
1203 // lowest bit isn't set when the string grows to max_size()
1204 if constexpr (uses_lsb) {
1205 res &= ~size_type(1);
1206 }
1207
1208 // We have to allocate space for the null terminator, but max_size() doesn't include
1209 // it.
1210 return res - 1;
1211 } else {
1212 return uses_lsb ? m - alignment - 1 : (m / 2) - alignment - 1;
1213 }
1214 }
1215
1216 constexpr size_type capacity() const noexcept {
1217 return (is_long() ? get_long_cap() : min_cap) - 1;
1218 }
1219
1220 constexpr void resize(size_type n, value_type c);
1221
1222 constexpr void resize(size_type n) {
1223 resize(n, value_type());
1224 }
1225
1226 constexpr void reserve(size_type requested_capacity);
1227
1228#if PLUGIFY_HAS_CXX23
1229 /*template <class Op>
1230 constexpr void resize_and_overwrite(size_type n, Op op) {
1231 size_type sz = size();
1232 size_type cap = capacity();
1233 if (n > cap) {
1234 grow_by_without_replace(cap, n - cap, sz, sz, 0);
1235 }
1236 annotate_delete();
1237 set_size(n);
1238 annotate_new(n);
1239 erase_to_end(std::move(op)(data(), auto(n)));
1240 }*/
1241#endif
1242
1243 constexpr void shrink_to_fit() noexcept;
1244 constexpr void clear() noexcept;
1245
1246 [[nodiscard]] constexpr bool empty() const noexcept {
1247 return size() == 0;
1248 }
1249
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);
1254 }
1255 return *(data() + pos);
1256 }
1257
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);
1262 }
1263 return *(get_pointer() + pos);
1264 }
1265
1266 constexpr const_reference at(size_type n) const;
1267 constexpr reference at(size_type n);
1268
1269 constexpr basic_string& operator+=(const basic_string& str) {
1270 return append(str);
1271 }
1272
1273 template <string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1274 constexpr basic_string& operator+=(const T& t) {
1275 self_view sv = t;
1276 return append(sv);
1277 }
1278
1279 constexpr basic_string& operator+=(const value_type* PLUGIFY_NO_NULL s) {
1280 return append(s);
1281 }
1282
1283 constexpr basic_string& operator+=(value_type c) {
1284 push_back(c);
1285 return *this;
1286 }
1287
1288 constexpr basic_string& operator+=(std::initializer_list<value_type> il) {
1289 return append(il);
1290 }
1291
1292 constexpr basic_string& append(const basic_string& str) {
1293 return append(str.data(), str.size());
1294 }
1295
1296 template <string_view_convertible_with_exceptiom<CharT, Traits, Allocator> T>
1297 constexpr basic_string& append(const T& t) {
1298 self_view sv = t;
1299 return append(sv.data(), sv.size());
1300 }
1301
1302 constexpr basic_string& append(const basic_string& str, size_type pos, size_type n = npos);
1303
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) {
1306 self_view sv = t;
1307 size_type sz = sv.size();
1308 if (pos > sz) {
1309 throw_out_of_range();
1310 }
1311 return append(sv.data() + pos, std::min(n, sz - pos));
1312 }
1313
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);
1317
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());
1322 return *this;
1323 }
1324
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));
1330 if (n == 0) {
1331 return *this;
1332 }
1333
1334 if (string_is_trivial_iterator_v<ForwardIterator> && !addr_in_range(*first)) {
1335 if (cap - sz < n) {
1336 grow_by_without_replace(cap, sz + n - cap, sz, sz, 0);
1337 }
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());
1341 set_size(sz + n);
1342 return *this;
1343 } else {
1344 const basic_string temp(first, last, alloc_);
1345 return append(temp.data(), temp.size());
1346 }
1347 }
1348
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));
1353 return *this;
1354 }
1355#endif
1356
1357 constexpr basic_string& append(std::initializer_list<value_type> il) {
1358 return append(il.begin(), il.size());
1359 }
1360
1361 constexpr void push_back(value_type c);
1362 constexpr void pop_back();
1363
1364 constexpr reference front() noexcept {
1365 PLUGIFY_ASSERT(!empty(), "string::front(): string is empty");
1366 return *get_pointer();
1367 }
1368
1369 constexpr const_reference front() const noexcept {
1370 PLUGIFY_ASSERT(!empty(), "string::front(): string is empty");
1371 return *data();
1372 }
1373
1374 constexpr reference back() noexcept {
1375 PLUGIFY_ASSERT(!empty(), "string::back(): string is empty");
1376 return *(get_pointer() + size() - 1);
1377 }
1378
1379 constexpr const_reference back() const noexcept {
1380 PLUGIFY_ASSERT(!empty(), "string::back(): string is empty");
1381 return *(data() + size() - 1);
1382 }
1383
1384 template <string_view_convertible<CharT, Traits> T>
1385 constexpr basic_string& assign(const T& t) {
1386 self_view sv = t;
1387 return assign(sv.data(), sv.size());
1388 }
1389
1390 constexpr void move_assign(basic_string&& str, size_type pos, size_type len) {
1391 // Pilfer the allocation from str.
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();
1396 }
1397 rep_ = str.rep_;
1398 str.rep_ = rep();
1399 str.annotate_new(0);
1400
1401 Traits::move(data(), data() + pos, len);
1402 set_size(len);
1403 Traits::assign(data()[len], value_type());
1404
1405 if (!is_long()) {
1406 annotate_new(len);
1407 } else if (old_sz > len) {
1408 annotate_shrink(old_sz);
1409 }
1410 }
1411
1412 constexpr basic_string& assign(const basic_string& str) {
1413 return *this = str;
1414 }
1415
1416 constexpr basic_string&
1417 assign(basic_string&& str) noexcept(alloc_traits::propagate_on_container_move_assignment::value
1418 ) {
1419 *this = std::move(str);
1420 return *this;
1421 }
1422
1423 constexpr basic_string& assign(const basic_string& str, size_type pos, size_type n = npos);
1424
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) {
1427 self_view sv = t;
1428 size_type sz = sv.size();
1429 if (pos > sz) {
1430 throw_out_of_range();
1431 }
1432 return assign(sv.data() + pos, std::min(n, sz - pos));
1433 }
1434
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);
1438
1439 template <std::input_iterator InputIterator>
1440 constexpr basic_string& assign(InputIterator first, InputIterator last) {
1441 assign_with_sentinel(first, last);
1442 return *this;
1443 }
1444
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);
1450 } else {
1451 assign_with_sentinel(first, last);
1452 }
1453
1454 return *this;
1455 }
1456
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);
1464
1465 } else {
1466 assign_with_sentinel(std::ranges::begin(range), std::ranges::end(range));
1467 }
1468
1469 return *this;
1470 }
1471#endif
1472
1473 constexpr basic_string& assign(std::initializer_list<value_type> il) {
1474 return assign(il.begin(), il.size());
1475 }
1476
1477 constexpr basic_string& insert(size_type pos1, const basic_string& str) {
1478 return insert(pos1, str.data(), str.size());
1479 }
1480
1481 template <string_view_convertible<CharT, Traits> T>
1482 constexpr basic_string& insert(size_type pos1, const T& t) {
1483 self_view sv = t;
1484 return insert(pos1, sv.data(), sv.size());
1485 }
1486
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) {
1490 self_view sv = t;
1491 size_type str_sz = sv.size();
1492 if (pos2 > str_sz) {
1493 throw_out_of_range();
1494 }
1495 return insert(pos1, sv.data() + pos2, std::min(n, str_sz - pos2));
1496 }
1497
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);
1504
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);
1511
1512 } else {
1513 basic_string temp(std::from_range, std::forward<Range>(range), alloc_);
1514 return insert(position, temp.data(), temp.data() + temp.size());
1515 }
1516 }
1517#endif
1518
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);
1522 return begin() + p;
1523 }
1524
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());
1529 }
1530
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);
1535 }
1536
1537 constexpr iterator insert(const_iterator pos, std::initializer_list<value_type> il) {
1538 return insert(pos, il.begin(), il.end());
1539 }
1540
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);
1544
1545 constexpr basic_string& replace(size_type pos1, size_type n1, const basic_string& str) {
1546 return replace(pos1, n1, str.data(), str.size());
1547 }
1548
1549 template <string_view_convertible<CharT, Traits> T>
1550 constexpr basic_string& replace(size_type pos1, size_type n1, const T& t) {
1551 self_view sv = t;
1552 return replace(pos1, n1, sv.data(), sv.size());
1553 }
1554
1555 constexpr basic_string&
1556 replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos);
1557
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) {
1561 self_view sv = t;
1562 size_type str_sz = sv.size();
1563 if (pos2 > str_sz) {
1564 throw_out_of_range();
1565 }
1566 return replace(pos1, n1, sv.data() + pos2, std::min(n2, str_sz - pos2));
1567 }
1568
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);
1574
1575 constexpr basic_string&
1576 replace(const_iterator i1, const_iterator i2, const basic_string& str) {
1577 return replace(
1578 static_cast<size_type>(i1 - begin()),
1579 static_cast<size_type>(i2 - i1),
1580 str.data(),
1581 str.size()
1582 );
1583 }
1584
1585 template <string_view_convertible<CharT, Traits> T>
1586 constexpr basic_string& replace(const_iterator i1, const_iterator i2, const T& t) {
1587 self_view sv = t;
1588 return replace(i1 - begin(), i2 - i1, sv);
1589 }
1590
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);
1594 }
1595
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);
1598 }
1599
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);
1603 }
1604
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);
1610 }
1611
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);
1618 }
1619#endif
1620
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());
1624 }
1625
1626 constexpr size_type copy(value_type* s, size_type n, size_type pos = 0) const;
1627
1628 constexpr basic_string substr(size_type pos = 0, size_type n = npos) const& {
1629 return basic_string(*this, pos, n);
1630 }
1631
1632 constexpr basic_string substr(size_type pos = 0, size_type n = npos) && {
1633 return basic_string(std::move(*this), pos, n);
1634 }
1635
1636 constexpr void swap(basic_string& str) noexcept;
1637
1638 // [string.ops]
1639 // ------------
1640
1641 constexpr const value_type* c_str() const noexcept {
1642 return data();
1643 }
1644
1645 constexpr const value_type* data() const noexcept {
1646 return std::to_address(get_pointer());
1647 }
1648
1649 constexpr value_type* data() noexcept {
1650 return std::to_address(get_pointer());
1651 }
1652
1653 constexpr allocator_type get_allocator() const noexcept {
1654 return alloc_;
1655 }
1656
1657 // find
1658
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());
1661 }
1662
1663 template <string_view_convertible<CharT, Traits> T>
1664 constexpr size_type find(const T& t, size_type pos = 0) const noexcept {
1665 self_view sv = t;
1666 return operator self_view().find(sv.data(), pos, sv.size());
1667 }
1668
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);
1672 }
1673
1674 constexpr size_type
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));
1678 }
1679
1680 constexpr size_type find(value_type c, size_type pos = 0) const noexcept {
1681 return operator self_view().find(c, pos);
1682 }
1683
1684 // rfind
1685
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());
1688 }
1689
1690 template <string_view_convertible<CharT, Traits> T>
1691 constexpr size_type rfind(const T& t, size_type pos = npos) const noexcept {
1692 self_view sv = t;
1693 return operator self_view().rfind(sv.data(), pos, sv.size());
1694 }
1695
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);
1699 }
1700
1701 constexpr size_type
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));
1705 }
1706
1707 constexpr size_type rfind(value_type c, size_type pos = npos) const noexcept {
1708 return operator self_view().rfind(c, pos);
1709 }
1710
1711 // find_first_of
1712
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());
1715 }
1716
1717 template <string_view_convertible<CharT, Traits> T>
1718 constexpr size_type find_first_of(const T& t, size_type pos = 0) const noexcept {
1719 self_view sv = t;
1720 return operator self_view().find_first_of(sv.data(), pos, sv.size());
1721 }
1722
1723 constexpr size_type
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);
1727 }
1728
1729 constexpr size_type
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));
1733 }
1734
1735 constexpr size_type find_first_of(value_type c, size_type pos = 0) const noexcept {
1736 return find(c, pos);
1737 }
1738
1739 // find_last_of
1740
1741 constexpr size_type
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());
1744 }
1745
1746 template <string_view_convertible<CharT, Traits> T>
1747 constexpr size_type find_last_of(const T& t, size_type pos = npos) const noexcept {
1748 self_view sv = t;
1749 return operator self_view().find_last_of(sv.data(), pos, sv.size());
1750 }
1751
1752 constexpr size_type
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);
1756 }
1757
1758 constexpr size_type
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));
1762 }
1763
1764 constexpr size_type find_last_of(value_type c, size_type pos = npos) const noexcept {
1765 return rfind(c, pos);
1766 }
1767
1768 // find_first_not_of
1769
1770 constexpr size_type
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());
1773 }
1774
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 {
1777 self_view sv = t;
1778 return operator self_view().find_first_not_of(sv.data(), pos, sv.size());
1779 }
1780
1781 constexpr size_type
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);
1785 }
1786
1787 constexpr size_type
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));
1791 }
1792
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);
1795 }
1796
1797 // find_last_not_of
1798
1799 constexpr size_type
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());
1802 }
1803
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 {
1806 self_view sv = t;
1807 return operator self_view().find_last_not_of(sv.data(), pos, sv.size());
1808 }
1809
1810 constexpr size_type
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);
1814 }
1815
1816 constexpr size_type
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));
1820 }
1821
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);
1824 }
1825
1826 // compare
1827
1828 constexpr int compare(const basic_string& str) const noexcept {
1829 return compare(self_view(str));
1830 }
1831
1832 template <string_view_convertible<CharT, Traits> T>
1833 constexpr int compare(const T& t) const noexcept {
1834 self_view sv = t;
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));
1838 if (result != 0) {
1839 return result;
1840 }
1841 if (lhs_sz < rhs_sz) {
1842 return -1;
1843 }
1844 if (lhs_sz > rhs_sz) {
1845 return 1;
1846 }
1847 return 0;
1848 }
1849
1850 template <string_view_convertible<CharT, Traits> T>
1851 constexpr int compare(size_type pos1, size_type n1, const T& t) const {
1852 self_view sv = t;
1853 return compare(pos1, n1, sv.data(), sv.size());
1854 }
1855
1856 constexpr int compare(size_type pos1, size_type n1, const basic_string& str) const {
1857 return compare(pos1, n1, str.data(), str.size());
1858 }
1859
1860 constexpr int compare(
1861 size_type pos1,
1862 size_type n1,
1863 const basic_string& str,
1864 size_type pos2,
1865 size_type n2 = npos
1866 ) const {
1867 return compare(pos1, n1, self_view(str), pos2, n2);
1868 }
1869
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 {
1873 self_view sv = t;
1874 return self_view(*this).substr(pos1, n1).compare(sv.substr(pos2, n2));
1875 }
1876
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));
1880 }
1881
1882 constexpr int
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));
1886 }
1887
1888 constexpr int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const;
1889
1890 // starts_with
1891
1892 constexpr bool starts_with(self_view sv) const noexcept {
1893 return self_view(typename self_view::assume_valid(), data(), size()).starts_with(sv);
1894 }
1895
1896 constexpr bool starts_with(value_type c) const noexcept {
1897 return !empty() && Traits::eq(front(), c);
1898 }
1899
1900 constexpr bool starts_with(const value_type* PLUGIFY_NO_NULL s) const noexcept {
1901 return starts_with(self_view(s));
1902 }
1903
1904 // ends_with
1905
1906 constexpr bool ends_with(self_view sv) const noexcept {
1907 return self_view(typename self_view::assume_valid(), data(), size()).ends_with(sv);
1908 }
1909
1910 constexpr bool ends_with(value_type c) const noexcept {
1911 return !empty() && Traits::eq(back(), c);
1912 }
1913
1914 constexpr bool ends_with(const value_type* PLUGIFY_NO_NULL s) const noexcept {
1915 return ends_with(self_view(s));
1916 }
1917
1918 // contains
1919
1920 constexpr bool contains(self_view sv) const noexcept {
1921 return self_view(typename self_view::assume_valid(), data(), size()).contains(sv);
1922 }
1923
1924 constexpr bool contains(value_type c) const noexcept {
1925 return self_view(typename self_view::assume_valid(), data(), size()).contains(c);
1926 }
1927
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);
1930 }
1931
1932 [[nodiscard]] constexpr bool invariants() const;
1933
1934 private:
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_;
1938 }
1939 return rep_.s.is_long_;
1940 }
1941
1942 static constexpr bool fits_in_sso(size_type sz) {
1943 return sz < min_cap;
1944 }
1945
1946 template <class Iterator, class Sentinel>
1947 constexpr void assign_trivial(Iterator first, Sentinel last, size_type n);
1948
1949 template <class Iterator, class Sentinel>
1950 constexpr void assign_with_sentinel(Iterator first, Sentinel last);
1951
1952 // Copy [first, last) into [dest, dest + (last - first)). Assumes that the ranges don't
1953 // overlap.
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>) {
1960 PLUGIFY_ASSERT(
1961 !is_overlapping_range(std::to_address(first), std::to_address(last), dest),
1962 "copy_non_overlapping_range called with an overlapping range!"
1963 );
1964 traits_type::copy(dest, std::to_address(first), last - first);
1965 return dest + (last - first);
1966 } else {
1967 for (; first != last; ++first) {
1968 traits_type::assign(*dest++, *first);
1969 }
1970 return dest;
1971 }
1972 }
1973
1974 template <class ForwardIterator, class Sentinel>
1975 constexpr iterator
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();
1979 value_type* p;
1980 if (cap - sz >= n) {
1981 annotate_increase(n);
1982 p = std::to_address(get_pointer());
1983 size_type n_move = sz - ip;
1984 if (n_move != 0) {
1985 traits_type::move(p + ip + n, p + ip, n_move);
1986 }
1987 } else {
1988 grow_by_without_replace(cap, sz + n - cap, sz, ip, 0, n);
1989 p = std::to_address(get_long_pointer());
1990 }
1991 sz += n;
1992 set_size(sz);
1993 traits_type::assign(p[sz], value_type());
1994 copy_non_overlapping_range(std::move(first), std::move(last), p + ip);
1995
1996 return begin() + ip;
1997 }
1998
1999 template <class Iterator, class Sentinel>
2000 constexpr iterator
2001 insert_with_size(const_iterator pos, Iterator first, Sentinel last, size_type n);
2002
2003 // internal buffer accessors
2004 // -------------------------
2005
2006 constexpr PLUGIFY_INTERNAL_MEMORY_ACCESS void set_short_size(size_type s) noexcept {
2007 PLUGIFY_ASSERT(
2008 s < min_cap,
2009 "s should never be greater than or equal to the short string capacity"
2010 );
2011 rep_.s.spare_size_ = (min_cap - 1) - s;
2012 rep_.s.is_long_ = false;
2013 }
2014
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_;
2018 }
2019
2020 constexpr void set_long_size(size_type s) noexcept {
2021 rep_.l.size_ = s;
2022 }
2023
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_;
2027 }
2028
2029 constexpr void set_size(size_type s) noexcept {
2030 if (is_long()) {
2031 set_long_size(s);
2032 } else {
2033 set_short_size(s);
2034 }
2035 }
2036
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;
2040 }
2041
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_);
2045 }
2046
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_);
2050 }
2051
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])
2055 );
2056 }
2057
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])
2061 );
2062 }
2063
2064 constexpr pointer get_pointer() noexcept {
2065 return is_long() ? get_long_pointer() : get_short_pointer();
2066 }
2067
2068 constexpr const_pointer get_pointer() const noexcept {
2069 return is_long() ? get_long_pointer() : get_short_pointer();
2070 }
2071
2072 // Internal buffer management
2073 // --------------------------
2074 //
2075 // These functions are only responsible for managing the buffer itself, not the value inside
2076 // the buffer. As such, none of these facilities ensure that there is a null terminator at
2077 // `data()[size()]`.
2078
2079 // Allocate a buffer of capacity size with alloc and return it
2080 static constexpr long_ allocate_long_buffer(Allocator& alloc, size_type capacity) {
2081 auto buffer = allocate_at_least(alloc, recommend(capacity) + 1);
2082
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]));
2086 }
2087 }
2088
2089 return long_(buffer, capacity);
2090 }
2091
2092 // Deallocate the long buffer if it exists and clear the short buffer so we are an empty
2093 // string
2094 constexpr void reset_internal_buffer() {
2095 annotate_delete();
2096 if (is_long()) {
2097 alloc_traits::deallocate(alloc_, get_long_pointer(), get_long_cap());
2098 }
2099 rep_.s = short_();
2100 }
2101
2102 // Replace the current buffer with alloc; the first size elements constitute a string
2103 constexpr void replace_internal_buffer(long_ alloc) {
2104 reset_internal_buffer();
2105 rep_.l = alloc;
2106 }
2107
2108 // Initialize the internal buffer to hold size elements
2109 // The elements and null terminator have to be set by the caller
2110 constexpr pointer init_internal_buffer(size_type size) {
2111 if (std::is_constant_evaluated()) {
2112 rep_ = rep();
2113 }
2114
2115 if (size > max_size()) {
2116 throw_length_error();
2117 }
2118
2119 if (fits_in_sso(size)) {
2120 set_short_size(size);
2121 annotate_new(size);
2122 return get_short_pointer();
2123 } else {
2124 rep_.l = allocate_long_buffer(alloc_, size);
2125 annotate_new(size);
2126 return get_long_pointer();
2127 }
2128 }
2129
2130 // ASan annotation helpers
2131 // -----------------------
2132
2133 // The following functions are no-ops outside of AddressSanitizer mode.
2134 constexpr void annotate_contiguous_container(
2135 const void* old_mid,
2136 const void* new_mid
2137 ) const {
2138 if (!is_long()) {
2139 return;
2140 }
2141
2143 data(),
2144 data() + capacity() + 1,
2145 old_mid,
2146 new_mid
2147 );
2148 }
2149
2150 constexpr void annotate_new(size_type current_size) const noexcept {
2151 annotate_contiguous_container(data() + capacity() + 1, data() + current_size + 1);
2152 }
2153
2154 constexpr void annotate_delete() const noexcept {
2155 annotate_contiguous_container(data() + size() + 1, data() + capacity() + 1);
2156 }
2157
2158 constexpr void annotate_increase(size_type n) const noexcept {
2159 annotate_contiguous_container(data() + size() + 1, data() + size() + 1 + n);
2160 }
2161
2162 constexpr void annotate_shrink(size_type old_size) const noexcept {
2163 annotate_contiguous_container(data() + old_size + 1, data() + size() + 1);
2164 }
2165
2166 // Disable ASan annotations and enable them again when going out of scope. It is assumed
2167 // that the string is in a valid state at that point, so `size()` can be called safely.
2168 struct [[nodiscard]] annotation_guard {
2169 annotation_guard(const annotation_guard&) = delete;
2170 annotation_guard& operator=(const annotation_guard&) = delete;
2171
2172 constexpr annotation_guard(basic_string& str)
2173 : str(str) {
2174 str.annotate_delete();
2175 }
2176
2177 constexpr ~annotation_guard() {
2178 str.annotate_new(str.size());
2179 }
2180
2181 basic_string& str;
2182 };
2183
2184 template <size_type a>
2185 static constexpr size_type align_it(size_type s) noexcept {
2186 return (s + (a - 1)) & ~(a - 1);
2187 }
2188
2189 enum { alignment = 8 };
2190
2191 static constexpr size_type recommend(size_type s) noexcept {
2192 if (s < min_cap) {
2193 return min_cap - 1;
2194 }
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;
2199 }
2200
2201 PLUGIFY_ASSERT(guess >= s, "recommendation is below the requested size");
2202 return guess;
2203 }
2204
2205 inline constexpr void init(const value_type* s, size_type sz);
2206 inline constexpr void init(size_type n, value_type c);
2207
2208 // Slow path for the (inlined) copy constructor for 'long' strings.
2209 // Always externally instantiated and not inlined.
2210 // Requires that s is zero terminated.
2211 // The main reason for this function to exist is because for unstable, we
2212 // want to allow inlining of the copy constructor. However, we don't want
2213 // to call the init() functions as those are marked as inline which may
2214 // result in over-aggressive inlining by the compiler, where our aim is
2215 // to only inline the fast path code directly in the ctor.
2216 PLUGIFY_NOINLINE constexpr void init_copy_ctor_external(const value_type* s, size_type sz);
2217
2218 template <std::input_iterator InputIterator>
2219 inline constexpr void init(InputIterator first, InputIterator last);
2220
2221 template <std::forward_iterator ForwardIterator>
2222 inline constexpr void init(ForwardIterator first, ForwardIterator last);
2223
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);
2228
2229 constexpr void grow_by_without_replace(
2230 size_type old_cap,
2231 size_type delta_cap,
2232 size_type old_sz,
2233 size_type n_copy,
2234 size_type n_del,
2235 size_type n_add = 0
2236 );
2237 constexpr void grow_by_and_replace(
2238 size_type old_cap,
2239 size_type delta_cap,
2240 size_type old_sz,
2241 size_type n_copy,
2242 size_type n_del,
2243 size_type n_add,
2244 const value_type* p_new_stuff
2245 );
2246
2247 // assign_no_alias is invoked for assignment operations where we
2248 // have proof that the input does not alias the current instance.
2249 // For example, operator=(basic_string) performs a 'self' check.
2250 template <bool is_short>
2251 PLUGIFY_NOINLINE constexpr basic_string& assign_no_alias(const value_type* s, size_type n);
2252
2253 constexpr void erase_to_end(size_type pos) {
2254 PLUGIFY_ASSERT(
2255 pos <= capacity(),
2256 "Trying to erase at position outside the strings capacity!"
2257 );
2258 null_terminate_at(std::to_address(get_pointer()), pos);
2259 }
2260
2261 // erase_external_with_move is invoked for erase() invocations where
2262 // `n ~= npos`, likely requiring memory moves on the string data.
2263 PLUGIFY_NOINLINE constexpr void erase_external_with_move(size_type pos, size_type n);
2264
2265 constexpr void copy_assign_alloc(const basic_string& str) {
2266 copy_assign_alloc(
2267 str,
2268 std::integral_constant<bool, alloc_traits::propagate_on_container_copy_assignment::value>()
2269 );
2270 }
2271
2272 constexpr void copy_assign_alloc(const basic_string& str, std::true_type) {
2273 if (alloc_ == str.alloc_) {
2274 alloc_ = str.alloc_;
2275 } else {
2276 if (!str.is_long()) {
2277 reset_internal_buffer();
2278 alloc_ = str.alloc_;
2279 } else {
2280 annotate_delete();
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);
2285 }
2286 }
2287 }
2288
2289 constexpr void copy_assign_alloc(const basic_string&, std::false_type) noexcept {
2290 }
2291
2292 constexpr void
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;
2296
2297 constexpr void move_assign_alloc(
2298 basic_string& str
2299 ) noexcept(!alloc_traits::propagate_on_container_move_assignment::value || std::is_nothrow_move_assignable_v<allocator_type>) {
2300 move_assign_alloc(
2301 str,
2302 std::integral_constant<bool, alloc_traits::propagate_on_container_move_assignment::value>()
2303 );
2304 }
2305
2306 constexpr void move_assign_alloc(
2307 basic_string& c,
2308 std::true_type
2309 ) noexcept(std::is_nothrow_move_assignable_v<allocator_type>) {
2310 alloc_ = std::move(c.alloc_);
2311 }
2312
2313 constexpr void move_assign_alloc(basic_string&, std::false_type) noexcept {
2314 }
2315
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);
2318
2319 // Assigns the value in s, guaranteed to be n < min_cap in length.
2320 inline constexpr basic_string& assign_short(const value_type* s, size_type n) {
2321 size_type old_size = size();
2322 if (n > old_size) {
2323 annotate_increase(n - old_size);
2324 }
2325 pointer p;
2326 if (is_long()) {
2327 set_long_size(n);
2328 p = get_long_pointer();
2329 } else {
2330 set_short_size(n);
2331 p = get_short_pointer();
2332 }
2333 traits_type::move(std::to_address(p), s, n);
2334 traits_type::assign(p[n], value_type());
2335 if (old_size > n) {
2336 annotate_shrink(old_size);
2337 }
2338 return *this;
2339 }
2340
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);
2345 }
2346 set_size(newsz);
2347 traits_type::assign(p[newsz], value_type());
2348 if (old_size > newsz) {
2349 annotate_shrink(old_size);
2350 }
2351 return *this;
2352 }
2353
2354 template <class T>
2355 constexpr bool addr_in_range(const T& v) const {
2356 return is_pointer_in_range(data(), data() + size() + 1, std::addressof(v));
2357 }
2358
2359 [[noreturn]] static void throw_length_error() {
2360 PLUGIFY_THROW("constructed string size would exceed max_size()", std::length_error);
2361 }
2362
2363 [[noreturn]] static void throw_out_of_range() {
2364 PLUGIFY_THROW("input index is out of bounds", std::out_of_range);
2365 }
2366
2367 friend constexpr basic_string
2368 concatenate_strings<>(const Allocator&, std::type_identity_t<self_view>, std::type_identity_t<self_view>);
2369
2370 template <class CharT2, class Traits2, class Allocator2>
2371 friend inline constexpr bool
2372 operator==(const basic_string<CharT2, Traits2, Allocator2>&, const CharT2*) noexcept;
2373 };
2374
2375 template <
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>;
2381
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>;
2385
2386 template <
2387 class CharT,
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>;
2393
2394#if PLUGIFY_HAS_CXX23
2395 template <
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>>,
2401 Allocator>;
2402#endif
2403
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());
2409 }
2410
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);
2416 }
2417
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());
2423 }
2424
2425 template <class CharT, class Traits, class Allocator>
2426 template <std::input_iterator InputIterator>
2427 constexpr void
2428 basic_string<CharT, Traits, Allocator>::init(InputIterator first, InputIterator last) {
2429 init_with_sentinel(std::move(first), std::move(last));
2430 }
2431
2432 template <class CharT, class Traits, class Allocator>
2433 template <class InputIterator, class Sentinel>
2434 constexpr void
2435 basic_string<CharT, Traits, Allocator>::init_with_sentinel(InputIterator first, Sentinel last) {
2436 rep_ = rep();
2437 annotate_new(0);
2438
2439#if PLUGIFY_HAS_EXCEPTIONS
2440 try {
2441#endif // PLUGIFY_HAS_EXCEPTIONS
2442 for (; first != last; ++first) {
2443 push_back(*first);
2444 }
2445#if PLUGIFY_HAS_EXCEPTIONS
2446 } catch (...) {
2447 reset_internal_buffer();
2448 throw;
2449 }
2450#endif // PLUGIFY_HAS_EXCEPTIONS
2451 }
2452
2453 template <class CharT, class Traits, class Allocator>
2454 template <std::forward_iterator ForwardIterator>
2455 constexpr void
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);
2459 }
2460
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,
2465 Sentinel last,
2466 size_type sz
2467 ) {
2468 pointer p = init_internal_buffer(sz);
2469
2470#if PLUGIFY_HAS_EXCEPTIONS
2471 try {
2472#endif // 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
2476 } catch (...) {
2477 reset_internal_buffer();
2478 throw;
2479 }
2480#endif // PLUGIFY_HAS_EXCEPTIONS
2481 }
2482
2483 template <class CharT, class Traits, class Allocator>
2484 constexpr void basic_string<CharT, Traits, Allocator>::grow_by_and_replace(
2485 size_type old_cap,
2486 size_type delta_cap,
2487 size_type old_sz,
2488 size_type n_copy,
2489 size_type n_del,
2490 size_type n_add,
2491 const value_type* p_new_stuff
2492 ) {
2493 size_type ms = max_size();
2494 if (delta_cap > ms - old_cap) {
2495 throw_length_error();
2496 }
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))
2500 : ms;
2501 annotate_delete();
2502 [[maybe_unused]] auto guard = make_scope_guard(annotate_new_size(*this));
2503 long_ buffer = allocate_long_buffer(alloc_, cap);
2504 if (n_copy != 0) {
2505 traits_type::copy(std::to_address(buffer.data_), std::to_address(old_p), n_copy);
2506 }
2507 if (n_add != 0) {
2508 traits_type::copy(std::to_address(buffer.data_) + n_copy, p_new_stuff, n_add);
2509 }
2510 size_type sec_cp_sz = old_sz - n_del - n_copy;
2511 if (sec_cp_sz != 0) {
2512 traits_type::copy(
2513 std::to_address(buffer.data_) + n_copy + n_add,
2514 std::to_address(old_p) + n_copy + n_del,
2515 sec_cp_sz
2516 );
2517 }
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);
2521 }
2522
2523 template <class CharT, class Traits, class Allocator>
2524 constexpr void basic_string<CharT, Traits, Allocator>::grow_by_without_replace(
2525 size_type old_cap,
2526 size_type delta_cap,
2527 size_type old_sz,
2528 size_type n_copy,
2529 size_type n_del,
2530 size_type n_add
2531 ) {
2532 annotate_delete();
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();
2537 }
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))
2541 : ms;
2542 long_ buffer = allocate_long_buffer(alloc_, cap);
2543 if (n_copy != 0) {
2544 traits_type::copy(std::to_address(buffer.data_), std::to_address(old_p), n_copy);
2545 }
2546 size_type sec_cp_sz = old_sz - n_del - n_copy;
2547 if (sec_cp_sz != 0) {
2548 traits_type::copy(
2549 std::to_address(buffer.data_) + n_copy + n_add,
2550 std::to_address(old_p) + n_copy + n_del,
2551 sec_cp_sz
2552 );
2553 }
2554
2555 // This is -1 to make sure the caller sets the size properly, since old versions of this
2556 // function didn't set the size at all.
2557 buffer.size_ = npos;
2558 replace_internal_buffer(buffer);
2559 set_long_size(old_sz - n_del + n_add);
2560 }
2561
2562 // assign
2563
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();
2570 if (n >= cap) {
2571 grow_by_and_replace(cap - 1, n - cap + 1, size, 0, size, n, s);
2572 return *this;
2573 }
2574
2575 annotate_delete();
2576 [[maybe_unused]] auto guard = make_scope_guard(annotate_new_size(*this));
2577 pointer p;
2578 if (is_short) {
2579 p = get_short_pointer();
2580 set_short_size(n);
2581 } else {
2582 p = get_long_pointer();
2583 set_long_size(n);
2584 }
2585 traits_type::copy(std::to_address(p), s, n);
2586 traits_type::assign(p[n], value_type());
2587 return *this;
2588 }
2589
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();
2595 if (cap >= n) {
2596 if (n > sz) {
2597 annotate_increase(n - sz);
2598 }
2599 value_type* p = std::to_address(get_pointer());
2600 traits_type::move(p, s, n);
2601 return null_terminate_at(p, n);
2602 } else {
2603 grow_by_and_replace(cap, n - cap, sz, 0, sz, n, s);
2604 return *this;
2605 }
2606 }
2607
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);
2614 }
2615
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();
2621 if (cap < n) {
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);
2626 }
2627 value_type* p = std::to_address(get_pointer());
2628 traits_type::assign(p, n, c);
2629 return null_terminate_at(p, n);
2630 }
2631
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);
2638 }
2639 pointer p;
2640 if (is_long()) {
2641 p = get_long_pointer();
2642 set_long_size(1);
2643 } else {
2644 p = get_short_pointer();
2645 set_short_size(1);
2646 }
2647 traits_type::assign(*p, c);
2648 traits_type::assign(*++p, value_type());
2649 if (old_size > 1) {
2650 annotate_shrink(old_size);
2651 }
2652 return *this;
2653 }
2654
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)) {
2659 return *this;
2660 }
2661
2662 copy_assign_alloc(str);
2663
2664 if (is_long()) {
2665 return assign_no_alias<false>(str.data(), str.size());
2666 }
2667
2668 if (str.is_long()) {
2669 return assign_no_alias<true>(str.data(), str.size());
2670 }
2671
2672 annotate_delete();
2673 [[maybe_unused]] auto guard = make_scope_guard(annotate_new_size(*this));
2674 rep_ = str.rep_;
2675
2676 return *this;
2677 }
2678
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
2683 ) {
2684 if (alloc_ != str.alloc_) {
2685 assign(str);
2686 } else {
2687 move_assign(str, std::true_type());
2688 }
2689 }
2690
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 {
2694 annotate_delete();
2695 if (is_long()) {
2696 reset_internal_buffer();
2697 }
2698 size_type str_old_size = str.size();
2699 bool str_was_short = !str.is_long();
2700
2701 move_assign_alloc(str);
2702 rep_ = str.rep_;
2703 str.set_short_size(0);
2704 traits_type::assign(str.get_short_pointer()[0], value_type());
2705
2706 if (str_was_short && this != std::addressof(str)) {
2707 str.annotate_shrink(str_old_size);
2708 } else {
2709 // ASan annotations: was long, so object memory is unpoisoned as new.
2710 // Or is same as *this, and annotate_delete() was called.
2711 str.annotate_new(0);
2712 }
2713
2714 // ASan annotations: Guard against `std::string s; s = std::move(s);`
2715 // You can find more here: https://en.cppreference.com/w/cpp/utility/move
2716 // Quote: "Unless otherwise specified, all standard library objects that have been moved
2717 // from are placed in a "valid but unspecified state", meaning the object's class
2718 // invariants hold (so functions without preconditions, such as the assignment operator,
2719 // can be safely used on the object after it was moved from):"
2720 // Quote: "v = std::move(v); // the value of v is unspecified"
2721 if (!is_long() && std::addressof(str) != this) {
2722 // If it is long string, delete was never called on original str's buffer.
2723 annotate_new(get_short_size());
2724 }
2725 }
2726
2727 template <class CharT, class Traits, class Allocator>
2728 template <class InputIterator, class Sentinel>
2729 constexpr void
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());
2733 }
2734
2735 template <class CharT, class Traits, class Allocator>
2736 template <class Iterator, class Sentinel>
2737 constexpr void
2738 basic_string<CharT, Traits, Allocator>::assign_trivial(Iterator first, Sentinel last, size_type n) {
2739 PLUGIFY_ASSERT(
2740 string_is_trivial_iterator_v<Iterator>,
2741 "The iterator type given to `assign_trivial` must be trivial"
2742 );
2743
2744 size_type old_size = size();
2745 size_type cap = capacity();
2746 if (cap < n) {
2747 // Unlike `append` functions, if the input range points into the string itself, there is
2748 // no case that the input range could get invalidated by reallocation:
2749 // 1. If the input range is a subset of the string itself, its size cannot exceed the
2750 // capacity of the string,
2751 // thus no reallocation would happen.
2752 // 2. In the exotic case where the input range is the byte representation of the string
2753 // itself, the string
2754 // object itself stays valid even if reallocation happens.
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);
2760 }
2761 pointer p = get_pointer();
2762 for (; first != last; ++p, (void) ++first) {
2763 traits_type::assign(*p, *first);
2764 }
2765 traits_type::assign(*p, value_type());
2766 set_size(n);
2767 if (n < old_size) {
2768 annotate_shrink(old_size);
2769 }
2770 }
2771
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();
2776 if (pos > sz) {
2777 this->throw_out_of_range();
2778 }
2779 return assign(str.data() + pos, std::min(n, sz - pos));
2780 }
2781
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));
2786 }
2787
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);
2794 }
2795 return assign_external(s);
2796 }
2797
2798 // append
2799
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();
2806 if (cap - sz < n) {
2807 grow_by_and_replace(cap, sz + n - cap, sz, sz, 0, n, s);
2808 return *this;
2809 }
2810
2811 if (n == 0) {
2812 return *this;
2813 }
2814
2815 annotate_increase(n);
2816 value_type* p = std::to_address(get_pointer());
2817 traits_type::copy(p + sz, s, n);
2818 sz += n;
2819 set_size(sz);
2820 traits_type::assign(p[sz], value_type());
2821 return *this;
2822 }
2823
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) {
2827 if (n == 0) {
2828 return *this;
2829 }
2830
2831 size_type cap = capacity();
2832 size_type sz = size();
2833 if (cap - sz < n) {
2834 grow_by_without_replace(cap, sz + n - cap, sz, sz, 0);
2835 }
2836 annotate_increase(n);
2837 pointer p = get_pointer();
2838 traits_type::assign(std::to_address(p) + sz, n, c);
2839 sz += n;
2840 set_size(sz);
2841 traits_type::assign(p[sz], value_type());
2842 return *this;
2843 }
2844
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();
2848 size_type cap;
2849 size_type sz;
2850 if (is_short) {
2851 cap = min_cap - 1;
2852 sz = get_short_size();
2853 } else {
2854 cap = get_long_cap() - 1;
2855 sz = get_long_size();
2856 }
2857 if (sz == cap) {
2858 grow_by_without_replace(cap, 1, sz, sz, 0);
2859 is_short = false; // the string is always long after grow_by
2860 }
2861 annotate_increase(1);
2862 pointer p;
2863 if (is_short) {
2864 p = get_short_pointer() + sz;
2865 set_short_size(sz + 1);
2866 } else {
2867 p = get_long_pointer() + sz;
2868 set_long_size(sz + 1);
2869 }
2870 traits_type::assign(*p, c);
2871 traits_type::assign(*++p, value_type());
2872 }
2873
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();
2878 if (pos > sz) {
2879 this->throw_out_of_range();
2880 }
2881 return append(str.data() + pos, std::min(n, sz - pos));
2882 }
2883
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));
2889 }
2890
2891 // insert
2892
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();
2898 if (pos > sz) {
2899 this->throw_out_of_range();
2900 }
2901 size_type cap = capacity();
2902
2903 if (cap - sz < n) {
2904 grow_by_and_replace(cap, sz + n - cap, sz, pos, 0, n, s);
2905 return *this;
2906 }
2907
2908 if (n == 0) {
2909 return *this;
2910 }
2911
2912 annotate_increase(n);
2913 value_type* p = std::to_address(get_pointer());
2914 size_type n_move = sz - pos;
2915 if (n_move != 0) {
2916 if (is_pointer_in_range(p + pos, p + sz, s)) {
2917 s += n;
2918 }
2919 traits_type::move(p + pos + n, p + pos, n_move);
2920 }
2921 traits_type::move(p + pos, s, n);
2922 sz += n;
2923 set_size(sz);
2924 traits_type::assign(p[sz], value_type());
2925 return *this;
2926 }
2927
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();
2932 if (pos > sz) {
2933 this->throw_out_of_range();
2934 }
2935
2936 if (n == 0) {
2937 return *this;
2938 }
2939
2940 size_type cap = capacity();
2941 value_type* p;
2942 if (cap - sz >= n) {
2943 annotate_increase(n);
2944 p = std::to_address(get_pointer());
2945 size_type n_move = sz - pos;
2946 if (n_move != 0) {
2947 traits_type::move(p + pos + n, p + pos, n_move);
2948 }
2949 } else {
2950 grow_by_without_replace(cap, sz + n - cap, sz, pos, 0, n);
2951 p = std::to_address(get_long_pointer());
2952 }
2953 traits_type::assign(p + pos, n, c);
2954 sz += n;
2955 set_size(sz);
2956 traits_type::assign(p[sz], value_type());
2957 return *this;
2958 }
2959
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(
2964 const_iterator pos,
2965 Iterator first,
2966 Sentinel last,
2967 size_type n
2968 ) {
2969 size_type ip = static_cast<size_type>(pos - begin());
2970 if (n == 0) {
2971 return begin() + ip;
2972 }
2973
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));
2976 } else {
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());
2979 }
2980 }
2981
2982 template <class CharT, class Traits, class Allocator>
2983 constexpr basic_string<CharT, Traits, Allocator>& basic_string<CharT, Traits, Allocator>::insert(
2984 size_type pos1,
2985 const basic_string& str,
2986 size_type pos2,
2987 size_type n
2988 ) {
2989 size_type str_sz = str.size();
2990 if (pos2 > str_sz) {
2991 this->throw_out_of_range();
2992 }
2993 return insert(pos1, str.data() + pos2, std::min(n, str_sz - pos2));
2994 }
2995
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));
3001 }
3002
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();
3009 value_type* p;
3010 if (cap == sz) {
3011 grow_by_without_replace(cap, 1, sz, ip, 0, 1);
3012 p = std::to_address(get_long_pointer());
3013 } else {
3014 annotate_increase(1);
3015 p = std::to_address(get_pointer());
3016 size_type n_move = sz - ip;
3017 if (n_move != 0) {
3018 traits_type::move(p + ip + 1, p + ip, n_move);
3019 }
3020 }
3021 traits_type::assign(p[ip], c);
3022 traits_type::assign(p[++sz], value_type());
3023 set_size(sz);
3024 return begin() + static_cast<difference_type>(ip);
3025 }
3026
3027 // replace
3028
3029 template <class CharT, class Traits, class Allocator>
3030 constexpr basic_string<CharT, Traits, Allocator>&
3031 basic_string<CharT, Traits, Allocator>::replace(
3032 size_type pos,
3033 size_type n1,
3034 const value_type* s,
3035 size_type n2
3036 ) {
3037 PLUGIFY_ASSERT(n2 == 0 || s != nullptr, "string::replace received nullptr");
3038 size_type sz = size();
3039 if (pos > sz) {
3040 this->throw_out_of_range();
3041 }
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);
3046 return *this;
3047 }
3048
3049 value_type* p = std::to_address(get_pointer());
3050 if (n1 != n2) {
3051 if (n2 > n1) {
3052 annotate_increase(n2 - n1);
3053 }
3054 size_type n_move = sz - pos - n1;
3055 if (n_move != 0) {
3056 if (n1 > n2) {
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));
3060 }
3061 if (is_pointer_in_range(p + pos + 1, p + sz, s)) {
3062 if (p + pos + n1 <= s) {
3063 s += n2 - n1;
3064 } else { // p + pos < s < p + pos + n1
3065 traits_type::move(p + pos, s, n1);
3066 pos += n1;
3067 s += n2;
3068 n2 -= n1;
3069 n1 = 0;
3070 }
3071 }
3072 traits_type::move(p + pos + n2, p + pos + n1, n_move);
3073 }
3074 }
3075 traits_type::move(p + pos, s, n2);
3076 return null_terminate_at(p, sz + (n2 - n1));
3077 }
3078
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();
3083 if (pos > sz) {
3084 this->throw_out_of_range();
3085 }
3086 n1 = std::min(n1, sz - pos);
3087 size_type cap = capacity();
3088 value_type* p;
3089 if (cap - sz + n1 >= n2) {
3090 p = std::to_address(get_pointer());
3091 if (n1 != n2) {
3092 if (n2 > n1) {
3093 annotate_increase(n2 - n1);
3094 }
3095 size_type n_move = sz - pos - n1;
3096 if (n_move != 0) {
3097 traits_type::move(p + pos + n2, p + pos + n1, n_move);
3098 }
3099 }
3100 } else {
3101 grow_by_without_replace(cap, sz - n1 + n2 - cap, sz, pos, n1, n2);
3102 p = std::to_address(get_long_pointer());
3103 }
3104 traits_type::assign(p + pos, n2, c);
3105 return null_terminate_at(p, sz - (n1 - n2));
3106 }
3107
3108 template <class CharT, class Traits, class Allocator>
3109 constexpr basic_string<CharT, Traits, Allocator>&
3110 basic_string<CharT, Traits, Allocator>::replace(
3111 size_type pos1,
3112 size_type n1,
3113 const basic_string& str,
3114 size_type pos2,
3115 size_type n2
3116 ) {
3117 size_type str_sz = str.size();
3118 if (pos2 > str_sz) {
3119 this->throw_out_of_range();
3120 }
3121 return replace(pos1, n1, str.data() + pos2, std::min(n2, str_sz - pos2));
3122 }
3123
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));
3129 }
3130
3131 // erase
3132
3133 // 'externally instantiated' erase() implementation, called when n != npos.
3134 // Does not check pos against size()
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) {
3138 if (n == 0) {
3139 return;
3140 }
3141
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;
3146 if (n_move != 0) {
3147 traits_type::move(p + pos, p + pos + n, n_move);
3148 }
3149 null_terminate_at(p, sz - n);
3150 }
3151
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) {
3155 if (pos > size()) {
3156 this->throw_out_of_range();
3157 }
3158 if (n == npos) {
3159 erase_to_end(pos);
3160 } else {
3161 erase_external_with_move(pos, n);
3162 }
3163 return *this;
3164 }
3165
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);
3172 erase(r, 1);
3173 return b + static_cast<difference_type>(r);
3174 }
3175
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);
3184 }
3185
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);
3190 }
3191
3192 template <class CharT, class Traits, class Allocator>
3193 inline constexpr void basic_string<CharT, Traits, Allocator>::clear() noexcept {
3194 size_type old_size;
3195 if (is_long()) {
3196 old_size = get_long_size();
3197 traits_type::assign(*get_long_pointer(), value_type());
3198 set_long_size(0);
3199 } else {
3200 old_size = get_short_size();
3201 traits_type::assign(*get_short_pointer(), value_type());
3202 set_short_size(0);
3203 }
3204 annotate_shrink(old_size);
3205 }
3206
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();
3210 if (n > sz) {
3211 append(n - sz, c);
3212 } else {
3213 erase_to_end(n);
3214 }
3215 }
3216
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();
3221 }
3222
3223 // Make sure reserve(n) never shrinks. This is technically only required in C++20
3224 // and later (since P0966R1), however we provide consistent behavior in all Standard
3225 // modes because this function is instantiated in the shared library.
3226 if (requested_capacity <= capacity()) {
3227 return;
3228 }
3229
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);
3235 }
3236
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()) {
3241 return;
3242 }
3243
3244 PLUGIFY_ASSERT(is_long(), "Trying to shrink small string");
3245
3246 // We're a long string and we're shrinking into the small buffer.
3247 const auto ptr = get_long_pointer();
3248 const auto size = get_long_size();
3249 const auto cap = get_long_cap();
3250
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);
3256 return;
3257 }
3258
3259#if PLUGIFY_HAS_EXCEPTIONS
3260 try {
3261#endif // PLUGIFY_HAS_EXCEPTIONS
3262 [[maybe_unused]] annotation_guard g(*this);
3263 long_ buffer = allocate_long_buffer(alloc_, size);
3264
3265 // The Standard mandates shrink_to_fit() does not increase the capacity.
3266 // With equal capacity keep the existing buffer. This avoids extra work
3267 // due to swapping the elements.
3268 if (buffer.cap_ * endian_factor - 1 >= capacity()) {
3269 alloc_traits::deallocate(alloc_, buffer.data_, buffer.cap_ * endian_factor);
3270 return;
3271 }
3272
3273 traits_type::copy(
3274 std::to_address(buffer.data_),
3275 std::to_address(get_long_pointer()),
3276 size + 1
3277 );
3278 replace_internal_buffer(buffer);
3279#if PLUGIFY_HAS_EXCEPTIONS
3280 } catch (...) {
3281 return;
3282 }
3283#endif // PLUGIFY_HAS_EXCEPTIONS
3284 }
3285
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 {
3289 if (n >= size()) {
3290 this->throw_out_of_range();
3291 }
3292 return (*this)[n];
3293 }
3294
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) {
3298 if (n >= size()) {
3299 this->throw_out_of_range();
3300 }
3301 return (*this)[n];
3302 }
3303
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();
3308 if (pos > sz) {
3309 this->throw_out_of_range();
3310 }
3311 size_type rlen = std::min(n, sz - pos);
3312 traits_type::copy(s, data() + pos, rlen);
3313 return rlen;
3314 }
3315
3316 template <class CharT, class Traits, class Allocator>
3317 inline constexpr void basic_string<CharT, Traits, Allocator>::swap(basic_string& str) noexcept {
3318 PLUGIFY_ASSERT(
3319 alloc_traits::propagate_on_container_swap::value || alloc_traits::is_always_equal::value || alloc_ == str.alloc_,
3320 "swapping non-equal allocators"
3321 );
3322 if (!is_long()) {
3323 annotate_delete();
3324 }
3325 if (this != std::addressof(str) && !str.is_long()) {
3326 str.annotate_delete();
3327 }
3328 std::swap(rep_, str.rep_);
3329 swap_allocator(alloc_, str.alloc_);
3330 if (!is_long()) {
3331 annotate_new(get_short_size());
3332 }
3333 if (this != std::addressof(str) && !str.is_long()) {
3334 str.annotate_new(str.get_short_size());
3335 }
3336 }
3337
3338 // compare
3339
3340 template <class CharT, class Traits, class Allocator>
3341 inline constexpr int basic_string<CharT, Traits, Allocator>::compare(
3342 size_type pos1,
3343 size_type n1,
3344 const value_type* s,
3345 size_type n2
3346 ) const {
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();
3351 }
3352 size_type rlen = std::min(n1, sz - pos1);
3353 int r = traits_type::compare(data() + pos1, s, std::min(rlen, n2));
3354 if (r == 0) {
3355 if (rlen < n2) {
3356 r = -1;
3357 } else if (rlen > n2) {
3358 r = 1;
3359 }
3360 }
3361 return r;
3362 }
3363
3364 // invariants
3365
3366 template <class CharT, class Traits, class Allocator>
3367 inline constexpr bool basic_string<CharT, Traits, Allocator>::invariants() const {
3368 if (size() > capacity()) {
3369 return false;
3370 }
3371 if (capacity() < min_cap - 1) {
3372 return false;
3373 }
3374 if (data() == nullptr) {
3375 return false;
3376 }
3377 if (!Traits::eq(data()[size()], value_type())) {
3378 return false;
3379 }
3380 return true;
3381 }
3382
3383 // operator==
3384
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
3389 ) noexcept {
3390 size_t lhs_sz = lhs.size();
3391 return lhs_sz == rhs.size() && Traits::compare(lhs.data(), rhs.data(), lhs_sz) == 0;
3392 }
3393
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
3398 ) noexcept {
3399 PLUGIFY_ASSERT(rhs != nullptr, "operator==(basic_string, char*): received nullptr");
3400
3401 using String = basic_string<CharT, Traits, Allocator>;
3402
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()) {
3406 return false;
3407 }
3408 }
3409 if (rhs_len != lhs.size()) {
3410 return false;
3411 }
3412 return lhs.compare(0, String::npos, rhs, rhs_len) == 0;
3413 }
3414
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
3419 ) noexcept {
3420 return std::basic_string_view<CharT, Traits>(lhs)
3421 <=> std::basic_string_view<CharT, Traits>(rhs);
3422 }
3423
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);
3428 }
3429
3430 // operator +
3431
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
3437 ) {
3438 using String = basic_string<CharT, Traits, Allocator>;
3439 String r(
3440 uninitialized_size_tag(),
3441 str1.size() + str2.size(),
3442 String::alloc_traits::select_on_container_copy_construction(alloc)
3443 );
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());
3448 return r;
3449 }
3450
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
3455 ) {
3456 return concatenate_strings<CharT, Traits>(lhs.get_allocator(), lhs, rhs);
3457 }
3458
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);
3463 }
3464
3465 // extern template string operator+ <char, std::char_traits<char>, allocator<char> >(char
3466 // const*, string const&);
3467
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),
3474 rhs
3475 );
3476 }
3477
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);
3482 }
3483
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(),
3489 lhs,
3490 std::basic_string_view<CharT, Traits>(std::addressof(rhs), 1)
3491 );
3492 }
3493#if PLUGIFY_HAS_CXX26
3494
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
3499 ) {
3500 return concatenate_strings<CharT, Traits>(lhs.get_allocator(), lhs, rhs);
3501 }
3502
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
3507 ) {
3508 return concatenate_strings<CharT, Traits>(rhs.get_allocator(), lhs, rhs);
3509 }
3510
3511#endif // PLUGIFY_HAS_CXX26
3512
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
3517 ) {
3518 return std::move(lhs.append(rhs));
3519 }
3520
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
3525 ) {
3526 return std::move(rhs.insert(0, lhs));
3527 }
3528
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));
3533 }
3534
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));
3539 }
3540
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);
3546 }
3547
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));
3552 }
3553
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) {
3557 lhs.push_back(rhs);
3558 return std::move(lhs);
3559 }
3560
3561#if PLUGIFY_HAS_CXX26
3562
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
3567 ) {
3568 return std::move(lhs.append(rhs));
3569 }
3570
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
3575 ) {
3576 return std::move(rhs.insert(0, lhs));
3577 }
3578
3579#endif // PLUGIFY_HAS_CXX26
3580
3581 // swap
3582
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))) {
3588 lhs.swap(rhs);
3589 }
3590
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();
3597 }
3598
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();
3605 }
3606
3607 // basic_string typedef-names
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>;
3613
3614#ifndef PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
3615 // 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);
3619
3620 auto ret = strtol(cstr, &ptr, base);
3621 if (pos != nullptr) {
3622 *pos = static_cast<size_t>(cstr - ptr);
3623 }
3624
3625 return static_cast<int>(ret);
3626 }
3627
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);
3631
3632 auto ret = strtol(cstr, &ptr, base);
3633 if (pos != nullptr) {
3634 *pos = static_cast<size_t>(cstr - ptr);
3635 }
3636
3637 return ret;
3638 }
3639
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);
3643
3644 auto ret = strtoll(cstr, &ptr, base);
3645 if (pos != nullptr) {
3646 *pos = static_cast<size_t>(cstr - ptr);
3647 }
3648
3649 return ret;
3650 }
3651
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);
3655
3656 auto ret = strtoul(cstr, &ptr, base);
3657 if (pos != nullptr) {
3658 *pos = static_cast<size_t>(cstr - ptr);
3659 }
3660
3661 return ret;
3662 }
3663
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);
3667
3668 auto ret = strtoull(cstr, &ptr, base);
3669 if (pos != nullptr) {
3670 *pos = static_cast<size_t>(cstr - ptr);
3671 }
3672
3673 return ret;
3674 }
3675
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);
3679
3680 auto ret = strtof(cstr, &ptr);
3681 if (pos != nullptr) {
3682 *pos = static_cast<size_t>(cstr - ptr);
3683 }
3684
3685 return ret;
3686 }
3687
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);
3691
3692 auto ret = strtod(cstr, &ptr);
3693 if (pos != nullptr) {
3694 *pos = static_cast<size_t>(cstr - ptr);
3695 }
3696
3697 return ret;
3698 }
3699
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);
3703
3704 auto ret = strtold(cstr, &ptr);
3705 if (pos != nullptr) {
3706 *pos = static_cast<size_t>(cstr - ptr);
3707 }
3708
3709 return ret;
3710 }
3711
3712 namespace detail {
3713 template <typename S, typename V>
3714 constexpr S to_string(V v) {
3715 // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
3716 // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented),
3717 // so we need +1 here.
3718 constexpr std::size_t bufSize = std::numeric_limits<V>::digits10 + 2; // +1 for minus,
3719 // +1 for
3720 // digits10
3721 char buf[bufSize];
3722 const auto res = std::to_chars(buf, buf + bufSize, v);
3723 return S(buf, res.ptr);
3724 }
3725
3726 typedef int (*wide_printf)(wchar_t* __restrict, std::size_t, const wchar_t* __restrict, ...);
3727
3728#if PLUGIFY_COMPILER_MSVC
3729 inline int truncate_snwprintf(
3730 wchar_t* __restrict buffer,
3731 std::size_t count,
3732 const wchar_t* __restrict format,
3733 ...
3734 ) {
3735 int r;
3736 va_list args;
3737 va_start(args, format);
3738 r = _vsnwprintf_s(buffer, count, _TRUNCATE, format, args);
3739 va_end(args);
3740 return r;
3741 }
3742#endif
3743
3744 constexpr wide_printf get_swprintf() noexcept {
3745#if PLUGIFY_COMPILER_MSVC
3746 return static_cast<
3747 int(__cdecl*)(wchar_t* __restrict, std::size_t, const wchar_t* __restrict, ...)>(
3748 truncate_snwprintf
3749 );
3750#else
3751 return swprintf;
3752#endif
3753 }
3754
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;
3758 S s;
3759 s.resize(s.capacity());
3760 size_type available = s.size();
3761 while (true) {
3762 int status = sprintf_like(&s[0], available + 1, fmt, v);
3763 if (status >= 0) {
3764 auto used = static_cast<size_type>(status);
3765 if (used <= available) {
3766 s.resize(used);
3767 break;
3768 }
3769 available = used; // Assume this is advice of how much space we need.
3770 } else {
3771 available = available * 2 + 1;
3772 }
3773 s.resize(available);
3774 }
3775 return s;
3776 }
3777 } // namespace detail
3778
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); }
3788
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); }
3798#endif // PLUGIFY_STRING_NO_NUMERIC_CONVERSIONS
3799
3800#ifndef PLUGIFY_STRING_NO_STD_HASH
3801 // hash support
3802 namespace detail {
3803 template <
3804 typename Char,
3805 typename Allocator,
3806 typename String = basic_string<Char, std::char_traits<Char>, Allocator>
3807 >
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));
3811 }
3812 };
3813 } // namespace detail
3814#endif // PLUGIFY_STRING_NO_STD_HASH
3815
3816#ifndef PLUGIFY_STRING_NO_STD_FORMAT
3817 // format support
3818 namespace detail {
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>) {
3822 return "{}";
3823 }
3824 if constexpr (std::is_same_v<Char, wchar_t>) {
3825 return L"{}";
3826 }
3827 if constexpr (std::is_same_v<Char, char16_t>) {
3828 return u"{}";
3829 }
3830 if constexpr (std::is_same_v<Char, char32_t>) {
3831 return U"{}";
3832 }
3833 return "";
3834 }
3835
3836 template <
3837 typename Char,
3838 typename Allocator,
3839 typename String = basic_string<Char, std::char_traits<Char>, Allocator>
3840 >
3842 constexpr auto parse(std::format_parse_context& ctx) {
3843 return ctx.begin();
3844 }
3845
3846 template <class FormatContext>
3847 auto format(const String& str, FormatContext& ctx) const {
3848 return std::format_to(ctx.out(), format_string<Char>(), str.c_str());
3849 }
3850 };
3851 }
3852#endif // PLUGIFY_STRING_NO_STD_FORMAT
3853
3854 inline namespace literals {
3855 inline namespace string_literals {
3857
3858#if PLUGIFY_COMPILER_CLANG
3859 PLUGIFY_WARN_IGNORE("-Wuser-defined-literals")
3860#elif PLUGIFY_COMPILER_GCC
3861 PLUGIFY_WARN_IGNORE("-Wliteral-suffix")
3862#elif PLUGIFY_COMPILER_MSVC
3864#endif
3865
3866 // suffix for basic_string literals
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}; }
3872
3873 PLUGIFY_WARN_POP()
3874 } // namespace string_literals
3875 } // namespace literals
3876} // namespace plg
3877
3878#ifndef PLUGIFY_STRING_NO_STD_HASH
3879// hash support
3880namespace std {
3881 template <typename Allocator>
3882 struct hash<plg::basic_string<char, std::char_traits<char>, Allocator>>
3883 : plg::detail::string_hash_base<char, Allocator> {};
3884
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> {};
3888
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> {};
3892
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> {};
3896
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> {};
3900} // namespace std
3901#endif // PLUGIFY_STRING_NO_STD_HASH
3902
3903#ifndef PLUGIFY_STRING_NO_STD_FORMAT
3904// format support
3905#ifdef FMT_HEADER_ONLY
3906namespace fmt {
3907#else
3908namespace std {
3909#endif
3910 template <typename Allocator>
3911 struct formatter<plg::basic_string<char, std::char_traits<char>, Allocator>>
3912 : plg::detail::string_formatter_base<char, Allocator> {};
3913
3914 template <typename Allocator>
3915 struct formatter<plg::basic_string<char8_t, std::char_traits<char8_t>, Allocator>>
3916 : plg::detail::string_formatter_base<char8_t, Allocator> {};
3917
3918 template <typename Allocator>
3919 struct formatter<plg::basic_string<char16_t, std::char_traits<char16_t>, Allocator>>
3920 : plg::detail::string_formatter_base<char16_t, Allocator> {};
3921
3922 template <typename Allocator>
3923 struct formatter<plg::basic_string<char32_t, std::char_traits<char32_t>, Allocator>>
3924 : plg::detail::string_formatter_base<char32_t, Allocator> {};
3925
3926 template <typename Allocator>
3927 struct formatter<plg::basic_string<wchar_t, std::char_traits<wchar_t>, Allocator>>
3928 : plg::detail::string_formatter_base<wchar_t, Allocator> {};
3929} // namespace std
3930#endif // PLUGIFY_STRING_NO_STD_FORMAT
3931
3932template <typename Char, typename Traits, typename Alloc>
3933std::ostream& operator<<(std::ostream& os, const plg::basic_string<Char, Traits, Alloc>& str) {
3934 os << str.c_str();
3935 return os;
3936}
3937
3938#ifndef PLUGIFY_STRING_NO_STD_FORMAT
3939#include <functional>
3940
3941namespace plg {
3942 template <typename Range>
3943 constexpr string join(const Range& range, std::string_view separator) {
3944 string result;
3945
3946 auto it = range.cbegin();
3947 auto end = range.cend();
3948
3949 if (it == end) {
3950 return result;
3951 }
3952
3953 // First pass: compute total size
3954 size_t total_size = 0;
3955 size_t count = 0;
3956
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();
3961 } else {
3962 total_size += std::formatted_size("{}", *tmp);
3963 }
3964 ++count;
3965 }
3966 if (count > 1) {
3967 total_size += (count - 1) * separator.size();
3968 }
3969 result.reserve(total_size);
3970
3971 auto in = std::back_inserter(result);
3972
3973 // Second pass: actual formatting
3974 /*if (it != end)*/ { std::format_to(in, "{}", *it++); }
3975 while (it != end) {
3976 std::format_to(in, "{}{}", separator, *it++);
3977 }
3978
3979 return result;
3980 }
3981
3982 template <typename Range, typename Proj>
3983 constexpr string join(const Range& range, Proj&& proj, std::string_view separator) {
3984 string result;
3985
3986 auto it = range.cbegin();
3987 auto end = range.cend();
3988
3989 if (it == end) {
3990 return result;
3991 }
3992
3993 // First pass: compute total size
3994 size_t total_size = 0;
3995 size_t count = 0;
3996
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();
4002 } else {
4003 total_size += std::formatted_size("{}", projected);
4004 }
4005 ++count;
4006 }
4007 if (count > 1) {
4008 total_size += (count - 1) * separator.size();
4009 }
4010 result.reserve(total_size);
4011
4012 auto out = std::back_inserter(result);
4013
4014 // Second pass: actual formatting
4015 {
4016 auto&& projected = std::invoke(std::forward<Proj>(proj), *it++);
4017 std::format_to(out, "{}", projected);
4018 }
4019 while (it != end) {
4020 auto&& projected = std::invoke(std::forward<Proj>(proj), *it++);
4021 std::format_to(out, "{}{}", separator, projected);
4022 }
4023
4024 return result;
4025 }
4026} // namespace plugify
4027#endif // PLUGIFY_STRING_NO_STD_FORMAT