| /** | |
| * D header file for interaction with C++ std::vector. | |
| * | |
| * Copyright: Copyright (c) 2018 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/vector.d) | |
| */ | |
| module core.stdcpp.vector; | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // std::vector declaration. | |
| // | |
| // Current caveats : | |
| // - missing noexcept | |
| // - nothrow @trusted @nogc for most functions depend on knowledge | |
| // of T's construction/destruction/assignment semantics | |
| /////////////////////////////////////////////////////////////////////////////// | |
| import core.stdcpp.allocator; | |
| enum DefaultConstruct { value } | |
| /// Constructor argument for default construction | |
| enum Default = DefaultConstruct(); | |
| extern(C++, "std"): | |
| alias vector(T) = vector!(T, allocator!T); | |
| extern(C++, class) struct vector(T, Alloc) | |
| { | |
| import core.lifetime : forward, move, core_emplace = emplace; | |
| static assert(!is(T == bool), "vector!bool not supported!"); | |
| extern(D): | |
| /// | |
| alias size_type = size_t; | |
| /// | |
| alias difference_type = ptrdiff_t; | |
| /// | |
| alias value_type = T; | |
| /// | |
| alias allocator_type = Alloc; | |
| /// | |
| alias pointer = T*; | |
| /// | |
| alias const_pointer = const(T)*; | |
| /// MSVC allocates on default initialisation in debug, which can't be modelled by D `struct` | |
| @disable this(); | |
| /// | |
| alias length = size; | |
| /// | |
| alias opDollar = length; | |
| /// | |
| 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(); } | |
| /// | |
| ref vector opAssign(U)(auto ref vector!(U, Alloc) s) { opAssign(s.as_array); return this; } | |
| /// | |
| ref vector opAssign(T[] array) | |
| { | |
| clear(); | |
| reserve(array.length); | |
| insert(0, array); | |
| return this; | |
| } | |
| /// | |
| void opIndexAssign()(auto ref T val, size_t index) { as_array[index] = val; } | |
| /// | |
| void opIndexAssign()(auto ref T val, size_t[2] slice) { as_array[slice[0] .. slice[1]] = val; } | |
| /// | |
| void opIndexAssign(T[] val, size_t[2] slice) { as_array[slice[0] .. slice[1]] = val[]; } | |
| /// | |
| void opIndexAssign()(auto ref T val) { as_array[] = val; } | |
| /// | |
| void opIndexAssign(T[] val) { as_array[] = val[]; } | |
| /// | |
| void opIndexOpAssign(string op)(auto ref T val, size_t index) { mixin("as_array[index] " ~ op ~ "= val;"); } | |
| /// | |
| void opIndexOpAssign(string op)(auto ref T val, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= val;"); } | |
| /// | |
| void opIndexOpAssign(string op)(T[] val, size_t[2] slice) { mixin("as_array[slice[0] .. slice[1]] " ~ op ~ "= val[];"); } | |
| /// | |
| void opIndexOpAssign(string op)(auto ref T val) { mixin("as_array[] " ~ op ~ "= val;"); } | |
| /// | |
| void opIndexOpAssign(string op)(T[] val) { mixin("as_array[] " ~ op ~ "= val[];"); } | |
| /// | |
| ref inout(T) front() inout pure nothrow @safe @nogc { return as_array[0]; } | |
| /// | |
| ref inout(T) back() inout pure nothrow @safe @nogc { return as_array[$-1]; } | |
| /// | |
| ref vector opOpAssign(string op : "~")(auto ref T item) { push_back(forward!item); return this; } | |
| /// | |
| ref vector opOpAssign(string op : "~")(T[] array) { insert(length, array); return this; } | |
| /// | |
| void append(T[] array) { insert(length, array); } | |
| /// Performs elementwise equality check. | |
| bool opEquals(this This, That)(auto ref That rhs) | |
| if (is(immutable That == immutable vector)) { return as_array == rhs.as_array; } | |
| /// Performs lexicographical comparison. | |
| static if (is(typeof((ref T a, ref T b) => a < b))) | |
| int opCmp(this This, That)(auto ref That rhs) | |
| if (is(immutable That == immutable vector)) { return __cmp(as_array, rhs.as_array); } | |
| /// Hash to allow `vector`s to be used as keys for built-in associative arrays. | |
| /// **The result will generally not be the same as C++ `std::hash<std::vector<T>>`.** | |
| size_t toHash() const { return .hashOf(as_array); } | |
| // Modifiers | |
| /// | |
| void push_back(U)(auto ref U element) | |
| { | |
| emplace_back(forward!element); | |
| } | |
| version (CppRuntime_Microsoft) | |
| { | |
| //---------------------------------------------------------------------------------- | |
| // Microsoft runtime | |
| //---------------------------------------------------------------------------------- | |
| /// | |
| this(DefaultConstruct) @nogc { _Alloc_proxy(); } | |
| /// | |
| this()(size_t count) | |
| { | |
| _Alloc_proxy(); | |
| _Buy(count); | |
| scope(failure) _Tidy(); | |
| _Get_data()._Mylast = _Udefault(_Get_data()._Myfirst, count); | |
| } | |
| /// | |
| this()(size_t count, auto ref T val) | |
| { | |
| _Alloc_proxy(); | |
| _Buy(count); | |
| scope(failure) _Tidy(); | |
| _Get_data()._Mylast = _Ufill(_Get_data()._Myfirst, count, val); | |
| } | |
| /// | |
| this()(T[] array) | |
| { | |
| _Alloc_proxy(); | |
| _Buy(array.length); | |
| scope(failure) _Tidy(); | |
| _Get_data()._Mylast = _Utransfer!false(array.ptr, array.ptr + array.length, _Get_data()._Myfirst); | |
| } | |
| /// | |
| this(this) | |
| { | |
| _Alloc_proxy(); | |
| pointer _First = _Get_data()._Myfirst; | |
| pointer _Last = _Get_data()._Mylast; | |
| _Buy(_Last - _First); | |
| scope(failure) _Tidy(); | |
| _Get_data()._Mylast = _Utransfer!false(_First, _Last, _Get_data()._Myfirst); | |
| } | |
| /// | |
| ~this() { _Tidy(); } | |
| /// | |
| ref inout(Alloc) get_allocator() inout pure nothrow @safe @nogc { return _Getal(); } | |
| /// | |
| size_type max_size() const pure nothrow @safe @nogc { return ((size_t.max / T.sizeof) - 1) / 2; } // HACK: clone the windows version precisely? | |
| /// | |
| size_type size() const pure nothrow @safe @nogc { return _Get_data()._Mylast - _Get_data()._Myfirst; } | |
| /// | |
| size_type capacity() const pure nothrow @safe @nogc { return _Get_data()._Myend - _Get_data()._Myfirst; } | |
| /// | |
| bool empty() const pure nothrow @safe @nogc { return _Get_data()._Myfirst == _Get_data()._Mylast; } | |
| /// | |
| inout(T)* data() inout pure nothrow @safe @nogc { return _Get_data()._Myfirst; } | |
| /// | |
| inout(T)[] as_array() inout pure nothrow @trusted @nogc { return _Get_data()._Myfirst[0 .. size()]; } | |
| /// | |
| ref inout(T) at(size_type i) inout pure nothrow @trusted @nogc { return _Get_data()._Myfirst[0 .. size()][i]; } | |
| /// | |
| ref T emplace_back(Args...)(auto ref Args args) | |
| { | |
| if (_Has_unused_capacity()) | |
| return _Emplace_back_with_unused_capacity(forward!args); | |
| return *_Emplace_reallocate(_Get_data()._Mylast, forward!args); | |
| } | |
| /// | |
| void reserve(const size_type newCapacity) | |
| { | |
| if (newCapacity > capacity()) | |
| { | |
| // if (newCapacity > max_size()) | |
| // _Xlength(); | |
| _Reallocate_exactly(newCapacity); | |
| } | |
| } | |
| /// | |
| void shrink_to_fit() | |
| { | |
| if (_Has_unused_capacity()) | |
| { | |
| if (empty()) | |
| _Tidy(); | |
| else | |
| _Reallocate_exactly(size()); | |
| } | |
| } | |
| /// | |
| void pop_back() | |
| { | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| { | |
| assert(!empty(), "vector empty before pop"); | |
| _Orphan_range(_Get_data()._Mylast - 1, _Get_data()._Mylast); | |
| } | |
| destroy!false(_Get_data()._Mylast[-1]); | |
| --_Get_data()._Mylast; | |
| } | |
| /// | |
| void clear() | |
| { | |
| _Base._Orphan_all(); | |
| _Destroy(_Get_data()._Myfirst, _Get_data()._Mylast); | |
| _Get_data()._Mylast = _Get_data()._Myfirst; | |
| } | |
| /// | |
| void resize()(const size_type newsize) | |
| { | |
| static assert(is(typeof({static T i;})), T.stringof ~ ".this() is annotated with @disable."); | |
| _Resize(newsize, (pointer _Dest, size_type _Count) => _Udefault(_Dest, _Count)); | |
| } | |
| /// | |
| void resize()(const size_type newsize, auto ref T val) | |
| { | |
| _Resize(newsize, (pointer _Dest, size_type _Count) => _Ufill(_Dest, _Count, forward!val)); | |
| } | |
| void emplace(Args...)(size_t offset, auto ref Args args) | |
| { | |
| pointer _Whereptr = _Get_data()._Myfirst + offset; | |
| pointer _Oldlast = _Get_data()._Mylast; | |
| if (_Has_unused_capacity()) | |
| { | |
| if (_Whereptr == _Oldlast) | |
| _Emplace_back_with_unused_capacity(forward!args); | |
| else | |
| { | |
| T _Obj = T(forward!args); | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Whereptr, _Oldlast); | |
| move(_Oldlast[-1], *_Oldlast); | |
| ++_Get_data()._Mylast; | |
| _Move_backward_unchecked(_Whereptr, _Oldlast - 1, _Oldlast); | |
| move(_Obj, *_Whereptr); | |
| } | |
| return; | |
| } | |
| _Emplace_reallocate(_Whereptr, forward!args); | |
| } | |
| /// | |
| void insert(size_t offset, T[] array) | |
| { | |
| pointer _Where = _Get_data()._Myfirst + offset; | |
| pointer _First = array.ptr; | |
| pointer _Last = _First + array.length; | |
| const size_type _Count = array.length; | |
| const size_type _Whereoff = offset; | |
| const bool _One_at_back = _Count == 1 && _Get_data()._Myfirst + _Whereoff == _Get_data()._Mylast; | |
| if (_Count == 0) | |
| { | |
| // nothing to do, avoid invalidating iterators | |
| } | |
| else if (_Count > _Unused_capacity()) | |
| { // reallocate | |
| const size_type _Oldsize = size(); | |
| // if (_Count > max_size() - _Oldsize) | |
| // _Xlength(); | |
| const size_type _Newsize = _Oldsize + _Count; | |
| const size_type _Newcapacity = _Calculate_growth(_Newsize); | |
| pointer _Newvec = _Getal().allocate(_Newcapacity); | |
| pointer _Constructed_last = _Newvec + _Whereoff + _Count; | |
| pointer _Constructed_first = _Constructed_last; | |
| try | |
| { | |
| _Utransfer!false(_First, _Last, _Newvec + _Whereoff); | |
| _Constructed_first = _Newvec + _Whereoff; | |
| if (_One_at_back) | |
| { | |
| _Utransfer!(true, true)(_Get_data()._Myfirst, _Get_data()._Mylast, _Newvec); | |
| } | |
| else | |
| { | |
| _Utransfer!true(_Get_data()._Myfirst, _Where, _Newvec); | |
| _Constructed_first = _Newvec; | |
| _Utransfer!true(_Where, _Get_data()._Mylast, _Newvec + _Whereoff + _Count); | |
| } | |
| } | |
| catch (Throwable e) | |
| { | |
| _Destroy(_Constructed_first, _Constructed_last); | |
| _Getal().deallocate(_Newvec, _Newcapacity); | |
| throw e; | |
| } | |
| _Change_array(_Newvec, _Newsize, _Newcapacity); | |
| } | |
| else | |
| { // Attempt to provide the strong guarantee for EmplaceConstructible failure. | |
| // If we encounter copy/move construction/assignment failure, provide the basic guarantee. | |
| // (For one-at-back, this provides the strong guarantee.) | |
| pointer _Oldlast = _Get_data()._Mylast; | |
| const size_type _Affected_elements = cast(size_type)(_Oldlast - _Where); | |
| if (_Count < _Affected_elements) | |
| { // some affected elements must be assigned | |
| _Get_data()._Mylast = _Utransfer!true(_Oldlast - _Count, _Oldlast, _Oldlast); | |
| _Move_backward_unchecked(_Where, _Oldlast - _Count, _Oldlast); | |
| _Destroy(_Where, _Where + _Count); | |
| try | |
| { | |
| _Utransfer!false(_First, _Last, _Where); | |
| } | |
| catch (Throwable e) | |
| { | |
| // glue the broken pieces back together | |
| try | |
| { | |
| _Utransfer!true(_Where + _Count, _Where + 2 * _Count, _Where); | |
| } | |
| catch (Throwable e) | |
| { | |
| // vaporize the detached piece | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Where, _Oldlast); | |
| _Destroy(_Where + _Count, _Get_data()._Mylast); | |
| _Get_data()._Mylast = _Where; | |
| throw e; | |
| } | |
| _Move_unchecked(_Where + 2 * _Count, _Get_data()._Mylast, _Where + _Count); | |
| _Destroy(_Oldlast, _Get_data()._Mylast); | |
| _Get_data()._Mylast = _Oldlast; | |
| throw e; | |
| } | |
| } | |
| else | |
| { // affected elements don't overlap before/after | |
| pointer _Relocated = _Where + _Count; | |
| _Get_data()._Mylast = _Utransfer!true(_Where, _Oldlast, _Relocated); | |
| _Destroy(_Where, _Oldlast); | |
| try | |
| { | |
| _Utransfer!false(_First, _Last, _Where); | |
| } | |
| catch (Throwable e) | |
| { | |
| // glue the broken pieces back together | |
| try | |
| { | |
| _Utransfer!true(_Relocated, _Get_data()._Mylast, _Where); | |
| } | |
| catch (Throwable e) | |
| { | |
| // vaporize the detached piece | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Where, _Oldlast); | |
| _Destroy(_Relocated, _Get_data()._Mylast); | |
| _Get_data()._Mylast = _Where; | |
| throw e; | |
| } | |
| _Destroy(_Relocated, _Get_data()._Mylast); | |
| _Get_data()._Mylast = _Oldlast; | |
| throw e; | |
| } | |
| } | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Where, _Oldlast); | |
| } | |
| } | |
| private: | |
| import core.stdcpp.xutility : MSVCLinkDirectives; | |
| // Make sure the object files wont link against mismatching objects | |
| mixin MSVCLinkDirectives!true; | |
| pragma(inline, true) | |
| { | |
| ref inout(_Base.Alloc) _Getal() inout pure nothrow @safe @nogc { return _Base._Mypair._Myval1; } | |
| ref inout(_Base.ValTy) _Get_data() inout pure nothrow @safe @nogc { return _Base._Mypair._Myval2; } | |
| } | |
| void _Alloc_proxy() @nogc | |
| { | |
| static if (_ITERATOR_DEBUG_LEVEL > 0) | |
| _Base._Alloc_proxy(); | |
| } | |
| void _AssignAllocator(ref const(allocator_type) al) nothrow @nogc | |
| { | |
| static if (_Base._Mypair._HasFirst) | |
| _Getal() = al; | |
| } | |
| bool _Buy(size_type _Newcapacity) @trusted @nogc | |
| { | |
| _Get_data()._Myfirst = null; | |
| _Get_data()._Mylast = null; | |
| _Get_data()._Myend = null; | |
| if (_Newcapacity == 0) | |
| return false; | |
| // TODO: how to handle this in D? kinda like a range exception... | |
| // if (_Newcapacity > max_size()) | |
| // _Xlength(); | |
| _Get_data()._Myfirst = _Getal().allocate(_Newcapacity); | |
| _Get_data()._Mylast = _Get_data()._Myfirst; | |
| _Get_data()._Myend = _Get_data()._Myfirst + _Newcapacity; | |
| return true; | |
| } | |
| static void _Destroy(pointer _First, pointer _Last) | |
| { | |
| for (; _First != _Last; ++_First) | |
| destroy!false(*_First); | |
| } | |
| void _Tidy() | |
| { | |
| _Base._Orphan_all(); | |
| if (_Get_data()._Myfirst) | |
| { | |
| _Destroy(_Get_data()._Myfirst, _Get_data()._Mylast); | |
| _Getal().deallocate(_Get_data()._Myfirst, capacity()); | |
| _Get_data()._Myfirst = null; | |
| _Get_data()._Mylast = null; | |
| _Get_data()._Myend = null; | |
| } | |
| } | |
| size_type _Unused_capacity() const pure nothrow @safe @nogc | |
| { | |
| return _Get_data()._Myend - _Get_data()._Mylast; | |
| } | |
| bool _Has_unused_capacity() const pure nothrow @safe @nogc | |
| { | |
| return _Get_data()._Myend != _Get_data()._Mylast; | |
| } | |
| ref T _Emplace_back_with_unused_capacity(Args...)(auto ref Args args) | |
| { | |
| core_emplace(_Get_data()._Mylast, forward!args); | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Get_data()._Mylast, _Get_data()._Mylast); | |
| return *_Get_data()._Mylast++; | |
| } | |
| pointer _Emplace_reallocate(_Valty...)(pointer _Whereptr, auto ref _Valty _Val) | |
| { | |
| const size_type _Whereoff = _Whereptr - _Get_data()._Myfirst; | |
| const size_type _Oldsize = size(); | |
| // TODO: what should we do in D? kinda like a range overflow? | |
| // if (_Oldsize == max_size()) | |
| // _Xlength(); | |
| const size_type _Newsize = _Oldsize + 1; | |
| const size_type _Newcapacity = _Calculate_growth(_Newsize); | |
| pointer _Newvec = _Getal().allocate(_Newcapacity); | |
| pointer _Constructed_last = _Newvec + _Whereoff + 1; | |
| pointer _Constructed_first = _Constructed_last; | |
| try | |
| { | |
| core_emplace(_Newvec + _Whereoff, forward!_Val); | |
| _Constructed_first = _Newvec + _Whereoff; | |
| if (_Whereptr == _Get_data()._Mylast) | |
| _Utransfer!(true, true)(_Get_data()._Myfirst, _Get_data()._Mylast, _Newvec); | |
| else | |
| { | |
| _Utransfer!true(_Get_data()._Myfirst, _Whereptr, _Newvec); | |
| _Constructed_first = _Newvec; | |
| _Utransfer!true(_Whereptr, _Get_data()._Mylast, _Newvec + _Whereoff + 1); | |
| } | |
| } | |
| catch (Throwable e) | |
| { | |
| _Destroy(_Constructed_first, _Constructed_last); | |
| _Getal().deallocate(_Newvec, _Newcapacity); | |
| throw e; | |
| } | |
| _Change_array(_Newvec, _Newsize, _Newcapacity); | |
| return _Get_data()._Myfirst + _Whereoff; | |
| } | |
| void _Resize(_Lambda)(const size_type _Newsize, _Lambda _Udefault_or_fill) | |
| { | |
| const size_type _Oldsize = size(); | |
| const size_type _Oldcapacity = capacity(); | |
| if (_Newsize > _Oldcapacity) | |
| { | |
| // if (_Newsize > max_size()) | |
| // _Xlength(); | |
| const size_type _Newcapacity = _Calculate_growth(_Newsize); | |
| pointer _Newvec = _Getal().allocate(_Newcapacity); | |
| pointer _Appended_first = _Newvec + _Oldsize; | |
| pointer _Appended_last = _Appended_first; | |
| try | |
| { | |
| _Appended_last = _Udefault_or_fill(_Appended_first, _Newsize - _Oldsize); | |
| _Utransfer!(true, true)(_Get_data()._Myfirst, _Get_data()._Mylast, _Newvec); | |
| } | |
| catch (Throwable e) | |
| { | |
| _Destroy(_Appended_first, _Appended_last); | |
| _Getal().deallocate(_Newvec, _Newcapacity); | |
| throw e; | |
| } | |
| _Change_array(_Newvec, _Newsize, _Newcapacity); | |
| } | |
| else if (_Newsize > _Oldsize) | |
| { | |
| pointer _Oldlast = _Get_data()._Mylast; | |
| _Get_data()._Mylast = _Udefault_or_fill(_Oldlast, _Newsize - _Oldsize); | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Oldlast, _Oldlast); | |
| } | |
| else if (_Newsize == _Oldsize) | |
| { | |
| // nothing to do, avoid invalidating iterators | |
| } | |
| else | |
| { | |
| pointer _Newlast = _Get_data()._Myfirst + _Newsize; | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| _Orphan_range(_Newlast, _Get_data()._Mylast); | |
| _Destroy(_Newlast, _Get_data()._Mylast); | |
| _Get_data()._Mylast = _Newlast; | |
| } | |
| } | |
| void _Reallocate_exactly(const size_type _Newcapacity) | |
| { | |
| import core.lifetime : moveEmplace; | |
| const size_type _Size = size(); | |
| pointer _Newvec = _Getal().allocate(_Newcapacity); | |
| try | |
| { | |
| for (size_t i = _Size; i > 0; ) | |
| { | |
| --i; | |
| moveEmplace(_Get_data()._Myfirst[i], _Newvec[i]); | |
| } | |
| } | |
| catch (Throwable e) | |
| { | |
| _Getal().deallocate(_Newvec, _Newcapacity); | |
| throw e; | |
| } | |
| _Change_array(_Newvec, _Size, _Newcapacity); | |
| } | |
| void _Change_array(pointer _Newvec, const size_type _Newsize, const size_type _Newcapacity) | |
| { | |
| _Base._Orphan_all(); | |
| if (_Get_data()._Myfirst != null) | |
| { | |
| _Destroy(_Get_data()._Myfirst, _Get_data()._Mylast); | |
| _Getal().deallocate(_Get_data()._Myfirst, capacity()); | |
| } | |
| _Get_data()._Myfirst = _Newvec; | |
| _Get_data()._Mylast = _Newvec + _Newsize; | |
| _Get_data()._Myend = _Newvec + _Newcapacity; | |
| } | |
| size_type _Calculate_growth(const size_type _Newsize) const pure nothrow @nogc @safe | |
| { | |
| const size_type _Oldcapacity = capacity(); | |
| if (_Oldcapacity > max_size() - _Oldcapacity/2) | |
| return _Newsize; | |
| const size_type _Geometric = _Oldcapacity + _Oldcapacity/2; | |
| if (_Geometric < _Newsize) | |
| return _Newsize; | |
| return _Geometric; | |
| } | |
| struct _Uninitialized_backout | |
| { | |
| this() @disable; | |
| this(pointer _Dest) | |
| { | |
| _First = _Dest; | |
| _Last = _Dest; | |
| } | |
| ~this() | |
| { | |
| _Destroy(_First, _Last); | |
| } | |
| void _Emplace_back(Args...)(auto ref Args args) | |
| { | |
| core_emplace(_Last, forward!args); | |
| ++_Last; | |
| } | |
| pointer _Release() | |
| { | |
| _First = _Last; | |
| return _Last; | |
| } | |
| private: | |
| pointer _First; | |
| pointer _Last; | |
| } | |
| pointer _Utransfer(bool _move, bool _ifNothrow = false)(pointer _First, pointer _Last, pointer _Dest) | |
| { | |
| // TODO: if copy/move are trivial, then we can memcpy/memmove | |
| auto _Backout = _Uninitialized_backout(_Dest); | |
| for (; _First != _Last; ++_First) | |
| { | |
| static if (_move && (!_ifNothrow || true)) // isNothrow!T (move in D is always nothrow! ...until opPostMove) | |
| _Backout._Emplace_back(move(*_First)); | |
| else | |
| _Backout._Emplace_back(*_First); | |
| } | |
| return _Backout._Release(); | |
| } | |
| pointer _Ufill()(pointer _Dest, size_t _Count, auto ref T val) | |
| { | |
| // TODO: if T.sizeof == 1 and no elaborate constructor, fast-path to memset | |
| // TODO: if copy ctor/postblit are nothrow, just range assign | |
| auto _Backout = _Uninitialized_backout(_Dest); | |
| for (; 0 < _Count; --_Count) | |
| _Backout._Emplace_back(val); | |
| return _Backout._Release(); | |
| } | |
| pointer _Udefault()(pointer _Dest, size_t _Count) | |
| { | |
| // TODO: if zero init, then fast-path to zeromem | |
| auto _Backout = _Uninitialized_backout(_Dest); | |
| for (; 0 < _Count; --_Count) | |
| _Backout._Emplace_back(); | |
| return _Backout._Release(); | |
| } | |
| pointer _Move_unchecked(pointer _First, pointer _Last, pointer _Dest) | |
| { | |
| // TODO: can `memmove` if conditions are right... | |
| for (; _First != _Last; ++_Dest, ++_First) | |
| move(*_First, *_Dest); | |
| return _Dest; | |
| } | |
| pointer _Move_backward_unchecked(pointer _First, pointer _Last, pointer _Dest) | |
| { | |
| while (_First != _Last) | |
| move(*--_Last, *--_Dest); | |
| return _Dest; | |
| } | |
| static if (_ITERATOR_DEBUG_LEVEL == 2) | |
| { | |
| void _Orphan_range(pointer _First, pointer _Last) const @nogc | |
| { | |
| import core.stdcpp.xutility : _Lockit, _LOCK_DEBUG; | |
| alias const_iterator = _Base.const_iterator; | |
| auto _Lock = _Lockit(_LOCK_DEBUG); | |
| const_iterator** _Pnext = cast(const_iterator**)_Get_data()._Base._Getpfirst(); | |
| if (!_Pnext) | |
| return; | |
| while (*_Pnext) | |
| { | |
| if ((*_Pnext)._Ptr < _First || _Last < (*_Pnext)._Ptr) | |
| { | |
| _Pnext = cast(const_iterator**)(*_Pnext)._Base._Getpnext(); | |
| } | |
| else | |
| { | |
| (*_Pnext)._Base._Clrcont(); | |
| *_Pnext = *cast(const_iterator**)(*_Pnext)._Base._Getpnext(); | |
| } | |
| } | |
| } | |
| } | |
| _Vector_alloc!(_Vec_base_types!(T, Alloc)) _Base; | |
| } | |
| else version (None) | |
| { | |
| size_type size() const pure nothrow @safe @nogc { return 0; } | |
| size_type capacity() const pure nothrow @safe @nogc { return 0; } | |
| bool empty() const pure nothrow @safe @nogc { return true; } | |
| inout(T)* data() inout pure nothrow @safe @nogc { return null; } | |
| inout(T)[] as_array() inout pure nothrow @trusted @nogc { return null; } | |
| ref inout(T) at(size_type i) inout pure nothrow @trusted @nogc { data()[0]; } | |
| } | |
| else | |
| { | |
| static assert(false, "C++ runtime not supported"); | |
| } | |
| } | |
| // platform detail | |
| private: | |
| version (CppRuntime_Microsoft) | |
| { | |
| import core.stdcpp.xutility : _ITERATOR_DEBUG_LEVEL; | |
| extern (C++, struct) struct _Vec_base_types(_Ty, _Alloc0) | |
| { | |
| alias Ty = _Ty; | |
| alias Alloc = _Alloc0; | |
| } | |
| extern (C++, class) struct _Vector_alloc(_Alloc_types) | |
| { | |
| import core.stdcpp.xutility : _Compressed_pair; | |
| extern(D): | |
| @nogc: | |
| alias Ty = _Alloc_types.Ty; | |
| alias Alloc = _Alloc_types.Alloc; | |
| alias ValTy = _Vector_val!Ty; | |
| void _Orphan_all() nothrow @safe | |
| { | |
| static if (is(typeof(ValTy._Base))) | |
| _Mypair._Myval2._Base._Orphan_all(); | |
| } | |
| static if (_ITERATOR_DEBUG_LEVEL != 0) | |
| { | |
| import core.stdcpp.xutility : _Container_proxy; | |
| alias const_iterator = _Vector_const_iterator!(ValTy); | |
| ~this() | |
| { | |
| _Free_proxy(); | |
| } | |
| void _Alloc_proxy() @trusted | |
| { | |
| import core.lifetime : emplace; | |
| alias _Alproxy = Alloc.rebind!_Container_proxy; | |
| _Alproxy _Proxy_allocator = _Alproxy(_Mypair._Myval1); | |
| _Mypair._Myval2._Base._Myproxy = _Proxy_allocator.allocate(1); | |
| emplace(_Mypair._Myval2._Base._Myproxy); | |
| _Mypair._Myval2._Base._Myproxy._Mycont = &_Mypair._Myval2._Base; | |
| } | |
| void _Free_proxy() | |
| { | |
| alias _Alproxy = Alloc.rebind!_Container_proxy; | |
| _Alproxy _Proxy_allocator = _Alproxy(_Mypair._Myval1); | |
| _Orphan_all(); | |
| destroy!false(_Mypair._Myval2._Base._Myproxy); | |
| _Proxy_allocator.deallocate(_Mypair._Myval2._Base._Myproxy, 1); | |
| _Mypair._Myval2._Base._Myproxy = null; | |
| } | |
| } | |
| _Compressed_pair!(Alloc, ValTy) _Mypair; | |
| } | |
| extern (C++, class) struct _Vector_val(T) | |
| { | |
| import core.stdcpp.xutility : _Container_base; | |
| import core.stdcpp.type_traits : is_empty; | |
| alias pointer = T*; | |
| static if (!is_empty!_Container_base.value) | |
| _Container_base _Base; | |
| pointer _Myfirst; // pointer to beginning of array | |
| pointer _Mylast; // pointer to current end of sequence | |
| pointer _Myend; // pointer to end of array | |
| } | |
| static if (_ITERATOR_DEBUG_LEVEL > 0) | |
| { | |
| extern (C++, class) struct _Vector_const_iterator(_Myvec) | |
| { | |
| import core.stdcpp.xutility : _Iterator_base; | |
| import core.stdcpp.type_traits : is_empty; | |
| static if (!is_empty!_Iterator_base.value) | |
| _Iterator_base _Base; | |
| _Myvec.pointer _Ptr; | |
| } | |
| } | |
| } |