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 28 version (Darwin) 29 { 30 // Apple decided to rock a different ABI... good for them! 31 version = _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT; 32 } 33 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 */ 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 /// 158 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 /// 164 ref inout(T) opIndex(size_t index) inout pure nothrow @safe @nogc { return as_array[index]; } 165 /// 166 inout(T)[] opIndex(size_t[2] slice) inout pure nothrow @safe @nogc { return as_array[slice[0] .. slice[1]]; } 167 /// 168 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. 171 bool opEquals(scope const ref basic_string s) const pure nothrow @safe { return as_array == s.as_array; } 172 /// ditto 173 bool opEquals(scope const T[] s) const pure nothrow @safe { return as_array == s; } 174 175 /// Performs lexicographical comparison. 176 int opCmp(scope const ref basic_string rhs) const pure nothrow @safe { return __cmp(as_array, rhs.as_array); } 177 /// ditto 178 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>>`.** 182 size_t toHash() const @nogc nothrow pure @safe { return .hashOf(as_array); } 183 184 /// 185 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 /// 196 ref inout(T) front() inout nothrow @safe { return this[0]; } 197 /// 198 ref inout(T) back() inout nothrow @safe { return this[$-1]; } 199 200 /// 201 const(T)* c_str() const nothrow @safe { return data(); } 202 203 // Modifiers 204 /// 205 ref basic_string opAssign()(auto ref basic_string str) { return assign(str); } 206 // ref basic_string assign(size_type n, T c); 207 /// 208 ref basic_string opAssign(const(T)[] str) { return assign(str); } 209 /// 210 ref basic_string opAssign(T c) { return assign((&c)[0 .. 1]); } 211 212 /// 213 ref basic_string opIndexAssign(T c, size_t index) { as_array[index] = c; return this; } 214 /// 215 ref basic_string opIndexAssign(T c, size_t[2] slice) { as_array[slice[0] .. slice[1]] = c; return this; } 216 /// 217 ref basic_string opIndexAssign(const(T)[] str, size_t[2] slice) { as_array[slice[0] .. slice[1]] = str[]; return this; } 218 /// 219 ref basic_string opIndexAssign(T c) { as_array[] = c; return this; } 220 /// 221 ref basic_string opIndexAssign(const(T)[] str) { as_array[] = str[]; return this; } 222 223 /// 224 ref basic_string opIndexOpAssign(string op)(T c, size_t index) { mixin("as_array[index] " ~ op ~ "= c;"); return this; } 225 /// 226 ref basic_string opIndexOpAssign(string op)(T c, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= c;"); return this; } 227 /// 228 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 /// 230 ref basic_string opIndexOpAssign(string op)(T c) { mixin("as_array[] " ~ op ~ "= c;"); return this; } 231 /// 232 ref basic_string opIndexOpAssign(string op)(const(T)[] str) { mixin("as_array[] " ~ op ~ "= str[];"); return this; } 233 /// 234 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 /// 241 ref basic_string insert(size_type pos, ref const(basic_string) str) { return insert(pos, str.data(), str.size()); } 242 /// 243 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 /// 259 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 /// 269 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 /// 280 ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str) { return replace(pos, len, str.data(), str.size()); } 281 /// 282 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 /// 292 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 /// 302 void push_back(T c) @trusted { append((&c)[0 .. 1]); } 303 /// 304 void pop_back() { erase(size() - 1); } 305 306 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 } 897 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 } 1876 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; }