/** * D header file for interaction with C++ std::string. * * Copyright: Copyright (c) 2019 D Language Foundation * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Guillaume Chatelet * Manu Evans * Source: $(DRUNTIMESRC core/stdcpp/string.d) */ module core.stdcpp.string; import core.stdcpp.allocator;import core.stdcpp.xutility : StdNamespace;import core.stdc.stddef : wchar_t; version (OSX) version = Darwin;else version (iOS) version = Darwin;else version (TVOS) version = Darwin;else version (WatchOS) version = Darwin; version (Darwin){ // Apple decided to rock a different ABI... good for them! version = _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT;} version (CppRuntime_GNU){ version (_GLIBCXX_USE_CXX98_ABI) { private enum StringNamespace = "std"; version = __GTHREADS; } else { import core.internal.traits : AliasSeq; private enum StringNamespace = AliasSeq!("std", "__cxx11"); }}else alias StringNamespace = StdNamespace; enum DefaultConstruct { value } /// Constructor argument for default constructionenum Default = DefaultConstruct(); @nogc: /** * Character traits classes specify character properties and provide specific * semantics for certain operations on characters and sequences of characters. */extern(C++, (StdNamespace)) struct char_traits(CharT){ alias char_type = CharT; static size_t length(const(char_type)* s) @trusted pure nothrow @nogc { static if (is(char_type == char) || is(char_type == ubyte)) { import core.stdc.string : strlen; return strlen(s); } else { size_t len = 0; for (; *s != char_type(0); ++s) ++len; return len; } } static char_type* move(char_type* s1, const char_type* s2, size_t n) @trusted pure nothrow @nogc { import core.stdc.string : memmove; import core.stdc.wchar_ : wmemmove; import core.stdc.stddef : wchar_t; if (n == 0) return s1; version (CRuntime_Microsoft) { enum crt = __traits(getTargetInfo, "cppRuntimeLibrary"); static if (crt.length >= 6 && crt[0 .. 6] == "msvcrt") enum use_wmemmove = false; // https://issues.dlang.org/show_bug.cgi?id=20456 else enum use_wmemmove = true; } else enum use_wmemmove = true; static if (use_wmemmove && (is(char_type == wchar_t) || is(char_type == ushort) && wchar_t.sizeof == ushort.sizeof // Windows || is(char_type == uint) && wchar_t.sizeof == uint.sizeof)) // POSIX return cast(char_type*) wmemmove(s1, s2, n); else return cast(char_type*) memmove(s1, s2, n * char_type.sizeof); }} // I don't think we can have these here, otherwise symbols are emit to druntime, and we don't want that...//alias std_string = basic_string!char;//alias std_u16string = basic_string!wchar; // TODO: can't mangle these yet either...//alias std_u32string = basic_string!dchar;//alias std_wstring = basic_string!wchar_t; // TODO: we can't mangle wchar_t properly (yet?) /** * D language counterpart to C++ std::basic_string. * * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string) */extern(C++, class)extern(C++, (StringNamespace))struct basic_string(T, Traits = char_traits!T, Alloc = allocator!T){extern(D):@nogc: /// enum size_type npos = size_type.max; /// alias size_type = size_t; /// alias difference_type = ptrdiff_t; /// alias value_type = T; /// alias traits_type = Traits; /// alias allocator_type = Alloc; /// alias pointer = value_type*; /// alias const_pointer = const(value_type)*; /// alias toString = as_array; /// MSVC allocates on default initialisation in debug, which can't be modelled by D `struct` @disable this(); /// alias length = size; /// alias opDollar = length; /// bool empty() const nothrow @trusted { return size() == 0; } /// size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const pure nothrow @safe @nogc { return [start, end]; } /// ref inout(T) opIndex(size_t index) inout pure nothrow @safe @nogc { return as_array[index]; } /// inout(T)[] opIndex(size_t[2] slice) inout pure nothrow @safe @nogc { return as_array[slice[0] .. slice[1]]; } /// inout(T)[] opIndex() inout pure nothrow @safe @nogc { return as_array(); } /// Two `basic_string`s are equal if they represent the same sequence of code units. bool opEquals(scope const ref basic_string s) const pure nothrow @safe { return as_array == s.as_array; } /// ditto bool opEquals(scope const T[] s) const pure nothrow @safe { return as_array == s; } /// Performs lexicographical comparison. int opCmp(scope const ref basic_string rhs) const pure nothrow @safe { return __cmp(as_array, rhs.as_array); } /// ditto int opCmp(scope const T[] rhs) const pure nothrow @safe { return __cmp(as_array, rhs); } /// Hash to allow `basic_string`s to be used as keys for built-in associative arrays. /// **The result will generally not be the same as C++ `std::hash<std::basic_string<T>>`.** size_t toHash() const @nogc nothrow pure @safe { return .hashOf(as_array); } /// void clear() { eos(0); } // TODO: bounds-check /// void resize(size_type n, T c = T(0)) @trusted { if (n <= size()) eos(n); else append(n - size(), c); } /// ref inout(T) front() inout nothrow @safe { return this[0]; } /// ref inout(T) back() inout nothrow @safe { return this[$-1]; } /// const(T)* c_str() const nothrow @safe { return data(); } // Modifiers /// ref basic_string opAssign()(auto ref basic_string str) { return assign(str); }// ref basic_string assign(size_type n, T c); /// ref basic_string opAssign(const(T)[] str) { return assign(str); } /// ref basic_string opAssign(T c) { return assign((&c)[0 .. 1]); } /// ref basic_string opIndexAssign(T c, size_t index) { as_array[index] = c; return this; } /// ref basic_string opIndexAssign(T c, size_t[2] slice) { as_array[slice[0] .. slice[1]] = c; return this; } /// ref basic_string opIndexAssign(const(T)[] str, size_t[2] slice) { as_array[slice[0] .. slice[1]] = str[]; return this; } /// ref basic_string opIndexAssign(T c) { as_array[] = c; return this; } /// ref basic_string opIndexAssign(const(T)[] str) { as_array[] = str[]; return this; } /// ref basic_string opIndexOpAssign(string op)(T c, size_t index) { mixin("as_array[index] " ~ op ~ "= c;"); return this; } /// ref basic_string opIndexOpAssign(string op)(T c, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= c;"); return this; } /// ref basic_string opIndexOpAssign(string op)(const(T)[] str, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= str[];"); return this; } /// ref basic_string opIndexOpAssign(string op)(T c) { mixin("as_array[] " ~ op ~ "= c;"); return this; } /// ref basic_string opIndexOpAssign(string op)(const(T)[] str) { mixin("as_array[] " ~ op ~ "= str[];"); return this; } /// ref basic_string append(T c) { return append((&c)[0 .. 1]); } /// ref basic_string opOpAssign(string op : "~")(const(T)[] str) { return append(str); } /// ref basic_string opOpAssign(string op : "~")(T c) { return append((&c)[0 .. 1]); } /// ref basic_string insert(size_type pos, ref const(basic_string) str) { return insert(pos, str.data(), str.size()); } /// ref basic_string insert(size_type pos, ref const(basic_string) str, size_type subpos, size_type sublen) @trusted { const _strsz = str.size(); assert(subpos <= _strsz);// if (subpos > _strsz)// throw new RangeError("subpos exceeds length of str"); return insert(pos, str.data() + subpos, min(sublen, _strsz - subpos)); } /// ref basic_string insert(S : size_type)(S pos, const(T)* s) { // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict. assert(s); return insert(pos, s, traits_type.length(s)); } /// ref basic_string insert(size_type pos, const(T)[] s) { insert(pos, &s[0], s.length); return this; } /// ref basic_string erase(size_type pos = 0) // TODO: bounds-check {// _My_data._Check_offset(pos); eos(pos); return this; } /// ref basic_string erase(size_type pos, size_type len) // TODO: bounds-check {// _My_data._Check_offset(pos); T[] str = as_array(); size_type new_len = str.length - len; this[pos .. new_len] = this[pos + len .. str.length]; // TODO: should be memmove! eos(new_len); return this; } /// ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str) { return replace(pos, len, str.data(), str.size()); } /// ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str, size_type subpos, size_type sublen=npos) { size_type strsz = str.size(); assert(subpos <= strsz);// if (subpos > strsz)// throw new RangeError("subpos exceeds size of str"); return replace(pos, len, str.data() + subpos, min(sublen, strsz - subpos)); } /// ref basic_string replace(size_type pos, size_type len, const(value_type)[] s) { return replace(pos, len, s.ptr, s.length); } /// ref basic_string replace(S : size_type)(S pos, size_type len, const(value_type)* s) { // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict. assert(s !is null, "string::replace received null"); return replace(pos, len, s, traits_type.length(s)); } /// void push_back(T c) @trusted { append((&c)[0 .. 1]); } /// void pop_back() { erase(size() - 1); } version (CppRuntime_Microsoft) { //---------------------------------------------------------------------------------- // Microsoft runtime //---------------------------------------------------------------------------------- /// this(DefaultConstruct) { _Alloc_proxy(); _Tidy_init(); } /// this(const(T)[] str) { _Alloc_proxy(); _Tidy_init(); assign(str); } /// this(const(T)[] str, ref const(allocator_type) al) { _Alloc_proxy(); _AssignAllocator(al); _Tidy_init(); assign(str); } /// this(this) { _Alloc_proxy(); if (_Get_data()._IsAllocated()) { T[] _Str = _Get_data()._Mystr; _Tidy_init(); assign(_Str); } } /// ~this() { _Tidy_deallocate(); } /// ref inout(Alloc) get_allocator() inout { return _Getal(); } /// size_type max_size() const nothrow @safe { return ((size_t.max / T.sizeof) - 1) / 2; } // HACK: clone the windows version precisely? /// size_type size() const nothrow @safe { return _Get_data()._Mysize; } /// size_type capacity() const nothrow @safe { return _Get_data()._Myres; } /// inout(T)* data() inout @safe { return _Get_data()._Myptr; } /// inout(T)[] as_array() return ref scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } /// ref basic_string assign(const(T)[] str) { size_type _Count = str.length; auto _My_data = &_Get_data(); if (_Count <= _My_data._Myres) { T* _Old_ptr = _My_data._Myptr; _My_data._Mysize = _Count; _Old_ptr[0 .. _Count] = str[]; // TODO: this needs to be a memmove(), does that work here? _Old_ptr[_Count] = T(0); return this; } return _Reallocate_for(_Count, (T* _New_ptr, size_type _Count, const(T)* _Ptr) nothrow { _New_ptr[0 .. _Count] = _Ptr[0 .. _Count]; _New_ptr[_Count] = T(0); }, str.ptr); } /// ref basic_string assign(const ref basic_string str) { if (&this != &str) assign(str.as_array); return this; } /// ref basic_string append(const(T)[] str) { size_type _Count = str.length; auto _My_data = &_Get_data(); size_type _Old_size = _My_data._Mysize; if (_Count <= _My_data._Myres - _Old_size) { pointer _Old_ptr = _My_data._Myptr; _My_data._Mysize = _Old_size + _Count; _Old_ptr[_Old_size .. _Old_size + _Count] = str[]; // TODO: this needs to be a memmove(), does that work here? _Old_ptr[_Old_size + _Count] = T(0); return this; } return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, const(T)[] _Str) { _New_ptr[0 .. _Old_str.length] = _Old_str[]; _New_ptr[_Old_str.length .. _Old_str.length + _Str.length] = _Str[]; _New_ptr[_Old_str.length + _Str.length] = T(0); }, str); } /// ref basic_string append(size_type n, T c) { alias _Count = n; alias _Ch = c; auto _My_data = &_Get_data(); const size_type _Old_size = _My_data._Mysize; if (_Count <= _My_data._Myres - _Old_size) { _My_data._Mysize = _Old_size + _Count; pointer _Old_ptr = _My_data._Myptr(); _Old_ptr[_Old_size .. _Old_size + _Count] = _Ch; _Old_ptr[_Old_size + _Count] = T(0); return this; } return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, size_type _Count, T _Ch) { _New_ptr[0 .. _Old_str.length] = _Old_str[]; _New_ptr[_Old_str.length .. _Old_str.length + _Count] = _Ch; _New_ptr[_Old_str.length + _Count] = T(0); }, _Count, _Ch); } /// void reserve(size_type _Newcap = 0) { // determine new minimum length of allocated storage auto _My_data = &_Get_data(); if (_My_data._Mysize > _Newcap) { // requested capacity is not large enough for current size, ignore return; // nothing to do } if (_My_data._Myres == _Newcap) { // we're already at the requested capacity return; // nothing to do } if (_My_data._Myres < _Newcap) { // reallocate to grow const size_type _Old_size = _My_data._Mysize; _Reallocate_grow_by( _Newcap - _Old_size, (T* _New_ptr, const(T)[] _Old_str) { _New_ptr[0 .. _Old_str.length] = _Old_str[]; _New_ptr[_Old_str.length] = _Old_str.ptr[_Old_str.length]; }); _My_data._Mysize = _Old_size; return; } if (_My_data._BUF_SIZE > _Newcap && _My_data._Large_string_engaged()) { // deallocate everything; switch back to "small" mode _Become_small(); return; } // ignore requests to reserve to [_BUF_SIZE, _Myres) } /// void shrink_to_fit() { // reduce capacity auto _My_data = &_Get_data(); if (!_My_data._Large_string_engaged()) { // can't shrink from small mode return; } if (_My_data._Mysize < _My_data._BUF_SIZE) { _Become_small(); return; } const size_type _Target_capacity = min(_My_data._Mysize | _My_data._ALLOC_MASK, max_size()); if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto _Al = &_Getal(); pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws _Base._Orphan_all(); _New_ptr[0 .. _My_data._Mysize + 1] = _My_data._Bx._Ptr[0 .. _My_data._Mysize + 1]; _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); _My_data._Bx._Ptr = _New_ptr; _My_data._Myres = _Target_capacity; } } /// ref basic_string insert(size_type pos, const(T)* s, size_type n) { // insert [_Ptr, _Ptr + _Count) at _Off alias _Off = pos;Lines 1–500 of 2593. Load more
/** * D header file for interaction with C++ std::string. * * Copyright: Copyright (c) 2019 D Language Foundation * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Guillaume Chatelet * Manu Evans * Source: $(DRUNTIMESRC core/stdcpp/string.d) */ module core.stdcpp.string; import core.stdcpp.allocator; import core.stdcpp.xutility : StdNamespace; import core.stdc.stddef : wchar_t; version (OSX) version = Darwin; else version (iOS) version = Darwin; else version (TVOS) version = Darwin; else version (WatchOS) version = Darwin; version (Darwin) { // Apple decided to rock a different ABI... good for them! version = _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT; } version (CppRuntime_GNU) { version (_GLIBCXX_USE_CXX98_ABI) { private enum StringNamespace = "std"; version = __GTHREADS; } else { import core.internal.traits : AliasSeq; private enum StringNamespace = AliasSeq!("std", "__cxx11"); } } else alias StringNamespace = StdNamespace; enum DefaultConstruct { value } /// Constructor argument for default construction enum Default = DefaultConstruct(); @nogc: /** * Character traits classes specify character properties and provide specific * semantics for certain operations on characters and sequences of characters. */ extern(C++, (StdNamespace)) struct char_traits(CharT) { alias char_type = CharT; static size_t length(const(char_type)* s) @trusted pure nothrow @nogc { static if (is(char_type == char) || is(char_type == ubyte)) { import core.stdc.string : strlen; return strlen(s); } else { size_t len = 0; for (; *s != char_type(0); ++s) ++len; return len; } } static char_type* move(char_type* s1, const char_type* s2, size_t n) @trusted pure nothrow @nogc { import core.stdc.string : memmove; import core.stdc.wchar_ : wmemmove; import core.stdc.stddef : wchar_t; if (n == 0) return s1; version (CRuntime_Microsoft) { enum crt = __traits(getTargetInfo, "cppRuntimeLibrary"); static if (crt.length >= 6 && crt[0 .. 6] == "msvcrt") enum use_wmemmove = false; // https://issues.dlang.org/show_bug.cgi?id=20456 else enum use_wmemmove = true; } else enum use_wmemmove = true; static if (use_wmemmove && (is(char_type == wchar_t) || is(char_type == ushort) && wchar_t.sizeof == ushort.sizeof // Windows || is(char_type == uint) && wchar_t.sizeof == uint.sizeof)) // POSIX return cast(char_type*) wmemmove(s1, s2, n); else return cast(char_type*) memmove(s1, s2, n * char_type.sizeof); } } // I don't think we can have these here, otherwise symbols are emit to druntime, and we don't want that... //alias std_string = basic_string!char; //alias std_u16string = basic_string!wchar; // TODO: can't mangle these yet either... //alias std_u32string = basic_string!dchar; //alias std_wstring = basic_string!wchar_t; // TODO: we can't mangle wchar_t properly (yet?) /** * D language counterpart to C++ std::basic_string. * * C++ reference: $(LINK2 https://en.cppreference.com/w/cpp/string/basic_string) */ extern(C++, class) extern(C++, (StringNamespace)) struct basic_string(T, Traits = char_traits!T, Alloc = allocator!T) { extern(D): @nogc: /// enum size_type npos = size_type.max; /// alias size_type = size_t; /// alias difference_type = ptrdiff_t; /// alias value_type = T; /// alias traits_type = Traits; /// alias allocator_type = Alloc; /// alias pointer = value_type*; /// alias const_pointer = const(value_type)*; /// alias toString = as_array; /// MSVC allocates on default initialisation in debug, which can't be modelled by D `struct` @disable this(); /// alias length = size; /// alias opDollar = length; /// bool empty() const nothrow @trusted { return size() == 0; } /// size_t[2] opSlice(size_t dim : 0)(size_t start, size_t end) const pure nothrow @safe @nogc { return [start, end]; } /// ref inout(T) opIndex(size_t index) inout pure nothrow @safe @nogc { return as_array[index]; } /// inout(T)[] opIndex(size_t[2] slice) inout pure nothrow @safe @nogc { return as_array[slice[0] .. slice[1]]; } /// inout(T)[] opIndex() inout pure nothrow @safe @nogc { return as_array(); } /// Two `basic_string`s are equal if they represent the same sequence of code units. bool opEquals(scope const ref basic_string s) const pure nothrow @safe { return as_array == s.as_array; } /// ditto bool opEquals(scope const T[] s) const pure nothrow @safe { return as_array == s; } /// Performs lexicographical comparison. int opCmp(scope const ref basic_string rhs) const pure nothrow @safe { return __cmp(as_array, rhs.as_array); } /// ditto int opCmp(scope const T[] rhs) const pure nothrow @safe { return __cmp(as_array, rhs); } /// Hash to allow `basic_string`s to be used as keys for built-in associative arrays. /// **The result will generally not be the same as C++ `std::hash<std::basic_string<T>>`.** size_t toHash() const @nogc nothrow pure @safe { return .hashOf(as_array); } /// void clear() { eos(0); } // TODO: bounds-check /// void resize(size_type n, T c = T(0)) @trusted { if (n <= size()) eos(n); else append(n - size(), c); } /// ref inout(T) front() inout nothrow @safe { return this[0]; } /// ref inout(T) back() inout nothrow @safe { return this[$-1]; } /// const(T)* c_str() const nothrow @safe { return data(); } // Modifiers /// ref basic_string opAssign()(auto ref basic_string str) { return assign(str); } // ref basic_string assign(size_type n, T c); /// ref basic_string opAssign(const(T)[] str) { return assign(str); } /// ref basic_string opAssign(T c) { return assign((&c)[0 .. 1]); } /// ref basic_string opIndexAssign(T c, size_t index) { as_array[index] = c; return this; } /// ref basic_string opIndexAssign(T c, size_t[2] slice) { as_array[slice[0] .. slice[1]] = c; return this; } /// ref basic_string opIndexAssign(const(T)[] str, size_t[2] slice) { as_array[slice[0] .. slice[1]] = str[]; return this; } /// ref basic_string opIndexAssign(T c) { as_array[] = c; return this; } /// ref basic_string opIndexAssign(const(T)[] str) { as_array[] = str[]; return this; } /// ref basic_string opIndexOpAssign(string op)(T c, size_t index) { mixin("as_array[index] " ~ op ~ "= c;"); return this; } /// ref basic_string opIndexOpAssign(string op)(T c, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= c;"); return this; } /// ref basic_string opIndexOpAssign(string op)(const(T)[] str, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= str[];"); return this; } /// ref basic_string opIndexOpAssign(string op)(T c) { mixin("as_array[] " ~ op ~ "= c;"); return this; } /// ref basic_string opIndexOpAssign(string op)(const(T)[] str) { mixin("as_array[] " ~ op ~ "= str[];"); return this; } /// ref basic_string append(T c) { return append((&c)[0 .. 1]); } /// ref basic_string opOpAssign(string op : "~")(const(T)[] str) { return append(str); } /// ref basic_string opOpAssign(string op : "~")(T c) { return append((&c)[0 .. 1]); } /// ref basic_string insert(size_type pos, ref const(basic_string) str) { return insert(pos, str.data(), str.size()); } /// ref basic_string insert(size_type pos, ref const(basic_string) str, size_type subpos, size_type sublen) @trusted { const _strsz = str.size(); assert(subpos <= _strsz); // if (subpos > _strsz) // throw new RangeError("subpos exceeds length of str"); return insert(pos, str.data() + subpos, min(sublen, _strsz - subpos)); } /// ref basic_string insert(S : size_type)(S pos, const(T)* s) { // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict. assert(s); return insert(pos, s, traits_type.length(s)); } /// ref basic_string insert(size_type pos, const(T)[] s) { insert(pos, &s[0], s.length); return this; } /// ref basic_string erase(size_type pos = 0) // TODO: bounds-check { // _My_data._Check_offset(pos); eos(pos); return this; } /// ref basic_string erase(size_type pos, size_type len) // TODO: bounds-check { // _My_data._Check_offset(pos); T[] str = as_array(); size_type new_len = str.length - len; this[pos .. new_len] = this[pos + len .. str.length]; // TODO: should be memmove! eos(new_len); return this; } /// ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str) { return replace(pos, len, str.data(), str.size()); } /// ref basic_string replace()(size_type pos, size_type len, auto ref basic_string str, size_type subpos, size_type sublen=npos) { size_type strsz = str.size(); assert(subpos <= strsz); // if (subpos > strsz) // throw new RangeError("subpos exceeds size of str"); return replace(pos, len, str.data() + subpos, min(sublen, strsz - subpos)); } /// ref basic_string replace(size_type pos, size_type len, const(value_type)[] s) { return replace(pos, len, s.ptr, s.length); } /// ref basic_string replace(S : size_type)(S pos, size_type len, const(value_type)* s) { // This overload is declared as a template to give precedence to the slice overload const(T)[] in case of conflict. assert(s !is null, "string::replace received null"); return replace(pos, len, s, traits_type.length(s)); } /// void push_back(T c) @trusted { append((&c)[0 .. 1]); } /// void pop_back() { erase(size() - 1); } version (CppRuntime_Microsoft) { //---------------------------------------------------------------------------------- // Microsoft runtime //---------------------------------------------------------------------------------- /// this(DefaultConstruct) { _Alloc_proxy(); _Tidy_init(); } /// this(const(T)[] str) { _Alloc_proxy(); _Tidy_init(); assign(str); } /// this(const(T)[] str, ref const(allocator_type) al) { _Alloc_proxy(); _AssignAllocator(al); _Tidy_init(); assign(str); } /// this(this) { _Alloc_proxy(); if (_Get_data()._IsAllocated()) { T[] _Str = _Get_data()._Mystr; _Tidy_init(); assign(_Str); } } /// ~this() { _Tidy_deallocate(); } /// ref inout(Alloc) get_allocator() inout { return _Getal(); } /// size_type max_size() const nothrow @safe { return ((size_t.max / T.sizeof) - 1) / 2; } // HACK: clone the windows version precisely? /// size_type size() const nothrow @safe { return _Get_data()._Mysize; } /// size_type capacity() const nothrow @safe { return _Get_data()._Myres; } /// inout(T)* data() inout @safe { return _Get_data()._Myptr; } /// inout(T)[] as_array() return ref scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } /// ref basic_string assign(const(T)[] str) { size_type _Count = str.length; auto _My_data = &_Get_data(); if (_Count <= _My_data._Myres) { T* _Old_ptr = _My_data._Myptr; _My_data._Mysize = _Count; _Old_ptr[0 .. _Count] = str[]; // TODO: this needs to be a memmove(), does that work here? _Old_ptr[_Count] = T(0); return this; } return _Reallocate_for(_Count, (T* _New_ptr, size_type _Count, const(T)* _Ptr) nothrow { _New_ptr[0 .. _Count] = _Ptr[0 .. _Count]; _New_ptr[_Count] = T(0); }, str.ptr); } /// ref basic_string assign(const ref basic_string str) { if (&this != &str) assign(str.as_array); return this; } /// ref basic_string append(const(T)[] str) { size_type _Count = str.length; auto _My_data = &_Get_data(); size_type _Old_size = _My_data._Mysize; if (_Count <= _My_data._Myres - _Old_size) { pointer _Old_ptr = _My_data._Myptr; _My_data._Mysize = _Old_size + _Count; _Old_ptr[_Old_size .. _Old_size + _Count] = str[]; // TODO: this needs to be a memmove(), does that work here? _Old_ptr[_Old_size + _Count] = T(0); return this; } return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, const(T)[] _Str) { _New_ptr[0 .. _Old_str.length] = _Old_str[]; _New_ptr[_Old_str.length .. _Old_str.length + _Str.length] = _Str[]; _New_ptr[_Old_str.length + _Str.length] = T(0); }, str); } /// ref basic_string append(size_type n, T c) { alias _Count = n; alias _Ch = c; auto _My_data = &_Get_data(); const size_type _Old_size = _My_data._Mysize; if (_Count <= _My_data._Myres - _Old_size) { _My_data._Mysize = _Old_size + _Count; pointer _Old_ptr = _My_data._Myptr(); _Old_ptr[_Old_size .. _Old_size + _Count] = _Ch; _Old_ptr[_Old_size + _Count] = T(0); return this; } return _Reallocate_grow_by(_Count, (T* _New_ptr, const(T)[] _Old_str, size_type _Count, T _Ch) { _New_ptr[0 .. _Old_str.length] = _Old_str[]; _New_ptr[_Old_str.length .. _Old_str.length + _Count] = _Ch; _New_ptr[_Old_str.length + _Count] = T(0); }, _Count, _Ch); } /// void reserve(size_type _Newcap = 0) { // determine new minimum length of allocated storage auto _My_data = &_Get_data(); if (_My_data._Mysize > _Newcap) { // requested capacity is not large enough for current size, ignore return; // nothing to do } if (_My_data._Myres == _Newcap) { // we're already at the requested capacity return; // nothing to do } if (_My_data._Myres < _Newcap) { // reallocate to grow const size_type _Old_size = _My_data._Mysize; _Reallocate_grow_by( _Newcap - _Old_size, (T* _New_ptr, const(T)[] _Old_str) { _New_ptr[0 .. _Old_str.length] = _Old_str[]; _New_ptr[_Old_str.length] = _Old_str.ptr[_Old_str.length]; }); _My_data._Mysize = _Old_size; return; } if (_My_data._BUF_SIZE > _Newcap && _My_data._Large_string_engaged()) { // deallocate everything; switch back to "small" mode _Become_small(); return; } // ignore requests to reserve to [_BUF_SIZE, _Myres) } /// void shrink_to_fit() { // reduce capacity auto _My_data = &_Get_data(); if (!_My_data._Large_string_engaged()) { // can't shrink from small mode return; } if (_My_data._Mysize < _My_data._BUF_SIZE) { _Become_small(); return; } const size_type _Target_capacity = min(_My_data._Mysize | _My_data._ALLOC_MASK, max_size()); if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto _Al = &_Getal(); pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws _Base._Orphan_all(); _New_ptr[0 .. _My_data._Mysize + 1] = _My_data._Bx._Ptr[0 .. _My_data._Mysize + 1]; _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); _My_data._Bx._Ptr = _New_ptr; _My_data._Myres = _Target_capacity; } } /// ref basic_string insert(size_type pos, const(T)* s, size_type n) { // insert [_Ptr, _Ptr + _Count) at _Off alias _Off = pos;