1 /**
2 * D header file for interaction with C++ std::string.
3 *
4 * Copyright: Copyright (c) 2019 D Language Foundation
5 * License: Distributed under the
6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7 * (See accompanying file LICENSE)
8 * Authors: Guillaume Chatelet
9 * Manu Evans
10 * Source: $(DRUNTIMESRC core/stdcpp/string.d)
11 */
12
13 module core.stdcpp.string;
14
15 import core.stdcpp.allocator;
16 import core.stdcpp.xutility : StdNamespace;
17 import core.stdc.stddef : wchar_t;
18
19 version (OSX)
20 version = Darwin;
21 else version (iOS)
22 version = Darwin;
23 else version (TVOS)
24 version = Darwin;
25 else version (WatchOS)
26 version = Darwin;
27
version(Darwin)28 version (Darwin)
29 {
30 // Apple decided to rock a different ABI... good for them!
31 version = _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT;
32 }
33
version(CppRuntime_Gcc)34 version (CppRuntime_Gcc)
35 {
36 version (_GLIBCXX_USE_CXX98_ABI)
37 {
38 private enum StringNamespace = "std";
39 version = __GTHREADS;
40 }
41 else
42 {
43 import core.internal.traits : AliasSeq;
44 private enum StringNamespace = AliasSeq!("std", "__cxx11");
45 }
46 }
47 else
48 alias StringNamespace = StdNamespace;
49
50 enum DefaultConstruct { value }
51
52 /// Constructor argument for default construction
53 enum Default = DefaultConstruct();
54
55 @nogc:
56
57 /**
58 * Character traits classes specify character properties and provide specific
59 * semantics for certain operations on characters and sequences of characters.
60 */
char_traits(CharT)61 extern(C++, (StdNamespace)) struct char_traits(CharT)
62 {
63 alias char_type = CharT;
64
65 static size_t length(const(char_type)* s) @trusted pure nothrow @nogc
66 {
67 static if (is(char_type == char) || is(char_type == ubyte))
68 {
69 import core.stdc.string : strlen;
70 return strlen(s);
71 }
72 else
73 {
74 size_t len = 0;
75 for (; *s != char_type(0); ++s)
76 ++len;
77 return len;
78 }
79 }
80
81 static char_type* move(char_type* s1, const char_type* s2, size_t n) @trusted pure nothrow @nogc
82 {
83 import core.stdc.string : memmove;
84 import core.stdc.wchar_ : wmemmove;
85 import core.stdc.stddef : wchar_t;
86
87 if (n == 0)
88 return s1;
89
90 version (CRuntime_Microsoft)
91 {
92 enum crt = __traits(getTargetInfo, "cppRuntimeLibrary");
93 static if (crt.length >= 6 && crt[0 .. 6] == "msvcrt")
94 enum use_wmemmove = false; // https://issues.dlang.org/show_bug.cgi?id=20456
95 else
96 enum use_wmemmove = true;
97 }
98 else
99 enum use_wmemmove = true;
100
101 static if (use_wmemmove
102 && (is(char_type == wchar_t)
103 || is(char_type == ushort) && wchar_t.sizeof == ushort.sizeof // Windows
104 || is(char_type == uint) && wchar_t.sizeof == uint.sizeof)) // POSIX
105 return cast(char_type*) wmemmove(s1, s2, n);
106 else
107 return cast(char_type*) memmove(s1, s2, n * char_type.sizeof);
108 }
109 }
110
111 // I don't think we can have these here, otherwise symbols are emit to druntime, and we don't want that...
112 //alias std_string = basic_string!char;
113 //alias std_u16string = basic_string!wchar; // TODO: can't mangle these yet either...
114 //alias std_u32string = basic_string!dchar;
115 //alias std_wstring = basic_string!wchar_t; // TODO: we can't mangle wchar_t properly (yet?)
116
117 /**
118 * D language counterpart to C++ std::basic_string.
119 *
120 * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string)
121 */
122 extern(C++, class)
123 extern(C++, (StringNamespace))
124 struct basic_string(T, Traits = char_traits!T, Alloc = allocator!T)
125 {
126 extern(D):
127 @nogc:
128
129 ///
130 enum size_type npos = size_type.max;
131
132 ///
133 alias size_type = size_t;
134 ///
135 alias difference_type = ptrdiff_t;
136 ///
137 alias value_type = T;
138 ///
139 alias traits_type = Traits;
140 ///
141 alias allocator_type = Alloc;
142 ///
143 alias pointer = value_type*;
144 ///
145 alias const_pointer = const(value_type)*;
146
147 ///
148 alias toString = as_array;
149
150 /// MSVC allocates on default initialisation in debug, which can't be modelled by D `struct`
151 @disable this();
152
153 ///
154 alias length = size;
155 ///
156 alias opDollar = length;
157 ///
emptybasic_string158 bool empty() const nothrow @safe { return size() == 0; }
159
160 ///
161 size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const pure nothrow @safe @nogc { return [start, end]; }
162
163 ///
inoutbasic_string164 ref inout(T) opIndex(size_t index) inout pure nothrow @safe @nogc { return as_array[index]; }
165 ///
inoutbasic_string166 inout(T)[] opIndex(size_t[2] slice) inout pure nothrow @safe @nogc { return as_array[slice[0] .. slice[1]]; }
167 ///
inoutbasic_string168 inout(T)[] opIndex() inout pure nothrow @safe @nogc { return as_array(); }
169
170 /// Two `basic_string`s are equal if they represent the same sequence of code units.
opEqualsbasic_string171 bool opEquals(scope const ref basic_string s) const pure nothrow @safe { return as_array == s.as_array; }
172 /// ditto
opEqualsbasic_string173 bool opEquals(scope const T[] s) const pure nothrow @safe { return as_array == s; }
174
175 /// Performs lexicographical comparison.
opCmpbasic_string176 int opCmp(scope const ref basic_string rhs) const pure nothrow @safe { return __cmp(as_array, rhs.as_array); }
177 /// ditto
opCmpbasic_string178 int opCmp(scope const T[] rhs) const pure nothrow @safe { return __cmp(as_array, rhs); }
179
180 /// Hash to allow `basic_string`s to be used as keys for built-in associative arrays.
181 /// **The result will generally not be the same as C++ `std::hash<std::basic_string<T>>`.**
toHashbasic_string182 size_t toHash() const @nogc nothrow pure @safe { return .hashOf(as_array); }
183
184 ///
clearbasic_string185 void clear() { eos(0); } // TODO: bounds-check
186 ///
187 void resize(size_type n, T c = T(0)) @trusted
188 {
189 if (n <= size())
190 eos(n);
191 else
192 append(n - size(), c);
193 }
194
195 ///
inoutbasic_string196 ref inout(T) front() inout nothrow @safe { return this[0]; }
197 ///
inoutbasic_string198 ref inout(T) back() inout nothrow @safe { return this[$-1]; }
199
200 ///
c_strbasic_string201 const(T)* c_str() const nothrow @safe { return data(); }
202
203 // Modifiers
204 ///
opAssignbasic_string205 ref basic_string opAssign()(auto ref basic_string str) { return assign(str); }
206 // ref basic_string assign(size_type n, T c);
207 ///
opAssignbasic_string208 ref basic_string opAssign(const(T)[] str) { return assign(str); }
209 ///
opAssignbasic_string210 ref basic_string opAssign(T c) { return assign((&c)[0 .. 1]); }
211
212 ///
opIndexAssignbasic_string213 ref basic_string opIndexAssign(T c, size_t index) { as_array[index] = c; return this; }
214 ///
opIndexAssignbasic_string215 ref basic_string opIndexAssign(T c, size_t[2] slice) { as_array[slice[0] .. slice[1]] = c; return this; }
216 ///
opIndexAssignbasic_string217 ref basic_string opIndexAssign(const(T)[] str, size_t[2] slice) { as_array[slice[0] .. slice[1]] = str[]; return this; }
218 ///
opIndexAssignbasic_string219 ref basic_string opIndexAssign(T c) { as_array[] = c; return this; }
220 ///
opIndexAssignbasic_string221 ref basic_string opIndexAssign(const(T)[] str) { as_array[] = str[]; return this; }
222
223 ///
opIndexOpAssignbasic_string224 ref basic_string opIndexOpAssign(string op)(T c, size_t index) { mixin("as_array[index] " ~ op ~ "= c;"); return this; }
225 ///
opIndexOpAssignbasic_string226 ref basic_string opIndexOpAssign(string op)(T c, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= c;"); return this; }
227 ///
opIndexOpAssignbasic_string228 ref basic_string opIndexOpAssign(string op)(const(T)[] str, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= str[];"); return this; }
229 ///
opIndexOpAssignbasic_string230 ref basic_string opIndexOpAssign(string op)(T c) { mixin("as_array[] " ~ op ~ "= c;"); return this; }
231 ///
opIndexOpAssignbasic_string232 ref basic_string opIndexOpAssign(string op)(const(T)[] str) { mixin("as_array[] " ~ op ~ "= str[];"); return this; }
233 ///
appendbasic_string234 ref basic_string append(T c) { return append((&c)[0 .. 1]); }
235 ///
236 ref basic_string opOpAssign(string op : "~")(const(T)[] str) { return append(str); }
237 ///
238 ref basic_string opOpAssign(string op : "~")(T c) { return append((&c)[0 .. 1]); }
239
240 ///
insertbasic_string241 ref basic_string insert(size_type pos, ref const(basic_string) str) { return insert(pos, str.data(), str.size()); }
242 ///
insertbasic_string243 ref basic_string insert(size_type pos, ref const(basic_string) str, size_type subpos, size_type sublen) @trusted
244 {
245 const _strsz = str.size();
246 assert(subpos <= _strsz);
247 // if (subpos > _strsz)
248 // throw new RangeError("subpos exceeds length of str");
249 return insert(pos, str.data() + subpos, min(sublen, _strsz - subpos));
250 }
251 ///
252 ref basic_string insert(S : size_type)(S pos, const(T)* s)
253 {
254 // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict.
255 assert(s);
256 return insert(pos, s, traits_type.length(s));
257 }
258 ///
insertbasic_string259 ref basic_string insert(size_type pos, const(T)[] s) { insert(pos, &s[0], s.length); return this; }
260
261 ///
262 ref basic_string erase(size_type pos = 0) // TODO: bounds-check
263 {
264 // _My_data._Check_offset(pos);
265 eos(pos);
266 return this;
267 }
268 ///
erasebasic_string269 ref basic_string erase(size_type pos, size_type len) // TODO: bounds-check
270 {
271 // _My_data._Check_offset(pos);
272 T[] str = as_array();
273 size_type new_len = str.length - len;
274 this[pos .. new_len] = this[pos + len .. str.length]; // TODO: should be memmove!
275 eos(new_len);
276 return this;
277 }
278
279 ///
replacebasic_string280 ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str) { return replace(pos, len, str.data(), str.size()); }
281 ///
replacebasic_string282 ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str,
283 size_type subpos, size_type sublen=npos)
284 {
285 size_type strsz = str.size();
286 assert(subpos <= strsz);
287 // if (subpos > strsz)
288 // throw new RangeError("subpos exceeds size of str");
289 return replace(pos, len, str.data() + subpos, min(sublen, strsz - subpos));
290 }
291 ///
replacebasic_string292 ref basic_string replace(size_type pos, size_type len, const(value_type)[] s) { return replace(pos, len, s.ptr, s.length); }
293 ///
294 ref basic_string replace(S : size_type)(S pos, size_type len, const(value_type)* s)
295 {
296 // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict.
297 assert(s !is null, "string::replace received null");
298 return replace(pos, len, s, traits_type.length(s));
299 }
300
301 ///
push_backbasic_string302 void push_back(T c) @trusted { append((&c)[0 .. 1]); }
303 ///
pop_backbasic_string304 void pop_back() { erase(size() - 1); }
305
versionbasic_string306 version (CppRuntime_Microsoft)
307 {
308 //----------------------------------------------------------------------------------
309 // Microsoft runtime
310 //----------------------------------------------------------------------------------
311
312 ///
313 this(DefaultConstruct) { _Alloc_proxy(); _Tidy_init(); }
314 ///
315 this(const(T)[] str) { _Alloc_proxy(); _Tidy_init(); assign(str); }
316 ///
317 this(const(T)[] str, ref const(allocator_type) al) { _Alloc_proxy(); _AssignAllocator(al); _Tidy_init(); assign(str); }
318 ///
319 this(this)
320 {
321 _Alloc_proxy();
322 if (_Get_data()._IsAllocated())
323 {
324 T[] _Str = _Get_data()._Mystr;
325 _Tidy_init();
326 assign(_Str);
327 }
328 }
329
330 ///
331 ~this() { _Tidy_deallocate(); }
332
333 ///
334 ref inout(Alloc) get_allocator() inout { return _Getal(); }
335
336 ///
337 size_type max_size() const nothrow @safe { return ((size_t.max / T.sizeof) - 1) / 2; } // HACK: clone the windows version precisely?
338
339 ///
340 size_type size() const nothrow @safe { return _Get_data()._Mysize; }
341 ///
342 size_type capacity() const nothrow @safe { return _Get_data()._Myres; }
343 ///
344 inout(T)* data() inout @safe { return _Get_data()._Myptr; }
345 ///
346 inout(T)[] as_array() scope return inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
347 ///
348 ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
349
350 ///
351 ref basic_string assign(const(T)[] str)
352 {
353 size_type _Count = str.length;
354 auto _My_data = &_Get_data();
355 if (_Count <= _My_data._Myres)
356 {
357 T* _Old_ptr = _My_data._Myptr;
358 _My_data._Mysize = _Count;
359 _Old_ptr[0 .. _Count] = str[]; // TODO: this needs to be a memmove(), does that work here?
360 _Old_ptr[_Count] = T(0);
361 return this;
362 }
363 return _Reallocate_for(_Count, (T* _New_ptr, size_type _Count, const(T)* _Ptr) nothrow {
364 _New_ptr[0 .. _Count] = _Ptr[0 .. _Count];
365 _New_ptr[_Count] = T(0);
366 }, str.ptr);
367 }
368
369 ///
370 ref basic_string assign(const ref basic_string str)
371 {
372 if (&this != &str)
373 assign(str.as_array);
374 return this;
375 }
376
377 ///
378 ref basic_string append(const(T)[] str)
379 {
380 size_type _Count = str.length;
381 auto _My_data = &_Get_data();
382 size_type _Old_size = _My_data._Mysize;
383 if (_Count <= _My_data._Myres - _Old_size)
384 {
385 pointer _Old_ptr = _My_data._Myptr;
386 _My_data._Mysize = _Old_size + _Count;
387 _Old_ptr[_Old_size .. _Old_size + _Count] = str[]; // TODO: this needs to be a memmove(), does that work here?
388 _Old_ptr[_Old_size + _Count] = T(0);
389 return this;
390 }
391 return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, const(T)[] _Str) {
392 _New_ptr[0 .. _Old_str.length] = _Old_str[];
393 _New_ptr[_Old_str.length .. _Old_str.length + _Str.length] = _Str[];
394 _New_ptr[_Old_str.length + _Str.length] = T(0);
395 }, str);
396 }
397
398 ///
399 ref basic_string append(size_type n, T c)
400 {
401 alias _Count = n;
402 alias _Ch = c;
403 auto _My_data = &_Get_data();
404 const size_type _Old_size = _My_data._Mysize;
405 if (_Count <= _My_data._Myres - _Old_size)
406 {
407 _My_data._Mysize = _Old_size + _Count;
408 pointer _Old_ptr = _My_data._Myptr();
409 _Old_ptr[_Old_size .. _Old_size + _Count] = _Ch;
410 _Old_ptr[_Old_size + _Count] = T(0);
411 return this;
412 }
413
414 return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, size_type _Count, T _Ch) {
415 _New_ptr[0 .. _Old_str.length] = _Old_str[];
416 _New_ptr[_Old_str.length .. _Old_str.length + _Count] = _Ch;
417 _New_ptr[_Old_str.length + _Count] = T(0);
418 }, _Count, _Ch);
419 }
420
421 ///
422 void reserve(size_type _Newcap = 0)
423 {
424 // determine new minimum length of allocated storage
425
426 auto _My_data = &_Get_data();
427
428 if (_My_data._Mysize > _Newcap)
429 {
430 // requested capacity is not large enough for current size, ignore
431 return; // nothing to do
432 }
433
434 if (_My_data._Myres == _Newcap)
435 {
436 // we're already at the requested capacity
437 return; // nothing to do
438 }
439
440 if (_My_data._Myres < _Newcap)
441 {
442 // reallocate to grow
443 const size_type _Old_size = _My_data._Mysize;
444 _Reallocate_grow_by(
445 _Newcap - _Old_size, (T* _New_ptr, const(T)[] _Old_str) {
446 _New_ptr[0 .. _Old_str.length] = _Old_str[];
447 _New_ptr[_Old_str.length] = _Old_str.ptr[_Old_str.length];
448 });
449
450 _My_data._Mysize = _Old_size;
451 return;
452 }
453
454 if (_My_data._BUF_SIZE > _Newcap && _My_data._Large_string_engaged())
455 {
456 // deallocate everything; switch back to "small" mode
457 _Become_small();
458 return;
459 }
460
461 // ignore requests to reserve to [_BUF_SIZE, _Myres)
462 }
463
464 ///
465 void shrink_to_fit()
466 {
467 // reduce capacity
468
469 auto _My_data = &_Get_data();
470 if (!_My_data._Large_string_engaged())
471 {
472 // can't shrink from small mode
473 return;
474 }
475
476 if (_My_data._Mysize < _My_data._BUF_SIZE)
477 {
478 _Become_small();
479 return;
480 }
481
482 const size_type _Target_capacity = min(_My_data._Mysize | _My_data._ALLOC_MASK, max_size());
483 if (_Target_capacity < _My_data._Myres)
484 {
485 // worth shrinking, do it
486 auto _Al = &_Getal();
487 pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws
488 _Base._Orphan_all();
489 _New_ptr[0 .. _My_data._Mysize + 1] = _My_data._Bx._Ptr[0 .. _My_data._Mysize + 1];
490 _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1);
491 _My_data._Bx._Ptr = _New_ptr;
492 _My_data._Myres = _Target_capacity;
493 }
494 }
495
496 ///
497 ref basic_string insert(size_type pos, const(T)* s, size_type n)
498 {
499 // insert [_Ptr, _Ptr + _Count) at _Off
500 alias _Off = pos;
501 alias _Ptr = s;
502 alias _Count = n;
503 auto _My_data = &_Get_data();
504 // _My_data._Check_offset(_Off);
505 const size_type _Old_size = _My_data._Mysize;
506 if (_Count <= _My_data._Myres - _Old_size)
507 {
508 _My_data._Mysize = _Old_size + _Count;
509 T* _Old_ptr = _My_data._Myptr();
510 T* _Insert_at = _Old_ptr + _Off;
511 // the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out,
512 // while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count
513 size_type _Ptr_shifted_after;
514 if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size)
515 {
516 // inserted content is before the shifted region, or does not alias
517 _Ptr_shifted_after = _Count; // none of _Ptr's data shifts
518 }
519 else if (_Insert_at <= _Ptr)
520 {
521 // all of [_Ptr, _Ptr + _Count) shifts
522 _Ptr_shifted_after = 0;
523 }
524 else
525 {
526 // [_Ptr, _Ptr + _Count) contains _Insert_at, so only the part after _Insert_at shifts
527 _Ptr_shifted_after = cast(size_type)(_Insert_at - _Ptr);
528 }
529
530 _Traits.move(_Insert_at + _Count, _Insert_at, _Old_size - _Off + 1); // move suffix + null down
531 _Insert_at[0 .. _Ptr_shifted_after] = _Ptr[0 .. _Ptr_shifted_after];
532 (_Insert_at + _Ptr_shifted_after)[0 .. _Count - _Ptr_shifted_after] = (_Ptr + _Count + _Ptr_shifted_after)[0 .. _Count - _Ptr_shifted_after];
533 return this;
534 }
535
536 return _Reallocate_grow_by(
537 _Count,
538 (T* _New_ptr, const(T)[] _Old_str, size_type _Off, const(T)* _Ptr, size_type _Count) {
539 _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
540 _New_ptr[_Off .. _Off + _Count] = _Ptr[0 .. _Count];
541 _New_ptr[_Off + _Count .. _Old_str.length + _Count + 1] = _Old_str.ptr[_Off .. _Old_str.length + 1];
542 },
543 _Off, _Ptr, _Count);
544 }
545
546 ///
547 ref basic_string insert(size_type pos, size_type n, T c)
548 {
549 // insert _Count * _Ch at _Off
550 alias _Off = pos;
551 alias _Count = n;
552 alias _Ch = c;
553 auto _My_data = &_Get_data();
554 // _My_data._Check_offset(_Off);
555 const size_type _Old_size = _My_data._Mysize;
556 if (_Count <= _My_data._Myres - _Old_size)
557 {
558 _My_data._Mysize = _Old_size + _Count;
559 T* _Old_ptr = _My_data._Myptr();
560 T* _Insert_at = _Old_ptr + _Off;
561 _Traits.move(_Insert_at + _Count, _Insert_at, _Old_size - _Off + 1); // move suffix + null down
562 _Insert_at[0 .. _Count] = _Ch; // fill hole
563 return this;
564 }
565
566 return _Reallocate_grow_by(
567 _Count,
568 (T* _New_ptr, const(T)[] _Old_str, size_type _Off, size_type _Count, T _Ch)
569 {
570 _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
571 _New_ptr[_Off .. _Off + _Count] = _Ch;
572 _New_ptr[_Off + _Count .. _Old_str.length + 1] = _Old_str.ptr[_Off .. _Old_str.length + 1];
573 },
574 _Off, _Count, _Ch);
575 }
576
577 ///
578 ref basic_string replace(size_type pos, size_type len, const(T)* s, size_type slen)
579 {
580 // replace [_Off, _Off + _N0) with [_Ptr, _Ptr + _Count)
581 alias _Off = pos;
582 alias _N0 = len;
583 alias _Ptr = s;
584 alias _Count = slen;
585 auto _My_data = &_Get_data();
586 // _Mypair._Myval2._Check_offset(_Off);
587 _N0 = _My_data._Clamp_suffix_size(_Off, _N0);
588 if (_N0 == _Count)
589 {
590 // size doesn't change, so a single move does the trick
591 _Traits.move(_My_data._Myptr() + _Off, _Ptr, _Count);
592 return this;
593 }
594
595 const size_type _Old_size = _My_data._Mysize;
596 const size_type _Suffix_size = _Old_size - _N0 - _Off + 1;
597 if (_Count < _N0)
598 {
599 // suffix shifts backwards; we don't have to move anything out of the way
600 _My_data._Mysize = _Old_size - (_N0 - _Count);
601 T* _Old_ptr = _My_data._Myptr();
602 T* _Insert_at = _Old_ptr + _Off;
603 _Traits.move(_Insert_at, _Ptr, _Count);
604 _Traits.move(_Insert_at + _Count, _Insert_at + _N0, _Suffix_size);
605 return this;
606 }
607
608 const size_type _Growth = cast(size_type)(_Count - _N0);
609 if (_Growth <= _My_data._Myres - _Old_size)
610 {
611 // growth fits
612 _My_data._Mysize = _Old_size + _Growth;
613 T* _Old_ptr = _My_data._Myptr();
614 T* _Insert_at = _Old_ptr + _Off;
615 T* _Suffix_at = _Insert_at + _N0;
616
617 size_type _Ptr_shifted_after; // see rationale in insert
618 if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size)
619 _Ptr_shifted_after = _Count;
620 else if (_Suffix_at <= _Ptr)
621 _Ptr_shifted_after = 0;
622 else
623 _Ptr_shifted_after = cast(size_type)(_Suffix_at - _Ptr);
624
625 _Traits.move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size);
626 // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole;
627 // this case doesn't occur in insert because the new content must come from outside the removed
628 // content there (because in insert there is no removed content)
629 _Traits.move(_Insert_at, _Ptr, _Ptr_shifted_after);
630 // the next case can be copy, because it comes from the chunk moved out of the way in the
631 // first move, and the hole we're filling can't alias the chunk we moved out of the way
632 _Insert_at[_Ptr_shifted_after .. _Count] = _Ptr[_Growth + _Ptr_shifted_after .. _Growth + _Count];
633 return this;
634 }
635
636 return _Reallocate_grow_by(
637 _Growth,
638 (T* _New_ptr, const(T)[] _Old_str, size_type _Off, size_type _N0, const(T)* _Ptr, size_type _Count) {
639 _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
640 _New_ptr[_Off .. _Count] = _Ptr[0 .. _Count];
641 const __n = _Old_str.length - _N0 - _Off + 1;
642 (_New_ptr + _Off + _Count)[0 .. __n] = (_Old_str.ptr + _Off + _N0)[0 .. __n];
643 },
644 _Off, _N0, _Ptr, _Count);
645 }
646
647 ///
648 ref basic_string replace(size_type _Off, size_type _N0, size_type _Count, T _Ch)
649 {
650 // replace [_Off, _Off + _N0) with _Count * _Ch
651 auto _My_data = &_Get_data();
652 // _My_data._Check_offset(_Off);
653 _N0 = _My_data._Clamp_suffix_size(_Off, _N0);
654 if (_Count == _N0)
655 {
656 _My_data._Myptr()[_Off .. _Off + _Count] = _Ch;
657 return this;
658 }
659
660 const size_type _Old_size = _My_data._Mysize;
661 if (_Count < _N0 || _Count - _N0 <= _My_data._Myres - _Old_size)
662 {
663 // either we are shrinking, or the growth fits
664 _My_data._Mysize = _Old_size + _Count - _N0; // may temporarily overflow;
665 // OK because size_type must be unsigned
666 T* _Old_ptr = _My_data._Myptr();
667 T* _Insert_at = _Old_ptr + _Off;
668 _Traits.move(_Insert_at + _Count, _Insert_at + _N0, _Old_size - _N0 - _Off + 1);
669 _Insert_at[0 .. _Count] = _Ch;
670 return this;
671 }
672
673 return _Reallocate_grow_by(
674 _Count - _N0,
675 (T* _New_ptr, const(T)[] _Old_str, size_type _Off, size_type _N0, size_type _Count, T _Ch) {
676 _New_ptr[0 .. _Off] = _Old_str[0 .. _Off];
677 _New_ptr[_Off .. _Off + _Count] = _Ch;
678 const __n = _Old_str.length - _N0 - _Off + 1;
679 (_New_ptr + _Off + _Count)[0 .. __n] = (_Old_str.ptr + _Off + _N0)[0 .. __n];
680 },
681 _Off, _N0, _Count, _Ch);
682 }
683
684 ///
685 void swap(ref basic_string _Right)
686 {
687 import core.internal.lifetime : swap;
688 import core.stdcpp.type_traits : is_empty;
689
690 if (&this != &_Right)
691 {
692 static if (!is_empty!allocator_type.value
693 && allocator_traits!allocator_type.propagate_on_container_swap)
694 {
695 swap(_Getal(), _Right._Getal());
696 }
697
698 static if (_ITERATOR_DEBUG_LEVEL != 0)
699 {
700 auto _My_data = &_Get_data();
701 const bool _My_large = _My_data._Large_string_engaged();
702 const bool _Right_large = _Right._Get_data()._Large_string_engaged();
703 if (!_My_large)
704 _Base._Orphan_all();
705
706 if (!_Right_large)
707 _Right._Base._Orphan_all();
708
709 if (_My_large || _Right_large)
710 _My_data._Base._Swap_proxy_and_iterators(_Right._Get_data()._Base);
711 } // _ITERATOR_DEBUG_LEVEL != 0
712 }
713
714 _Swap_data!_Can_memcpy_val(_Right);
715 }
716
717 private:
718 import core.stdcpp.xutility : MSVCLinkDirectives;
719 import core.stdcpp.xutility : _Container_base;
720
721 alias _Traits = traits_type;
722 alias _Scary_val = _String_val!T;
723
724 enum bool _Can_memcpy_val = is(_Traits == char_traits!E, E) && is(pointer == U*, U);
725 // This offset skips over the _Container_base members, if any
726 enum size_t _Memcpy_val_offset = _Size_after_ebco_v!_Container_base;
727 enum size_t _Memcpy_val_size = _Scary_val.sizeof - _Memcpy_val_offset;
728
729 // Make sure the object files wont link against mismatching objects
730 mixin MSVCLinkDirectives!true;
731
732 pragma (inline, true)
733 {
734 void eos(size_type offset) nothrow { _Get_data()._Myptr[_Get_data()._Mysize = offset] = T(0); }
735
736 ref inout(_Base.Alloc) _Getal() inout nothrow @safe { return _Base._Mypair._Myval1; }
737 ref inout(_Base.ValTy) _Get_data() inout nothrow @safe { return _Base._Mypair._Myval2; }
738 }
739
740 void _Alloc_proxy() nothrow
741 {
742 static if (_ITERATOR_DEBUG_LEVEL > 0)
743 _Base._Alloc_proxy();
744 }
745
746 void _AssignAllocator(ref const(allocator_type) al) nothrow
747 {
748 static if (_Base._Mypair._HasFirst)
749 _Getal() = al;
750 }
751
752 void _Become_small()
753 {
754 // release any held storage and return to small string mode
755 // pre: *this is in large string mode
756 // pre: this is small enough to return to small string mode
757 auto _My_data = &_Get_data();
758 _Base._Orphan_all();
759 pointer _Ptr = _My_data._Bx._Ptr;
760 auto _Al = &_Getal();
761 _My_data._Bx._Buf[0 .. _My_data._Mysize + 1] = _Ptr[0 .. _My_data._Mysize + 1];
762 _Al.deallocate(_Ptr, _My_data._Myres + 1);
763 _My_data._Myres = _My_data._BUF_SIZE - 1;
764 }
765
766 void _Tidy_init() nothrow
767 {
768 auto _My_data = &_Get_data();
769 _My_data._Mysize = 0;
770 _My_data._Myres = _My_data._BUF_SIZE - 1;
771 _My_data._Bx._Buf[0] = T(0);
772 }
773
774 size_type _Calculate_growth(size_type _Requested) const nothrow
775 {
776 auto _My_data = &_Get_data();
777 size_type _Masked = _Requested | _My_data._ALLOC_MASK;
778 size_type _Old = _My_data._Myres;
779 size_type _Expanded = _Old + _Old / 2;
780 return _Masked > _Expanded ? _Masked : _Expanded;
781 }
782
783 ref basic_string _Reallocate_for(_ArgTys...)(size_type _New_size, void function(pointer, size_type, _ArgTys) nothrow @nogc _Fn, _ArgTys _Args)
784 {
785 auto _My_data = &_Get_data();
786 size_type _Old_capacity = _My_data._Myres;
787 size_type _New_capacity = _Calculate_growth(_New_size);
788 auto _Al = &_Getal();
789 pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws
790 _Base._Orphan_all();
791 _My_data._Mysize = _New_size;
792 _My_data._Myres = _New_capacity;
793 _Fn(_New_ptr, _New_size, _Args);
794 if (_My_data._BUF_SIZE <= _Old_capacity)
795 _Al.deallocate(_My_data._Bx._Ptr, _Old_capacity + 1);
796 _My_data._Bx._Ptr = _New_ptr;
797 return this;
798 }
799
800 ref basic_string _Reallocate_grow_by(_ArgTys...)(size_type _Size_increase, void function(pointer, const(T)[], _ArgTys) nothrow @nogc _Fn, _ArgTys _Args)
801 {
802 auto _My_data = &_Get_data();
803 size_type _Old_size = _My_data._Mysize;
804 size_type _New_size = _Old_size + _Size_increase;
805 size_type _Old_capacity = _My_data._Myres;
806 size_type _New_capacity = _Calculate_growth(_New_size);
807 auto _Al = &_Getal();
808 pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws
809 _Base._Orphan_all();
810 _My_data._Mysize = _New_size;
811 _My_data._Myres = _New_capacity;
812 if (_My_data._BUF_SIZE <= _Old_capacity)
813 {
814 pointer _Old_ptr = _My_data._Bx._Ptr;
815 _Fn(_New_ptr, _Old_ptr[0 .. _Old_size], _Args);
816 _Al.deallocate(_Old_ptr, _Old_capacity + 1);
817 }
818 else
819 _Fn(_New_ptr, _My_data._Bx._Buf[0 .. _Old_size], _Args);
820 _My_data._Bx._Ptr = _New_ptr;
821 return this;
822 }
823
824 void _Tidy_deallocate()
825 {
826 _Base._Orphan_all();
827 auto _My_data = &_Get_data();
828 if (_My_data._BUF_SIZE <= _My_data._Myres)
829 {
830 pointer _Ptr = _My_data._Bx._Ptr;
831 auto _Al = &_Getal();
832 _Al.deallocate(_Ptr, _My_data._Myres + 1);
833 }
834 _My_data._Mysize = 0;
835 _My_data._Myres = _My_data._BUF_SIZE - 1;
836 _My_data._Bx._Buf[0] = T(0);
837 }
838
839 void _Swap_data(bool _memcpy : true)(ref basic_string _Right)
840 {
841 import core.stdc.string : memcpy;
842
843 // exchange _String_val instances with _Right, memcpy optimization
844 auto _My_data = &_Get_data();
845 auto _My_data_mem = cast(ubyte*)_My_data + _Memcpy_val_offset;
846 auto _Right_data_mem = cast(ubyte*)(&_Right._Get_data()) + _Memcpy_val_offset;
847 ubyte[_Memcpy_val_size] _Temp_mem;
848 memcpy(_Temp_mem.ptr, _My_data_mem, _Memcpy_val_size);
849 memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size);
850 memcpy(_Right_data_mem, _Temp_mem.ptr, _Memcpy_val_size);
851 }
852
853 void _Swap_data(bool _memcpy : false)(ref basic_string _Right)
854 {
855 import core.lifetime : swap;
856
857 // exchange _String_val instances with _Right, general case
858 auto _My_data = &_Get_data();
859 auto _Right_data = &_Right._Get_data();
860 const bool _My_large = _My_data._Large_string_engaged();
861 const bool _Right_large = _Right_data._Large_string_engaged();
862 if (_My_large)
863 {
864 if (_Right_large) // swap buffers, iterators preserved
865 swap(_My_data._Bx._Ptr, _Right_data._Bx._Ptr);
866 else // swap large with small
867 _Swap_bx_large_with_small(*_My_data, *_Right_data);
868 }
869 else
870 {
871 if (_Right_large) // swap small with large
872 _Swap_bx_large_with_small(*_Right_data, *_My_data);
873 else
874 {
875 enum _BUF_SIZE = _My_data._BUF_SIZE;
876 T[_BUF_SIZE] _Temp_buf;
877 _Temp_buf[0 .. _BUF_SIZE] = _My_data._Bx._Buf[0 .. _BUF_SIZE];
878 _My_data._Bx._Buf[0 .. _BUF_SIZE] = _Right_data._Bx._Buf[0 .. _BUF_SIZE];
879 _Right_data._Bx._Buf[0 .. _BUF_SIZE] = _Temp_buf[0 .. _BUF_SIZE];
880 }
881 }
882
883 swap(_My_data._Mysize, _Right_data._Mysize);
884 swap(_My_data._Myres, _Right_data._Myres);
885 }
886
887 void _Swap_bx_large_with_small(ref _Scary_val _Starts_large, ref _Scary_val _Starts_small)
888 {
889 // exchange a string in large mode with one in small mode
890 pointer _Ptr = _Starts_large._Bx._Ptr;
891 _Starts_large._Bx._Buf[] = _Starts_small._Bx._Buf[];
892 _Starts_small._Bx._Ptr = _Ptr;
893 }
894
895 _String_alloc!(_String_base_types!(T, Alloc)) _Base;
896 }
versionbasic_string897 else version (CppRuntime_Gcc)
898 {
899 version (_GLIBCXX_USE_CXX98_ABI)
900 {
901 //----------------------------------------------------------------------------------
902 // Old GCC/libstdc++ ref-counted implementation
903 //----------------------------------------------------------------------------------
904
905 ///
906 this(DefaultConstruct)
907 {
908 version (_GLIBCXX_FULLY_DYNAMIC_STRING)
909 static_assert(false, "DO WE NEED THIS?");
910 else
911 _M_data = _S_empty_rep()._M_refdata();
912 }
913 ///
914 this(const(T)[] str, ref const(allocator_type) al) { _M_assign_allocator(al); this(str); }
915 ///
916 this(const(T)[] str)
917 {
918 _M_data = _S_construct(str.ptr, str.ptr + str.length, _M_get_allocator);
919 }
920 ///
921 this(const ref basic_string str)
922 {
923 import core.stdcpp.type_traits : is_empty;
924
925 static if (!is_empty!allocator_type.value)
926 _M_Alloc = str.get_allocator();
927 _M_data = str._M_rep()._M_grab(get_allocator(), str.get_allocator());
928 }
929
930 ///
931 ~this() { _M_rep()._M_dispose(get_allocator()); }
932
933 ///
934 ref inout(Alloc) get_allocator() inout { return _M_get_allocator(); }
935
936 ///
937 size_type max_size() const nothrow @safe { return _Rep._S_max_size; }
938
939 ///
940 size_type size() const nothrow @safe { return _M_rep()._M_length; }
941 ///
942 size_type capacity() const nothrow { return _M_rep()._M_capacity; }
943 ///
944 inout(T)* data() inout @safe { return _M_data; }
945 ///
946 inout(T)[] as_array() inout nothrow @trusted { return _M_data[0 .. _M_rep()._M_length]; }
947 ///
948 ref inout(T) at(size_type i) inout nothrow { return _M_data[0 .. _M_rep()._M_length][i]; }
949
950 ///
951 ref basic_string assign(const(T)[] str)
952 {
953 const(T)* __s = str.ptr;
954 size_t __n = str.length;
955 // __glibcxx_requires_string_len(__s, __n);
956 _M_check_length(size(), __n, "basic_string::assign");
957 if (_M_disjunct(__s) || _M_rep()._M_is_shared())
958 return _M_replace_safe(size_type(0), this.size(), __s, __n);
959 else
960 {
961 const size_type __pos = __s - _M_data;
962 if (__pos >= __n)
963 _S_copy(_M_data, __s, __n);
964 else if (__pos)
965 _S_move(_M_data, __s, __n);
966 _M_rep()._M_set_length_and_sharable(__n);
967 return this;
968 }
969 }
970
971 ///
972 ref basic_string assign(const ref basic_string str)
973 {
974 if (_M_rep() != str._M_rep())
975 {
976 // XXX MT
977 allocator_type __a = this.get_allocator();
978 T* __tmp = str._M_rep()._M_grab(__a, str.get_allocator());
979 _M_rep()._M_dispose(__a);
980 _M_data = __tmp;
981 }
982 return this;
983 }
984
985 ///
986 ref basic_string append(const(T)[] str)
987 {
988 const(T)* __s = str.ptr;
989 size_t __n = str.length;
990 // __glibcxx_requires_string_len(__s, __n);
991 if (__n)
992 {
993 _M_check_length(size_type(0), __n, "basic_string::append");
994 const size_type __len = __n + size();
995 if (__len > capacity() || _M_rep()._M_is_shared())
996 {
997 if (_M_disjunct(__s))
998 reserve(__len);
999 else
1000 {
1001 const size_type __off = __s - _M_data;
1002 reserve(__len);
1003 __s = _M_data + __off;
1004 }
1005 }
1006 _S_copy(_M_data + size(), __s, __n);
1007 _M_rep()._M_set_length_and_sharable(__len);
1008 }
1009 return this;
1010 }
1011
1012 ///
1013 ref basic_string append(size_type __n, T __c)
1014 {
1015 if (__n)
1016 {
1017 _M_check_length(size_type(0), __n, "basic_string::append");
1018 const size_type __len = __n + size();
1019 if (__len > capacity() || _M_rep()._M_is_shared())
1020 reserve(__len);
1021 const __sz = size();
1022 _M_data[__sz .. __sz + __n] = __c;
1023 _M_rep()._M_set_length_and_sharable(__len);
1024 }
1025 return this;
1026 }
1027
1028 ///
1029 void reserve(size_type __res = 0)
1030 {
1031 if (__res != capacity() || _M_rep()._M_is_shared())
1032 {
1033 // Make sure we don't shrink below the current size
1034 if (__res < size())
1035 __res = size();
1036 allocator_type __a = get_allocator();
1037 T* __tmp = _M_rep()._M_clone(__a, __res - size());
1038 _M_rep()._M_dispose(__a);
1039 _M_data = __tmp;
1040 }
1041 }
1042
1043 ///
1044 void shrink_to_fit() nothrow
1045 {
1046 if (capacity() > size())
1047 {
1048 try reserve(0);
1049 catch (Throwable) {}
1050 }
1051 }
1052
1053 ///
1054 ref basic_string insert(size_type __pos, const(T)* __s, size_type __n)
1055 {
1056 // __glibcxx_requires_string_len(__s, __n);
1057 cast(void) _M_check(__pos, "basic_string::insert");
1058 _M_check_length(size_type(0), __n, "basic_string::insert");
1059 if (_M_disjunct(__s) || _M_rep()._M_is_shared())
1060 return _M_replace_safe(__pos, size_type(0), __s, __n);
1061 else
1062 {
1063 // Work in-place.
1064 const size_type __off = __s - _M_data;
1065 _M_mutate(__pos, 0, __n);
1066 __s = _M_data + __off;
1067 T* __p = _M_data + __pos;
1068 if (__s + __n <= __p)
1069 __p[0 .. __n] = __s[0 .. __n];
1070 else if (__s >= __p)
1071 __p[0 .. __n] = (__s + __n)[0 .. __n];
1072 else
1073 {
1074 const size_type __nleft = __p - __s;
1075 __p[0 .. __nleft] = __s[0.. __nleft];
1076 (__p + __nleft)[0 .. __n - __nleft] = (__p + __n)[0 .. __n - __nleft];
1077 }
1078 return this;
1079 }
1080 }
1081
1082 ///
1083 ref basic_string insert(size_type pos, size_type n, T c)
1084 {
1085 return _M_replace_aux(_M_check(pos, "basic_string::insert"), size_type(0), n, c);
1086 }
1087
1088 ///
1089 ref basic_string replace(size_type __pos, size_type __n1, const(T)* __s, size_type __n2)
1090 {
1091 // __glibcxx_requires_string_len(__s, __n2);
1092 cast(void) _M_check(__pos, "basic_string::replace");
1093 __n1 = _M_limit(__pos, __n1);
1094 _M_check_length(__n1, __n2, "basic_string::replace");
1095 bool __left;
1096 if (_M_disjunct(__s) || _M_rep()._M_is_shared())
1097 return _M_replace_safe(__pos, __n1, __s, __n2);
1098 else if ((__left = __s + __n2 <= _M_data + __pos) == true || _M_data + __pos + __n1 <= __s)
1099 {
1100 // Work in-place: non-overlapping case.
1101 size_type __off = __s - _M_data;
1102 __left ? __off : (__off += __n2 - __n1);
1103 _M_mutate(__pos, __n1, __n2);
1104 (_M_data + __pos)[0 .. __n2] = (_M_data + __off)[0 .. __n2];
1105 return this;
1106 }
1107 else
1108 {
1109 // Todo: overlapping case.
1110 auto __tmp = basic_string(__s[0 .. __n2]);
1111 return _M_replace_safe(__pos, __n1, __tmp._M_data, __n2);
1112 }
1113 }
1114
1115 ///
1116 ref basic_string replace(size_type pos, size_type n1, size_type n2, T c)
1117 {
1118 return _M_replace_aux(_M_check(pos, "basic_string::replace"), _M_limit(pos, n1), n2, c);
1119 }
1120
1121 ///
1122 void swap(ref basic_string __s)
1123 {
1124 if (_M_rep()._M_is_leaked())
1125 _M_rep()._M_set_sharable();
1126 if (__s._M_rep()._M_is_leaked())
1127 __s._M_rep()._M_set_sharable();
1128 if (this.get_allocator() == __s.get_allocator())
1129 {
1130 T* __tmp = _M_data;
1131 _M_data = __s._M_data;
1132 __s._M_data = __tmp;
1133 }
1134 // The code below can usually be optimized away.
1135 else
1136 {
1137 import core.lifetime : move;
1138
1139 auto __tmp1 = basic_string(this[], __s.get_allocator());
1140 auto __tmp2 = basic_string(__s[], this.get_allocator());
1141 this = move(__tmp2);
1142 __s = move(__tmp1);
1143 }
1144 }
1145
1146 private:
1147 import core.stdcpp.type_traits : is_empty;
1148
1149 version (__GTHREADS)
1150 {
1151 import core.atomic;
1152 alias _Atomic_word = int; // should we use atomic!int?
1153 }
1154 else
1155 alias _Atomic_word = int;
1156
1157 struct _Rep_base
1158 {
1159 size_type _M_length;
1160 size_type _M_capacity;
1161 _Atomic_word _M_refcount;
1162 }
1163
1164 struct _Rep
1165 {
1166 _Rep_base base;
1167 alias base this;
1168
1169 alias _Raw_bytes_alloc = Alloc.rebind!char;
1170
1171 enum size_type _S_max_size = (((npos - _Rep_base.sizeof) / T.sizeof) - 1) / 4;
1172 enum T _S_terminal = T(0);
1173
1174 __gshared size_type[(_Rep_base.sizeof + T.sizeof + size_type.sizeof - 1) / size_type.sizeof] _S_empty_rep_storage;
1175
1176 static ref _Rep _S_empty_rep() nothrow @trusted { return *cast(_Rep*)_S_empty_rep_storage.ptr; }
1177
1178 void _M_set_sharable() nothrow
1179 {
1180 _M_refcount = 0;
1181 }
1182
1183 void _M_set_length_and_sharable(size_type __n) nothrow
1184 {
1185 if (&this != &_S_empty_rep())
1186 {
1187 _M_set_sharable();
1188 _M_length = __n;
1189 _M_refdata()[__n] = _S_terminal;
1190 }
1191 }
1192
1193 bool _M_is_leaked() const nothrow
1194 {
1195 import core.atomic : atomicLoad;
1196
1197 version (__GTHREADS)
1198 return atomicLoad!(MemoryOrder.raw)(this._M_refcount) < 0;
1199 else
1200 return _M_refcount < 0;
1201 }
1202 //
1203 bool _M_is_shared() const nothrow
1204 {
1205 import core.atomic : atomicLoad;
1206
1207 version (__GTHREADS)
1208 return atomicLoad!(MemoryOrder.acq)(this._M_refcount) > 0;
1209 else
1210 return _M_refcount > 0;
1211 }
1212
1213 T* _M_refdata() nothrow @trusted { return cast(T*)(&this + 1); }
1214
1215 T* _M_grab(ref allocator_type __alloc1, const ref allocator_type __alloc2)
1216 {
1217 return (!_M_is_leaked() && __alloc1 == __alloc2)
1218 ? _M_refcopy() : _M_clone(__alloc1);
1219 }
1220
1221 static _Rep* _S_create(size_type __capacity, size_type __old_capacity, ref Alloc __alloc)
1222 {
1223 assert(__capacity <= _S_max_size);
1224 // if (__capacity > _S_max_size)
1225 // __throw_length_error(__N("basic_string::_S_create"));
1226
1227 enum __pagesize = 4096;
1228 enum __malloc_header_size = 4 * pointer.sizeof;
1229
1230 if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)
1231 __capacity = 2 * __old_capacity;
1232
1233 size_type __size = (__capacity + 1) * T.sizeof + _Rep.sizeof;
1234
1235 const size_type __adj_size = __size + __malloc_header_size;
1236 if (__adj_size > __pagesize && __capacity > __old_capacity)
1237 {
1238 const size_type __extra = __pagesize - __adj_size % __pagesize;
1239 __capacity += __extra / T.sizeof;
1240 if (__capacity > _S_max_size)
1241 __capacity = _S_max_size;
1242 __size = (__capacity + 1) * T.sizeof + _Rep.sizeof;
1243 }
1244
1245 _Rep* __p = cast(_Rep*)_Raw_bytes_alloc(__alloc).allocate(__size);
1246 *__p = _Rep.init;
1247 __p._M_capacity = __capacity;
1248 __p._M_set_sharable();
1249 return __p;
1250 }
1251
1252 void _M_dispose(ref Alloc __a)
1253 {
1254 import core.stdcpp.xutility : __exchange_and_add_dispatch;
1255
1256 if (&this != &_S_empty_rep())
1257 {
1258 // Be race-detector-friendly. For more info see bits/c++config.
1259 // _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&this._M_refcount);
1260 // Decrement of _M_refcount is acq_rel, because:
1261 // - all but last decrements need to release to synchronize with
1262 // the last decrement that will delete the object.
1263 // - the last decrement needs to acquire to synchronize with
1264 // all the previous decrements.
1265 // - last but one decrement needs to release to synchronize with
1266 // the acquire load in _M_is_shared that will conclude that
1267 // the object is not shared anymore.
1268 if (__exchange_and_add_dispatch(&this._M_refcount, -1) <= 0)
1269 {
1270 // _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&this._M_refcount);
1271 _M_destroy(__a);
1272 }
1273 }
1274 }
1275
1276 void _M_destroy(ref Alloc __a)
1277 {
1278 const size_type __size = _Rep_base.sizeof + (_M_capacity + 1) * T.sizeof;
1279 _Raw_bytes_alloc(__a).deallocate(cast(char*)&this, __size);
1280 }
1281
1282 T* _M_refcopy() nothrow @trusted
1283 {
1284 import core.stdcpp.xutility : __atomic_add_dispatch;
1285
1286 if (&this != &_S_empty_rep())
1287 __atomic_add_dispatch(&this._M_refcount, 1);
1288 return _M_refdata();
1289 // XXX MT
1290 }
1291
1292 T* _M_clone(ref Alloc __alloc, size_type __res = 0)
1293 {
1294 const size_type __requested_cap = _M_length + __res;
1295 _Rep* __r = _S_create(__requested_cap, _M_capacity, __alloc);
1296 if (_M_length)
1297 _S_copy(__r._M_refdata(), _M_refdata(), _M_length);
1298
1299 __r._M_set_length_and_sharable(_M_length);
1300 return __r._M_refdata();
1301 }
1302 }
1303
1304 static if (!is_empty!allocator_type.value)
1305 allocator_type _M_Alloc;
1306 T* _M_p; // The actual data.
1307
1308 alias _M_data = _M_p;
1309
1310 pragma (inline, true)
1311 {
1312 void eos(size_type offset)
1313 {
1314 _M_mutate(offset, size() - offset, size_type(0));
1315 }
1316
1317 ref inout(allocator_type) _M_get_allocator() inout
1318 {
1319 static if (!is_empty!allocator_type.value)
1320 return _M_Alloc;
1321 else
1322 return *cast(inout(allocator_type)*)&this;
1323 }
1324
1325 _Rep* _M_rep() const nothrow @trusted { return &(cast(_Rep*)_M_data)[-1]; }
1326
1327 size_type _M_limit(size_type __pos, size_type __off) const @safe nothrow @nogc pure
1328 {
1329 const bool __testoff = __off < size() - __pos;
1330 return __testoff ? __off : size() - __pos;
1331 }
1332 }
1333
1334 size_type _M_check(size_type __pos, const char* __s) const
1335 {
1336 assert(__pos <= size());
1337 // if (__pos > size())
1338 // __throw_out_of_range_fmt(__N("%s: __pos (which is %zu) > "
1339 // "this->size() (which is %zu)"),
1340 // __s, __pos, this->size());
1341 return __pos;
1342 }
1343
1344 static ref _Rep _S_empty_rep() nothrow
1345 {
1346 return _Rep._S_empty_rep();
1347 }
1348
1349 static T* _S_construct(const(T)* __beg, const(T)* __end, ref Alloc __a)
1350 {
1351 version (_GLIBCXX_FULLY_DYNAMIC_STRING) {} else
1352 {
1353 if (__beg == __end && __a == Alloc())
1354 return _S_empty_rep()._M_refdata();
1355 }
1356
1357 const size_type __dnew = __end - __beg;
1358
1359 _Rep* __r = _Rep._S_create(__dnew, size_type(0), __a);
1360 _S_copy(__r._M_refdata(), __beg, __end - __beg);
1361 __r._M_set_length_and_sharable(__dnew);
1362 return __r._M_refdata();
1363 }
1364
1365 ref basic_string _M_replace_safe(size_type __pos1, size_type __n1, const(T)* __s, size_type __n2)
1366 {
1367 _M_mutate(__pos1, __n1, __n2);
1368 if (__n2)
1369 _S_copy(_M_data + __pos1, __s, __n2);
1370 return this;
1371 }
1372
1373 ref basic_string _M_replace_aux(size_type __pos1, size_type __n1, size_type __n2, T __c)
1374 {
1375 _M_check_length(__n1, __n2, "basic_string::_M_replace_aux");
1376 _M_mutate(__pos1, __n1, __n2);
1377 if (__n2)
1378 _M_data[__pos1 .. __pos1 + __n2] = __c;
1379 return this;
1380 }
1381
1382 void _M_mutate(size_type __pos, size_type __len1, size_type __len2)
1383 {
1384 const size_type __old_size = size();
1385 const size_type __new_size = __old_size + __len2 - __len1;
1386 const size_type __how_much = __old_size - __pos - __len1;
1387
1388 if (__new_size > capacity() || _M_rep()._M_is_shared())
1389 {
1390 allocator_type __a = get_allocator();
1391 _Rep* __r = _Rep._S_create(__new_size, capacity(), __a);
1392
1393 if (__pos)
1394 _S_copy(__r._M_refdata(), _M_data, __pos);
1395 if (__how_much)
1396 _S_copy(__r._M_refdata() + __pos + __len2, _M_data + __pos + __len1, __how_much);
1397
1398 allocator_type* __al = cast() &__a;
1399 _M_rep()._M_dispose(*__al);
1400 _M_data = __r._M_refdata();
1401 }
1402 else if (__how_much && __len1 != __len2)
1403 _S_move(_M_data + __pos + __len2, _M_data + __pos + __len1, __how_much);
1404 _M_rep()._M_set_length_and_sharable(__new_size);
1405 }
1406 }
1407 else
1408 {
1409 pragma(msg, "libstdc++ std::__cxx11::basic_string is not yet supported; the struct contains an interior pointer which breaks D move semantics!");
1410
1411 //----------------------------------------------------------------------------------
1412 // GCC/libstdc++ modern implementation
1413 //----------------------------------------------------------------------------------
1414
1415 ///
1416 this(DefaultConstruct) { _M_p = _M_local_data(); _M_set_length(0); }
1417 ///
1418 this(const(T)[] str, ref const(allocator_type) al) { _M_assign_allocator(al); this(str); }
1419 ///
1420 this(const(T)[] str)
1421 {
1422 _M_p = _M_local_data();
1423 _M_construct(str.ptr, str.length);
1424 }
1425 ///
1426 this(this)
1427 {
1428 assert(false);
1429 // TODO: how do I know if it was local before?!
1430 }
1431
1432 ///
1433 ~this() { _M_dispose(); }
1434
1435 ///
1436 ref inout(Alloc) get_allocator() inout { return _M_get_allocator(); }
1437
1438 ///
1439 size_type max_size() const nothrow @safe { return ((size_t.max / T.sizeof) - 1) / 2; }
1440
1441 ///
1442 size_type size() const nothrow @safe { return _M_string_length; }
1443 ///
1444 size_type capacity() const nothrow { return _M_is_local ? _S_local_capacity : _M_allocated_capacity; }
1445 ///
1446 inout(T)* data() inout @safe { return _M_data; }
1447 ///
1448 inout(T)[] as_array() inout nothrow @trusted { return _M_data[0 .. _M_string_length]; }
1449 ///
1450 ref inout(T) at(size_type i) inout nothrow { return _M_data[0 .. _M_string_length][i]; }
1451
1452 ///
1453 ref basic_string assign(const(T)[] str)
1454 {
1455 // __glibcxx_requires_string_len(str.ptr, str.length);
1456 return _M_replace(size_type(0), size(), str.ptr, str.length);
1457 }
1458
1459 ///
1460 ref basic_string assign(const ref basic_string str)
1461 {
1462 if (&this != &str)
1463 assign(str.as_array);
1464 return this;
1465 }
1466
1467 ///
1468 ref basic_string append(const(T)[] str)
1469 {
1470 // __glibcxx_requires_string_len(str.ptr, str.length);
1471 _M_check_length(size_type(0), str.length, "basic_string::append");
1472 return _M_append(str.ptr, str.length);
1473 }
1474
1475 ///
1476 ref basic_string append(size_type n, T c)
1477 {
1478 return _M_replace_aux(size(), size_type(0), n, c);
1479 }
1480
1481 ///
1482 void reserve(size_type __res = 0)
1483 {
1484 // Make sure we don't shrink below the current size.
1485 if (__res < length())
1486 __res = length();
1487
1488 const size_type __capacity = capacity();
1489 if (__res != __capacity)
1490 {
1491 if (__res > __capacity || __res > size_type(_S_local_capacity))
1492 {
1493 pointer __tmp = _M_create(__res, __capacity);
1494 _S_copy(__tmp, _M_data, length() + 1);
1495 _M_dispose();
1496 _M_data = __tmp;
1497 _M_capacity = __res;
1498 }
1499 else if (!_M_is_local())
1500 {
1501 _S_copy(_M_local_data(), _M_data, length() + 1);
1502 _M_destroy(__capacity);
1503 _M_data = _M_local_data();
1504 }
1505 }
1506 }
1507
1508 ///
1509 void shrink_to_fit() nothrow
1510 {
1511 if (capacity() > size())
1512 {
1513 try reserve(0);
1514 catch (Throwable) {}
1515 }
1516 }
1517
1518 ///
1519 ref basic_string insert(size_type pos, const(T)* s, size_type n)
1520 {
1521 return replace(pos, size_type(0), s, n);
1522 }
1523
1524 ///
1525 ref basic_string insert(size_type pos, size_type n, T c)
1526 {
1527 return _M_replace_aux(_M_check(pos, "basic_string::insert"), size_type(0), n, c);
1528 }
1529
1530 ///
1531 ref basic_string replace(size_type pos, size_type n1, const(T)* s, size_type n2)
1532 {
1533 // __glibcxx_requires_string_len(s, n2);
1534 return _M_replace(_M_check(pos, "basic_string::replace"), _M_limit(pos, n1), s, n2);
1535 }
1536
1537 ///
1538 ref basic_string replace(size_type pos, size_type n1, size_type n2, T c)
1539 {
1540 return _M_replace_aux(_M_check(pos, "basic_string::replace"), _M_limit(pos, n1), n2, c);
1541 }
1542
1543 ///
1544 void swap(ref basic_string __s)
1545 {
1546 if (&this == &__s)
1547 return;
1548
1549 __alloc_on_swap(__s._M_get_allocator());
1550
1551 if (_M_is_local())
1552 {
1553 if (__s._M_is_local())
1554 {
1555 if (length() && __s.length())
1556 {
1557 T[_S_local_capacity + 1] __tmp_data;
1558 __tmp_data[] = __s._M_local_buf[];
1559 __s._M_local_buf[] = _M_local_buf[];
1560 _M_local_buf[] = __tmp_data[];
1561 }
1562 else if (__s.length())
1563 {
1564 _M_local_buf[] = __s._M_local_buf[];
1565 _M_length = __s.length();
1566 __s._M_set_length(0);
1567 return;
1568 }
1569 else if (length())
1570 {
1571 __s._M_local_buf[] = _M_local_buf[];
1572 __s._M_length = length();
1573 _M_set_length(0);
1574 return;
1575 }
1576 }
1577 else
1578 {
1579 const size_type __tmp_capacity = __s._M_allocated_capacity;
1580 __s._M_local_buf[] = _M_local_buf[];
1581 _M_data = __s._M_data;
1582 __s._M_data = __s._M_local_buf.ptr;
1583 _M_capacity = __tmp_capacity;
1584 }
1585 }
1586 else
1587 {
1588 const size_type __tmp_capacity = _M_allocated_capacity;
1589 if (__s._M_is_local())
1590 {
1591 _M_local_buf[] = __s._M_local_buf[];
1592 __s._M_data = _M_data;
1593 _M_data = _M_local_buf.ptr;
1594 }
1595 else
1596 {
1597 pointer __tmp_ptr = _M_data;
1598 _M_data = __s._M_data;
1599 __s._M_data = __tmp_ptr;
1600 _M_capacity = __s._M_allocated_capacity;
1601 }
1602 __s._M_capacity = __tmp_capacity;
1603 }
1604
1605 const size_type __tmp_length = length();
1606 _M_length = __s.length();
1607 __s._M_length = __tmp_length;
1608 }
1609
1610 private:
1611 // import core.exception : RangeError;
1612 import core.stdcpp.type_traits : is_empty;
1613
1614 static if (!is_empty!allocator_type.value)
1615 allocator_type _M_Alloc;
1616 pointer _M_p; // The actual data.
1617 size_type _M_string_length;
1618
1619 enum size_type _S_local_capacity = 15 / T.sizeof;
1620 union
1621 {
1622 T[_S_local_capacity + 1] _M_local_buf;
1623 size_type _M_allocated_capacity;
1624 }
1625
1626 alias _M_length = _M_string_length;
1627 alias _M_capacity = _M_allocated_capacity;
1628 alias _M_data = _M_p;
1629
1630 pragma (inline, true)
1631 {
1632 void eos(size_type offset) nothrow { _M_set_length(offset); }
1633
1634 inout(pointer) _M_local_data() inout { return _M_local_buf.ptr; }
1635 bool _M_is_local() const { return _M_data == _M_local_data; }
1636
1637 ref inout(allocator_type) _M_get_allocator() inout
1638 {
1639 static if (!is_empty!allocator_type.value)
1640 return _M_Alloc;
1641 else
1642 return *cast(inout(allocator_type)*)&this;
1643 }
1644
1645 void _M_set_length(size_type __n)
1646 {
1647 _M_length = __n;
1648 _M_data[__n] = T(0);
1649 }
1650
1651 size_type _M_check(size_type __pos, const char* __s) const
1652 {
1653 assert(__pos <= size());
1654 // if (__pos > size())
1655 // __throw_out_of_range_fmt(__N("%s: __pos (which is %zu) > "
1656 // "this->size() (which is %zu)"),
1657 // __s, __pos, this->size());
1658 return __pos;
1659 }
1660
1661 // NB: _M_limit doesn't check for a bad __pos value.
1662 size_type _M_limit(size_type __pos, size_type __off) const nothrow pure @nogc @safe
1663 {
1664 const bool __testoff = __off < size() - __pos;
1665 return __testoff ? __off : size() - __pos;
1666 }
1667
1668 void __alloc_on_swap()(ref allocator_type __a)
1669 if (!is_empty!allocator_type.value)
1670 {
1671 import core.internal.lifetime : swap;
1672
1673 static if (allocator_traits!allocator_type.propagate_on_container_swap)
1674 swap(_M_get_allocator(), __a);
1675 }
1676
1677 void __alloc_on_swap()(ref allocator_type __a)
1678 if (is_empty!allocator_type.value)
1679 {
1680 import core.internal.lifetime : swap;
1681 import core.lifetime : move;
1682
1683 static if (allocator_traits!allocator_type.propagate_on_container_swap)
1684 {
1685 static if (is(typeof(_M_get_allocator().opAssign(move(__a)))))
1686 swap(_M_get_allocator(), __a);
1687 }
1688 }
1689 }
1690
1691 void _M_construct(const(T)* __beg, size_type __dnew)
1692 {
1693 if (__dnew > _S_local_capacity)
1694 {
1695 _M_data = _M_create(__dnew, size_type(0));
1696 _M_capacity = __dnew;
1697 }
1698 _M_data[0 .. __dnew] = __beg[0 .. __dnew];
1699 _M_set_length(__dnew);
1700 }
1701
1702 pointer _M_create(ref size_type __capacity, size_type __old_capacity)
1703 {
1704 assert(__capacity <= max_size());
1705 // if (__capacity > max_size())
1706 // throw new RangeError("Length exceeds `max_size()`"); // std::__throw_length_error(__N("basic_string::_M_create"));
1707 if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)
1708 {
1709 __capacity = 2 * __old_capacity;
1710 if (__capacity > max_size())
1711 __capacity = max_size();
1712 }
1713 return _M_get_allocator().allocate(__capacity + 1);
1714 }
1715
1716 ref basic_string _M_replace(size_type __pos, size_type __len1, const(T)* __s, const size_type __len2)
1717 {
1718 _M_check_length(__len1, __len2, "basic_string::_M_replace");
1719
1720 const size_type __old_size = size();
1721 const size_type __new_size = __old_size + __len2 - __len1;
1722
1723 if (__new_size <= capacity())
1724 {
1725 pointer __p = _M_data + __pos;
1726
1727 const size_type __how_much = __old_size - __pos - __len1;
1728 if (_M_disjunct(__s))
1729 {
1730 if (__how_much && __len1 != __len2)
1731 _S_move(__p + __len2, __p + __len1, __how_much);
1732 if (__len2)
1733 _S_copy(__p, __s, __len2);
1734 }
1735 else
1736 {
1737 // Work in-place.
1738 if (__len2 && __len2 <= __len1)
1739 _S_move(__p, __s, __len2);
1740 if (__how_much && __len1 != __len2)
1741 _S_move(__p + __len2, __p + __len1, __how_much);
1742 if (__len2 > __len1)
1743 {
1744 if (__s + __len2 <= __p + __len1)
1745 _S_move(__p, __s, __len2);
1746 else if (__s >= __p + __len1)
1747 _S_copy(__p, __s + __len2 - __len1, __len2);
1748 else
1749 {
1750 const size_type __nleft = (__p + __len1) - __s;
1751 _S_move(__p, __s, __nleft);
1752 _S_copy(__p + __nleft, __p + __len2,
1753 __len2 - __nleft);
1754 }
1755 }
1756 }
1757 }
1758 else
1759 _M_mutate(__pos, __len1, __s, __len2);
1760
1761 _M_set_length(__new_size);
1762 return this;
1763 }
1764
1765 ref basic_string _M_replace_aux(size_type __pos1, size_type __n1, size_type __n2, T __c)
1766 {
1767 _M_check_length(__n1, __n2, "basic_string::_M_replace_aux");
1768
1769 const size_type __old_size = size();
1770 const size_type __new_size = __old_size + __n2 - __n1;
1771
1772 if (__new_size <= capacity())
1773 {
1774 pointer __p = _M_data + __pos1;
1775
1776 const size_type __how_much = __old_size - __pos1 - __n1;
1777 if (__how_much && __n1 != __n2)
1778 _S_move(__p + __n2, __p + __n1, __how_much);
1779 }
1780 else
1781 _M_mutate(__pos1, __n1, null, __n2);
1782
1783 if (__n2)
1784 _M_data[__pos1 .. __pos1 + __n2] = __c;
1785
1786 _M_set_length(__new_size);
1787 return this;
1788 }
1789
1790 ref basic_string _M_append(const(T)* __s, size_type __n)
1791 {
1792 const size_type __len = __n + size();
1793 if (__len <= capacity())
1794 {
1795 if (__n)
1796 _S_copy(_M_data + size(), __s, __n);
1797 }
1798 else
1799 _M_mutate(size(), size_type(0), __s, __n);
1800 _M_set_length(__len);
1801 return this;
1802 }
1803
1804 void _M_mutate(size_type __pos, size_type __len1, const(T)* __s, size_type __len2)
1805 {
1806 const size_type __how_much = length() - __pos - __len1;
1807
1808 size_type __new_capacity = length() + __len2 - __len1;
1809 pointer __r = _M_create(__new_capacity, capacity());
1810
1811 if (__pos)
1812 _S_copy(__r, _M_data, __pos);
1813 if (__s && __len2)
1814 _S_copy(__r + __pos, __s, __len2);
1815 if (__how_much)
1816 _S_copy(__r + __pos + __len2,
1817 _M_data + __pos + __len1, __how_much);
1818
1819 _M_dispose();
1820 _M_data = __r;
1821 _M_capacity = __new_capacity;
1822 }
1823
1824 void _M_dispose()
1825 {
1826 if (!_M_is_local)
1827 _M_destroy(_M_allocated_capacity);
1828 }
1829
1830 void _M_destroy(size_type __size)
1831 {
1832 _M_get_allocator().deallocate(_M_data, __size + 1);
1833 }
1834 }
1835
1836 // common GCC/stdlibc++ code
1837
1838 void _M_check_length(size_type __n1, size_type __n2, const char* __s) const
1839 {
1840 assert (!(max_size() - (size() - __n1) < __n2));
1841 // if (max_size() - (size() - __n1) < __n2)
1842 // __throw_length_error(__N(__s));
1843 }
1844
1845 void _M_assign_allocator(ref const(allocator_type) al) nothrow
1846 {
1847 static if (!is_empty!allocator_type.value)
1848 _M_Alloc = al;
1849 }
1850
1851 bool _M_disjunct(const(T)* __s) const nothrow
1852 {
1853 return __s < _M_data || _M_data + size() < __s;
1854 }
1855
1856 static void _S_move(T* __d, const(T)* __s, size_type __n)
1857 {
1858 if (__d == __s)
1859 return;
1860 if (__d < __s)
1861 {
1862 for (size_t i = 0; i < __n; ++i)
1863 __d[i] = __s[i];
1864 }
1865 else
1866 {
1867 for (ptrdiff_t i = __n - 1; i >= 0; --i)
1868 __d[i] = __s[i];
1869 }
1870 }
1871 static void _S_copy(T* __d, const(T)* __s, size_type __n)
1872 {
1873 __d[0 .. __n] = __s[0 .. __n];
1874 }
1875 }
versionbasic_string1876 else version (CppRuntime_Clang)
1877 {
1878 //----------------------------------------------------------------------------------
1879 // Clang/libc++ implementation
1880 //----------------------------------------------------------------------------------
1881
1882 ///
1883 this(DefaultConstruct) { __zero(); }
1884 ///
1885 this(const(T)[] str, ref const(allocator_type) al) { __assign_allocator(al); this(str); }
1886 ///
1887 this(const(T)[] str) { __init(str.ptr, str.length); }
1888 ///
1889 this(this)
1890 {
1891 if (__is_long())
1892 __init(__get_long_pointer(), __get_long_size());
1893 }
1894
1895 ///
1896 ~this()
1897 {
1898 // __get_db()->__erase_c(this); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
1899 if (__is_long())
1900 __alloc().deallocate(__get_long_pointer(), __get_long_cap());
1901 }
1902
1903 ///
1904 ref inout(Alloc) get_allocator() inout { return __alloc(); }
1905
1906 ///
1907 size_type max_size() const nothrow @safe
1908 {
1909 size_type __m = size_t.max; // TODO: __alloc_traits::max_size(__alloc());
1910 version (BigEndian)
1911 return (__m <= ~__long_mask ? __m : __m/2) - __alignment;
1912 else
1913 return __m - __alignment;
1914 }
1915
1916 ///
1917 size_type size() const nothrow { return __is_long() ? __get_long_size() : __get_short_size(); }
1918 ///
1919 size_type capacity() const nothrow { return (__is_long() ? __get_long_cap() : __min_cap) - 1; }
1920 ///
1921 inout(T)* data() inout @safe { return __get_pointer(); }
1922 ///
1923 inout(T)[] as_array() scope return inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
1924 ///
1925 ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; }
1926
1927 ///
1928 ref basic_string assign(const(T)[] str)
1929 {
1930 const(value_type)* __s = str.ptr;
1931 size_type __n = str.length;
1932 size_type __cap = capacity();
1933 if (__cap >= __n)
1934 {
1935 value_type* __p = __get_pointer();
1936 __p[0 .. __n] = __s[0 .. __n]; // TODO: is memmove?
1937 __p[__n] = value_type(0);
1938 __set_size(__n);
1939 // __invalidate_iterators_past(__n); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
1940 }
1941 else
1942 {
1943 size_type __sz = size();
1944 __grow_by_and_replace(__cap, __n - __cap, __sz, 0, __sz, __n, __s);
1945 }
1946 return this;
1947 }
1948
1949 ///
1950 ref basic_string assign(const ref basic_string str)
1951 {
1952 if (&this != &str)
1953 assign(str.as_array);
1954 return this;
1955 }
1956
1957 ///
1958 ref basic_string append(const(T)[] str)
1959 {
1960 const(value_type)* __s = str.ptr;
1961 size_type __n = str.length;
1962 size_type __cap = capacity();
1963 size_type __sz = size();
1964 if (__cap - __sz >= __n)
1965 {
1966 if (__n)
1967 {
1968 value_type* __p = __get_pointer();
1969 (__p + __sz)[0 .. __n] = __s[0 .. __n];
1970 __sz += __n;
1971 __set_size(__sz);
1972 __p[__sz] = value_type(0);
1973 }
1974 }
1975 else
1976 __grow_by_and_replace(__cap, __sz + __n - __cap, __sz, __sz, 0, __n, __s);
1977 return this;
1978 }
1979
1980 ///
1981 ref basic_string append(size_type __n, value_type __c)
1982 {
1983 if (__n)
1984 {
1985 size_type __cap = capacity();
1986 size_type __sz = size();
1987 if (__cap - __sz < __n)
1988 __grow_by(__cap, __sz + __n - __cap, __sz, __sz, 0);
1989 pointer __p = __get_pointer();
1990 __p[__sz .. __sz + __n] = __c;
1991 __sz += __n;
1992 __set_size(__sz);
1993 __p[__sz] = value_type(0);
1994 }
1995 return this;
1996 }
1997
1998 ///
1999 void reserve(size_type __res_arg = 0)
2000 {
2001 assert(__res_arg <= max_size());
2002 // if (__res_arg > max_size())
2003 // __throw_length_error();
2004 size_type __cap = capacity();
2005 size_type __sz = size();
2006 __res_arg = max(__res_arg, __sz);
2007 __res_arg = __recommend(__res_arg);
2008 if (__res_arg != __cap)
2009 {
2010 pointer __new_data, __p;
2011 bool __was_long, __now_long;
2012 if (__res_arg == __min_cap - 1)
2013 {
2014 __was_long = true;
2015 __now_long = false;
2016 __new_data = __get_short_pointer();
2017 __p = __get_long_pointer();
2018 }
2019 else
2020 {
2021 if (__res_arg > __cap)
2022 __new_data = __alloc().allocate(__res_arg+1);
2023 else
2024 {
2025 try
2026 __new_data = __alloc().allocate(__res_arg+1);
2027 catch (Throwable)
2028 return;
2029 }
2030 __now_long = true;
2031 __was_long = __is_long();
2032 __p = __get_pointer();
2033 }
2034 __new_data[0 .. size()+1] = __p[0 .. size()+1];
2035 if (__was_long)
2036 __alloc().deallocate(__p, __cap+1);
2037 if (__now_long)
2038 {
2039 __set_long_cap(__res_arg+1);
2040 __set_long_size(__sz);
2041 __set_long_pointer(__new_data);
2042 }
2043 else
2044 __set_short_size(__sz);
2045 // __invalidate_all_iterators(); // TODO:
2046 }
2047 }
2048
2049 ///
2050 void shrink_to_fit()
2051 {
2052 reserve();
2053 }
2054
2055 ///
2056 ref basic_string insert(size_type __pos, const(value_type)* __s, size_type __n)
2057 {
2058 assert(__n == 0 || __s != null, "string::insert received null");
2059 size_type __sz = size();
2060 assert(__pos <= __sz);
2061 // if (__pos > __sz)
2062 // this->__throw_out_of_range();
2063 size_type __cap = capacity();
2064 if (__cap - __sz >= __n)
2065 {
2066 if (__n)
2067 {
2068 value_type* __p = __get_pointer();
2069 size_type __n_move = __sz - __pos;
2070 if (__n_move != 0)
2071 {
2072 if (__p + __pos <= __s && __s < __p + __sz)
2073 __s += __n;
2074 traits_type.move(__p + __pos + __n, __p + __pos, __n_move);
2075 }
2076 traits_type.move(__p + __pos, __s, __n);
2077 __sz += __n;
2078 __set_size(__sz);
2079 __p[__sz] = value_type(0);
2080 }
2081 }
2082 else
2083 __grow_by_and_replace(__cap, __sz + __n - __cap, __sz, __pos, 0, __n, __s);
2084 return this;
2085 }
2086
2087 ///
2088 ref basic_string insert(size_type pos, size_type n, value_type c)
2089 {
2090 alias __pos = pos;
2091 alias __n = n;
2092 alias __c = c;
2093 size_type __sz = size();
2094 assert(__pos <= __sz);
2095 // if (__pos > __sz)
2096 // __throw_out_of_range();
2097 if (__n)
2098 {
2099 size_type __cap = capacity();
2100 value_type* __p;
2101 if (__cap - __sz >= __n)
2102 {
2103 __p = __get_pointer();
2104 size_type __n_move = __sz - __pos;
2105 if (__n_move != 0)
2106 traits_type.move(__p + __pos + __n, __p + __pos, __n_move);
2107 }
2108 else
2109 {
2110 __grow_by(__cap, __sz + __n - __cap, __sz, __pos, 0, __n);
2111 __p = __get_long_pointer();
2112 }
2113 __p[__pos .. __pos + __n] = __c;
2114 __sz += __n;
2115 __set_size(__sz);
2116 __p[__sz] = value_type(0);
2117 }
2118 return this;
2119 }
2120
2121 ///
2122 ref basic_string replace(size_type __pos, size_type __n1, const(T)* __s, size_type __n2)
2123 {
2124 assert(__n2 == 0 || __s != null, "string::replace received null");
2125 size_type __sz = size();
2126 assert(__pos <= __sz);
2127 // if (__pos > __sz)
2128 // __throw_out_of_range();
2129 __n1 = min(__n1, __sz - __pos);
2130 size_type __cap = capacity();
2131 if (__cap - __sz + __n1 >= __n2)
2132 {
2133 value_type* __p = __get_pointer();
2134 if (__n1 != __n2)
2135 {
2136 size_type __n_move = __sz - __pos - __n1;
2137 if (__n_move != 0)
2138 {
2139 if (__n1 > __n2)
2140 {
2141 traits_type.move(__p + __pos, __s, __n2);
2142 traits_type.move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
2143 goto __finish;
2144 }
2145 if (__p + __pos < __s && __s < __p + __sz)
2146 {
2147 if (__p + __pos + __n1 <= __s)
2148 __s += __n2 - __n1;
2149 else // __p + __pos < __s < __p + __pos + __n1
2150 {
2151 traits_type.move(__p + __pos, __s, __n1);
2152 __pos += __n1;
2153 __s += __n2;
2154 __n2 -= __n1;
2155 __n1 = 0;
2156 }
2157 }
2158 traits_type.move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
2159 }
2160 }
2161 traits_type.move(__p + __pos, __s, __n2);
2162 __finish:
2163 // __sz += __n2 - __n1; in this and the below function below can cause unsigned integer overflow,
2164 // but this is a safe operation, so we disable the check.
2165 __sz += __n2 - __n1;
2166 __set_size(__sz);
2167 // __invalidate_iterators_past(__sz); // TODO
2168 __p[__sz] = value_type(0);
2169 }
2170 else
2171 __grow_by_and_replace(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2, __s);
2172 return this;
2173 }
2174
2175 ///
2176 ref basic_string replace(size_type __pos, size_type __n1, size_type __n2, value_type __c)
2177 {
2178 size_type __sz = size();
2179 assert(__pos <= __sz);
2180 // if (__pos > __sz)
2181 // __throw_out_of_range();
2182 __n1 = min(__n1, __sz - __pos);
2183 size_type __cap = capacity();
2184 value_type* __p;
2185 if (__cap - __sz + __n1 >= __n2)
2186 {
2187 __p = __get_pointer();
2188 if (__n1 != __n2)
2189 {
2190 size_type __n_move = __sz - __pos - __n1;
2191 if (__n_move != 0)
2192 traits_type.move(__p + __pos + __n2, __p + __pos + __n1, __n_move);
2193 }
2194 }
2195 else
2196 {
2197 __grow_by(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2);
2198 __p = __get_long_pointer();
2199 }
2200 __p[__pos .. __pos + __n2] = __c;
2201 __sz += __n2 - __n1;
2202 __set_size(__sz);
2203 // __invalidate_iterators_past(__sz); // TODO
2204 __p[__sz] = value_type(0);
2205 return this;
2206 }
2207
2208 ///
2209 void swap(ref basic_string __str)
2210 {
2211 import core.internal.lifetime : swap;
2212 // static if (_LIBCPP_DEBUG_LEVEL >= 2)
2213 // {
2214 // if (!__is_long())
2215 // __get_db().__invalidate_all(&this);
2216 // if (!__str.__is_long())
2217 // __get_db().__invalidate_all(&__str);
2218 // __get_db().swap(&this, &__str);
2219 // }
2220 assert(
2221 __alloc_traits.propagate_on_container_swap ||
2222 __alloc_traits.is_always_equal ||
2223 __alloc() == __str.__alloc(), "swapping non-equal allocators");
2224 swap(__r_.first(), __str.__r_.first());
2225 __swap_allocator(__alloc(), __str.__alloc());
2226 }
2227
2228 private:
2229 // import core.exception : RangeError;
2230 import core.stdcpp.xutility : __compressed_pair;
2231
2232 alias __alloc_traits = allocator_traits!allocator_type;
2233
2234 enum __alignment = 16;
2235
2236 version (_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT)
2237 {
2238 struct __long
2239 {
2240 pointer __data_;
2241 size_type __size_;
2242 size_type __cap_;
2243 }
2244
2245 version (BigEndian)
2246 {
2247 enum size_type __short_mask = 0x01;
2248 enum size_type __long_mask = 0x1;
2249 }
2250 else
2251 {
2252 enum size_type __short_mask = 0x80;
2253 enum size_type __long_mask = ~(size_type(~0) >> 1);
2254 }
2255
2256 enum size_type __min_cap = (__long.sizeof - 1)/value_type.sizeof > 2 ? (__long.sizeof - 1)/value_type.sizeof : 2;
2257
2258 struct __short
2259 {
2260 value_type[__min_cap] __data_;
2261 struct
2262 {
2263 static if (value_type.sizeof > 1)
2264 ubyte[value_type.sizeof-1] __xx; // __padding<value_type>
2265 ubyte __size_;
2266 }
2267 }
2268 }
2269 else
2270 {
2271 struct __long
2272 {
2273 size_type __cap_;
2274 size_type __size_;
2275 pointer __data_;
2276 }
2277
2278 version (BigEndian)
2279 {
2280 enum size_type __short_mask = 0x80;
2281 enum size_type __long_mask = ~(size_type(~0) >> 1);
2282 }
2283 else
2284 {
2285 enum size_type __short_mask = 0x01;
2286 enum size_type __long_mask = 0x1;
2287 }
2288
2289 enum size_type __min_cap = (__long.sizeof - 1)/value_type.sizeof > 2 ? (__long.sizeof - 1)/value_type.sizeof : 2;
2290
2291 struct __short
2292 {
2293 union
2294 {
2295 ubyte __size_;
2296 value_type __lx;
2297 }
2298 value_type[__min_cap] __data_;
2299 }
2300 }
2301
2302 union __ulx { __long __lx; __short __lxx; }
2303 enum __n_words = __ulx.sizeof / size_type.sizeof;
2304
2305 struct __raw
2306 {
2307 size_type[__n_words] __words;
2308 }
2309
2310 struct __rep
2311 {
2312 union
2313 {
2314 __long __l;
2315 __short __s;
2316 __raw __r;
2317 }
2318 }
2319
2320 __compressed_pair!(__rep, allocator_type) __r_;
2321
2322 pragma (inline, true)
2323 {
2324 void eos(size_type offset) nothrow
2325 {
2326 __set_size(offset);
2327 // __invalidate_iterators_past(__sz); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
2328 __get_pointer()[offset] = value_type(0);
2329 }
2330
2331 version (_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT)
2332 {
2333 version (BigEndian)
2334 {
2335 void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s << 1); }
2336 size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_ >> 1; }
2337 }
2338 else
2339 {
2340 void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s);}
2341 size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_;}
2342 }
2343 }
2344 else
2345 {
2346 version (BigEndian)
2347 {
2348 void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s); }
2349 size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_; }
2350 }
2351 else
2352 {
2353 void __set_short_size(size_type __s) nothrow @safe { __r_.first().__s.__size_ = cast(ubyte)(__s << 1); }
2354 size_type __get_short_size() const nothrow @safe { return __r_.first().__s.__size_ >> 1; }
2355 }
2356 }
2357 void __set_long_size(size_type __s) nothrow { __r_.first().__l.__size_ = __s; }
2358 size_type __get_long_size() const nothrow { return __r_.first().__l.__size_; }
2359 void __set_size(size_type __s) nothrow { if (__is_long()) __set_long_size(__s); else __set_short_size(__s); }
2360
2361 void __set_long_cap(size_type __s) nothrow { __r_.first().__l.__cap_ = __long_mask | __s; }
2362 size_type __get_long_cap() const nothrow { return __r_.first().__l.__cap_ & size_type(~__long_mask); }
2363
2364 void __set_long_pointer(pointer __p) nothrow { __r_.first().__l.__data_ = __p; }
2365 inout(T)* __get_long_pointer() inout nothrow { return __r_.first().__l.__data_; }
2366 inout(T)* __get_short_pointer() inout nothrow @safe { return &__r_.first().__s.__data_[0]; }
2367 inout(T)* __get_pointer() inout nothrow { return __is_long() ? __get_long_pointer() : __get_short_pointer(); }
2368
2369 bool __is_long() const nothrow @safe { return (__r_.first().__s.__size_ & __short_mask) != 0; }
2370
2371 void __zero() nothrow @safe { __r_.first().__r.__words[] = 0; }
2372
2373 ref inout(allocator_type) __alloc() inout nothrow @safe { return __r_.second(); }
2374
2375 void __init(const(value_type)* __s, size_type __sz) { return __init(__s, __sz, __sz); }
2376 }
2377
2378 void __assign_allocator(ref const(allocator_type) al) nothrow
2379 {
2380 static if (!__r_.Ty2Empty)
2381 __alloc() = al;
2382 }
2383
2384 void __init(const(value_type)* __s, size_type __sz, size_type __reserve)
2385 {
2386 assert(__reserve <= max_size());
2387 // if (__reserve > max_size())
2388 // throw new RangeError("Length exceeds `max_size()`"); // this->__throw_length_error();
2389 pointer __p;
2390 if (__reserve < __min_cap)
2391 {
2392 __set_short_size(__sz);
2393 __p = __get_short_pointer();
2394 }
2395 else
2396 {
2397 size_type __cap = __recommend(__reserve);
2398 __p = __alloc().allocate(__cap+1, null);
2399 __set_long_pointer(__p);
2400 __set_long_cap(__cap+1);
2401 __set_long_size(__sz);
2402 }
2403 __p[0 .. __sz] = __s[0 .. __sz];
2404 __p[__sz] = value_type(0);
2405 }
2406
2407 static size_type __recommend(size_type __s) nothrow @safe
2408 {
2409 static size_type __align_it(size_type __a)(size_type __s) nothrow @safe { return (__s + (__a-1)) & ~(__a-1); }
2410 if (__s < __min_cap) return __min_cap - 1;
2411 size_type __guess = __align_it!(value_type.sizeof < __alignment ? __alignment/value_type.sizeof : 1)(__s+1) - 1;
2412 if (__guess == __min_cap) ++__guess;
2413 return __guess;
2414 }
2415
2416 void __grow_by_and_replace(size_type __old_cap, size_type __delta_cap, size_type __old_sz, size_type __n_copy,
2417 size_type __n_del, size_type __n_add, const(value_type)* __p_new_stuff)
2418 {
2419 size_type __ms = max_size();
2420 assert(__delta_cap <= __ms - __old_cap - 1);
2421 // if (__delta_cap > __ms - __old_cap - 1)
2422 // throw new RangeError("Length exceeds `max_size()`"); // this->__throw_length_error();
2423 pointer __old_p = __get_pointer();
2424 size_type __cap = __old_cap < __ms / 2 - __alignment ?
2425 __recommend(max(__old_cap + __delta_cap, 2 * __old_cap)) :
2426 __ms - 1;
2427 pointer __p = __alloc().allocate(__cap+1);
2428 // __invalidate_all_iterators(); // TODO: support `_LIBCPP_DEBUG_LEVEL >= 2` ??
2429 if (__n_copy != 0)
2430 __p[0 .. __n_copy] = __old_p[0 .. __n_copy];
2431 if (__n_add != 0)
2432 (__p + __n_copy)[0 .. __n_add] = __p_new_stuff[0 .. __n_add];
2433 size_type __sec_cp_sz = __old_sz - __n_del - __n_copy;
2434 if (__sec_cp_sz != 0)
2435 (__p + __n_copy + __n_add)[0 .. __sec_cp_sz] = (__old_p + __n_copy + __n_del)[0 .. __sec_cp_sz];
2436 if (__old_cap+1 != __min_cap)
2437 __alloc().deallocate(__old_p, __old_cap+1);
2438 __set_long_pointer(__p);
2439 __set_long_cap(__cap+1);
2440 __old_sz = __n_copy + __n_add + __sec_cp_sz;
2441 __set_long_size(__old_sz);
2442 __p[__old_sz] = value_type(0);
2443 }
2444
2445 void __grow_by(size_type __old_cap, size_type __delta_cap, size_type __old_sz,
2446 size_type __n_copy, size_type __n_del, size_type __n_add = 0)
2447 {
2448 size_type __ms = max_size();
2449 assert(__delta_cap <= __ms - __old_cap);
2450 // if (__delta_cap > __ms - __old_cap)
2451 // __throw_length_error();
2452 pointer __old_p = __get_pointer();
2453 size_type __cap = __old_cap < __ms / 2 - __alignment ?
2454 __recommend(max(__old_cap + __delta_cap, 2 * __old_cap)) :
2455 __ms - 1;
2456 pointer __p = __alloc().allocate(__cap+1);
2457 // __invalidate_all_iterators(); // TODO:
2458 if (__n_copy != 0)
2459 __p[0 .. __n_copy] = __old_p[0 .. __n_copy];
2460 size_type __sec_cp_sz = __old_sz - __n_del - __n_copy;
2461 if (__sec_cp_sz != 0)
2462 (__p + __n_copy + __n_add)[0 .. __sec_cp_sz] = (__old_p + __n_copy + __n_del)[0 .. __sec_cp_sz];
2463 if (__old_cap+1 != __min_cap)
2464 __alloc().deallocate(__old_p, __old_cap+1);
2465 __set_long_pointer(__p);
2466 __set_long_cap(__cap+1);
2467 }
2468 }
2469 else
2470 {
2471 static assert(false, "C++ runtime not supported");
2472 }
2473 }
2474
2475
2476 // platform detail
2477 private:
2478 version (CppRuntime_Microsoft)
2479 {
2480 import core.stdcpp.xutility : _ITERATOR_DEBUG_LEVEL;
2481
2482 extern(C++, (StdNamespace)):
2483 extern (C++) struct _String_base_types(_Elem, _Alloc)
2484 {
2485 alias Ty = _Elem;
2486 alias Alloc = _Alloc;
2487 }
2488
2489 extern (C++, class) struct _String_alloc(_Alloc_types)
2490 {
2491 import core.stdcpp.xutility : _Compressed_pair;
2492
2493 alias Ty = _Alloc_types.Ty;
2494 alias Alloc = _Alloc_types.Alloc;
2495 alias ValTy = _String_val!Ty;
2496
2497 extern(D) @safe @nogc:
2498 pragma(inline, true)
2499 {
2500 ref inout(Alloc) _Getal() return inout pure nothrow { return _Mypair._Myval1; }
2501 ref inout(ValTy) _Get_data() return inout pure nothrow { return _Mypair._Myval2; }
2502 }
2503
2504 void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); }
2505
2506 static if (_ITERATOR_DEBUG_LEVEL > 0)
2507 {
2508 import core.stdcpp.xutility : _Container_proxy;
2509
2510 ~this()
2511 {
2512 _Free_proxy();
2513 }
2514
2515 pragma(inline, true)
2516 ref inout(_Container_proxy*) _Myproxy() inout pure nothrow { return _Get_data._Base._Myproxy; }
2517
2518 void _Alloc_proxy() nothrow @trusted
2519 {
2520 import core.lifetime : emplace;
2521
2522 alias _Alproxy = Alloc.rebind!_Container_proxy;
2523 try // TODO: or should we make allocator<T>::allocate() `nothrow`?
2524 _Myproxy() = _Alproxy(_Getal()).allocate(1);
2525 catch (Throwable)
2526 assert(false, "Failed to allocate iterator debug container proxy");
2527 emplace!_Container_proxy(_Myproxy());
2528 _Myproxy()._Mycont = &_Get_data()._Base;
2529 }
2530 void _Free_proxy() nothrow @trusted
2531 {
2532 alias _Alproxy = Alloc.rebind!_Container_proxy;
2533 _Orphan_all();
2534 destroy!false(*_Myproxy());
2535 try // TODO: or should we make allocator<T>::deallocate() `nothrow`?
2536 _Alproxy(_Getal()).deallocate(_Myproxy(), 1);
2537 catch (Throwable)
2538 assert(false, "Failed to deallocate iterator debug container proxy");
2539 _Myproxy() = null;
2540 }
2541 }
2542
2543 _Compressed_pair!(Alloc, ValTy) _Mypair;
2544 }
2545
2546 extern (C++, class) struct _String_val(T)
2547 {
2548 import core.stdcpp.xutility : _Container_base;
2549 import core.stdcpp.type_traits : is_empty;
2550
2551 enum _BUF_SIZE = 16 / T.sizeof < 1 ? 1 : 16 / T.sizeof;
2552 enum _ALLOC_MASK = T.sizeof <= 1 ? 15 : T.sizeof <= 2 ? 7 : T.sizeof <= 4 ? 3 : T.sizeof <= 8 ? 1 : 0;
2553
2554 static if (!is_empty!_Container_base.value)
2555 _Container_base _Base;
2556 else
2557 ref inout(_Container_base) _Base() inout { return *cast(inout(_Container_base)*)&this; }
2558
2559 union _Bxty
2560 {
2561 T[_BUF_SIZE] _Buf;
2562 T* _Ptr;
2563 }
2564
2565 _Bxty _Bx;
2566 size_t _Mysize = 0; // current length of string
2567 size_t _Myres = _BUF_SIZE - 1; // current storage reserved for string
2568
2569 pragma (inline, true):
2570 extern (D):
2571 pure nothrow @nogc:
2572 bool _IsAllocated() const @safe { return _BUF_SIZE <= _Myres; }
2573 alias _Large_string_engaged = _IsAllocated;
2574 @property inout(T)* _Myptr() inout @trusted { return _BUF_SIZE <= _Myres ? _Bx._Ptr : _Bx._Buf.ptr; }
2575 @property inout(T)[] _Mystr() inout @trusted { return _BUF_SIZE <= _Myres ? _Bx._Ptr[0 .. _Mysize] : _Bx._Buf[0 .. _Mysize]; }
2576
2577 auto _Clamp_suffix_size(T)(const T _Off, const T _Size) const
2578 {
2579 // trims _Size to the longest it can be assuming a string at/after _Off
2580 return min(_Size, _Mysize - _Off);
2581 }
2582 }
2583
2584 template _Size_after_ebco_v(_Ty)
2585 {
2586 import core.stdcpp.type_traits : is_empty;
2587
2588 enum size_t _Size_after_ebco_v = is_empty!_Ty.value ? 0 : _Ty.sizeof; // get _Ty's size after being EBCO'd
2589 }
2590 }
2591
2592 auto ref T max(T)(auto ref T a, auto ref T b) { return b > a ? b : a; }
2593 auto ref T min(T)(auto ref T a, auto ref T b) { return b < a ? b : a; }
2594