From c6a99b114e7093cc050b1d84fad8ff8bcd267261 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 1 Mar 2017 19:23:54 +0100 Subject: [PATCH] SList.insertFront for ranges --- source/tanya/container/list.d | 692 +++---- source/tanya/container/vector.d | 3038 +++++++++++++++---------------- 2 files changed, 1885 insertions(+), 1845 deletions(-) diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 6405baa..34a20a1 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -3,414 +3,454 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** + * Linked list. + * * Copyright: Eugene Wissner 2016-2017. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * Mozilla Public License, v. 2.0). * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - */ + */ module tanya.container.list; import std.algorithm.comparison; +import std.algorithm.mutation; import std.algorithm.searching; +import std.range.primitives; import std.traits; import tanya.container.entry; import tanya.memory; private struct Range(E) - if (__traits(isSame, TemplateOf!E, SEntry)) + if (__traits(isSame, TemplateOf!E, SEntry)) { - private alias T = typeof(E.content); + private alias T = typeof(E.content); - private E* head; + private E* head; - private this(E* head) - { - this.head = head; - } + private this(E* head) + { + this.head = head; + } - @property Range save() - { - return this; - } + @property Range save() + { + return this; + } - @property size_t length() const - { - return count(opIndex()); - } + @property size_t length() const + { + return count(opIndex()); + } - @property bool empty() const - { - return head is null; - } + @property bool empty() const + { + return head is null; + } - @property ref inout(T) front() inout - in - { - assert(!empty); - } - body - { - return head.content; - } + @property ref inout(T) front() inout + in + { + assert(!empty); + } + body + { + return head.content; + } - void popFront() - in - { - assert(!empty); - } - body - { - head = head.next; - } + void popFront() + in + { + assert(!empty); + } + body + { + head = head.next; + } - Range opIndex() - { - return typeof(return)(head); - } + Range opIndex() + { + return typeof(return)(head); + } - Range!(const E) opIndex() const - { - return typeof(return)(head); - } + Range!(const E) opIndex() const + { + return typeof(return)(head); + } } /** * Singly-linked list. * * Params: - * T = Content type. + * T = Content type. */ struct SList(T) { - private alias Entry = SEntry!T; + private alias Entry = SEntry!T; - // 0th element of the list. - private Entry* head; + // 0th element of the list. + private Entry* head; - /** - * Removes all elements from the list. - */ - ~this() - { - clear(); - } + /** + * Removes all elements from the list. + */ + ~this() + { + clear(); + } - /** - * Removes all contents from the list. - */ - void clear() - { - while (!empty) - { - removeFront(); - } - } + /** + * Removes all contents from the list. + */ + void clear() + { + while (!empty) + { + removeFront(); + } + } - /// - unittest - { - SList!int l; + /// + unittest + { + SList!int l; - l.insertFront(8); - l.insertFront(5); - l.clear(); - assert(l.empty); - } + l.insertFront(8); + l.insertFront(5); + l.clear(); + assert(l.empty); + } - /** - * Returns: First element. - */ - @property ref inout(T) front() inout - in - { - assert(!empty); - } - body - { - return head.content; - } + /** + * Returns: First element. + */ + @property ref inout(T) front() inout + in + { + assert(!empty); + } + body + { + return head.content; + } - /** - * Inserts a new element at the beginning. - * - * Params: - * x = New element. - */ - void insertFront(ref T x) - { - auto temp = allocator.make!Entry; - - temp.content = x; - temp.next = head; - head = temp; - } + /** + * Inserts a new element at the beginning. + * + * Params: + * R = Type of the inserted value(s). + * x = New element. + * + * Returns: The number of elements inserted. + */ + size_t insertFront(R)(ref R x) @trusted + if (isImplicitlyConvertible!(R, T)) + { + head = allocator.make!Entry(x, head); + return 1; + } - /// Ditto. - void insertFront(T x) - { - insertFront(x); - } + /// Ditto. + size_t insertFront(R)(R x) @trusted + if (isImplicitlyConvertible!(R, T)) + { + auto temp = cast(Entry*) allocator.allocate(Entry.sizeof); - /// Ditto. - alias insert = insertFront; + x.moveEmplace(temp.content); + temp.next = head; - /// - unittest - { - SList!int l; + head = temp; + return 1; + } - l.insertFront(8); - assert(l.front == 8); - l.insertFront(9); - assert(l.front == 9); - } + /// Ditto. + size_t insertFront(R)(R el) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + size_t retLength; + foreach (e; el) + { + retLength += insertFront(e); + } + return retLength; + } - /** - * Returns: How many elements are in the list. - */ - @property size_t length() const - { - return count(opIndex()); - } + /// Ditto. + size_t insertFront(size_t R)(T[R] el) + { + return insertFront!(T[])(el[]); + } - /// - unittest - { - SList!int l; + /// Ditto. + alias insert = insertFront; - l.insertFront(8); - l.insertFront(9); - assert(l.length == 2); - l.removeFront(); - assert(l.length == 1); - l.removeFront(); - assert(l.length == 0); - } + /// + @nogc @safe unittest + { + SList!int l1; - /** - * Comparison for equality. - * - * Params: - * that = The list to compare with. - * - * Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false) - * otherwise. - */ - bool opEquals()(auto ref typeof(this) that) @trusted - { - return equal(opIndex(), that[]); - } + assert(l1.insertFront(8) == 1); + assert(l1.front == 8); + assert(l1.insertFront(9) == 1); + assert(l1.front == 9); - /// Ditto. - bool opEquals()(in auto ref typeof(this) that) const @trusted - { - return equal(opIndex(), that[]); - } + SList!int l2; + assert(l2.insertFront([25, 30, 15]) == 3); + assert(l2.front == 15); - /// - unittest - { - SList!int l1, l2; + l2.insertFront(l1[]); + assert(l2.length == 5); + assert(l2.front == 8); + } - l1.insertFront(8); - l1.insertFront(9); - l2.insertFront(8); - l2.insertFront(10); - assert(l1 != l2); + /** + * Returns: How many elements are in the list. + */ + @property size_t length() const + { + return count(opIndex()); + } - l1.removeFront(); - assert(l1 != l2); + /// + unittest + { + SList!int l; - l2.removeFront(); - assert(l1 == l2); + l.insertFront(8); + l.insertFront(9); + assert(l.length == 2); + l.removeFront(); + assert(l.length == 1); + l.removeFront(); + assert(l.length == 0); + } - l1.removeFront(); - assert(l1 != l2); + /** + * Comparison for equality. + * + * Params: + * that = The list to compare with. + * + * Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false) + * otherwise. + */ + bool opEquals()(auto ref typeof(this) that) @trusted + { + return equal(opIndex(), that[]); + } - l2.removeFront(); - assert(l1 == l2); - } + /// Ditto. + bool opEquals()(in auto ref typeof(this) that) const @trusted + { + return equal(opIndex(), that[]); + } - /** - * Returns: $(D_KEYWORD true) if the list is empty. - */ - @property bool empty() const - { - return head is null; - } + /// + unittest + { + SList!int l1, l2; - /** - * Returns the first element and moves to the next one. - * - * Returns: The first element. - * - * Precondition: $(D_INLINECODE !empty) - */ - void removeFront() - in - { - assert(!empty); - } - body - { - auto n = head.next; + l1.insertFront(8); + l1.insertFront(9); + l2.insertFront(8); + l2.insertFront(10); + assert(l1 != l2); - allocator.dispose(head); - head = n; - } + l1.removeFront(); + assert(l1 != l2); - /// - unittest - { - SList!int l; + l2.removeFront(); + assert(l1 == l2); - l.insertFront(8); - l.insertFront(9); - assert(l.front == 9); - l.removeFront(); - assert(l.front == 8); - l.removeFront(); - assert(l.empty); - } + l1.removeFront(); + assert(l1 != l2); - /** - * Removes $(D_PARAM howMany) elements from the list. - * - * Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not - * remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is - * greater than the list length, all elements are removed. - * - * Params: - * howMany = How many elements should be removed. - * - * Returns: The number of elements removed. - */ - size_t removeFront(in size_t howMany) - out (removed) - { - assert(removed <= howMany); - } - body - { - size_t i; - for (; i < howMany && !empty; ++i) - { - removeFront(); - } - return i; - } + l2.removeFront(); + assert(l1 == l2); + } - /// Ditto. - alias remove = removeFront; + /** + * Returns: $(D_KEYWORD true) if the list is empty. + */ + @property bool empty() const + { + return head is null; + } - /// - unittest - { - SList!int l; + /** + * Returns the first element and moves to the next one. + * + * Returns: The first element. + * + * Precondition: $(D_INLINECODE !empty) + */ + void removeFront() + in + { + assert(!empty); + } + body + { + auto n = head.next; - l.insertFront(8); - l.insertFront(5); - l.insertFront(4); - assert(l.removeFront(0) == 0); - assert(l.removeFront(2) == 2); - assert(l.removeFront(3) == 1); - assert(l.removeFront(3) == 0); - } + allocator.dispose(head); + head = n; + } - /** - * $(D_KEYWORD foreach) iteration. - * - * Params: - * dg = $(D_KEYWORD foreach) body. - * - * Returns: The value returned from $(D_PARAM dg). - */ - int opApply(scope int delegate(ref size_t i, ref T) @nogc dg) - { - int result; - size_t i; + /// + unittest + { + SList!int l; - for (auto pos = head; pos; pos = pos.next, ++i) - { - result = dg(i, pos.content); + l.insertFront(8); + l.insertFront(9); + assert(l.front == 9); + l.removeFront(); + assert(l.front == 8); + l.removeFront(); + assert(l.empty); + } - if (result != 0) - { - return result; - } - } - return result; - } + /** + * Removes $(D_PARAM howMany) elements from the list. + * + * Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not + * remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is + * greater than the list length, all elements are removed. + * + * Params: + * howMany = How many elements should be removed. + * + * Returns: The number of elements removed. + */ + size_t removeFront(in size_t howMany) + out (removed) + { + assert(removed <= howMany); + } + body + { + size_t i; + for (; i < howMany && !empty; ++i) + { + removeFront(); + } + return i; + } - /// Ditto. - int opApply(scope int delegate(ref T) @nogc dg) - { - int result; + /// Ditto. + alias remove = removeFront; - for (auto pos = head; pos; pos = pos.next) - { - result = dg(pos.content); + /// + unittest + { + SList!int l; - if (result != 0) - { - return result; - } - } - return result; - } + l.insertFront(8); + l.insertFront(5); + l.insertFront(4); + assert(l.removeFront(0) == 0); + assert(l.removeFront(2) == 2); + assert(l.removeFront(3) == 1); + assert(l.removeFront(3) == 0); + } - /// - unittest - { - SList!int l; + /** + * $(D_KEYWORD foreach) iteration. + * + * Params: + * dg = $(D_KEYWORD foreach) body. + * + * Returns: The value returned from $(D_PARAM dg). + */ + int opApply(scope int delegate(ref size_t i, ref T) @nogc dg) + { + int result; + size_t i; - l.insertFront(5); - l.insertFront(4); - l.insertFront(9); - foreach (i, e; l) - { - assert(i != 0 || e == 9); - assert(i != 1 || e == 4); - assert(i != 2 || e == 5); - } - } + for (auto pos = head; pos; pos = pos.next, ++i) + { + result = dg(i, pos.content); - Range!Entry opIndex() - { - return typeof(return)(head); - } + if (result != 0) + { + return result; + } + } + return result; + } - Range!(const Entry) opIndex() const - { - return typeof(return)(head); - } + /// Ditto. + int opApply(scope int delegate(ref T) @nogc dg) + { + int result; - mixin DefaultAllocator; + for (auto pos = head; pos; pos = pos.next) + { + result = dg(pos.content); + + if (result != 0) + { + return result; + } + } + return result; + } + + /// + unittest + { + SList!int l; + + l.insertFront(5); + l.insertFront(4); + l.insertFront(9); + foreach (i, e; l) + { + assert(i != 0 || e == 9); + assert(i != 1 || e == 4); + assert(i != 2 || e == 5); + } + } + + Range!Entry opIndex() + { + return typeof(return)(head); + } + + Range!(const Entry) opIndex() const + { + return typeof(return)(head); + } + + mixin DefaultAllocator; } /// unittest { - SList!int l; - size_t i; + SList!int l; + size_t i; - l.insertFront(5); - l.insertFront(4); - l.insertFront(9); - foreach (e; l) - { - assert(i != 0 || e == 9); - assert(i != 1 || e == 4); - assert(i != 2 || e == 5); - ++i; - } - assert(i == 3); + l.insertFront(5); + l.insertFront(4); + l.insertFront(9); + foreach (e; l) + { + assert(i != 0 || e == 9); + assert(i != 1 || e == 4); + assert(i != 2 || e == 5); + ++i; + } + assert(i == 3); } unittest { - interface Stuff - { - } - static assert(is(SList!Stuff)); + interface Stuff + { + } + static assert(is(SList!Stuff)); } diff --git a/source/tanya/container/vector.d b/source/tanya/container/vector.d index 954ca20..c14e060 100644 --- a/source/tanya/container/vector.d +++ b/source/tanya/container/vector.d @@ -9,7 +9,7 @@ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, * Mozilla Public License, v. 2.0). * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) - */ + */ module tanya.container.vector; import core.checkedint; @@ -25,1586 +25,1586 @@ import tanya.memory; // Defines the container's primary range. private struct Range(E) { - private E* begin, end; + private E* begin, end; - invariant - { - assert(begin <= end); - } + invariant + { + assert(begin <= end); + } - private this(E* begin, E* end) - { - this.begin = begin; - this.end = end; - } + private this(E* begin, E* end) + { + this.begin = begin; + this.end = end; + } - @property Range save() - { - return this; - } + @property Range save() + { + return this; + } - @property bool empty() const - { - return begin == end; - } + @property bool empty() const + { + return begin == end; + } - @property size_t length() const - { - return end - begin; - } + @property size_t length() const + { + return end - begin; + } - alias opDollar = length; + alias opDollar = length; - @property ref inout(E) front() inout - in - { - assert(!empty); - } - body - { - return *begin; - } + @property ref inout(E) front() inout + in + { + assert(!empty); + } + body + { + return *begin; + } - @property ref inout(E) back() inout @trusted - in - { - assert(!empty); - } - body - { - return *(end - 1); - } + @property ref inout(E) back() inout @trusted + in + { + assert(!empty); + } + body + { + return *(end - 1); + } - void popFront() @trusted - in - { - assert(!empty); - } - body - { - ++begin; - } + void popFront() @trusted + in + { + assert(!empty); + } + body + { + ++begin; + } - void popBack() @trusted - in - { - assert(!empty); - } - body - { - --end; - } + void popBack() @trusted + in + { + assert(!empty); + } + body + { + --end; + } - ref inout(E) opIndex(in size_t i) inout @trusted - in - { - assert(i < length); - } - body - { - return *(begin + i); - } + ref inout(E) opIndex(in size_t i) inout @trusted + in + { + assert(i < length); + } + body + { + return *(begin + i); + } - Range opIndex() - { - return typeof(return)(begin, end); - } + Range opIndex() + { + return typeof(return)(begin, end); + } - Range!(const E) opIndex() const - { - return typeof(return)(begin, end); - } + Range!(const E) opIndex() const + { + return typeof(return)(begin, end); + } - Range opSlice(in size_t i, in size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(begin + i, begin + j); - } + Range opSlice(in size_t i, in size_t j) @trusted + in + { + assert(i <= j); + assert(j <= length); + } + body + { + return typeof(return)(begin + i, begin + j); + } - Range!(const E) opSlice(in size_t i, in size_t j) const @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(begin + i, begin + j); - } + Range!(const E) opSlice(in size_t i, in size_t j) const @trusted + in + { + assert(i <= j); + assert(j <= length); + } + body + { + return typeof(return)(begin + i, begin + j); + } - inout(E[]) get() inout @trusted - { - return begin[0 .. length]; - } + inout(E[]) get() inout @trusted + { + return begin[0 .. length]; + } } /** * One dimensional array. * * Params: - * T = Content type. + * T = Content type. */ struct Vector(T) { - private size_t length_; - private T* vector; - private size_t capacity_; - - invariant - { - assert(length_ <= capacity_); - assert(capacity_ == 0 || vector !is null); - } - - /** - * Creates a new $(D_PSYMBOL Vector) with the elements from another input - * range or a static array $(D_PARAM init). - * - * Params: - * R = Type of the initial range or size of the static array. - * init = Values to initialize the array with. - * to generate a list. - * allocator = Allocator. - */ - this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator) - { - this(allocator); - insertBack!(T[])(init[]); - } - - /// Ditto. - this(R)(R init, shared Allocator allocator = defaultAllocator) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - { - this(allocator); - insertBack(init); - } - - /** - * Initializes this vector from another one. - * - * If $(D_PARAM init) is passed by value, it won't be copied, but moved - * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator), - * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s - * storage, otherwise, the storage will be allocated with - * $(D_PARAM allocator) and all elements will be moved; - * $(D_PARAM init) will be destroyed at the end. - * - * If $(D_PARAM init) is passed by reference, it will be copied. - * - * Params: - * R = Vector type. - * init = Source vector. - * allocator = Allocator. - */ - this(R)(ref R init, shared Allocator allocator = defaultAllocator) - if (is(Unqual!R == Vector)) - { - this(allocator); - insertBack(init[]); - } - - /// Ditto. - this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted - if (is(R == Vector)) - { - this(allocator); - if (allocator is init.allocator) - { - // Just steal all references and the allocator. - vector = init.vector; - length_ = init.length_; - capacity_ = init.capacity_; - - // Reset the source vector, so it can't destroy the moved storage. - init.length_ = init.capacity_ = 0; - init.vector = null; - } - else - { - // Move each element. - reserve(init.length); - moveEmplaceAll(init.vector[0 .. init.length_], vector[0 .. init.length_]); - length_ = init.length; - // Destructor of init should destroy it here. - } - } - - /// - unittest - { - auto v1 = Vector!int([1, 2, 3]); - auto v2 = Vector!int(v1); - assert(v1.vector !is v2.vector); - assert(v1 == v2); - - auto v3 = Vector!int(Vector!int([1, 2, 3])); - assert(v1 == v3); - assert(v3.length == 3); - assert(v3.capacity == 3); - } - - unittest // const constructor tests - { - auto v1 = const Vector!int([1, 2, 3]); - auto v2 = Vector!int(v1); - assert(v1.vector !is v2.vector); - assert(v1 == v2); - - auto v3 = const Vector!int(Vector!int([1, 2, 3])); - assert(v1 == v3); - assert(v3.length == 3); - assert(v3.capacity == 3); - } - - /** - * Creates a new $(D_PSYMBOL Vector). - * - * Params: - * len = Initial length of the vector. - * allocator = Allocator. - */ - this(in size_t len, shared Allocator allocator = defaultAllocator) - { - this(allocator); - length = len; - } - - /** - * Creates a new $(D_PSYMBOL Vector). - * - * Params: - * len = Initial length of the vector. - * init = Initial value to fill the vector with. - * allocator = Allocator. - */ - this(in size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted - { - this(allocator); - reserve(len); - uninitializedFill(vector[0 .. len], init); - length_ = len; - } - - /// Ditto. - this(shared Allocator allocator) - in - { - assert(allocator !is null); - } - body - { - allocator_ = allocator; - } - - /// - unittest - { - auto v = Vector!int([3, 8, 2]); - - assert(v.capacity == 3); - assert(v.length == 3); - assert(v[0] == 3 && v[1] == 8 && v[2] == 2); - } - - /// - unittest - { - auto v = Vector!int(3, 5); - - assert(v.capacity == 3); - assert(v.length == 3); - assert(v[0] == 5 && v[1] == 5 && v[2] == 5); - } - - @safe unittest - { - auto v1 = Vector!int(defaultAllocator); - } - - /** - * Destroys this $(D_PSYMBOL Vector). - */ - ~this() @trusted - { - clear(); - allocator.deallocate(vector[0 .. capacity_]); - } - - /** - * Copies the vector. - */ - this(this) - { - auto buf = opIndex(); - length_ = capacity_ = 0; - vector = null; - insertBack(buf); - } - - /** - * Removes all elements. - */ - void clear() - { - length = 0; - } - - /// - unittest - { - auto v = Vector!int([18, 20, 15]); - v.clear(); - assert(v.length == 0); - assert(v.capacity == 3); - } - - /** - * Returns: How many elements the vector can contain without reallocating. - */ - @property size_t capacity() const - { - return capacity_; - } - - /// - @safe @nogc unittest - { - auto v = Vector!int(4); - assert(v.capacity == 4); - } - - /** - * Returns: Vector length. - */ - @property size_t length() const - { - return length_; - } - - /// Ditto. - size_t opDollar() const - { - return length; - } - - /** - * Expands/shrinks the vector. - * - * Params: - * len = New length. - */ - @property void length(in size_t len) @trusted - { - if (len == length) - { - return; - } - else if (len > length) - { - reserve(len); - initializeAll(vector[length_ .. len]); - } - else - { - static if (hasElaborateDestructor!T) - { - const T* end = vector + length_ - 1; - for (T* e = vector + len; e != end; ++e) - { - destroy(*e); - } - } - } - length_ = len; - } - - /// - unittest - { - Vector!int v; - - v.length = 5; - assert(v.length == 5); - assert(v.capacity == 5); - - v.length = 7; - assert(v.length == 7); - assert(v.capacity == 7); - - assert(v[$ - 1] == 0); - v[$ - 1] = 3; - assert(v[$ - 1] == 3); - - v.length = 0; - assert(v.length == 0); - assert(v.capacity == 7); - } - - /** - * Reserves space for $(D_PARAM size) elements. - * - * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the - * function call does not cause a reallocation and the vector capacity is not - * affected. - * - * Params: - * size = Desired size. - */ - void reserve(in size_t size) @trusted - { - if (capacity_ >= size) - { - return; - } - bool overflow; - immutable byteSize = mulu(size, T.sizeof, overflow); - assert(!overflow); - - void[] buf = vector[0 .. capacity_]; - if (!allocator.reallocateInPlace(buf, byteSize)) - { - buf = allocator.allocate(byteSize); - if (buf is null) - { - onOutOfMemoryErrorNoGC(); - } - scope (failure) - { - allocator.deallocate(buf); - } - const T* end = vector + length_; - for (T* src = vector, dest = cast(T*) buf; src != end; ++src, ++dest) - { - moveEmplace(*src, *dest); - static if (hasElaborateDestructor!T) - { - destroy(*src); - } - } - allocator.deallocate(vector[0 .. capacity_]); - vector = cast(T*) buf; - } - capacity_ = size; - } - - /// - @nogc @safe unittest - { - Vector!int v; - assert(v.capacity == 0); - assert(v.length == 0); - - v.reserve(3); - assert(v.capacity == 3); - assert(v.length == 0); - } - - /** - * Requests the vector to reduce its capacity to fit the $(D_PARAM size). - * - * The request is non-binding. The vector won't become smaller than the - * $(D_PARAM length). - * - * Params: - * size = Desired size. - */ - void shrink(in size_t size) @trusted - { - if (capacity_ <= size) - { - return; - } - immutable n = max(length, size); - void[] buf = vector[0 .. capacity_]; - if (allocator.reallocateInPlace(buf, n * T.sizeof)) - { - capacity_ = n; - } - } - - /// - @nogc @safe unittest - { - Vector!int v; - assert(v.capacity == 0); - assert(v.length == 0); - - v.reserve(5); - v.insertBack(1); - v.insertBack(3); - assert(v.capacity == 5); - assert(v.length == 2); - - v.shrink(4); - assert(v.capacity == 4); - assert(v.length == 2); - - v.shrink(1); - assert(v.capacity == 2); - assert(v.length == 2); - } - - /** - * Returns: $(D_KEYWORD true) if the vector is empty. - */ - @property bool empty() const - { - return length == 0; - } - - /** - * Removes the value at the back of the vector. - * - * Returns: The number of elements removed - * - * Precondition: $(D_INLINECODE !empty) - */ - void removeBack() - in - { - assert(!empty); - } - body - { - length = length - 1; - } - - /** - * Removes $(D_PARAM howMany) elements from the vector. - * - * This method doesn't fail if it could not remove $(D_PARAM howMany) - * elements. Instead, if $(D_PARAM howMany) is greater than the vector - * length, all elements are removed. - * - * Params: - * howMany = How many elements should be removed. - * - * Returns: The number of elements removed - */ - size_t removeBack(in size_t howMany) - out (removed) - { - assert(removed <= howMany); - } - body - { - immutable toRemove = min(howMany, length); - - length = length - toRemove; - - return toRemove; - } - - /// - unittest - { - auto v = Vector!int([5, 18, 17]); - - assert(v.removeBack(0) == 0); - assert(v.removeBack(2) == 2); - assert(v.removeBack(3) == 1); - assert(v.removeBack(3) == 0); - } - - /** - * Remove all elements beloning to $(D_PARAM r). - * - * Params: - * r = Range originally obtained from this vector. - * - * Returns: Elements in $(D_PARAM r) after removing. - * - * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). - */ - Range!T remove(Range!T r) @trusted - in - { - assert(r.begin >= vector); - assert(r.end <= vector + length_); - } - body - { - auto end = vector + length_; - moveAll(Range!T(r.end, end), Range!T(r.begin, end)); - length = length - r.length; - return r; - } - - /// - @safe @nogc unittest - { - auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]); - - assert(v.remove(v[1 .. 3]).length == 2); - assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1); - assert(v.length == 5); - - assert(v.remove(v[4 .. 4]).length == 0); - assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1); - assert(v.length == 5); - - assert(v.remove(v[4 .. 5]).length == 1); - assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6); - assert(v.length == 4); - - assert(v.remove(v[]).length == 4); - assert(v.empty); - - assert(v.remove(v[]).length == 0); - assert(v.empty); - } - - private void moveBack(R)(ref R el) @trusted - if (isImplicitlyConvertible!(R, T)) - { - reserve(length + 1); - moveEmplace(el, *(vector + length_)); - ++length_; - } - - /** - * Inserts the $(D_PARAM el) into the vector. - * - * Params: - * R = Type of the inserted value(s) (single value, range or static array). - * el = Value(s) should be inserted. - * - * Returns: The number of elements inserted. - */ - size_t insertBack(R)(auto ref R el) @trusted - if (isImplicitlyConvertible!(R, T)) - { - static if (__traits(isRef, el)) - { - reserve(length + 1); - emplace(vector + length_, el); - ++length_; - } - else - { - moveBack(el); - } - return 1; - } - - /// Ditto. - size_t insertBack(R)(R el) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - { - static if (hasLength!R) - { - reserve(length + el.length); - } - size_t retLength; - foreach (e; el) - { - retLength += insertBack(e); - } - return retLength; - } - - /// Ditto. - size_t insertBack(size_t R)(T[R] el) - { - return insertBack!(T[])(el[]); - } - - /// Ditto. - alias insert = insertBack; - - /// - unittest - { - struct TestRange - { - int counter = 6; - - int front() - { - return counter; - } - - void popFront() - { - counter -= 2; - } - - bool empty() - { - return counter == 0; - } - } - - Vector!int v1; - - assert(v1.insertBack(5) == 1); - assert(v1.length == 1); - assert(v1.capacity == 1); - assert(v1.back == 5); - - assert(v1.insertBack(TestRange()) == 3); - assert(v1.length == 4); - assert(v1.capacity == 4); - assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2); - - assert(v1.insertBack([34, 234]) == 2); - assert(v1.length == 6); - assert(v1.capacity == 6); - assert(v1[4] == 34 && v1[5] == 234); - } - - /** - * Inserts $(D_PARAM el) before or after $(D_PARAM r). - * - * Params: - * R = Type of the inserted value(s) (single value, range or static array). - * r = Range originally obtained from this vector. - * el = Value(s) should be inserted. - * - * Returns: The number of elements inserted. - */ - size_t insertAfter(R)(Range!T r, R el) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - in - { - assert(r.begin >= vector); - assert(r.end <= vector + length_); - } - body - { - immutable oldLen = length; - immutable offset = r.end - vector; - immutable inserted = insertBack(el); - bringToFront(vector[offset .. oldLen], vector[oldLen .. length]); - return inserted; - } - - /// Ditto. - size_t insertAfter(size_t R)(Range!T r, T[R] el) - { - return insertAfter!(T[])(r, el[]); - } - - /// Ditto. - size_t insertAfter(R)(Range!T r, auto ref R el) - if (isImplicitlyConvertible!(R, T)) - in - { - assert(r.begin >= vector); - assert(r.end <= vector + length_); - } - body - { - immutable oldLen = length; - immutable offset = r.end - vector; - - static if (__traits(isRef, el)) - { - insertBack(el); - } - else - { - moveBack(el); - } - bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); - - return 1; - } - - /// Ditto. - size_t insertBefore(R)(Range!T r, R el) - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - in - { - assert(r.begin >= vector); - assert(r.end <= vector + length_); - } - body - { - return insertAfter(Range!T(vector, r.begin), el); - } - - /// Ditto. - size_t insertBefore(size_t R)(Range!T r, T[R] el) - { - return insertBefore!(T[])(r, el[]); - } - - /// Ditto. - size_t insertBefore(R)(Range!T r, auto ref R el) - if (isImplicitlyConvertible!(R, T)) - in - { - assert(r.begin >= vector); - assert(r.end <= vector + length_); - } - body - { - immutable oldLen = length; - immutable offset = r.begin - vector; - - static if (__traits(isRef, el)) - { - insertBack(el); - } - else - { - moveBack(el); - } - bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); - - return 1; - } - - /// - unittest - { - Vector!int v1; - v1.insertAfter(v1[], [2, 8]); - assert(v1[0] == 2); - assert(v1[1] == 8); - assert(v1.length == 2); - - v1.insertAfter(v1[], [1, 2]); - assert(v1[0] == 2); - assert(v1[1] == 8); - assert(v1[2] == 1); - assert(v1[3] == 2); - assert(v1.length == 4); - - v1.insertAfter(v1[0 .. 0], [1, 2]); - assert(v1[0] == 1); - assert(v1[1] == 2); - assert(v1[2] == 2); - assert(v1[3] == 8); - assert(v1[4] == 1); - assert(v1[5] == 2); - assert(v1.length == 6); - - v1.insertAfter(v1[0 .. 4], 9); - assert(v1[0] == 1); - assert(v1[1] == 2); - assert(v1[2] == 2); - assert(v1[3] == 8); - assert(v1[4] == 9); - assert(v1[5] == 1); - assert(v1[6] == 2); - assert(v1.length == 7); - } - - /// - unittest - { - Vector!int v1; - v1.insertBefore(v1[], [2, 8]); - assert(v1[0] == 2); - assert(v1[1] == 8); - assert(v1.length == 2); - - v1.insertBefore(v1[], [1, 2]); - assert(v1[0] == 1); - assert(v1[1] == 2); - assert(v1[2] == 2); - assert(v1[3] == 8); - assert(v1.length == 4); - - v1.insertBefore(v1[0 .. 1], [1, 2]); - assert(v1[0] == 1); - assert(v1[1] == 2); - assert(v1[2] == 1); - assert(v1[3] == 2); - assert(v1[4] == 2); - assert(v1[5] == 8); - assert(v1.length == 6); - - v1.insertBefore(v1[2 .. $], 9); - assert(v1[0] == 1); - assert(v1[1] == 2); - assert(v1[2] == 9); - assert(v1[3] == 1); - assert(v1[4] == 2); - assert(v1[5] == 2); - assert(v1[6] == 8); - assert(v1.length == 7); - } - - /** - * Assigns a value to the element with the index $(D_PARAM pos). - * - * Params: - * value = Value. - * pos = Position. - * - * Returns: Assigned value. - * - * Precondition: $(D_INLINECODE length > pos) - */ - ref T opIndexAssign(ref T value, in size_t pos) - { - return opIndex(pos) = value; - } - - @safe unittest - { - Vector!int a = Vector!int(1); - a[0] = 5; - assert(a[0] == 5); - } - - /// Ditto. - T opIndexAssign(T value, in size_t pos) - { - return opIndexAssign(value, pos); - } - - /// Ditto. - Range!T opIndexAssign(T value) - { - return opSliceAssign(value, 0, length); - } - - /// Ditto. - Range!T opIndexAssign(ref T value) - { - return opSliceAssign(value, 0, length); - } - - /** - * Assigns a range or a static array. - * - * Params: - * R = Range type or static array length. - * value = Value. - * - * Returns: Assigned value. - * - * Precondition: $(D_INLINECODE length == value.length) - */ - Range!T opIndexAssign(R)(R value) - if (!isInfinite!R && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - { - return opSliceAssign!R(value, 0, length); - } - - /// Ditto. - Range!T opIndexAssign(size_t R)(T[R] value) - { - return opSliceAssign!R(value, 0, length); - } - - /// - @nogc unittest - { - auto v1 = Vector!int([12, 1, 7]); - - v1[] = 3; - assert(v1[0] == 3); - assert(v1[1] == 3); - assert(v1[2] == 3); - - v1[] = [7, 1, 12]; - assert(v1[0] == 7); - assert(v1[1] == 1); - assert(v1[2] == 12); - } - - /** - * Params: - * pos = Index. - * - * Returns: The value at a specified index. - * - * Precondition: $(D_INLINECODE length > pos) - */ - ref inout(T) opIndex(in size_t pos) inout @trusted - in - { - assert(length > pos); - } - body - { - return *(vector + pos); - } - - /** - * Returns: Random access range that iterates over elements of the vector, in - * forward order. - */ - Range!T opIndex() @trusted - { - return typeof(return)(vector, vector + length_); - } - - /// Ditto. - Range!(const T) opIndex() const @trusted - { - return typeof(return)(vector, vector + length_); - } - - /// - unittest - { - const v1 = Vector!int([6, 123, 34, 5]); - - assert(v1[0] == 6); - assert(v1[1] == 123); - assert(v1[2] == 34); - assert(v1[3] == 5); - static assert(is(typeof(v1[0]) == const(int))); - static assert(is(typeof(v1[]))); - } - - /** - * Comparison for equality. - * - * Params: - * that = The vector to compare with. - * - * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false) - * otherwise. - */ - bool opEquals()(auto ref typeof(this) that) @trusted - { - return equal(vector[0 .. length_], that.vector[0 .. that.length_]); - } - - /// Ditto. - bool opEquals()(in auto ref typeof(this) that) const @trusted - { - return equal(vector[0 .. length_], that.vector[0 .. that.length_]); - } - - /// Ditto. - bool opEquals(Range!T that) - { - return equal(opIndex(), that); - } - - /** - * Comparison for equality. - * - * Params: - * R = Right hand side type. - * that = Right hand side vector range. - * - * Returns: $(D_KEYWORD true) if the vector and the range are equal, - * $(D_KEYWORD false) otherwise. - */ - bool opEquals(R)(Range!R that) const - if (is(Unqual!R == T)) - { - return equal(opIndex(), that); - } - - /// - unittest - { - Vector!int v1, v2; - assert(v1 == v2); - - v1.length = 1; - v2.length = 2; - assert(v1 != v2); - - v1.length = 2; - v1[0] = v2[0] = 2; - v1[1] = 3; - v2[1] = 4; - assert(v1 != v2); - - v2[1] = 3; - assert(v1 == v2); - } - - /** - * $(D_KEYWORD foreach) iteration. - * - * Params: - * dg = $(D_KEYWORD foreach) body. - * - * Returns: The value returned from $(D_PARAM dg). - */ - int opApply(scope int delegate(ref T) @nogc dg) - { - T* end = vector + length_ - 1; - for (T* begin = vector; begin != end; ++begin) - { - int result = dg(*begin); - if (result != 0) - { - return result; - } - } - return 0; - } - - /// Ditto. - int opApply(scope int delegate(ref size_t i, ref T) @nogc dg) - { - for (size_t i = 0; i < length; ++i) - { - assert(i < length); - int result = dg(i, *(vector + i)); - - if (result != 0) - { - return result; - } - } - return 0; - } - - /// Ditto. - int opApplyReverse(scope int delegate(ref T) dg) - { - for (T* end = vector + length - 1; vector != end; --end) - { - int result = dg(*end); - if (result != 0) - { - return result; - } - } - return 0; - } - - /// Ditto. - int opApplyReverse(scope int delegate(ref size_t i, ref T) dg) - { - if (length > 0) - { - size_t i = length; - do - { - --i; - assert(i < length); - int result = dg(i, *(vector + i)); - - if (result != 0) - { - return result; - } - } - while (i > 0); - } - return 0; - } - - /// - unittest - { - auto v = Vector!int([5, 15, 8]); - - size_t i; - foreach (j, ref e; v) - { - i = j; - } - assert(i == 2); - - foreach (j, e; v) - { - assert(j != 0 || e == 5); - assert(j != 1 || e == 15); - assert(j != 2 || e == 8); - } - } - - /// - unittest - { - auto v = Vector!int([5, 15, 8]); - size_t i; - - foreach_reverse (j, ref e; v) - { - i = j; - } - assert(i == 0); - - foreach_reverse (j, e; v) - { - assert(j != 2 || e == 8); - assert(j != 1 || e == 15); - assert(j != 0 || e == 5); - } - } - - /** - * Returns: The first element. - * - * Precondition: $(D_INLINECODE !empty) - */ - @property ref inout(T) front() inout - in - { - assert(!empty); - } - body - { - return *vector; - } - - /// - @safe unittest - { - auto v = Vector!int([5]); - - assert(v.front == 5); - - v.length = 2; - v[1] = 15; - assert(v.front == 5); - } - - /** - * Returns: The last element. - * - * Precondition: $(D_INLINECODE !empty) - */ - @property ref inout(T) back() inout @trusted - in - { - assert(!empty); - } - body - { - return *(vector + length_ - 1); - } - - /// - unittest - { - auto v = Vector!int([5]); - - assert(v.back == 5); - - v.length = 2; - v[1] = 15; - assert(v.back == 15); - } - - /** - * Params: - * i = Slice start. - * j = Slice end. - * - * Returns: A range that iterates over elements of the container from - * index $(D_PARAM i) up to (excluding) index $(D_PARAM j). - * - * Precondition: $(D_INLINECODE i <= j && j <= length) - */ - Range!T opSlice(in size_t i, in size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(vector + i, vector + j); - } - - /// Ditto. - Range!(const T) opSlice(in size_t i, in size_t j) const @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - return typeof(return)(vector + i, vector + j); - } - - /// - unittest - { - Vector!int v; - auto r = v[]; - assert(r.length == 0); - assert(r.empty); - } - - /// - unittest - { - auto v = Vector!int([1, 2, 3]); - auto r = v[]; - - assert(r.front == 1); - assert(r.back == 3); - - r.popFront(); - assert(r.front == 2); - - r.popBack(); - assert(r.back == 2); - - assert(r.length == 1); - } - - /// - unittest - { - auto v = Vector!int([1, 2, 3, 4]); - auto r = v[1 .. 4]; - assert(r.length == 3); - assert(r[0] == 2); - assert(r[1] == 3); - assert(r[2] == 4); - - r = v[0 .. 0]; - assert(r.length == 0); - - r = v[4 .. 4]; - assert(r.length == 0); - } - - /** - * Slicing assignment. - * - * Params: - * R = Type of the assigned slice or length of the static array should be - * assigned. - * value = New value (single value, input range or static array). - * i = Slice start. - * j = Slice end. - * - * Returns: Slice with the assigned part of the vector. - * - * Precondition: $(D_INLINECODE i <= j && j <= length - * && value.length == j - i) - */ - Range!T opSliceAssign(R)(R value, in size_t i, in size_t j) @trusted - if (!isInfinite!R - && isInputRange!R - && isImplicitlyConvertible!(ElementType!R, T)) - in - { - assert(i <= j); - assert(j <= length); - assert(j - i == walkLength(value)); - } - body - { - copy(value, vector[i .. j]); - return opSlice(i, j); - } - - /// Ditto. - Range!T opSliceAssign(size_t R)(T[R] value, in size_t i, in size_t j) - { - return opSliceAssign!(T[])(value[], i, j); - } - - /// Ditto. - Range!T opSliceAssign(ref T value, in size_t i, in size_t j) @trusted - in - { - assert(i <= j); - assert(j <= length); - } - body - { - fill(vector[i .. j], value); - return opSlice(i, j); - } - - /// Ditto. - Range!T opSliceAssign(T value, in size_t i, in size_t j) - { - return opSliceAssign(value, i, j); - } - - /// - @nogc @safe unittest - { - auto v1 = Vector!int([3, 3, 3]); - auto v2 = Vector!int([1, 2]); - - v1[0 .. 2] = 286; - assert(v1[0] == 286); - assert(v1[1] == 286); - assert(v1[2] == 3); - - v2[0 .. $] = v1[1 .. 3]; - assert(v2[0] == 286); - assert(v2[1] == 3); - - v1[0 .. 2] = [5, 8]; - assert(v1[0] == 5); - assert(v1[1] == 8); - assert(v1[2] == 3); - } - - /** - * Returns an array used internally by the vector to store its owned elements. - * The length of the returned array may differ from the size of the allocated - * memory for the vector: the array contains only initialized elements, but - * not the reserved memory. - * - * Returns: The array with elements of this vector. - */ - inout(T[]) get() inout @trusted - { - return vector[0 .. length]; - } - - /// - unittest - { - auto v = Vector!int([1, 2, 4]); - auto data = v.get(); - - assert(data[0] == 1); - assert(data[1] == 2); - assert(data[2] == 4); - assert(data.length == 3); - - data = v[1 .. 2].get(); - assert(data[0] == 2); - assert(data.length == 1); - } - - mixin DefaultAllocator; + private size_t length_; + private T* vector; + private size_t capacity_; + + invariant + { + assert(length_ <= capacity_); + assert(capacity_ == 0 || vector !is null); + } + + /** + * Creates a new $(D_PSYMBOL Vector) with the elements from another input + * range or a static array $(D_PARAM init). + * + * Params: + * R = Type of the initial range or size of the static array. + * init = Values to initialize the array with. + * to generate a list. + * allocator = Allocator. + */ + this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator) + { + this(allocator); + insertBack!(T[])(init[]); + } + + /// Ditto. + this(R)(R init, shared Allocator allocator = defaultAllocator) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + this(allocator); + insertBack(init); + } + + /** + * Initializes this vector from another one. + * + * If $(D_PARAM init) is passed by value, it won't be copied, but moved. + * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator), + * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s + * storage, otherwise, the storage will be allocated with + * $(D_PARAM allocator) and all elements will be moved; + * $(D_PARAM init) will be destroyed at the end. + * + * If $(D_PARAM init) is passed by reference, it will be copied. + * + * Params: + * R = Vector type. + * init = Source vector. + * allocator = Allocator. + */ + this(R)(ref R init, shared Allocator allocator = defaultAllocator) + if (is(Unqual!R == Vector)) + { + this(allocator); + insertBack(init[]); + } + + /// Ditto. + this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted + if (is(R == Vector)) + { + this(allocator); + if (allocator is init.allocator) + { + // Just steal all references and the allocator. + vector = init.vector; + length_ = init.length_; + capacity_ = init.capacity_; + + // Reset the source vector, so it can't destroy the moved storage. + init.length_ = init.capacity_ = 0; + init.vector = null; + } + else + { + // Move each element. + reserve(init.length); + moveEmplaceAll(init.vector[0 .. init.length_], vector[0 .. init.length_]); + length_ = init.length; + // Destructor of init should destroy it here. + } + } + + /// + unittest + { + auto v1 = Vector!int([1, 2, 3]); + auto v2 = Vector!int(v1); + assert(v1.vector !is v2.vector); + assert(v1 == v2); + + auto v3 = Vector!int(Vector!int([1, 2, 3])); + assert(v1 == v3); + assert(v3.length == 3); + assert(v3.capacity == 3); + } + + unittest // const constructor tests + { + auto v1 = const Vector!int([1, 2, 3]); + auto v2 = Vector!int(v1); + assert(v1.vector !is v2.vector); + assert(v1 == v2); + + auto v3 = const Vector!int(Vector!int([1, 2, 3])); + assert(v1 == v3); + assert(v3.length == 3); + assert(v3.capacity == 3); + } + + /** + * Creates a new $(D_PSYMBOL Vector). + * + * Params: + * len = Initial length of the vector. + * allocator = Allocator. + */ + this(in size_t len, shared Allocator allocator = defaultAllocator) + { + this(allocator); + length = len; + } + + /** + * Creates a new $(D_PSYMBOL Vector). + * + * Params: + * len = Initial length of the vector. + * init = Initial value to fill the vector with. + * allocator = Allocator. + */ + this(in size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted + { + this(allocator); + reserve(len); + uninitializedFill(vector[0 .. len], init); + length_ = len; + } + + /// Ditto. + this(shared Allocator allocator) + in + { + assert(allocator !is null); + } + body + { + allocator_ = allocator; + } + + /// + unittest + { + auto v = Vector!int([3, 8, 2]); + + assert(v.capacity == 3); + assert(v.length == 3); + assert(v[0] == 3 && v[1] == 8 && v[2] == 2); + } + + /// + unittest + { + auto v = Vector!int(3, 5); + + assert(v.capacity == 3); + assert(v.length == 3); + assert(v[0] == 5 && v[1] == 5 && v[2] == 5); + } + + @safe unittest + { + auto v1 = Vector!int(defaultAllocator); + } + + /** + * Destroys this $(D_PSYMBOL Vector). + */ + ~this() @trusted + { + clear(); + allocator.deallocate(vector[0 .. capacity_]); + } + + /** + * Copies the vector. + */ + this(this) + { + auto buf = opIndex(); + length_ = capacity_ = 0; + vector = null; + insertBack(buf); + } + + /** + * Removes all elements. + */ + void clear() + { + length = 0; + } + + /// + unittest + { + auto v = Vector!int([18, 20, 15]); + v.clear(); + assert(v.length == 0); + assert(v.capacity == 3); + } + + /** + * Returns: How many elements the vector can contain without reallocating. + */ + @property size_t capacity() const + { + return capacity_; + } + + /// + @safe @nogc unittest + { + auto v = Vector!int(4); + assert(v.capacity == 4); + } + + /** + * Returns: Vector length. + */ + @property size_t length() const + { + return length_; + } + + /// Ditto. + size_t opDollar() const + { + return length; + } + + /** + * Expands/shrinks the vector. + * + * Params: + * len = New length. + */ + @property void length(in size_t len) @trusted + { + if (len == length) + { + return; + } + else if (len > length) + { + reserve(len); + initializeAll(vector[length_ .. len]); + } + else + { + static if (hasElaborateDestructor!T) + { + const T* end = vector + length_ - 1; + for (T* e = vector + len; e != end; ++e) + { + destroy(*e); + } + } + } + length_ = len; + } + + /// + unittest + { + Vector!int v; + + v.length = 5; + assert(v.length == 5); + assert(v.capacity == 5); + + v.length = 7; + assert(v.length == 7); + assert(v.capacity == 7); + + assert(v[$ - 1] == 0); + v[$ - 1] = 3; + assert(v[$ - 1] == 3); + + v.length = 0; + assert(v.length == 0); + assert(v.capacity == 7); + } + + /** + * Reserves space for $(D_PARAM size) elements. + * + * If $(D_PARAM size) is less than or equal to the $(D_PSYMBOL capacity), the + * function call does not cause a reallocation and the vector capacity is not + * affected. + * + * Params: + * size = Desired size. + */ + void reserve(in size_t size) @trusted + { + if (capacity_ >= size) + { + return; + } + bool overflow; + immutable byteSize = mulu(size, T.sizeof, overflow); + assert(!overflow); + + void[] buf = vector[0 .. capacity_]; + if (!allocator.reallocateInPlace(buf, byteSize)) + { + buf = allocator.allocate(byteSize); + if (buf is null) + { + onOutOfMemoryErrorNoGC(); + } + scope (failure) + { + allocator.deallocate(buf); + } + const T* end = vector + length_; + for (T* src = vector, dest = cast(T*) buf; src != end; ++src, ++dest) + { + moveEmplace(*src, *dest); + static if (hasElaborateDestructor!T) + { + destroy(*src); + } + } + allocator.deallocate(vector[0 .. capacity_]); + vector = cast(T*) buf; + } + capacity_ = size; + } + + /// + @nogc @safe unittest + { + Vector!int v; + assert(v.capacity == 0); + assert(v.length == 0); + + v.reserve(3); + assert(v.capacity == 3); + assert(v.length == 0); + } + + /** + * Requests the vector to reduce its capacity to fit the $(D_PARAM size). + * + * The request is non-binding. The vector won't become smaller than the + * $(D_PARAM length). + * + * Params: + * size = Desired size. + */ + void shrink(in size_t size) @trusted + { + if (capacity_ <= size) + { + return; + } + immutable n = max(length, size); + void[] buf = vector[0 .. capacity_]; + if (allocator.reallocateInPlace(buf, n * T.sizeof)) + { + capacity_ = n; + } + } + + /// + @nogc @safe unittest + { + Vector!int v; + assert(v.capacity == 0); + assert(v.length == 0); + + v.reserve(5); + v.insertBack(1); + v.insertBack(3); + assert(v.capacity == 5); + assert(v.length == 2); + + v.shrink(4); + assert(v.capacity == 4); + assert(v.length == 2); + + v.shrink(1); + assert(v.capacity == 2); + assert(v.length == 2); + } + + /** + * Returns: $(D_KEYWORD true) if the vector is empty. + */ + @property bool empty() const + { + return length == 0; + } + + /** + * Removes the value at the back of the vector. + * + * Returns: The number of elements removed + * + * Precondition: $(D_INLINECODE !empty) + */ + void removeBack() + in + { + assert(!empty); + } + body + { + length = length - 1; + } + + /** + * Removes $(D_PARAM howMany) elements from the vector. + * + * This method doesn't fail if it could not remove $(D_PARAM howMany) + * elements. Instead, if $(D_PARAM howMany) is greater than the vector + * length, all elements are removed. + * + * Params: + * howMany = How many elements should be removed. + * + * Returns: The number of elements removed + */ + size_t removeBack(in size_t howMany) + out (removed) + { + assert(removed <= howMany); + } + body + { + immutable toRemove = min(howMany, length); + + length = length - toRemove; + + return toRemove; + } + + /// + unittest + { + auto v = Vector!int([5, 18, 17]); + + assert(v.removeBack(0) == 0); + assert(v.removeBack(2) == 2); + assert(v.removeBack(3) == 1); + assert(v.removeBack(3) == 0); + } + + /** + * Remove all elements beloning to $(D_PARAM r). + * + * Params: + * r = Range originally obtained from this vector. + * + * Returns: Elements in $(D_PARAM r) after removing. + * + * Precondition: $(D_PARAM r) refers to a region of $(D_KEYWORD this). + */ + Range!T remove(Range!T r) @trusted + in + { + assert(r.begin >= vector); + assert(r.end <= vector + length_); + } + body + { + auto end = vector + length_; + moveAll(Range!T(r.end, end), Range!T(r.begin, end)); + length = length - r.length; + return r; + } + + /// + @safe @nogc unittest + { + auto v = Vector!int([5, 18, 17, 2, 4, 6, 1]); + + assert(v.remove(v[1 .. 3]).length == 2); + assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1); + assert(v.length == 5); + + assert(v.remove(v[4 .. 4]).length == 0); + assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6 && v[4] == 1); + assert(v.length == 5); + + assert(v.remove(v[4 .. 5]).length == 1); + assert(v[0] == 5 && v[1] == 2 && v[2] == 4 && v[3] == 6); + assert(v.length == 4); + + assert(v.remove(v[]).length == 4); + assert(v.empty); + + assert(v.remove(v[]).length == 0); + assert(v.empty); + } + + private void moveBack(R)(ref R el) @trusted + if (isImplicitlyConvertible!(R, T)) + { + reserve(length + 1); + moveEmplace(el, *(vector + length_)); + ++length_; + } + + /** + * Inserts the $(D_PARAM el) into the vector. + * + * Params: + * R = Type of the inserted value(s) (single value, range or static array). + * el = Value(s) should be inserted. + * + * Returns: The number of elements inserted. + */ + size_t insertBack(R)(auto ref R el) @trusted + if (isImplicitlyConvertible!(R, T)) + { + static if (__traits(isRef, el)) + { + reserve(length + 1); + emplace(vector + length_, el); + ++length_; + } + else + { + moveBack(el); + } + return 1; + } + + /// Ditto. + size_t insertBack(R)(R el) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + static if (hasLength!R) + { + reserve(length + el.length); + } + size_t retLength; + foreach (e; el) + { + retLength += insertBack(e); + } + return retLength; + } + + /// Ditto. + size_t insertBack(size_t R)(T[R] el) + { + return insertBack!(T[])(el[]); + } + + /// Ditto. + alias insert = insertBack; + + /// + unittest + { + struct TestRange + { + int counter = 6; + + int front() + { + return counter; + } + + void popFront() + { + counter -= 2; + } + + bool empty() + { + return counter == 0; + } + } + + Vector!int v1; + + assert(v1.insertBack(5) == 1); + assert(v1.length == 1); + assert(v1.capacity == 1); + assert(v1.back == 5); + + assert(v1.insertBack(TestRange()) == 3); + assert(v1.length == 4); + assert(v1.capacity == 4); + assert(v1[0] == 5 && v1[1] == 6 && v1[2] == 4 && v1[3] == 2); + + assert(v1.insertBack([34, 234]) == 2); + assert(v1.length == 6); + assert(v1.capacity == 6); + assert(v1[4] == 34 && v1[5] == 234); + } + + /** + * Inserts $(D_PARAM el) before or after $(D_PARAM r). + * + * Params: + * R = Type of the inserted value(s) (single value, range or static array). + * r = Range originally obtained from this vector. + * el = Value(s) should be inserted. + * + * Returns: The number of elements inserted. + */ + size_t insertAfter(R)(Range!T r, R el) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + in + { + assert(r.begin >= vector); + assert(r.end <= vector + length_); + } + body + { + immutable oldLen = length; + immutable offset = r.end - vector; + immutable inserted = insertBack(el); + bringToFront(vector[offset .. oldLen], vector[oldLen .. length]); + return inserted; + } + + /// Ditto. + size_t insertAfter(size_t R)(Range!T r, T[R] el) + { + return insertAfter!(T[])(r, el[]); + } + + /// Ditto. + size_t insertAfter(R)(Range!T r, auto ref R el) + if (isImplicitlyConvertible!(R, T)) + in + { + assert(r.begin >= vector); + assert(r.end <= vector + length_); + } + body + { + immutable oldLen = length; + immutable offset = r.end - vector; + + static if (__traits(isRef, el)) + { + insertBack(el); + } + else + { + moveBack(el); + } + bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); + + return 1; + } + + /// Ditto. + size_t insertBefore(R)(Range!T r, R el) + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + in + { + assert(r.begin >= vector); + assert(r.end <= vector + length_); + } + body + { + return insertAfter(Range!T(vector, r.begin), el); + } + + /// Ditto. + size_t insertBefore(size_t R)(Range!T r, T[R] el) + { + return insertBefore!(T[])(r, el[]); + } + + /// Ditto. + size_t insertBefore(R)(Range!T r, auto ref R el) + if (isImplicitlyConvertible!(R, T)) + in + { + assert(r.begin >= vector); + assert(r.end <= vector + length_); + } + body + { + immutable oldLen = length; + immutable offset = r.begin - vector; + + static if (__traits(isRef, el)) + { + insertBack(el); + } + else + { + moveBack(el); + } + bringToFront(vector[offset .. oldLen], vector[oldLen .. length_]); + + return 1; + } + + /// + unittest + { + Vector!int v1; + v1.insertAfter(v1[], [2, 8]); + assert(v1[0] == 2); + assert(v1[1] == 8); + assert(v1.length == 2); + + v1.insertAfter(v1[], [1, 2]); + assert(v1[0] == 2); + assert(v1[1] == 8); + assert(v1[2] == 1); + assert(v1[3] == 2); + assert(v1.length == 4); + + v1.insertAfter(v1[0 .. 0], [1, 2]); + assert(v1[0] == 1); + assert(v1[1] == 2); + assert(v1[2] == 2); + assert(v1[3] == 8); + assert(v1[4] == 1); + assert(v1[5] == 2); + assert(v1.length == 6); + + v1.insertAfter(v1[0 .. 4], 9); + assert(v1[0] == 1); + assert(v1[1] == 2); + assert(v1[2] == 2); + assert(v1[3] == 8); + assert(v1[4] == 9); + assert(v1[5] == 1); + assert(v1[6] == 2); + assert(v1.length == 7); + } + + /// + unittest + { + Vector!int v1; + v1.insertBefore(v1[], [2, 8]); + assert(v1[0] == 2); + assert(v1[1] == 8); + assert(v1.length == 2); + + v1.insertBefore(v1[], [1, 2]); + assert(v1[0] == 1); + assert(v1[1] == 2); + assert(v1[2] == 2); + assert(v1[3] == 8); + assert(v1.length == 4); + + v1.insertBefore(v1[0 .. 1], [1, 2]); + assert(v1[0] == 1); + assert(v1[1] == 2); + assert(v1[2] == 1); + assert(v1[3] == 2); + assert(v1[4] == 2); + assert(v1[5] == 8); + assert(v1.length == 6); + + v1.insertBefore(v1[2 .. $], 9); + assert(v1[0] == 1); + assert(v1[1] == 2); + assert(v1[2] == 9); + assert(v1[3] == 1); + assert(v1[4] == 2); + assert(v1[5] == 2); + assert(v1[6] == 8); + assert(v1.length == 7); + } + + /** + * Assigns a value to the element with the index $(D_PARAM pos). + * + * Params: + * value = Value. + * pos = Position. + * + * Returns: Assigned value. + * + * Precondition: $(D_INLINECODE length > pos) + */ + ref T opIndexAssign(ref T value, in size_t pos) + { + return opIndex(pos) = value; + } + + @safe unittest + { + Vector!int a = Vector!int(1); + a[0] = 5; + assert(a[0] == 5); + } + + /// Ditto. + T opIndexAssign(T value, in size_t pos) + { + return opIndexAssign(value, pos); + } + + /// Ditto. + Range!T opIndexAssign(T value) + { + return opSliceAssign(value, 0, length); + } + + /// Ditto. + Range!T opIndexAssign(ref T value) + { + return opSliceAssign(value, 0, length); + } + + /** + * Assigns a range or a static array. + * + * Params: + * R = Range type or static array length. + * value = Value. + * + * Returns: Assigned value. + * + * Precondition: $(D_INLINECODE length == value.length) + */ + Range!T opIndexAssign(R)(R value) + if (!isInfinite!R && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + { + return opSliceAssign!R(value, 0, length); + } + + /// Ditto. + Range!T opIndexAssign(size_t R)(T[R] value) + { + return opSliceAssign!R(value, 0, length); + } + + /// + @nogc unittest + { + auto v1 = Vector!int([12, 1, 7]); + + v1[] = 3; + assert(v1[0] == 3); + assert(v1[1] == 3); + assert(v1[2] == 3); + + v1[] = [7, 1, 12]; + assert(v1[0] == 7); + assert(v1[1] == 1); + assert(v1[2] == 12); + } + + /** + * Params: + * pos = Index. + * + * Returns: The value at a specified index. + * + * Precondition: $(D_INLINECODE length > pos) + */ + ref inout(T) opIndex(in size_t pos) inout @trusted + in + { + assert(length > pos); + } + body + { + return *(vector + pos); + } + + /** + * Returns: Random access range that iterates over elements of the vector, in + * forward order. + */ + Range!T opIndex() @trusted + { + return typeof(return)(vector, vector + length_); + } + + /// Ditto. + Range!(const T) opIndex() const @trusted + { + return typeof(return)(vector, vector + length_); + } + + /// + unittest + { + const v1 = Vector!int([6, 123, 34, 5]); + + assert(v1[0] == 6); + assert(v1[1] == 123); + assert(v1[2] == 34); + assert(v1[3] == 5); + static assert(is(typeof(v1[0]) == const(int))); + static assert(is(typeof(v1[]))); + } + + /** + * Comparison for equality. + * + * Params: + * that = The vector to compare with. + * + * Returns: $(D_KEYWORD true) if the vectors are equal, $(D_KEYWORD false) + * otherwise. + */ + bool opEquals()(auto ref typeof(this) that) @trusted + { + return equal(vector[0 .. length_], that.vector[0 .. that.length_]); + } + + /// Ditto. + bool opEquals()(in auto ref typeof(this) that) const @trusted + { + return equal(vector[0 .. length_], that.vector[0 .. that.length_]); + } + + /// Ditto. + bool opEquals(Range!T that) + { + return equal(opIndex(), that); + } + + /** + * Comparison for equality. + * + * Params: + * R = Right hand side type. + * that = Right hand side vector range. + * + * Returns: $(D_KEYWORD true) if the vector and the range are equal, + * $(D_KEYWORD false) otherwise. + */ + bool opEquals(R)(Range!R that) const + if (is(Unqual!R == T)) + { + return equal(opIndex(), that); + } + + /// + unittest + { + Vector!int v1, v2; + assert(v1 == v2); + + v1.length = 1; + v2.length = 2; + assert(v1 != v2); + + v1.length = 2; + v1[0] = v2[0] = 2; + v1[1] = 3; + v2[1] = 4; + assert(v1 != v2); + + v2[1] = 3; + assert(v1 == v2); + } + + /** + * $(D_KEYWORD foreach) iteration. + * + * Params: + * dg = $(D_KEYWORD foreach) body. + * + * Returns: The value returned from $(D_PARAM dg). + */ + int opApply(scope int delegate(ref T) @nogc dg) + { + T* end = vector + length_ - 1; + for (T* begin = vector; begin != end; ++begin) + { + int result = dg(*begin); + if (result != 0) + { + return result; + } + } + return 0; + } + + /// Ditto. + int opApply(scope int delegate(ref size_t i, ref T) @nogc dg) + { + for (size_t i = 0; i < length; ++i) + { + assert(i < length); + int result = dg(i, *(vector + i)); + + if (result != 0) + { + return result; + } + } + return 0; + } + + /// Ditto. + int opApplyReverse(scope int delegate(ref T) dg) + { + for (T* end = vector + length - 1; vector != end; --end) + { + int result = dg(*end); + if (result != 0) + { + return result; + } + } + return 0; + } + + /// Ditto. + int opApplyReverse(scope int delegate(ref size_t i, ref T) dg) + { + if (length > 0) + { + size_t i = length; + do + { + --i; + assert(i < length); + int result = dg(i, *(vector + i)); + + if (result != 0) + { + return result; + } + } + while (i > 0); + } + return 0; + } + + /// + unittest + { + auto v = Vector!int([5, 15, 8]); + + size_t i; + foreach (j, ref e; v) + { + i = j; + } + assert(i == 2); + + foreach (j, e; v) + { + assert(j != 0 || e == 5); + assert(j != 1 || e == 15); + assert(j != 2 || e == 8); + } + } + + /// + unittest + { + auto v = Vector!int([5, 15, 8]); + size_t i; + + foreach_reverse (j, ref e; v) + { + i = j; + } + assert(i == 0); + + foreach_reverse (j, e; v) + { + assert(j != 2 || e == 8); + assert(j != 1 || e == 15); + assert(j != 0 || e == 5); + } + } + + /** + * Returns: The first element. + * + * Precondition: $(D_INLINECODE !empty) + */ + @property ref inout(T) front() inout + in + { + assert(!empty); + } + body + { + return *vector; + } + + /// + @safe unittest + { + auto v = Vector!int([5]); + + assert(v.front == 5); + + v.length = 2; + v[1] = 15; + assert(v.front == 5); + } + + /** + * Returns: The last element. + * + * Precondition: $(D_INLINECODE !empty) + */ + @property ref inout(T) back() inout @trusted + in + { + assert(!empty); + } + body + { + return *(vector + length_ - 1); + } + + /// + unittest + { + auto v = Vector!int([5]); + + assert(v.back == 5); + + v.length = 2; + v[1] = 15; + assert(v.back == 15); + } + + /** + * Params: + * i = Slice start. + * j = Slice end. + * + * Returns: A range that iterates over elements of the container from + * index $(D_PARAM i) up to (excluding) index $(D_PARAM j). + * + * Precondition: $(D_INLINECODE i <= j && j <= length) + */ + Range!T opSlice(in size_t i, in size_t j) @trusted + in + { + assert(i <= j); + assert(j <= length); + } + body + { + return typeof(return)(vector + i, vector + j); + } + + /// Ditto. + Range!(const T) opSlice(in size_t i, in size_t j) const @trusted + in + { + assert(i <= j); + assert(j <= length); + } + body + { + return typeof(return)(vector + i, vector + j); + } + + /// + unittest + { + Vector!int v; + auto r = v[]; + assert(r.length == 0); + assert(r.empty); + } + + /// + unittest + { + auto v = Vector!int([1, 2, 3]); + auto r = v[]; + + assert(r.front == 1); + assert(r.back == 3); + + r.popFront(); + assert(r.front == 2); + + r.popBack(); + assert(r.back == 2); + + assert(r.length == 1); + } + + /// + unittest + { + auto v = Vector!int([1, 2, 3, 4]); + auto r = v[1 .. 4]; + assert(r.length == 3); + assert(r[0] == 2); + assert(r[1] == 3); + assert(r[2] == 4); + + r = v[0 .. 0]; + assert(r.length == 0); + + r = v[4 .. 4]; + assert(r.length == 0); + } + + /** + * Slicing assignment. + * + * Params: + * R = Type of the assigned slice or length of the static array should be + * assigned. + * value = New value (single value, input range or static array). + * i = Slice start. + * j = Slice end. + * + * Returns: Slice with the assigned part of the vector. + * + * Precondition: $(D_INLINECODE i <= j && j <= length + * && value.length == j - i) + */ + Range!T opSliceAssign(R)(R value, in size_t i, in size_t j) @trusted + if (!isInfinite!R + && isInputRange!R + && isImplicitlyConvertible!(ElementType!R, T)) + in + { + assert(i <= j); + assert(j <= length); + assert(j - i == walkLength(value)); + } + body + { + copy(value, vector[i .. j]); + return opSlice(i, j); + } + + /// Ditto. + Range!T opSliceAssign(size_t R)(T[R] value, in size_t i, in size_t j) + { + return opSliceAssign!(T[])(value[], i, j); + } + + /// Ditto. + Range!T opSliceAssign(ref T value, in size_t i, in size_t j) @trusted + in + { + assert(i <= j); + assert(j <= length); + } + body + { + fill(vector[i .. j], value); + return opSlice(i, j); + } + + /// Ditto. + Range!T opSliceAssign(T value, in size_t i, in size_t j) + { + return opSliceAssign(value, i, j); + } + + /// + @nogc @safe unittest + { + auto v1 = Vector!int([3, 3, 3]); + auto v2 = Vector!int([1, 2]); + + v1[0 .. 2] = 286; + assert(v1[0] == 286); + assert(v1[1] == 286); + assert(v1[2] == 3); + + v2[0 .. $] = v1[1 .. 3]; + assert(v2[0] == 286); + assert(v2[1] == 3); + + v1[0 .. 2] = [5, 8]; + assert(v1[0] == 5); + assert(v1[1] == 8); + assert(v1[2] == 3); + } + + /** + * Returns an array used internally by the vector to store its owned elements. + * The length of the returned array may differ from the size of the allocated + * memory for the vector: the array contains only initialized elements, but + * not the reserved memory. + * + * Returns: The array with elements of this vector. + */ + inout(T[]) get() inout @trusted + { + return vector[0 .. length]; + } + + /// + unittest + { + auto v = Vector!int([1, 2, 4]); + auto data = v.get(); + + assert(data[0] == 1); + assert(data[1] == 2); + assert(data[2] == 4); + assert(data.length == 3); + + data = v[1 .. 2].get(); + assert(data[0] == 2); + assert(data.length == 1); + } + + mixin DefaultAllocator; } /// unittest { - auto v = Vector!int([5, 15, 8]); + auto v = Vector!int([5, 15, 8]); - assert(v.front == 5); - assert(v[1] == 15); - assert(v.back == 8); + assert(v.front == 5); + assert(v[1] == 15); + assert(v.back == 8); - auto r = v[]; - r[0] = 7; - assert(r.front == 7); - assert(r.front == v.front); + auto r = v[]; + r[0] = 7; + assert(r.front == 7); + assert(r.front == v.front); } @nogc unittest { - const v1 = Vector!int(); - const Vector!int v2; - const v3 = Vector!int([1, 5, 8]); - static assert(is(PointerTarget!(typeof(v3.vector)) == const(int))); + const v1 = Vector!int(); + const Vector!int v2; + const v3 = Vector!int([1, 5, 8]); + static assert(is(PointerTarget!(typeof(v3.vector)) == const(int))); } @nogc unittest { - // Test that const vectors return usable ranges. - auto v = const Vector!int([1, 2, 4]); - auto r1 = v[]; + // Test that const vectors return usable ranges. + auto v = const Vector!int([1, 2, 4]); + auto r1 = v[]; - assert(r1.back == 4); - r1.popBack(); - assert(r1.back == 2); - r1.popBack(); - assert(r1.back == 1); - r1.popBack(); - assert(r1.length == 0); + assert(r1.back == 4); + r1.popBack(); + assert(r1.back == 2); + r1.popBack(); + assert(r1.back == 1); + r1.popBack(); + assert(r1.length == 0); - static assert(!is(typeof(r1[0] = 5))); - static assert(!is(typeof(v[0] = 5))); + static assert(!is(typeof(r1[0] = 5))); + static assert(!is(typeof(v[0] = 5))); - const r2 = r1[]; - static assert(is(typeof(r2[]))); + const r2 = r1[]; + static assert(is(typeof(r2[]))); } @nogc unittest { - Vector!int v1; - const Vector!int v2; + Vector!int v1; + const Vector!int v2; - auto r1 = v1[]; - auto r2 = v1[]; + auto r1 = v1[]; + auto r2 = v1[]; - assert(r1.length == 0); - assert(r2.empty); - assert(r1 == r2); + assert(r1.length == 0); + assert(r2.empty); + assert(r1 == r2); - v1.insertBack([1, 2, 4]); - assert(v1[] == v1); - assert(v2[] == v2); - assert(v2[] != v1); - assert(v1[] != v2); - assert(v1[].equal(v1[])); - assert(v2[].equal(v2[])); - assert(!v1[].equal(v2[])); + v1.insertBack([1, 2, 4]); + assert(v1[] == v1); + assert(v2[] == v2); + assert(v2[] != v1); + assert(v1[] != v2); + assert(v1[].equal(v1[])); + assert(v2[].equal(v2[])); + assert(!v1[].equal(v2[])); } @nogc unittest { - struct MutableEqualsStruct - { - int opEquals(typeof(this) that) @nogc - { - return true; - } - } - struct ConstEqualsStruct - { - int opEquals(in typeof(this) that) const @nogc - { - return true; - } - } - auto v1 = Vector!ConstEqualsStruct(); - auto v2 = Vector!ConstEqualsStruct(); - assert(v1 == v2); - assert(v1[] == v2); - assert(v1 == v2[]); - assert(v1[].equal(v2[])); + struct MutableEqualsStruct + { + int opEquals(typeof(this) that) @nogc + { + return true; + } + } + struct ConstEqualsStruct + { + int opEquals(in typeof(this) that) const @nogc + { + return true; + } + } + auto v1 = Vector!ConstEqualsStruct(); + auto v2 = Vector!ConstEqualsStruct(); + assert(v1 == v2); + assert(v1[] == v2); + assert(v1 == v2[]); + assert(v1[].equal(v2[])); - auto v3 = const Vector!ConstEqualsStruct(); - auto v4 = const Vector!ConstEqualsStruct(); - assert(v3 == v4); - assert(v3[] == v4); - assert(v3 == v4[]); - assert(v3[].equal(v4[])); + auto v3 = const Vector!ConstEqualsStruct(); + auto v4 = const Vector!ConstEqualsStruct(); + assert(v3 == v4); + assert(v3[] == v4); + assert(v3 == v4[]); + assert(v3[].equal(v4[])); - auto v7 = Vector!MutableEqualsStruct(1, MutableEqualsStruct()); - auto v8 = Vector!MutableEqualsStruct(1, MutableEqualsStruct()); - assert(v7 == v8); - assert(v7[] == v8); - assert(v7 == v8[]); - assert(v7[].equal(v8[])); + auto v7 = Vector!MutableEqualsStruct(1, MutableEqualsStruct()); + auto v8 = Vector!MutableEqualsStruct(1, MutableEqualsStruct()); + assert(v7 == v8); + assert(v7[] == v8); + assert(v7 == v8[]); + assert(v7[].equal(v8[])); } @nogc unittest { - struct SWithDtor - { - ~this() @nogc - { - } - } - auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors. + struct SWithDtor + { + ~this() @nogc + { + } + } + auto v = Vector!SWithDtor(); // Destructor can destroy empty vectors. } unittest { - class A - { - } - A a1, a2; - auto v1 = Vector!A([a1, a2]); + class A + { + } + A a1, a2; + auto v1 = Vector!A([a1, a2]); }